]> git.llucax.com Git - software/pymin.git/blobdiff - services/dhcp/__init__.py
Factored out a lot of common code.
[software/pymin.git] / services / dhcp / __init__.py
index 1df0062761fe2e2ba94c1b499a1f5ed8b515b9b2..7ffadcf7a4dc424a421bb6d77fc2b71d8df8f76e 100644 (file)
@@ -1,30 +1,18 @@
 # vim: set encoding=utf-8 et sw=4 sts=4 :
 
 # vim: set encoding=utf-8 et sw=4 sts=4 :
 
-from mako.template import Template
-from mako.runtime import Context
 from os import path
 from os import path
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-try:
-    from dispatcher import handler
-except ImportError:
-    def handler(f): return f # NOP for testing
 
 
-__ALL__ = ('DhcpHandler',)
+from seqtools import Sequence
+from dispatcher import Handler, handler, HandlerError
+from services.util import Restorable, ConfigWriter
+from services.util import InitdHandler, TransactionalHandler
 
 
-pickle_ext = '.pkl'
-pickle_vars = 'vars'
-pickle_hosts = 'hosts'
+__ALL__ = ('DhcpHandler', 'Error', 'HostError', 'HostAlreadyExistsError',
+            'HostNotFoundError', 'ParameterError', 'ParameterNotFoundError')
 
 
-config_filename = 'dhcpd.conf'
-
-template_dir = path.join(path.dirname(__file__), 'templates')
-
-class Error(RuntimeError):
+class Error(HandlerError):
     r"""
     r"""
-    Error(command) -> Error instance :: Base DhcpHandler exception class.
+    Error(message) -> Error instance :: Base DhcpHandler exception class.
 
     All exceptions raised by the DhcpHandler inherits from this one, so you can
     easily catch any DhcpHandler exception.
 
     All exceptions raised by the DhcpHandler inherits from this one, so you can
     easily catch any DhcpHandler exception.
@@ -97,7 +85,7 @@ class ParameterNotFoundError(ParameterError):
         self.message = 'Parameter not found: "%s"' % paramname
 
 
         self.message = 'Parameter not found: "%s"' % paramname
 
 
-class Host:
+class Host(Sequence):
     r"""Host(name, ip, mac) -> Host instance :: Class representing a host.
 
     name - Host name, should be a fully qualified name, but no checks are done.
     r"""Host(name, ip, mac) -> Host instance :: Class representing a host.
 
     name - Host name, should be a fully qualified name, but no checks are done.
@@ -111,7 +99,11 @@ class Host:
         self.ip = ip
         self.mac = mac
 
         self.ip = ip
         self.mac = mac
 
-class HostHandler:
+    def as_tuple(self):
+        r"Return a tuple representing the host."
+        return (self.name, self.ip, self.mac)
+
+class HostHandler(Handler):
     r"""HostHandler(hosts) -> HostHandler instance :: Handle a list of hosts.
 
     This class is a helper for DhcpHandler to do all the work related to hosts
     r"""HostHandler(hosts) -> HostHandler instance :: Handle a list of hosts.
 
     This class is a helper for DhcpHandler to do all the work related to hosts
@@ -124,14 +116,14 @@ class HostHandler:
         r"Initialize HostHandler object, see class documentation for details."
         self.hosts = hosts
 
         r"Initialize HostHandler object, see class documentation for details."
         self.hosts = hosts
 
-    @handler
+    @handler(u'Add a new host.')
     def add(self, name, ip, mac):
         r"add(name, ip, mac) -> None :: Add a host to the hosts list."
         if name in self.hosts:
             raise HostAlreadyExistsError(name)
         self.hosts[name] = Host(name, ip, mac)
 
     def add(self, name, ip, mac):
         r"add(name, ip, mac) -> None :: Add a host to the hosts list."
         if name in self.hosts:
             raise HostAlreadyExistsError(name)
         self.hosts[name] = Host(name, ip, mac)
 
-    @handler
+    @handler(u'Update a host.')
     def update(self, name, ip=None, mac=None):
         r"update(name[, ip[, mac]]) -> None :: Update a host of the hosts list."
         if not name in self.hosts:
     def update(self, name, ip=None, mac=None):
         r"update(name[, ip[, mac]]) -> None :: Update a host of the hosts list."
         if not name in self.hosts:
@@ -141,43 +133,31 @@ class HostHandler:
         if mac is not None:
             self.hosts[name].mac = mac
 
         if mac is not None:
             self.hosts[name].mac = mac
 
-    @handler
+    @handler(u'Delete a host.')
     def delete(self, name):
         r"delete(name) -> None :: Delete a host of the hosts list."
         if not name in self.hosts:
             raise HostNotFoundError(name)
         del self.hosts[name]
 
     def delete(self, name):
         r"delete(name) -> None :: Delete a host of the hosts list."
         if not name in self.hosts:
             raise HostNotFoundError(name)
         del self.hosts[name]
 
-    @handler
+    @handler(u'Get information about a host.')
     def get(self, name):
     def get(self, name):
