1 # vim: set encoding=utf-8 et sw=4 sts=4 :
3 from subprocess import Popen, PIPE
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, call
11 __ALL__ = ('IpHandler', 'Error','DeviceError', 'DeviceNotFoundError',
12 'RouteError', 'RouteNotFoundError', 'RouteAlreadyExistsError',
13 'AddressError', 'AddressNotFoundError', 'AddressAlreadyExistsError')
15 class Error(HandlerError):
17 Error(command) -> Error instance :: Base IpHandler exception class.
19 All exceptions raised by the IpHandler inherits from this one, so you can
20 easily catch any IpHandler exception.
22 message - A descriptive error message.
26 class DeviceError(Error):
28 def __init__(self, device):
29 self.message = u'Device error : "%s"' % device
31 class DeviceNotFoundError(DeviceError):
33 def __init__(self, device):
34 self.message = u'Device not found : "%s"' % device
36 class AddressError(Error):
38 def __init__(self, addr):
39 self.message = u'Address error : "%s"' % addr
41 class AddressNotFoundError(AddressError):
43 def __init__(self, address):
44 self.message = u'Address not found : "%s"' % address
46 class AddressAlreadyExistsError(AddressError):
48 def __init__(self, address):
49 self.message = u'Address already exists : "%s"' % address
51 class RouteError(Error):
53 def __init__(self, route):
54 self.message = u'Route error : "%s"' % route
56 class RouteNotFoundError(RouteError):
58 def __init__(self, route):
59 self.message = u'Route not found : "%s"' % route
61 class RouteAlreadyExistsError(RouteError):
63 def __init__(self, route):
64 self.message = u'Route already exists : "%s"' % route
67 class Route(Sequence):
69 def __init__(self, net_addr, prefix, gateway):
70 self.net_addr = net_addr
72 self.gateway = gateway
75 return(self.addr, self.prefix, self.gateway)
77 def __cmp__(self, other):
78 if self.net_addr == other.net_addr \
79 and self.prefix == other.prefix \
80 and self.gateway == other.gateway:
82 return cmp(id(self), id(other))
84 class RouteHandler(Handler):
86 handler_help = u"Manage IP routes"
88 def __init__(self, parent):
91 @handler(u'Adds a route to a device')
92 def add(self, device, net_addr, prefix, gateway):
93 if not device in self.parent.devices:
94 raise DeviceNotFoundError(device)
95 r = Route(net_addr, prefix, gateway)
97 self.parent.devices[device].routes.index(r)
98 raise RouteAlreadyExistsError(net_addr + '/' + prefix + '->' + gateway)
100 self.parent.devices[device].routes.append(r)
102 @handler(u'Deletes a route from a device')
103 def delete(self, device, net_addr, prefix, gateway):
104 if not device in self.parent.devices:
105 raise DeviceNotFoundError(device)
106 r = Route(net_addr, prefix, gateway)
108 self.parent.devices[device].routes.remove(r)
110 raise RouteNotFoundError(net_addr + '/' + prefix + '->' + gateway)
112 @handler(u'Flushes routes from a device')
113 def flush(self, device):
114 if not device in self.parent.devices:
115 raise DeviceNotFoundError(device)
116 self.parent.devices[device].routes = list()
119 @handler(u'List routes')
120 def list(self, device):
122 k = self.parent.devices[device].routes.keys()
127 @handler(u'Get information about all routes')
130 k = self.parent.devices[device].routes.values()
136 class Address(Sequence):
138 def __init__(self, ip, prefix, broadcast):
141 self.broadcast = broadcast
144 return (self.ip, self.prefix, self.broadcast)
146 class AddressHandler(Handler):
148 handler_help = u"Manage IP addresses"
150 def __init__(self, parent):
153 @handler(u'Adds an address to a device')
154 def add(self, device, ip, prefix, broadcast='+'):
155 if not device in self.parent.devices:
156 raise DeviceNotFoundError(device)
157 if ip in self.parent.devices[device].addrs:
158 raise AddressAlreadyExistsError(ip)
159 self.parent.devices[device].addrs[ip] = Address(ip, prefix, broadcast)
161 @handler(u'Deletes an address from a device')
162 def delete(self, device, ip):
163 if not device in self.parent.devices:
164 raise DeviceNotFoundError(device)
165 if not ip in self.parent.devices[device].addrs:
166 raise AddressNotFoundError(ip)
167 del self.parent.devices[device].addrs[ip]
169 @handler(u'Flushes addresses from a device')
170 def flush(self, device):
171 if not device in self.parent.devices:
172 raise DeviceNotFoundError(device)
173 self.parent.devices[device].addrs = dict()
175 @handler(u'List all addresses from a device')
176 def list(self, device):
178 k = self.parent.devices[device].addrs.keys()
183 @handler(u'Get information about addresses from a device')
184 def show(self, device):
186 k = self.parent.devices[device].addrs.values()
192 class Device(Sequence):
194 def __init__(self, name, mac):
201 return (self.name, self.mac)
203 class DeviceHandler(Handler):
205 handler_help = u"Manage network devices"
207 def __init__(self, parent):
208 # FIXME remove templates to execute commands
209 from mako.template import Template
211 template_dir = path.join(path.dirname(__file__), 'templates')
212 dev_fn = path.join(template_dir, 'device')
213 self.device_template = Template(filename=dev_fn)
215 @handler(u'Bring the device up')
217 if name in self.devices:
218 call(self.device_template.render(dev=name, action='up'), shell=True)
220 raise DeviceNotFoundError(name)
222 @handler(u'Bring the device down')
223 def down(self, name):
224 if name in self.devices:
225 call(self.device_template.render(dev=name, action='down'), shell=True)
227 raise DeviceNotFoundError(name)
229 @handler(u'List all devices')
231 return self.devices.keys()
233 @handler(u'Get information about a device')
235 return self.devices.items()
239 p = Popen(('ip', 'link', 'list'), stdout=PIPE, close_fds=True)
240 string = p.stdout.read()
243 i = string.find('eth')
246 m = string.find('link/ether', i+4)
247 mac = string[ m+11 : m+11+17]
248 d[eth] = Device(eth, mac)
249 i = string.find('eth', m+11+17)
252 class IpHandler(Restorable, ConfigWriter, TransactionalHandler):
254 handler_help = u"Manage IP devices, addresses and routes"
256 _persistent_attrs = 'devices'
258 _restorable_defaults = dict(devices=get_devices())
260 _config_writer_files = ('device', 'ip_add', 'ip_del', 'ip_flush',
261 'route_add', 'route_del', 'route_flush')
262 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
264 def __init__(self, pickle_dir='.', config_dir='.'):
265 r"Initialize DhcpHandler object, see class documentation for details."
266 self._persistent_dir = pickle_dir
267 self._config_writer_cfg_dir = config_dir
268 self._config_build_templates()
270 self.addr = AddressHandler(self)
271 self.route = RouteHandler(self)
272 self.dev = DeviceHandler(self)
274 def _write_config(self):
275 r"_write_config() -> None :: Execute all commands."
276 for device in self.devices.values():
277 call(self._render_config('route_flush', dict(dev=device.name)), shell=True)
278 call(self._render_config('ip_flush', dict(dev=device.name)), shell=True)
279 for address in device.addrs.values():
280 call(self._render_config('ip_add', dict(
283 prefix = address.prefix,
284 broadcast = address.broadcast,
287 for route in device.routes:
288 call(self._render_config('route_add', dict(
290 net_addr = route.net_addr,
291 prefix = route.prefix,
292 gateway = route.gateway,
297 if __name__ == '__main__':
300 print '----------------------'
302 ip.addr.add('eth0','192.168.0.23','24','192.168.255.255')
303 ip.addr.add('eth0','192.168.0.26','24')
305 ip.route.add('eth0','192.168.0.0','24','192.168.0.1')
306 ip.route.add('eth0','192.168.0.5','24','192.168.0.1')
308 ip.route.flush('eth0')
310 ip.addr.delete('eth0','192.168.0.23')