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