-        r"""get(name) -> CSV string :: List all the information of a host.
-
-        The host is returned as a CSV list of: hostname,ip,mac
-        """
+        r"get(name) -> Host :: List all the information of a host."
         if not name in self.hosts:
             raise HostNotFoundError(name)
         if not name in self.hosts:
             raise HostNotFoundError(name)
-        h = self.hosts[name]
-        return '%s,%s,%s' % (h.name, h.ip, h.mac)
+        return self.hosts[name]
 
 
-    @handler
+    @handler(u'List hosts.')
     def list(self):
     def list(self):
-        r"""list() -> CSV string :: List all the hostnames.
+        r"list() -> tuple :: List all the hostnames."
+        return self.hosts.keys()
 
 
-        The list is returned as a single CSV line with all the hostnames.
-        """
-        return ','.join(self.hosts)
-
-    @handler
+    @handler(u'Get information about all hosts.')
     def show(self):
     def show(self):
-        r"""show() -> CSV string :: List all the complete hosts information.
-
-        The hosts are returned as a CSV list with each host in a line, like:
-        hostname,ip,mac
-        """
-        hosts = self.hosts.values()
-        return '\n'.join('%s,%s,%s' % (h.name, h.ip, h.mac) for h in hosts)
+        r"show() -> list of Hosts :: List all the complete hosts information."
+        return self.hosts.values()
 
 
-class DhcpHandler:
+class DhcpHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler):
     r"""DhcpHandler([pickle_dir[, config_dir]]) -> DhcpHandler instance.
 
     Handles DHCP service commands for the dhcpd program.
     r"""DhcpHandler([pickle_dir[, config_dir]]) -> DhcpHandler instance.
 
     Handles DHCP service commands for the dhcpd program.
@@ -189,19 +169,13 @@ class DhcpHandler:
     Both defaults to the current working directory.
     """
 
     Both defaults to the current working directory.
     """
 
-    def __init__(self, pickle_dir='.', config_dir='.'):
-        r"Initialize DhcpHandler object, see class documentation for details."
-        self.pickle_dir = pickle_dir
-        self.config_dir = config_dir
-        filename = path.join(template_dir, config_filename)
-        self.template = Template(filename=filename)
-        try:
-            self._load()
-        except IOError:
-            # This is the first time the handler is used, create a basic
-            # setup using some nice defaults
-            self.hosts = dict()
-            self.vars = dict(
+    _initd_name = 'dhcpd'
+
+    _persistent_vars = ('vars', 'hosts')
+
+    _restorable_defaults = dict(
+            hosts = dict(),
+            vars  = dict(
                 domain_name = 'example.com',
                 dns_1       = 'ns1.example.com',
                 dns_2       = 'ns2.example.com',
                 domain_name = 'example.com',
                 dns_1       = 'ns1.example.com',
                 dns_2       = 'ns2.example.com',
@@ -210,159 +184,80 @@ class DhcpHandler:
                 net_start   = '192.168.0.100',
                 net_end     = '192.168.0.200',
                 net_gateway = '192.168.0.1',
                 net_start   = '192.168.0.100',
                 net_end     = '192.168.0.200',
                 net_gateway = '192.168.0.1',
-            )
-            self._dump()
-            self._write_config()
+            ),
+    )
+
+    _config_writer_files = 'dhcpd.conf'
+    _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates')
+
+    def __init__(self, pickle_dir='.', config_dir='.'):
+        r"Initialize DhcpHandler object, see class documentation for details."
+        self._persistent_dir = pickle_dir
+        self._config_writer_cfg_dir = config_dir
+        self._config_build_templates()
+        self._restore()
         self.host = HostHandler(self.hosts)
 
         self.host = HostHandler(self.hosts)
 
-    @handler
+    def _get_config_vars(self, config_file):
+        return dict(hosts=self.hosts.values(), **self.vars)
+
+    @handler(u'Set a DHCP parameter.')
     def set(self, param, value):
         r"set(param, value) -> None :: Set a DHCP parameter."
         if not param in self.vars:
             raise ParameterNotFoundError(param)
         self.vars[param] = value
 
     def set(self, param, value):
         r"set(param, value) -> None :: Set a DHCP parameter."
         if not param in self.vars:
             raise ParameterNotFoundError(param)
         self.vars[param] = value
 
-    @handler
+    @handler(u'Get a DHCP parameter.')
     def get(self, param):
         r"get(param) -> None :: Get a DHCP parameter."
         if not param in self.vars:
             raise ParameterNotFoundError(param)
         return self.vars[param]
 
     def get(self, param):
         r"get(param) -> None :: Get a DHCP parameter."
         if not param in self.vars:
             raise ParameterNotFoundError(param)
         return self.vars[param]
 
-    @handler
+    @handler(u'List all available DHCP parameters.')
     def list(self):
     def list(self):
