]> git.llucax.com Git - software/pymin.git/blob - services/ip/__init__.py
Se modifica el comando ip.
[software/pymin.git] / services / ip / __init__.py
1 # vim: set encoding=utf-8 et sw=4 sts=4 :
2
3 from mako.template import Template
4 from mako.runtime import Context
5 from subprocess import Popen, PIPE
6 from os import path
7 try:
8     import cPickle as pickle
9 except ImportError:
10     import pickle
11
12 try:
13     from seqtools import Sequence
14 except ImportError:
15     # NOP for testing
16     class Sequence: pass
17 try:
18     from dispatcher import handler, HandlerError
19 except ImportError:
20     # NOP for testing
21     class HandlerError(RuntimeError): pass
22     def handler(f): return f
23
24 __ALL__ = ('IpHandler',)
25
26 pickle_ext = '.pkl'
27 pickle_devices = 'devs'
28
29 template_dir = path.join(path.dirname(__file__), 'templates')
30 command_filename = 'command'
31
32
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'
40
41 class Error(HandlerError):
42     r"""
43     Error(command) -> Error instance :: Base IpHandler exception class.
44
45     All exceptions raised by the IpHandler inherits from this one, so you can
46     easily catch any IpHandler exception.
47
48     message - A descriptive error message.
49     """
50
51     def __init__(self, message):
52         r"Initialize the Error object. See class documentation for more info."
53         self.message = message
54
55     def __str__(self):
56         return self.message
57
58 class DeviceError(Error):
59
60     def __init__(self, device):
61         self.message = 'Device error : "%s"' % device
62
63 class DeviceNotFoundError(DeviceError):
64
65     def __init__(self, device):
66         self.message = 'Device not found : "%s"' % device
67
68 class AddressError(Error):
69
70     def __init__(self, addr):
71         self.message = 'Address error : "%s"' % addr
72
73 class AddressNotFoundError(AddressError):
74
75     def __init__(self, address):
76         self.message = 'Address not found : "%s"' % address
77
78 class AddressAlreadyExistsError(AddressError):
79
80     def __init__(self, address):
81         self.message = 'Address already exists : "%s"' % address
82
83 class RouteError(Error):
84
85     def __init__(self, route):
86         self.message = 'Route error : "%s"' % route
87
88 class RouteNotFoundError(RouteError):
89
90     def __init__(self, route):
91         self.message = 'Route not found : "%s"' % route
92
93 class RouteAlreadyExistsError(RouteError):
94
95     def __init__(self, route):
96         self.message = 'Route already exists : "%s"' % route
97
98 class Route(Sequence):
99
100     def __init__(self, net_addr, prefix, gateway):
101         self.net_addr = net_addr
102         self.prefix = prefix
103         self.gateway = gateway
104
105     def as_tuple(self):
106         return(self.addr, self.prefix, self.gateway)
107
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:
112             return 0
113         return cmp(id(self), id(other))
114
115 class RouteHandler:
116
117     def __init__(self, devices):
118         self.devices = devices
119
120     @handler
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)
125         try:
126             self.devices[device].routes.index(r)
127             raise RouteAlreadyExistsError(net_addr + '/' + prefix + '->' + gateway)
128         except ValueError:
129             self.devices[device].routes.append(r)
130
131     @handler
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)
136         try:
137             self.devices[device].routes.remove(r)
138         except ValueError:
139             raise RouteNotFoundError(net_addr + '/' + prefix + '->' + gateway)
140
141     @handler
142     def flush(self, device):
143         if not device in self.devices:
144             raise DeviceNotFoundError(device)
145         self.devices[device].routes = list()
146
147
148     @handler
149     def list(self, device):
150         try:
151             k = self.devices[device].routes.keys()
152         except ValueError:
153             k = list()
154         return k
155
156     @handler
157     def show(self):
158         try:
159             k = self.devices[device].routes.values()
160         except ValueError:
161             k = list()
162         return k
163
164 class Address(Sequence):
165
166     def __init__(self, ip, prefix, broadcast):
167         self.ip = ip
168         self.prefix = prefix
169         self.broadcast = broadcast
170
171     def as_tuple(self):
172         return (self.ip, self.prefix, self.broadcast)
173
174 class AddressHandler:
175
176     def __init__(self, devices):
177         self.devices = devices
178
179     @handler
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)
186
187     @handler
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]
194
195     @handler
196     def flush(self, device):
197         if not device in self.devices:
198             raise DeviceNotFoundError(device)
199         self.devices[device].addrs = dict()
200
201     @handler
202     def list(self, device):
203         try:
204             k = self.devices[device].addrs.keys()
205         except ValueError:
206             k = list()
207         return k
208
209     @handler
210     def show(self, device):
211         try:
212             k = self.devices[device].addrs.values()
213         except ValueError:
214             k = list()
215         return k
216
217 class Device(Sequence):
218
219     def __init__(self, name, mac):
220         self.name = name
221         self.mac = mac
222         self.addrs = dict()
223         self.routes = list()
224
225     def as_tuple(self):
226         return (self.name, self.mac)
227
228 class DeviceHandler:
229
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)
234
235     @handler
236     def up(self, name):
237         if name in self.devices:
238             print self.device_template.render(dev=name, action='up')
239         else:
240             raise DeviceNotFoundError(name)
241
242     @handler
243     def down(self, name):
244         if name in self.devices:
245             print self.device_template.render(dev=name, action='down')
246         else:
247             raise DeviceNotFoundError(name)
248
249     @handler
250     def list(self):
251         return self.devices.keys()
252
253     @handler
254     def show(self):
255         return self.devices.items()
256
257 class IpHandler:
258
259     def __init__(self, pickle_dir='.', config_dir='.'):
260         r"Initialize DhcpHandler object, see class documentation for details."
261
262         self.pickle_dir = pickle_dir
263         self.config_dir = config_dir
264
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)
271
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)
278
279         try:
280             self._load()
281         except IOError:
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)
287             self._dump()
288         self.addr = AddressHandler(self.devices)
289         self.route = RouteHandler(self.devices)
290         self.dev = DeviceHandler(self.devices)
291         self.commit()
292
293     @handler
294     def commit(self):
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
299         self._dump()
300         self._write_config()
301
302     @handler
303     def rollback(self):
304         r"rollback() -> None :: Discard the changes not yet commited."
305         self._load()
306
307     def _dump(self):
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)
311
312
313     def _load(self):
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)
317
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
322
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)
328         pkl_file.close()
329
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)))
334
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(
342                     dev=device.name,
343                     addr=address.ip,
344                     prefix=address.prefix,
345                     broadcast=address.broadcast
346                     )
347             for route in device.routes:
348                 print self.route_add_template.render(
349                     dev=device.name,
350                     net_addr=route.net_addr,
351                     prefix=route.prefix,
352                     gateway=route.gateway
353                     )
354
355     def _get_devices(string):
356        l = list()
357        i = string.find('eth')
358        while i != -1:
359            eth = string[i:i+4]
360            m = string.find('link/ether', i+4)
361            mac = string[ m+11 : m+11+17]
362            l.append((eth,mac))
363            i = string.find('eth', m+11+17)
364        return l
365
366 if __name__ == '__main__':
367
368     ip = IpHandler()
369     print '----------------------'
370     ip.dev.up('eth0')
371     ip.addr.add('eth0','192.168.0.23','24','192.168.255.255')
372     ip.addr.add('eth0','192.168.0.26','24')
373     ip.commit()
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')
376     ip.commit()
377     ip.route.flush('eth0')
378     ip.commit()
379     ip.addr.delete('eth0','192.168.0.23')
380     ip.commit()
381
382
383
384