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.service.util import Restorable, ConfigWriter, InitdHandler, \
9 TransactionalHandler, SubHandler, call, \
10 get_network_devices, ListComposedSubHandler, \
11 DictComposedSubHandler, ExecutionError
13 __all__ = ('QoSHandler')
16 class DeviceError(HandlerError):
18 def __init__(self, dev):
19 self.message = u'Devive error : "%s"' % dev
22 class DeviceNotFoundError(DeviceError):
24 def __init__(self, dev):
25 self.message = u'Device not found : "%s"' % dev
28 class ClassError(HandlerError):
30 def __init__(self, cls):
31 self.message = u'Class error : "%s"' % cls
34 class ClassNotFoundError(ClassError):
36 def __init__(self, cls):
37 self.message = u'Class not found : "%s"' % cls
40 class ClassAlreadyExistsError(ClassError):
42 def __init__(self, cls):
43 self.message = u'Class already exists : "%s"' % cls
46 class HostError(HandlerError):
48 def __init__(self, host):
49 self.message = u'Host error : "%s"' % host
52 class HostNotFoundError(HostError):
54 def __init__(self, ip):
55 self.message = u'Host not found : "%s"' % host
58 class HostAlreadyExistsError(HostError):
60 def __init__(self, ip):
61 self.message = u'Host already exists : "%s"' % host
64 class Class(Sequence):
66 def __init__(self, cid, rate=None):
72 return (self.cid, self.rate)
74 def __cmp__(self, other):
75 if self.cid == other.cid:
77 return cmp(id(self), id(other))
80 class ClassHandler(Handler):
82 def __init__(self, parent):
85 @handler('Adds a class : add <id> <device> <rate>')
86 def add(self, dev, cid, rate):
87 if not dev in self.parent.devices:
88 raise DeviceNotFoundError(dev)
91 self.parent.devices[dev].classes[cid] = Class(cid, rate)
93 raise ClassAlreadyExistsError(cid + ' -> ' + dev)
95 @handler(u'Deletes a class : delete <id> <device>')
96 def delete(self, dev, cid):
97 if not dev in self.parent.devices:
98 raise DeviceNotFoundError(dev)
101 del self.parent.devices[dev].classes[cid]
103 raise ClassNotFoundError(cid + ' -> ' + dev)
105 @handler(u'Lists classes : list <dev>')
108 k = self.parent.devices[dev].classes.items()
114 class Host(Sequence):
116 def __init__(self, ip):
122 def __cmp__(self, other):
123 if self.ip == other.ip:
125 return cmp(id(self), id(other))
128 class HostHandler(SubHandler):
130 def __init__(self, parent):
133 @handler('Adds a host to a class : add <device> <class id> <ip>')
134 def add(self, dev, cid, ip):
135 if not dev in self.parent.devices:
136 raise DeviceNotFoundError(dev)
138 if not cid in self.parent.devices[dev].classes:
139 raise ClassNotFoundError(cid)
142 self.parent.devices[dev].classes[cid].hosts[ip] = Host(ip)
144 raise HostAlreadyExistsError(h + ' -> ' + dev)
146 @handler(u'Lists hosts : list <dev> <class id>')
147 def list(self, dev, cid):
149 k = self.parent.devices[dev].classes[cid].hosts.keys()
155 class Device(Sequence):
157 def __init__(self, name, mac):
160 self.classes = dict()
163 return (self.name, self.mac)
166 class DeviceHandler(SubHandler):
168 handler_help = u"Manage network devices"
170 def __init__(self, parent):
171 # FIXME remove templates to execute commands
172 from mako.template import Template
174 template_dir = path.join(path.dirname(__file__), 'templates')
175 dev_fn = path.join(template_dir, 'device')
176 self.device_template = Template(filename=dev_fn)
178 @handler(u'Bring the device up')
180 if name in self.parent.devices:
182 call(self.device_template.render(dev=name, action='add'), shell=True)
183 except ExecutionError:
186 raise DeviceNotFoundError(name)
188 @handler(u'Bring the device down')
189 def down(self, name):
190 if name in self.parent.devices:
192 call(self.device_template.render(dev=name, action='del'), shell=True)
193 except ExecutionError:
196 raise DeviceNotFoundError(name)
198 @handler(u'List all devices')
200 return self.parent.devices.keys()
202 @handler(u'Get information about a device')
204 return self.parent.devices.items()
207 class QoSHandler(Restorable, ConfigWriter, TransactionalHandler):
209 handler_help = u"Manage QoS devices, classes and hosts"
211 _persistent_attrs = ('devices')
213 _restorable_defaults = dict(
214 devices=dict((dev, Device(dev, mac))
215 for (dev, mac) in get_network_devices().items())
218 _config_writer_files = ('device', 'class_add', 'class_del', 'host_add')
220 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
222 def __init__(self, pickle_dir='.', config_dir='.'):
223 r"Initialize QoSHandler object, see class documentation for details."
224 self._persistent_dir = pickle_dir
225 self._config_writer_cfg_dir = config_dir
226 self._config_build_templates()
229 self.dev = DeviceHandler(self)
230 self.cls = ClassHandler(self)
231 self.host = HostHandler(self)
233 def _write_config(self):
234 r"_write_config() -> None :: Execute all commands."
235 for device in self.devices.values():
237 call(self._render_config('device', dict(dev=device.name, action='del')), shell=True)
238 except ExecutionError:
242 call(self._render_config('device', dict(dev=device.name, action='add')), shell=True)
243 except ExecutionError:
246 for cls in device.classes.values():
248 call(self._render_config('class_add', dict(
254 except ExecutionError:
257 for host in cls.hosts.values():
259 call(self._render_config('host_add', dict(
265 except ExecutionError:
268 def handle_timer(self):
269 self.refresh_devices()
271 def refresh_devices(self):
272 devices = get_network_devices()
273 #add not registered devices
274 for k, v in devices.items():
275 if k not in self.devices:
276 self.devices[k] = Device(k, v)
278 for k in self.devices.keys():
283 if __name__ == '__main__':
286 print '----------------------'