-        r"""list() -> CSV string :: List all the parameter names.
-
-        The list is returned as a single CSV line with all the names.
-        """
-        return ','.join(self.vars)
+        r"list() -> tuple :: List all the parameter names."
+        return self.vars.keys()
 
 
-    @handler
+    @handler(u'Get all DHCP parameters, with their values.')
     def show(self):
     def show(self):
-        r"""show() -> CSV string :: List all the parameters (with their values).
-
-        The parameters are returned as a CSV list with each parameter in a
-        line, like:
-        name,value
-        """
-        return '\n'.join(('%s,%s' % (k, v) for (k, v) in self.vars.items()))
-
-    @handler
-    def start(self):
-        r"start() -> None :: Start the DHCP service."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler
-    def stop(self):
-        r"stop() -> None :: Stop the DHCP service."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler
-    def restart(self):
-        r"restart() -> None :: Restart the DHCP service."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler
-    def reload(self):
-        r"reload() -> None :: Reload the configuration of the DHCP service."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler
-    def commit(self):
-        r"commit() -> None :: Commit the changes and reload the DHCP service."
-        #esto seria para poner en una interfaz
-        #y seria que hace el pickle deberia llamarse
-        #al hacerse un commit
-        self._dump()
-        self._write_config()
-        self.reload()
-
-    @handler
-    def rollback(self):
-        r"rollback() -> None :: Discard the changes not yet commited."
-        self._load()
-
-    def _dump(self):
-        r"_dump() -> None :: Dump all persistent data to pickle files."
-        # XXX podría ir en una clase base
-        self._dump_var(self.vars, pickle_vars)
-        self._dump_var(self.hosts, pickle_hosts)
-
-    def _load(self):
-        r"_load() -> None :: Load all persistent data from pickle files."
-        # XXX podría ir en una clase base
-        self.vars = self._load_var(pickle_vars)
-        self.hosts = self._load_var(pickle_hosts)
-
-    def _pickle_filename(self, name):
-        r"_pickle_filename() -> string :: Construct a pickle filename."
-        # XXX podría ir en una clase base
-        return path.join(self.pickle_dir, name) + pickle_ext
-
-    def _dump_var(self, var, name):
-        r"_dump_var() -> None :: Dump a especific variable to a pickle file."
-        # XXX podría ir en una clase base
-        pkl_file = file(self._pickle_filename(name), 'wb')
-        pickle.dump(var, pkl_file, 2)
-        pkl_file.close()
-
-    def _load_var(self, name):
-        r"_load_var() -> object :: Load a especific pickle file."
-        # XXX podría ir en una clase base
-        return pickle.load(file(self._pickle_filename(name)))
-
-    def _write_config(self):
-        r"_write_config() -> None :: Generate all the configuration files."
-        # XXX podría ir en una clase base, ver como generalizar variables a
-        # reemplazar en la template
-        out_file = file(path.join(self.config_dir, config_filename), 'w')
-        ctx = Context(out_file, hosts=self.hosts.values(), **self.vars)
-        self.template.render_context(ctx)
-        out_file.close()
+        r"show() -> (key, value) tuples :: List all the parameters."
+        return self.vars.items()
 
 if __name__ == '__main__':
 
     import os
 
 
 if __name__ == '__main__':
 
     import os
 
-    dhcp_handler = DhcpHandler()
+    h = DhcpHandler()
 
     def dump():
         print '-' * 80
 
     def dump():
         print '-' * 80
-        print 'Variables:', dhcp_handler.list()
-        print dhcp_handler.show()
+        print 'Variables:', h.list()
+        print h.show()
         print
         print
-        print 'Hosts:', dhcp_handler.host.list()
-        print dhcp_handler.host.show()
+        print 'Hosts:', h.host.list()
+        print h.host.show()
         print '-' * 80
 
     dump()
 
         print '-' * 80
 
     dump()
 
-    dhcp_handler.host.add('my_name','192.168.0.102','00:12:ff:56')
+    h.host.add('my_name','192.168.0.102','00:12:ff:56')
 
 
-    dhcp_handler.host.update('my_name','192.168.0.192','00:12:ff:56')
+    h.host.update('my_name','192.168.0.192','00:12:ff:56')
 
 
-    dhcp_handler.host.add('nico','192.168.0.188','00:00:00:00')
+    h.host.add('nico','192.168.0.188','00:00:00:00')
 
 
-    dhcp_handler.set('domain_name','baryon.com.ar')
+    h.set('domain_name','baryon.com.ar')
 
     try:
 
     try:
-        dhcp_handler.set('sarasa','baryon.com.ar')
+        h.set('sarasa','baryon.com.ar')
     except KeyError, e:
         print 'Error:', e
 
     except KeyError, e:
         print 'Error:', e
 
-    dhcp_handler.commit()
+    h.commit()
 
     dump()
 
 
     dump()
 
-    for f in (pickle_vars + pickle_ext, pickle_hosts + pickle_ext,
-                                                            config_filename):
-        os.unlink(f)
+    os.system('rm -f *.pkl ' + ' '.join(h._config_writer_files))