1 # vim: set encoding=utf-8 et sw=4 sts=4 :
3 from mako.template import Template
4 from mako.runtime import Context
5 from subprocess import Popen, PIPE
8 import cPickle as pickle
13 from seqtools import Sequence
18 from dispatcher import handler, HandlerError
21 class HandlerError(RuntimeError): pass
22 def handler(f): return f
24 __ALL__ = ('IpHandler',)
27 pickle_devices = 'devs'
29 template_dir = path.join(path.dirname(__file__), 'templates')
30 command_filename = 'command'
33 device_com = 'device.command'
34 ip_add_com = 'ip_add.command'
35 ip_del_com = 'ip_del.command'
36 ip_flush_com = 'ip_flush.command'
37 route_add_com = 'route_add.command'
38 route_del_com = 'route_del.command'
39 route_flush_com = 'route_flush.command'
41 class Error(HandlerError):
43 Error(command) -> Error instance :: Base IpHandler exception class.
45 All exceptions raised by the IpHandler inherits from this one, so you can
46 easily catch any IpHandler exception.
48 message - A descriptive error message.
51 def __init__(self, message):
52 r"Initialize the Error object. See class documentation for more info."
53 self.message = message
58 class DeviceError(Error):
60 def __init__(self, device):
61 self.message = 'Device error : "%s"' % device
63 class DeviceNotFoundError(DeviceError):
65 def __init__(self, device):
66 self.message = 'Device not found : "%s"' % device
68 class AddressError(Error):
70 def __init__(self, addr):
71 self.message = 'Address error : "%s"' % addr
73 class AddressNotFoundError(AddressError):
75 def __init__(self, address):
76 self.message = 'Address not found : "%s"' % address
78 class AddressAlreadyExistsError(AddressError):
80 def __init__(self, address):
81 self.message = 'Address already exists : "%s"' % address
83 class RouteError(Error):
85 def __init__(self, route):
86 self.message = 'Route error : "%s"' % route
88 class RouteNotFoundError(RouteError):
90 def __init__(self, route):
91 self.message = 'Route not found : "%s"' % route
93 class RouteAlreadyExistsError(RouteError):
95 def __init__(self, route):
96 self.message = 'Route already exists : "%s"' % route
98 class Route(Sequence):
100 def __init__(self, net_addr, prefix, gateway):
101 self.net_addr = net_addr
103 self.gateway = gateway
106 return(self.addr, self.prefix, self.gateway)
108 def __cmp__(self, other):
109 if self.net_addr == other.net_addr \
110 and self.prefix == other.prefix \
111 and self.gateway == other.gateway:
113 return cmp(id(self), id(other))
117 def __init__(self, devices):
118 self.devices = devices
121 def add(self, device, net_addr, prefix, gateway):
122 if not device in self.devices:
123 raise DeviceNotFoundError(device)
124 r = Route(net_addr, prefix, gateway)
126 self.devices[device].routes.index(r)
127 raise RouteAlreadyExistsError(net_addr + '/' + prefix + '->' + gateway)
129 self.devices[device].routes.append(r)
132 def delete(self, device, net_addr, prefix, gateway):
133 if not device in self.devices:
134 raise DeviceNotFoundError(device)
135 r = Route(net_addr, prefix, gateway)
137 self.devices[device].routes.remove(r)
139 raise RouteNotFoundError(net_addr + '/' + prefix + '->' + gateway)
142 def flush(self, device):
143 if not device in self.devices:
144 raise DeviceNotFoundError(device)
145 self.devices[device].routes = list()
149 def list(self, device):
151 k = self.devices[device].routes.keys()
159 k = self.devices[device].routes.values()
164 class Address(Sequence):
166 def __init__(self, ip, prefix, broadcast):
169 self.broadcast = broadcast
172 return (self.ip, self.prefix, self.broadcast)
174 class AddressHandler:
176 def __init__(self, devices):
177 self.devices = devices
180 def add(self, device, ip, prefix, broadcast='+'):
181 if not device in self.devices:
182 raise DeviceNotFoundError(device)
183 if ip in self.devices[device].addrs:
184 raise AddressAlreadyExistsError(ip)
185 self.devices[device].addrs[ip] = Address(ip, prefix, broadcast)
188 def delete(self, device, ip):
189 if not device in self.devices:
190 raise DeviceNotFoundError(device)
191 if not ip in self.devices[device].addrs:
192 raise AddressNotFoundError(ip)
193 del self.devices[device].addrs[ip]
196 def flush(self, device):
197 if not device in self.devices:
198 raise DeviceNotFoundError(device)
199 self.devices[device].addrs = dict()
202 def list(self, device):
204 k = self.devices[device].addrs.keys()
210 def show(self, device):
212 k = self.devices[device].addrs.values()
217 class Device(Sequence):
219 def __init__(self, name, mac):
226 return (self.name, self.mac)
230 def __init__(self, devices):
231 self.devices = devices
232 dev_fn = path.join(template_dir, device_com)
233 self.device_template = Template(filename=dev_fn)
237 if name in self.devices:
238 print self.device_template.render(dev=name, action='up')
240 raise DeviceNotFoundError(name)
243 def down(self, name):
244 if name in self.devices:
245 print self.device_template.render(dev=name, action='down')
247 raise DeviceNotFoundError(name)
251 return self.devices.keys()
255 return self.devices.items()
259 def __init__(self, pickle_dir='.', config_dir='.'):
260 r"Initialize DhcpHandler object, see class documentation for details."
262 self.pickle_dir = pickle_dir
263 self.config_dir = config_dir
265 ip_add_fn = path.join(template_dir, ip_add_com)
266 ip_del_fn = path.join(template_dir, ip_del_com)
267 ip_flush_fn = path.join(template_dir, ip_flush_com)
268 self.ip_add_template = Template(filename=ip_add_fn)
269 self.ip_del_template = Template(filename=ip_del_fn)
270 self.ip_flush_template = Template(filename=ip_flush_fn)
272 route_add_fn = path.join(template_dir, route_add_com)
273 route_del_fn = path.join(template_dir, route_del_com)
274 route_flush_fn = path.join(template_dir, route_flush_com)
275 self.route_add_template = Template(filename=route_add_fn)
276 self.route_del_template = Template(filename=route_del_fn)
277 self.route_flush_template = Template(filename=route_flush_fn)
282 p = Popen('ip link list', shell=True, stdout=PIPE, close_fds=True)
283 devs = _get_devices(p.stdout.read())
284 self.devices = dict()
285 for eth, mac in devs.values():
286 self.devices[eth] = Device(eth, mac)
288 self.addr = AddressHandler(self.devices)
289 self.route = RouteHandler(self.devices)
290 self.dev = DeviceHandler(self.devices)
295 r"commit() -> None :: Commit the changes and reload the DHCP service."
296 #esto seria para poner en una interfaz
297 #y seria que hace el pickle deberia llamarse
298 #al hacerse un commit
304 r"rollback() -> None :: Discard the changes not yet commited."
308 r"_dump() -> None :: Dump all persistent data to pickle files."
309 # XXX podría ir en una clase base
310 self._dump_var(self.devices, pickle_devices)
314 r"_load() -> None :: Load all persistent data from pickle files."
315 # XXX podría ir en una clase base
316 self.devices = self._load_var(pickle_devices)
318 def _pickle_filename(self, name):
319 r"_pickle_filename() -> string :: Construct a pickle filename."
320 # XXX podría ir en una clase base
321 return path.join(self.pickle_dir, name) + pickle_ext
323 def _dump_var(self, var, name):
324 r"_dump_var() -> None :: Dump a especific variable to a pickle file."
325 # XXX podría ir en una clase base
326 pkl_file = file(self._pickle_filename(name), 'wb')
327 pickle.dump(var, pkl_file, 2)
330 def _load_var(self, name):
331 r"_load_var()7 -> object :: Load a especific pickle file."
332 # XXX podría ir en una clase base
333 return pickle.load(file(self._pickle_filename(name)))
335 def _write_config(self):
336 r"_write_config() -> None :: Execute all commands."
337 for device in self.devices.values():
338 print self.route_flush_template.render(dev=device.name)
339 print self.ip_flush_template.render(dev=device.name)
340 for address in device.addrs.values():
341 print self.ip_add_template.render(
344 prefix=address.prefix,
345 broadcast=address.broadcast
347 for route in device.routes:
348 print self.route_add_template.render(
350 net_addr=route.net_addr,
352 gateway=route.gateway
355 def _get_devices(string):
357 i = string.find('eth')
360 m = string.find('link/ether', i+4)
361 mac = string[ m+11 : m+11+17]
363 i = string.find('eth', m+11+17)
366 if __name__ == '__main__':
369 print '----------------------'
371 ip.addr.add('eth0','192.168.0.23','24','192.168.255.255')
372 ip.addr.add('eth0','192.168.0.26','24')
374 ip.route.add('eth0','192.168.0.0','24','192.168.0.1')
375 ip.route.add('eth0','192.168.0.5','24','192.168.0.1')
377 ip.route.flush('eth0')
379 ip.addr.delete('eth0','192.168.0.23')