]> 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 b728162c278276837a5a35771b859aa611cd9474..7ffadcf7a4dc424a421bb6d77fc2b71d8df8f76e 100644 (file)
@@ -1,38 +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 seqtools import Sequence
-except ImportError:
-    # NOP for testing
-    class Sequence: pass
-try:
-    from dispatcher import handler, HandlerError
-except ImportError:
-    # NOP for testing
-    class HandlerError(RuntimeError): pass
-    def handler(f): return f
+from seqtools import Sequence
+from dispatcher import Handler, handler, HandlerError
+from services.util import Restorable, ConfigWriter
+from services.util import InitdHandler, TransactionalHandler
 
 
-__ALL__ = ('DhcpHandler',)
-
-pickle_ext = '.pkl'
-pickle_vars = 'vars'
-pickle_hosts = 'hosts'
-
-config_filename = 'dhcpd.conf'
-
-template_dir = path.join(path.dirname(__file__), 'templates')
+__ALL__ = ('DhcpHandler', 'Error', 'HostError', 'HostAlreadyExistsError',
+            'HostNotFoundError', 'ParameterError', 'ParameterNotFoundError')
 
 class Error(HandlerError):
     r"""
 
 class Error(HandlerError):
     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.
@@ -123,7 +103,7 @@ class Host(Sequence):
         r"Return a tuple representing the host."
         return (self.name, self.ip, self.mac)
 
         r"Return a tuple representing the host."
         return (self.name, self.ip, self.mac)
 
-class HostHandler:
+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
@@ -136,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:
@@ -153,41 +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)
         return self.hosts[name]
 
         if not name in self.hosts:
             raise HostNotFoundError(name)
         return self.hosts[name]
 
-    @handler
+    @handler(u'List hosts.')
     def list(self):
     def list(self):
-        r"""list() -> CSV string :: List all the hostnames.
-
-        The list is returned as a single CSV line with all the hostnames.
-        """
+        r"list() -> tuple :: List all the hostnames."
         return self.hosts.keys()
 
         return self.hosts.keys()
 
-    @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
-        """
+        r"show() -> list of Hosts :: List all the complete hosts information."
         return self.hosts.values()
 
         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.
@@ -199,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',
@@ -220,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.
-        """
+        r"list() -> tuple :: List all the parameter names."
         return self.vars.keys()
 
         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
-        """
+        r"show() -> (key, value) tuples :: List all the parameters."
         return self.vars.items()
 
         return 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()
-
 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))