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', 'get_service')
16 def get_service(config):
17 return QoSHandler(config.qos.pickle_dir, config.qos.config_dir)
20 class DeviceError(HandlerError):
22 def __init__(self, dev):
23 self.message = u'Devive error : "%s"' % dev
26 class DeviceNotFoundError(DeviceError):
28 def __init__(self, dev):
29 self.message = u'Device not found : "%s"' % dev
32 class ClassError(HandlerError):
34 def __init__(self, cls):
35 self.message = u'Class error : "%s"' % cls
38 class ClassNotFoundError(ClassError):
40 def __init__(self, cls):
41 self.message = u'Class not found : "%s"' % cls
44 class ClassAlreadyExistsError(ClassError):
46 def __init__(self, cls):
47 self.message = u'Class already exists : "%s"' % cls
50 class HostError(HandlerError):
52 def __init__(self, host):
53 self.message = u'Host error : "%s"' % host
56 class HostNotFoundError(HostError):
58 def __init__(self, ip):
59 self.message = u'Host not found : "%s"' % host
62 class HostAlreadyExistsError(HostError):
64 def __init__(self, ip):
65 self.message = u'Host already exists : "%s"' % host
68 class Class(Sequence):
70 def __init__(self, cid, rate=None):
76 return (self.cid, self.rate)
78 def __cmp__(self, other):
79 if self.cid == other.cid:
81 return cmp(id(self), id(other))
84 class ClassHandler(Handler):
86 def __init__(self, parent):
89 @handler('Adds a class : add <id> <device> <rate>')
90 def add(self, dev, cid, rate):
91 if not dev in self.parent.devices:
92 raise DeviceNotFoundError(dev)
95 self.parent.devices[dev].classes[cid] = Class(cid, rate)
97 raise ClassAlreadyExistsError(cid + ' -> ' + dev)
99 @handler(u'Deletes a class : delete <id> <device>')
100 def delete(self, dev, cid):
101 if not dev in self.parent.devices:
102 raise DeviceNotFoundError(dev)
105 del self.parent.devices[dev].classes[cid]
107 raise ClassNotFoundError(cid + ' -> ' + dev)
109 @handler(u'Lists classes : list <dev>')
112 k = self.parent.devices[dev].classes.items()
118 class Host(Sequence):
120 def __init__(self, ip):
126 def __cmp__(self, other):
127 if self.ip == other.ip:
129 return cmp(id(self), id(other))
132 class HostHandler(SubHandler):
134 def __init__(self, parent):
137 @handler('Adds a host to a class : add <device> <class id> <ip>')
138 def add(self, dev, cid, ip):
139 if not dev in self.parent.devices:
140 raise DeviceNotFoundError(dev)
142 if not cid in self.parent.devices[dev].classes:
143 raise ClassNotFoundError(cid)
146 self.parent.devices[dev].classes[cid].hosts[ip] = Host(ip)
148 raise HostAlreadyExistsError(h + ' -> ' + dev)
150 @handler(u'Lists hosts : list <dev> <class id>')
151 def list(self, dev, cid):
153 k = self.parent.devices[dev].classes[cid].hosts.keys()
159 class Device(Sequence):
161 def __init__(self, name, mac):
164 self.classes = dict()
167 return (self.name, self.mac)
170 class DeviceHandler(SubHandler):
172 handler_help = u"Manage network devices"
174 def __init__(self, parent):
175 # FIXME remove templates to execute commands
176 from mako.template import Template
178 template_dir = path.join(path.dirname(__file__), 'templates')
179 dev_fn = path.join(template_dir, 'device')
180 self.device_template = Template(filename=dev_fn)
182 @handler(u'Bring the device up')
184 if name in self.parent.devices:
186 call(self.device_template.render(dev=name, action='add'), shell=True)
187 except ExecutionError:
190 raise DeviceNotFoundError(name)
192 @handler(u'Bring the device down')
193 def down(self, name):
194 if name in self.parent.devices:
196 call(self.device_template.render(dev=name, action='del'), shell=True)
197 except ExecutionError:
200 raise DeviceNotFoundError(name)
202 @handler(u'List all devices')
204 return self.parent.devices.keys()
206 @handler(u'Get information about a device')
208 return self.parent.devices.items()
211 class QoSHandler(Restorable, ConfigWriter, TransactionalHandler):
213 handler_help = u"Manage QoS devices, classes and hosts"
215 _persistent_attrs = ('devices')
217 _restorable_defaults = dict(
218 devices=dict((dev, Device(dev, mac))
219 for (dev, mac) in get_network_devices().items())
222 _config_writer_files = ('device', 'class_add', 'class_del', 'host_add')
224 _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
226 def __init__(self, pickle_dir='.', config_dir='.'):
227 r"Initialize QoSHandler object, see class documentation for details."
228 self._persistent_dir = pickle_dir
229 self._config_writer_cfg_dir = config_dir
230 self._config_build_templates()
233 self.dev = DeviceHandler(self)
234 self.cls = ClassHandler(self)
235 self.host = HostHandler(self)
237 def _write_config(self):
238 r"_write_config() -> None :: Execute all commands."
239 for device in self.devices.values():
241 call(self._render_config('device', dict(dev=device.name, action='del')), shell=True)
242 except ExecutionError:
246 call(self._render_config('device', dict(dev=device.name, action='add')), shell=True)
247 except ExecutionError:
250 for cls in device.classes.values():
252 call(self._render_config('class_add', dict(
258 except ExecutionError:
261 for host in cls.hosts.values():
263 call(self._render_config('host_add', dict(
269 except ExecutionError:
272 def handle_timer(self):
273 self.refresh_devices()
275 def refresh_devices(self):
276 devices = get_network_devices()
277 #add not registered devices
278 for k, v in devices.items():
279 if k not in self.devices:
280 self.devices[k] = Device(k, v)
282 for k in self.devices.keys():
287 if __name__ == '__main__':
290 print '----------------------'