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, SubHandler, 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(SubHandler):
86 handler_help = u"Manage IP routes"
88 @handler(u'Adds a route to a device')
89 def add(self, device, net_addr, prefix, gateway):
90 if not device in self.parent.devices:
91 raise DeviceNotFoundError(device)
92 r = Route(net_addr, prefix, gateway)
94 self.parent.devices[device].routes.index(r)
95 raise RouteAlreadyExistsError(net_addr + '/' + prefix + '->' + gateway)
97 self.parent.devices[device].routes.append(r)
99 @handler(u'Deletes a route from a device')
100 def delete(self, device, net_addr, prefix, gateway):
101 if not device in self.parent.devices:
102 raise DeviceNotFoundError(device)
103 r = Route(net_addr, prefix, gateway)
105 self.parent.devices[device].routes.remove(r)
107 raise RouteNotFoundError(net_addr + '/' + prefix + '->' + gateway)
109 @handler(u'Flushes routes from a device')
110 def flush(self, device):
111 if not device in self.parent.devices:
112 raise DeviceNotFoundError(device)
113 self.parent.devices[device].routes = list()
116 @handler(u'List routes')
117 def list(self, device):
119 k = self.parent.devices[device].routes.keys()
124 @handler(u'Get information about all routes')
127 k = self.parent.devices[device].routes.values()
133 class Address(Sequence):
135 def __init__(self, ip, prefix, broadcast):
138 self.broadcast = broadcast
141 return (self.ip, self.prefix, self.broadcast)
143 class AddressHandler(SubHandler):
145 handler_help = u"Manage IP addresses"
147 @handler(u'Adds an address to a device')
148 def add(self, device, ip, prefix, broadcast='+'):
149 if not device in self.parent.devices:
150 raise DeviceNotFoundError(device)
151 if ip in self.parent.devices[device].addrs:
152 raise AddressAlreadyExistsError(ip)
153 self.parent.devices[device].addrs[ip] = Address(ip, prefix, broadcast)
155 @handler(u'Deletes an address from a device')
156 def delete(self, device, ip):
157 if not device in self.parent.devices:
158 raise DeviceNotFoundError(device)
159 if not ip in self.parent.devices[device].addrs:
160 raise AddressNotFoundError(ip)
161 del self.parent.devices[device].addrs[ip]
163 @handler(u'Flushes addresses from a device')
164 def flush(self, device):
165 if not device in self.parent.devices:
166 raise DeviceNotFoundError(device)
167 self.parent.devices[device].addrs = dict()
169 @handler(u'List all addresses from a device')
170 def list(self, device):
172 k = self.parent.devices[device].addrs.keys()
177 @handler(u'Get information about addresses from a device')
178 def show(self, device):
180 k = self.parent.devices[device].addrs.values()
186 class Device(Sequence):
188 def __init__(self, name, mac):
195 return (self.name, self.mac)
197 class DeviceHandler(SubHandler):
199 handler_help = u"Manage network devices"
201 def __init__(self, parent):
202 # FIXME remove templates to execute commands
203 from mako.template import Template
205 template_dir = path.join(path.dirname(__file__), 'templates')
206 dev_fn = path.join(template_dir, 'device')
207 self.device_template = Template(filename=dev_fn)
209 @handler(u'Bring the device up')
211 if name in self.devices:
212 call(self.device_template.render(dev=name, action='up'), shell=True)
214 raise DeviceNotFoundError(name)
216 @handler(u'Bring the device down')
217 def down(self, name):
218 if name in self.devices:
219 call(self.device_template.render(dev=name, action='down'), shell=True)
221 raise DeviceNotFoundError(name)
223 @handler(u'List all devices')
225 return self.devices.keys()
227 @handler(u'Get information about a device')
229 return self.devices.items()
233 p = Popen(('ip', 'link', 'list'), stdout=PIPE, close_fds=True)
234 string = p.stdout.read()
237 i = string.find('eth')
240 m = string.find('link/ether', i+4)
241 mac = string[ m+11 : m+11+17]
242 d[eth] = Device(eth, mac)
243 i = string.find('eth', m+11+17)
246 class IpHandler(Restorable, ConfigWriter, TransactionalHandler):
248 handler_help = u"Manage IP devices, addresses and routes"
250 _persistent_attrs = 'devices'
252 _restorable_defaults = dict(devices=get_devices())
254 _config_writer_files = ('device', 'ip_add', 'ip_del', 'ip_flush',
255 'route_add', 'route_del', 'route_flush')
256 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
258 def __init__(self, pickle_dir='.', config_dir='.'):
259 r"Initialize DhcpHandler object, see class documentation for details."
260 self._persistent_dir = pickle_dir
261 self._config_writer_cfg_dir = config_dir
262 self._config_build_templates()
264 self.addr = AddressHandler(self)
265 self.route = RouteHandler(self)
266 self.dev = DeviceHandler(self)
268 def _write_config(self):
269 r"_write_config() -> None :: Execute all commands."
270 for device in self.devices.values():
271 call(self._render_config('route_flush', dict(dev=device.name)), shell=True)
272 call(self._render_config('ip_flush', dict(dev=device.name)), shell=True)
273 for address in device.addrs.values():
274 call(self._render_config('ip_add', dict(
277 prefix = address.prefix,
278 broadcast = address.broadcast,
281 for route in device.routes:
282 call(self._render_config('route_add', dict(
284 net_addr = route.net_addr,
285 prefix = route.prefix,
286 gateway = route.gateway,
291 if __name__ == '__main__':
294 print '----------------------'
296 ip.addr.add('eth0','192.168.0.23','24','192.168.255.255')
297 ip.addr.add('eth0','192.168.0.26','24')
299 ip.route.add('eth0','192.168.0.0','24','192.168.0.1')
300 ip.route.add('eth0','192.168.0.5','24','192.168.0.1')
302 ip.route.flush('eth0')
304 ip.addr.delete('eth0','192.168.0.23')