]> git.llucax.com Git - software/pymin.git/blob - pymin/services/ip/__init__.py
Merge branch 'master' of baryon.com.ar:/home/luca/pymin
[software/pymin.git] / pymin / services / ip / __init__.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 from subprocess import Popen, PIPE
4 from os import path
5
6 from pymin.seqtools import Sequence
7 from pymin.dispatcher import handler, HandlerError, Handler
8 from pymin.services.util import Restorable, ConfigWriter, InitdHandler, \
9                                 TransactionalHandler, SubHandler, call, \
10                                 get_network_devices, ListComposedSubHandler, \
11                                 DictComposedSubHandler, Device, Address, ExecutionError
12
13 __ALL__ = ('IpHandler',)
14
15 # TODO: convertir HopHandler a ComposedSubHandler
16
17 class HopError(HandlerError):
18
19     def __init__(self, hop):
20         self.message = u'Hop error : "%s"' % hop
21
22 class HopNotFoundError(HopError):
23
24     def __init__(self, hop):
25         self.message = u'Hop not found : "%s"' % hop
26
27 class HopAlreadyExistsError(HopError):
28
29     def __init__(self, hop):
30         self.message = u'Hop already exists : "%s"' % hop
31
32
33 class Hop(Sequence):
34
35     def __init__(self, gateway, device):
36         self.gateway = gateway
37         self.device = device
38
39     def as_tuple(self):
40         return (self.gateway, self.device)
41
42     def __cmp__(self, other):
43         if self.gateway == other.gateway \
44                 and self.device == other.device:
45             return 0
46         return cmp(id(self), id(other))
47
48 class HopHandler(Handler):
49
50     def __init__(self, parent):
51         self.parent = parent
52
53     @handler('Adds a hop : add <gateway> <device>')
54     def add(self, gw, dev):
55         if not dev in self.parent.devices:
56             raise DeviceNotFoundError(device)
57         h = Hop(gw, dev)
58         try:
59             self.parent.hops.index(h)
60             raise HopAlreadyExistsError(gw  + '->' + dev)
61         except ValueError:
62             self.parent.hops.append(h)
63
64     @handler(u'Deletes a hop : delete <gateway> <device>')
65     def delete(self, gw, dev):
66         if not dev in self.parent.devices:
67             raise DeviceNotFoundError(device)
68         h = Hop(gw, dev)
69         try:
70             self.parent.hops.remove(h)
71         except ValueError:
72             raise HopNotFoundError(gw + '->' + dev)
73
74     @handler(u'Lists hops : list <dev>')
75     def list(self, device):
76         try:
77             k = self.parent.hops.keys()
78         except ValueError:
79             k = list()
80         return k
81
82     @handler(u'Get information about all hops: show <dev>')
83     def show(self, device):
84         try:
85             k = self.parent.hops.values()
86         except ValueError:
87             k = list()
88         return k
89
90 class Route(Sequence):
91     def __init__(self, net_addr, prefix, gateway):
92         self.net_addr = net_addr
93         self.prefix = prefix
94         self.gateway = gateway
95
96     def update(self, net_addr=None, prefix=None, gateway=None):
97         if net_addr is not None: self.net_addr = net_addr
98         if prefix is not None: self.prefix = prefix
99         if gateway is not None: self.gateway = gateway
100
101     def as_tuple(self):
102         return(self.net_addr, self.prefix, self.gateway)
103
104     def __cmp__(self, other):
105         if self.net_addr == other.net_addr \
106                 and self.prefix == other.prefix \
107                 and self.gateway == other.gateway:
108             return 0
109         return cmp(id(self), id(other))
110
111 class RouteHandler(ListComposedSubHandler):
112     handler_help = u"Manage IP routes"
113     _comp_subhandler_cont = 'devices'
114     _comp_subhandler_attr = 'routes'
115     _comp_subhandler_class = Route
116
117     @handler(u'Adds a route to : ip route add <net_addr> <prefix> <gateway> [device]')
118     def add(self, net_addr, prefix, gateway, dev=None):
119         if dev is not None:
120             ListComposedSubHandler.add(self, dev, net_addr, prefix, gateway)
121         else:
122             r = Route(net_addr, prefix, gateway)
123             if not r in self.parent.no_device_routes:
124                 self.parent.no_device_routes.append(r)
125
126     @handler("Deletes a route : ip route delete <route_number_in_show> [dev]")
127     def delete(self, index, dev=None):
128         if dev is not None:
129             ListComposedSubHandler.delete(self, dev, index)
130         else:
131             i = int(index)
132             del self.parent.no_device_routes[i]
133
134     @handler("Shows routes : ip route show [dev]")
135     def show(self, dev=None):
136         if dev is not None:
137             return ListComposedSubHandler.show(self, dev)
138         else:
139             return self.parent.no_device_routes
140
141 class AddressHandler(DictComposedSubHandler):
142     handler_help = u"Manage IP addresses"
143     _comp_subhandler_cont = 'devices'
144     _comp_subhandler_attr = 'addrs'
145     _comp_subhandler_class = Address
146
147
148 class DeviceHandler(SubHandler):
149
150     handler_help = u"Manage network devices"
151
152     def __init__(self, parent):
153         # FIXME remove templates to execute commands
154         from mako.template import Template
155         self.parent = parent
156         template_dir = path.join(path.dirname(__file__), 'templates')
157         dev_fn = path.join(template_dir, 'device')
158         self.device_template = Template(filename=dev_fn)
159
160     @handler(u'Bring the device up')
161     def up(self, name):
162         if name in self.parent.devices:
163             call(self.device_template.render(dev=name, action='up'), shell=True)
164             #bring up all the route asocitaed to the device
165             for route in self.parent.devices[name].routes:
166                 try:
167                     call(self.parent._render_config('route_add', dict(
168                             dev = name,
169                             net_addr = route.net_addr,
170                             prefix = route.prefix,
171                             gateway = route.gateway,
172                         )
173                     ), shell=True)
174                 except ExecutionError, e:
175                     print e
176             self.parent._bring_up_no_dev_routes()
177             self.parent._restart_services()
178         else:
179             raise DeviceNotFoundError(name)
180
181     @handler(u'Bring the device down')
182     def down(self, name):
183         if name in self.parent.devices:
184             call(self.device_template.render(dev=name, action='down'), shell=True)
185             self.parent._bring_up_no_dev_routes()
186             self.parent._restart_services()
187         else:
188             raise DeviceNotFoundError(name)
189
190     @handler(u'List all devices')
191     def list(self):
192         return self.parent.devices.keys()
193
194     @handler(u'Get information about a device')
195     def show(self):
196         return self.parent.devices.items()
197
198 class IpHandler(Restorable, ConfigWriter, TransactionalHandler):
199
200     handler_help = u"Manage IP devices, addresses, routes and hops"
201
202     _persistent_attrs = ('devices','hops','no_device_routes')
203
204     _restorable_defaults = dict(
205                             devices=get_network_devices(),
206                             hops = list(),
207                             no_device_routes = list(),
208                             )
209
210     _config_writer_files = ('device', 'ip_add', 'ip_del', 'ip_flush',
211                             'route_add', 'route_del', 'route_flush', 'hop')
212     _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
213
214     def __init__(self, pickle_dir='.', config_dir='.'):
215         r"Initialize DhcpHandler object, see class documentation for details."
216         self._persistent_dir = pickle_dir
217         self._config_writer_cfg_dir = config_dir
218         self._config_build_templates()
219         self._restore()
220         self._write_config()
221         self.addr = AddressHandler(self)
222         self.route = RouteHandler(self)
223         self.dev = DeviceHandler(self)
224         self.hop = HopHandler(self)
225         self.no_device_routes = list()
226         self.services = list()
227
228     def _write_config(self):
229         r"_write_config() -> None :: Execute all commands."
230         for device in self.devices.values():
231             if device.active:
232                 self._write_config_for_device(device)
233         self._bring_up_no_dev_routes()
234         self._write_hops()
235
236     def _bring_up_no_dev_routes(self):
237         for route in self.no_device_routes:
238             try:
239                 call(self._render_config('route_add', dict(
240                         dev = None,
241                         net_addr = route.net_addr,
242                         prefix = route.prefix,
243                         gateway = route.gateway,
244                     )
245                 ), shell=True)
246             except ExecutionError, e:
247                 print e
248
249     def _write_hops(self):
250         r"_write_hops() -> None :: Execute all hops."
251         if self.hops:
252             try:
253                 call('ip route del default', shell=True)
254             except ExecutionError, e:
255                 print e
256             try:
257                 #get hops for active devices
258                 active_hops = dict()
259                 for h in self.hops:
260                     if h.device in self.devices:
261                         if self.devices[h.device].active:
262                             active_hops.append(h)
263                 call(self._render_config('hop', dict(
264                     hops = active_hops,
265                         )
266                 ), shell=True)
267             except ExecutionError, e:
268                 print e
269
270     def _write_config_for_device(self, device):
271         r"_write_config_for_device(self, device) -> None :: Execute all commands for a device."
272         try:
273             call(self._render_config('route_flush', dict(dev=device.name)), shell=True)
274         except ExecutionError, e:
275             print e
276         try:
277             call(self._render_config('ip_flush', dict(dev=device.name)), shell=True)
278         except ExecutionError, e:
279             print e
280         for address in device.addrs.values():
281             broadcast = address.broadcast
282             if broadcast is None:
283                 broadcast = '+'
284             try:
285                 call(self._render_config('ip_add', dict(
286                     dev = device.name,
287                     addr = address.ip,
288                     netmask = address.netmask,
289                     peer = address.peer,
290                     broadcast = broadcast,
291                     )
292                 ), shell=True)
293             except ExecutionError, e:
294                 print e
295         for route in device.routes:
296             try:
297                 call(self._render_config('route_add', dict(
298                         dev = device.name,
299                         net_addr = route.net_addr,
300                         prefix = route.prefix,
301                         gateway = route.gateway,
302                     )
303                 ), shell=True)
304             except ExecutionError, e:
305                 print e
306
307     def handle_timer(self):
308         self.refresh_devices()
309
310
311     def refresh_devices(self):
312         devices = get_network_devices()
313         #add not registered and active devices
314         go_active = False
315         for k,v in devices.items():
316             if k not in self.devices:
317                 self.devices[k] = v
318             elif not self.devices[k].active:
319                 self.active = True
320                 go_active = True
321                 self._write_config_for_device(self.devices[k])
322         if go_active:
323             self._write_hops()
324             self._bring_up_no_dev_routes()
325             self._restart_services()
326
327         #mark inactive devices
328         for k in self.devices.keys():
329             go_down = False
330             if k not in devices:
331                 self.devices[k].active = False
332                 go_down = True
333             if go_down:
334                 self._bring_up_no_dev_routes()
335
336     def _restart_services(self):
337         for s in self.services:
338             if s._service_running:
339                 try:
340                      s.stop()
341                 except ExecutionError:
342                     pass
343                 try:
344                     s.start()
345                 except ExecutionError:
346                     pass
347
348         #hooks a service to the ip handler, so when
349         #a device is brought up one can restart the service
350         #that need to refresh their device list
351     def device_up_hook(self, serv):
352         if hasattr(serv, 'stop') and hasattr(serv, 'start'):
353             self.services.append(serv)
354
355
356
357
358
359 if __name__ == '__main__':
360
361     ip = IpHanlder()
362     print '----------------------'
363     ip.hop.add('201.21.32.53','eth0')
364     ip.hop.add('205.65.65.25','eth1')
365     ip.commit()
366     ip.dev.up('eth0')
367     ip.addr.add('eth0','192.168.0.23','24','192.168.255.255')
368     ip.addr.add('eth0','192.168.0.26','24')
369     ip.commit()
370     ip.route.add('eth0','192.168.0.0','24','192.168.0.1')
371     ip.route.add('eth0','192.168.0.5','24','192.168.0.1')
372     ip.commit()
373     ip.hop.delete('201.21.32.53','eth0')
374     ip.route.clear('eth0')
375     ip.commit()
376
377
378