From: Leandro Lucarella Date: Mon, 16 Jun 2008 19:52:07 +0000 (-0300) Subject: Split plug-in code from handler code for services (refs #27). X-Git-Url: https://git.llucax.com/software/pymin.git/commitdiff_plain/3a113e91bcbc8e34d9ef66915ccec80e6187332b Split plug-in code from handler code for services (refs #27). --- diff --git a/services/dhcp/__init__.py b/services/dhcp/__init__.py index c33a555..d493b1c 100644 --- a/services/dhcp/__init__.py +++ b/services/dhcp/__init__.py @@ -1,145 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from os import path -import logging ; log = logging.getLogger('pymin.services.dhcp') - -from pymin.seqtools import Sequence -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, ParametersHandler, \ - DictSubHandler, ReloadHandler - -__all__ = ('DhcpHandler', 'get_service') - +from handler import DhcpHandler def get_service(config): return DhcpHandler(config.dhcp.pickle_dir, config.dhcp.config_dir) - -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. - ip - IP assigned to the hostname. - mac - MAC address to associate to the hostname. - """ - - def __init__(self, name, ip, mac): - r"Initialize Host object, see class documentation for details." - self.name = name - self.ip = ip - self.mac = mac - - def as_tuple(self): - r"Return a tuple representing the host." - return (self.name, self.ip, self.mac) - - def update(self, ip=None, mac=None): - if ip is not None: - self.ip = ip - if mac is not None: - self.mac = mac - -class HostHandler(DictSubHandler): - r"""HostHandler(parent) -> HostHandler instance :: Handle a list of hosts. - - This class is a helper for DhcpHandler to do all the work related to hosts - administration. - """ - - handler_help = u"Manage DHCP hosts" - - _cont_subhandler_attr = 'hosts' - _cont_subhandler_class = Host - -class DhcpHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler, - ParametersHandler, InitdHandler): - r"""DhcpHandler([pickle_dir[, config_dir]]) -> DhcpHandler instance. - - Handles DHCP service commands for the dhcpd program. - - pickle_dir - Directory where to write the persistent configuration data. - - config_dir - Directory where to store de generated configuration files. - - Both defaults to the current working directory. - """ - - handler_help = u"Manage DHCP service" - - _initd_name = 'dhcpd' - - _persistent_attrs = ('params', 'hosts') - - _restorable_defaults = dict( - hosts = dict(), - params = dict( - domain_name = 'example.com', - dns_1 = 'ns1.example.com', - dns_2 = 'ns2.example.com', - net_address = '192.168.0.0', - net_mask = '255.255.255.0', - net_start = '192.168.0.100', - net_end = '192.168.0.200', - net_gateway = '192.168.0.1', - ), - ) - - _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." - log.debug(u'DhcpHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._config_build_templates() - InitdHandler.__init__(self) - self.host = HostHandler(self) - - def _get_config_vars(self, config_file): - return dict(hosts=self.hosts.values(), **self.params) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - import os - - h = DhcpHandler() - - def dump(): - print '-' * 80 - print 'Variables:', h.list() - print h.show() - print - print 'Hosts:', h.host.list() - print h.host.show() - print '-' * 80 - - dump() - - h.host.add('my_name','192.168.0.102','00:12:ff:56') - - h.host.update('my_name','192.168.0.192','00:12:ff:56') - - h.host.add('nico','192.168.0.188','00:00:00:00') - - h.set('domain_name','baryon.com.ar') - - try: - h.set('sarasa','baryon.com.ar') - except KeyError, e: - print 'Error:', e - - h.commit() - - dump() - - os.system('rm -f *.pkl ' + ' '.join(h._config_writer_files)) - diff --git a/services/dhcp/handler.py b/services/dhcp/handler.py new file mode 100644 index 0000000..c33a555 --- /dev/null +++ b/services/dhcp/handler.py @@ -0,0 +1,145 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from os import path +import logging ; log = logging.getLogger('pymin.services.dhcp') + +from pymin.seqtools import Sequence +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, ParametersHandler, \ + DictSubHandler, ReloadHandler + +__all__ = ('DhcpHandler', 'get_service') + + +def get_service(config): + return DhcpHandler(config.dhcp.pickle_dir, config.dhcp.config_dir) + + +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. + ip - IP assigned to the hostname. + mac - MAC address to associate to the hostname. + """ + + def __init__(self, name, ip, mac): + r"Initialize Host object, see class documentation for details." + self.name = name + self.ip = ip + self.mac = mac + + def as_tuple(self): + r"Return a tuple representing the host." + return (self.name, self.ip, self.mac) + + def update(self, ip=None, mac=None): + if ip is not None: + self.ip = ip + if mac is not None: + self.mac = mac + +class HostHandler(DictSubHandler): + r"""HostHandler(parent) -> HostHandler instance :: Handle a list of hosts. + + This class is a helper for DhcpHandler to do all the work related to hosts + administration. + """ + + handler_help = u"Manage DHCP hosts" + + _cont_subhandler_attr = 'hosts' + _cont_subhandler_class = Host + +class DhcpHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler, + ParametersHandler, InitdHandler): + r"""DhcpHandler([pickle_dir[, config_dir]]) -> DhcpHandler instance. + + Handles DHCP service commands for the dhcpd program. + + pickle_dir - Directory where to write the persistent configuration data. + + config_dir - Directory where to store de generated configuration files. + + Both defaults to the current working directory. + """ + + handler_help = u"Manage DHCP service" + + _initd_name = 'dhcpd' + + _persistent_attrs = ('params', 'hosts') + + _restorable_defaults = dict( + hosts = dict(), + params = dict( + domain_name = 'example.com', + dns_1 = 'ns1.example.com', + dns_2 = 'ns2.example.com', + net_address = '192.168.0.0', + net_mask = '255.255.255.0', + net_start = '192.168.0.100', + net_end = '192.168.0.200', + net_gateway = '192.168.0.1', + ), + ) + + _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." + log.debug(u'DhcpHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._config_build_templates() + InitdHandler.__init__(self) + self.host = HostHandler(self) + + def _get_config_vars(self, config_file): + return dict(hosts=self.hosts.values(), **self.params) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + import os + + h = DhcpHandler() + + def dump(): + print '-' * 80 + print 'Variables:', h.list() + print h.show() + print + print 'Hosts:', h.host.list() + print h.host.show() + print '-' * 80 + + dump() + + h.host.add('my_name','192.168.0.102','00:12:ff:56') + + h.host.update('my_name','192.168.0.192','00:12:ff:56') + + h.host.add('nico','192.168.0.188','00:00:00:00') + + h.set('domain_name','baryon.com.ar') + + try: + h.set('sarasa','baryon.com.ar') + except KeyError, e: + print 'Error:', e + + h.commit() + + dump() + + os.system('rm -f *.pkl ' + ' '.join(h._config_writer_files)) + diff --git a/services/dns/__init__.py b/services/dns/__init__.py index 23046c5..1b5e4e5 100644 --- a/services/dns/__init__.py +++ b/services/dns/__init__.py @@ -1,295 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -# TODO COMMENT -from os import path -from os import unlink -import logging ; log = logging.getLogger('pymin.services.dns') - -from pymin.seqtools import Sequence -from pymin.dispatcher import handler, HandlerError, Handler -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, ParametersHandler, \ - DictComposedSubHandler, DictSubHandler, call - -__all__ = ('DnsHandler', 'get_service') - +from handler import DnsHandler def get_service(config): return DnsHandler(config.dns.pickle_dir, config.dns.config_dir) - -class Host(Sequence): - def __init__(self, name, ip): - self.name = name - self.ip = ip - def update(self, ip=None): - if ip is not None: self.ip = ip - def as_tuple(self): - return (self.name, self.ip) - -class HostHandler(DictComposedSubHandler): - handler_help = u"Manage DNS hosts" - _comp_subhandler_cont = 'zones' - _comp_subhandler_attr = 'hosts' - _comp_subhandler_class = Host - -class MailExchange(Sequence): - def __init__(self, mx, prio): - self.mx = mx - self.prio = prio - def update(self, prio=None): - if prio is not None: self.prio = prio - def as_tuple(self): - return (self.mx, self.prio) - -class MailExchangeHandler(DictComposedSubHandler): - handler_help = u"Manage DNS mail exchangers (MX)" - _comp_subhandler_cont = 'zones' - _comp_subhandler_attr = 'mxs' - _comp_subhandler_class = MailExchange - -class NameServer(Sequence): - def __init__(self, name): - self.name = name - def as_tuple(self): - return (self.name,) - -class NameServerHandler(DictComposedSubHandler): - handler_help = u"Manage DNS name servers (NS)" - _comp_subhandler_cont = 'zones' - _comp_subhandler_attr = 'nss' - _comp_subhandler_class = NameServer - -class Zone(Sequence): - def __init__(self, name): - self.name = name - self.hosts = dict() - self.mxs = dict() - self.nss = dict() - self._add = False - self._update = False - self._delete = False - def as_tuple(self): - return (self.name, self.hosts, self.mxs, self.nss) - -class ZoneHandler(DictSubHandler): - handler_help = u"Manage DNS zones" - _cont_subhandler_attr = 'zones' - _cont_subhandler_class = Zone - -class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, - ParametersHandler): - r"""DnsHandler([pickle_dir[, config_dir]]) -> DnsHandler instance. - - Handles DNS service commands for the dns program. - - pickle_dir - Directory where to write the persistent configuration data. - - config_dir - Directory where to store de generated configuration files. - - Both defaults to the current working directory. - """ - - handler_help = u"Manage DNS service" - - _initd_name = 'named' - - _persistent_attrs = ('params', 'zones') - - _restorable_defaults = dict( - zones = dict(), - params = dict( - isp_dns1 = '', - isp_dns2 = '', - bind_addr1 = '', - bind_addr2 = '' - ), - ) - - _config_writer_files = ('named.conf', 'zoneX.zone') - _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') - - def __init__(self, pickle_dir='.', config_dir='.'): - r"Initialize DnsHandler object, see class documentation for details." - log.debug(u'DnsHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._update = False - self._config_build_templates() - InitdHandler.__init__(self) - self.host = HostHandler(self) - self.zone = ZoneHandler(self) - self.mx = MailExchangeHandler(self) - self.ns = NameServerHandler(self) - - def _zone_filename(self, zone): - return zone.name + '.zone' - - def _get_config_vars(self, config_file): - return dict(zones=self.zones.values(), **self.params) - - def _write_config(self): - r"_write_config() -> None :: Generate all the configuration files." - log.debug(u'DnsHandler._write_config()') - delete_zones = list() - for a_zone in self.zones.values(): - log.debug(u'DnsHandler._write_config: processing zone %s', a_zone) - if a_zone._update or a_zone._add: - if not a_zone._add and self._service_running: - log.debug(u'DnsHandler._write_config: zone updated and ' - u'the service is running, freezing zone') - call(('rndc', 'freeze', a_zone.name)) - vars = dict( - zone = a_zone, - hosts = a_zone.hosts.values(), - mxs = a_zone.mxs.values(), - nss = a_zone.nss.values() - ) - self._write_single_config('zoneX.zone', - self._zone_filename(a_zone), vars) - a_zone._update = False - if not a_zone._add and self._service_running: - log.debug(u'DnsHandler._write_config: unfreezing zone') - call(('rndc', 'thaw', a_zone.name)) - else : - self._update = True - a_zone._add = False - if a_zone._delete: - #borro el archivo .zone - log.debug(u'DnsHandler._write_config: zone deleted, removing ' - u'the file %r', self._zone_filename(a_zone)) - try: - self._update = True - unlink(self._zone_filename(a_zone)) - except OSError: - #la excepcion pude darse en caso que haga un add de una zona y - #luego el del, como no hice commit, no se crea el archivo - log.debug(u'DnsHandler._write_config: file not found') - pass - delete_zones.append(a_zone.name) - #borro las zonas - for z in delete_zones: - del self.zones[z] - #archivo general - if self._update: - self._write_single_config('named.conf') - self._update = False - return False # Do reload - return True # we don't need to reload - - # HACK!!!! - def handle_timer(self): - log.debug(u'DnsHandler.handle_timer()') - import subprocess - p = subprocess.Popen(('pgrep', '-f', '/usr/sbin/named'), - stdout=subprocess.PIPE) - pid = p.communicate()[0] - if p.returncode == 0 and len(pid) > 0: - log.debug(u'DnsHandler.handle_timer: pid present, running') - self._service_running = True - else: - log.debug(u'DnsHandler.handle_timer: pid absent, NOT running') - self._service_running = False - - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - dns = DnsHandler(); - - dns.set('isp_dns1','la_garcha.com') - dns.set('bind_addr1','localhost') - dns.zone.add('zona_loca.com') - #dns.zone.update('zona_loca.com','ns1.dominio.com') - - dns.host.add('zona_loca.com','hostname_loco','192.168.0.23') - dns.host.update('zona_loca.com','hostname_loco','192.168.0.66') - - dns.host.add('zona_loca.com','hostname_kuak','192.168.0.23') - dns.host.delete('zona_loca.com','hostname_kuak') - - dns.host.add('zona_loca.com','hostname_kuang','192.168.0.24') - dns.host.add('zona_loca.com','hostname_chan','192.168.0.25') - dns.host.add('zona_loca.com','hostname_kaine','192.168.0.26') - - dns.mx.add('zona_loca.com','mx1.sarasa.com',10) - dns.mx.update('zona_loca.com','mx1.sarasa.com',20) - dns.mx.add('zona_loca.com','mx2.sarasa.com',30) - dns.mx.add('zona_loca.com','mx3.sarasa.com',40) - dns.mx.delete('zona_loca.com','mx3.sarasa.com') - - dns.ns.add('zona_loca.com','ns1.jua.com') - dns.ns.add('zona_loca.com','ns2.jua.com') - dns.ns.add('zona_loca.com','ns3.jua.com') - dns.ns.delete('zona_loca.com','ns3.jua.com') - - dns.zone.add('zona_oscura') - - dns.host.add('zona_oscura','hostname_a','192.168.0.24') - dns.host.add('zona_oscura','hostname_b','192.168.0.25') - dns.host.add('zona_oscura','hostname_c','192.168.0.26') - - dns.zone.delete('zona_oscura') - - dns.commit() - - print 'ZONAS :', dns.zone.show() - for z in dns.zones: - print 'HOSTS from', z, ':', dns.host.show(z) - - #test zone errors - #try: - # dns.zone.update('zone-sarasa','lalal') - #except ZoneNotFoundError, inst: - # print 'Error: ', inst - - from pymin.services.util import ItemNotFoundError, ItemAlreadyExistsError, \ - ContainerNotFoundError - - try: - dns.zone.delete('zone-sarasa') - except ItemNotFoundError, inst: - print 'Error: ', inst - - #try: - # dns.zone.add('zona_loca.com','ns1.dom.com','ns2.dom.com') - #except ZoneAlreadyExistsError, inst: - # print 'Error: ', inst - - - #test hosts errors - try: - dns.host.update('zone-sarasa','kuak','192.68') - except ContainerNotFoundError, inst: - print 'Error: ', inst - - try: - dns.host.update('zona_loca.com','kuak','192.68') - except ItemNotFoundError, inst: - print 'Error: ', inst - - try: - dns.host.delete('zone-sarasa','lala') - except ContainerNotFoundError, inst: - print 'Error: ', inst - - try: - dns.host.delete('zona_loca.com','lala') - except ItemNotFoundError, inst: - print 'Error: ', inst - - try: - dns.host.add('zona','hostname_loco','192.168.0.23') - except ContainerNotFoundError, inst: - print 'Error: ', inst - - try: - dns.host.add('zona_loca.com','hostname_loco','192.168.0.23') - except ItemAlreadyExistsError, inst: - print 'Error: ', inst diff --git a/services/dns/handler.py b/services/dns/handler.py new file mode 100644 index 0000000..23046c5 --- /dev/null +++ b/services/dns/handler.py @@ -0,0 +1,295 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +# TODO COMMENT +from os import path +from os import unlink +import logging ; log = logging.getLogger('pymin.services.dns') + +from pymin.seqtools import Sequence +from pymin.dispatcher import handler, HandlerError, Handler +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, ParametersHandler, \ + DictComposedSubHandler, DictSubHandler, call + +__all__ = ('DnsHandler', 'get_service') + + +def get_service(config): + return DnsHandler(config.dns.pickle_dir, config.dns.config_dir) + + +class Host(Sequence): + def __init__(self, name, ip): + self.name = name + self.ip = ip + def update(self, ip=None): + if ip is not None: self.ip = ip + def as_tuple(self): + return (self.name, self.ip) + +class HostHandler(DictComposedSubHandler): + handler_help = u"Manage DNS hosts" + _comp_subhandler_cont = 'zones' + _comp_subhandler_attr = 'hosts' + _comp_subhandler_class = Host + +class MailExchange(Sequence): + def __init__(self, mx, prio): + self.mx = mx + self.prio = prio + def update(self, prio=None): + if prio is not None: self.prio = prio + def as_tuple(self): + return (self.mx, self.prio) + +class MailExchangeHandler(DictComposedSubHandler): + handler_help = u"Manage DNS mail exchangers (MX)" + _comp_subhandler_cont = 'zones' + _comp_subhandler_attr = 'mxs' + _comp_subhandler_class = MailExchange + +class NameServer(Sequence): + def __init__(self, name): + self.name = name + def as_tuple(self): + return (self.name,) + +class NameServerHandler(DictComposedSubHandler): + handler_help = u"Manage DNS name servers (NS)" + _comp_subhandler_cont = 'zones' + _comp_subhandler_attr = 'nss' + _comp_subhandler_class = NameServer + +class Zone(Sequence): + def __init__(self, name): + self.name = name + self.hosts = dict() + self.mxs = dict() + self.nss = dict() + self._add = False + self._update = False + self._delete = False + def as_tuple(self): + return (self.name, self.hosts, self.mxs, self.nss) + +class ZoneHandler(DictSubHandler): + handler_help = u"Manage DNS zones" + _cont_subhandler_attr = 'zones' + _cont_subhandler_class = Zone + +class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, + ParametersHandler): + r"""DnsHandler([pickle_dir[, config_dir]]) -> DnsHandler instance. + + Handles DNS service commands for the dns program. + + pickle_dir - Directory where to write the persistent configuration data. + + config_dir - Directory where to store de generated configuration files. + + Both defaults to the current working directory. + """ + + handler_help = u"Manage DNS service" + + _initd_name = 'named' + + _persistent_attrs = ('params', 'zones') + + _restorable_defaults = dict( + zones = dict(), + params = dict( + isp_dns1 = '', + isp_dns2 = '', + bind_addr1 = '', + bind_addr2 = '' + ), + ) + + _config_writer_files = ('named.conf', 'zoneX.zone') + _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') + + def __init__(self, pickle_dir='.', config_dir='.'): + r"Initialize DnsHandler object, see class documentation for details." + log.debug(u'DnsHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._update = False + self._config_build_templates() + InitdHandler.__init__(self) + self.host = HostHandler(self) + self.zone = ZoneHandler(self) + self.mx = MailExchangeHandler(self) + self.ns = NameServerHandler(self) + + def _zone_filename(self, zone): + return zone.name + '.zone' + + def _get_config_vars(self, config_file): + return dict(zones=self.zones.values(), **self.params) + + def _write_config(self): + r"_write_config() -> None :: Generate all the configuration files." + log.debug(u'DnsHandler._write_config()') + delete_zones = list() + for a_zone in self.zones.values(): + log.debug(u'DnsHandler._write_config: processing zone %s', a_zone) + if a_zone._update or a_zone._add: + if not a_zone._add and self._service_running: + log.debug(u'DnsHandler._write_config: zone updated and ' + u'the service is running, freezing zone') + call(('rndc', 'freeze', a_zone.name)) + vars = dict( + zone = a_zone, + hosts = a_zone.hosts.values(), + mxs = a_zone.mxs.values(), + nss = a_zone.nss.values() + ) + self._write_single_config('zoneX.zone', + self._zone_filename(a_zone), vars) + a_zone._update = False + if not a_zone._add and self._service_running: + log.debug(u'DnsHandler._write_config: unfreezing zone') + call(('rndc', 'thaw', a_zone.name)) + else : + self._update = True + a_zone._add = False + if a_zone._delete: + #borro el archivo .zone + log.debug(u'DnsHandler._write_config: zone deleted, removing ' + u'the file %r', self._zone_filename(a_zone)) + try: + self._update = True + unlink(self._zone_filename(a_zone)) + except OSError: + #la excepcion pude darse en caso que haga un add de una zona y + #luego el del, como no hice commit, no se crea el archivo + log.debug(u'DnsHandler._write_config: file not found') + pass + delete_zones.append(a_zone.name) + #borro las zonas + for z in delete_zones: + del self.zones[z] + #archivo general + if self._update: + self._write_single_config('named.conf') + self._update = False + return False # Do reload + return True # we don't need to reload + + # HACK!!!! + def handle_timer(self): + log.debug(u'DnsHandler.handle_timer()') + import subprocess + p = subprocess.Popen(('pgrep', '-f', '/usr/sbin/named'), + stdout=subprocess.PIPE) + pid = p.communicate()[0] + if p.returncode == 0 and len(pid) > 0: + log.debug(u'DnsHandler.handle_timer: pid present, running') + self._service_running = True + else: + log.debug(u'DnsHandler.handle_timer: pid absent, NOT running') + self._service_running = False + + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + dns = DnsHandler(); + + dns.set('isp_dns1','la_garcha.com') + dns.set('bind_addr1','localhost') + dns.zone.add('zona_loca.com') + #dns.zone.update('zona_loca.com','ns1.dominio.com') + + dns.host.add('zona_loca.com','hostname_loco','192.168.0.23') + dns.host.update('zona_loca.com','hostname_loco','192.168.0.66') + + dns.host.add('zona_loca.com','hostname_kuak','192.168.0.23') + dns.host.delete('zona_loca.com','hostname_kuak') + + dns.host.add('zona_loca.com','hostname_kuang','192.168.0.24') + dns.host.add('zona_loca.com','hostname_chan','192.168.0.25') + dns.host.add('zona_loca.com','hostname_kaine','192.168.0.26') + + dns.mx.add('zona_loca.com','mx1.sarasa.com',10) + dns.mx.update('zona_loca.com','mx1.sarasa.com',20) + dns.mx.add('zona_loca.com','mx2.sarasa.com',30) + dns.mx.add('zona_loca.com','mx3.sarasa.com',40) + dns.mx.delete('zona_loca.com','mx3.sarasa.com') + + dns.ns.add('zona_loca.com','ns1.jua.com') + dns.ns.add('zona_loca.com','ns2.jua.com') + dns.ns.add('zona_loca.com','ns3.jua.com') + dns.ns.delete('zona_loca.com','ns3.jua.com') + + dns.zone.add('zona_oscura') + + dns.host.add('zona_oscura','hostname_a','192.168.0.24') + dns.host.add('zona_oscura','hostname_b','192.168.0.25') + dns.host.add('zona_oscura','hostname_c','192.168.0.26') + + dns.zone.delete('zona_oscura') + + dns.commit() + + print 'ZONAS :', dns.zone.show() + for z in dns.zones: + print 'HOSTS from', z, ':', dns.host.show(z) + + #test zone errors + #try: + # dns.zone.update('zone-sarasa','lalal') + #except ZoneNotFoundError, inst: + # print 'Error: ', inst + + from pymin.services.util import ItemNotFoundError, ItemAlreadyExistsError, \ + ContainerNotFoundError + + try: + dns.zone.delete('zone-sarasa') + except ItemNotFoundError, inst: + print 'Error: ', inst + + #try: + # dns.zone.add('zona_loca.com','ns1.dom.com','ns2.dom.com') + #except ZoneAlreadyExistsError, inst: + # print 'Error: ', inst + + + #test hosts errors + try: + dns.host.update('zone-sarasa','kuak','192.68') + except ContainerNotFoundError, inst: + print 'Error: ', inst + + try: + dns.host.update('zona_loca.com','kuak','192.68') + except ItemNotFoundError, inst: + print 'Error: ', inst + + try: + dns.host.delete('zone-sarasa','lala') + except ContainerNotFoundError, inst: + print 'Error: ', inst + + try: + dns.host.delete('zona_loca.com','lala') + except ItemNotFoundError, inst: + print 'Error: ', inst + + try: + dns.host.add('zona','hostname_loco','192.168.0.23') + except ContainerNotFoundError, inst: + print 'Error: ', inst + + try: + dns.host.add('zona_loca.com','hostname_loco','192.168.0.23') + except ItemAlreadyExistsError, inst: + print 'Error: ', inst diff --git a/services/firewall/__init__.py b/services/firewall/__init__.py index 50a8dfb..2c8e091 100644 --- a/services/firewall/__init__.py +++ b/services/firewall/__init__.py @@ -1,145 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -# TODO See if it's better (more secure) to execute commands via python instead -# of using script templates. - -from os import path -from formencode import Invalid -from formencode.validators import OneOf, CIDR, Int -import logging ; log = logging.getLogger('pymin.services.firewall') - -from pymin.item import Item -from pymin.validatedclass import Field -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, ServiceHandler, \ - TransactionalHandler, ListSubHandler - -__all__ = ('FirewallHandler', 'get_service') - +from handler import FirewallHandler def get_service(config): return FirewallHandler(config.firewall.pickle_dir, config.firewall.config_dir) - -class UpOneOf(OneOf): - def validate_python(self, value, state): - value = value.upper() - return OneOf.validate_python(self, value, state) - -class Rule(Item): - r"""Rule(chain, target[, src[, dst[, ...]]]) -> Rule instance. - - chain - INPUT, OUTPUT or FORWARD. - target - ACCEPT, REJECT or DROP. - src - Source subnet as IP/mask. - dst - Destination subnet as IP/mask. - protocol - ICMP, UDP, TCP or ALL. - src_port - Source port (only for UDP or TCP protocols). - dst_port - Destination port (only for UDP or TCP protocols). - """ - chain = Field(UpOneOf(['INPUT', 'OUTPUT', 'FORWARD'], not_empty=True)) - target = Field(UpOneOf(['ACCEPT', 'REJECT', 'DROP'], not_empty=True)) - src = Field(CIDR(if_empty=None, if_missing=None)) - dst = Field(CIDR(if_empty=None, if_missing=None)) - protocol = Field(UpOneOf(['ICMP', 'UDP', 'TCP', 'ALL'], if_missing=None)) - src_port = Field(Int(min=0, max=65535, if_empty=None, if_missing=None)) - dst_port = Field(Int(min=0, max=65535, if_empty=None, if_missing=None)) - def chained_validator(self, fields, state): - errors = dict() - if fields['protocol'] not in ('TCP', 'UDP'): - for name in ('src_port', 'dst_port'): - if fields[name] is not None: - errors[name] = u"Should be None if protocol " \ - "(%(protocol)s) is not TCP or UDP" % fields - if errors: - raise Invalid(u"You can't specify any ports if the protocol " - u'is not TCP or UDP', fields, state, error_dict=errors) - -class RuleHandler(ListSubHandler): - r"""RuleHandler(parent) -> RuleHandler instance :: Handle a list of rules. - - This class is a helper for FirewallHandler to do all the work related to rules - administration. - - parent - The parent service handler. - """ - - handler_help = u"Manage firewall rules" - - _cont_subhandler_attr = 'rules' - _cont_subhandler_class = Rule - -class FirewallHandler(Restorable, ConfigWriter, ServiceHandler, - TransactionalHandler): - r"""FirewallHandler([pickle_dir[, config_dir]]) -> FirewallHandler instance. - - Handles firewall commands using iptables. - - pickle_dir - Directory where to write the persistent configuration data. - - config_dir - Directory where to store de generated configuration files. - - Both defaults to the current working directory. - """ - - handler_help = u"Manage firewall service" - - _persistent_attrs = ['rules'] - - _restorable_defaults = dict(rules=list()) - - _config_writer_files = 'iptables.sh' - _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') - - def __init__(self, pickle_dir='.', config_dir='.'): - r"Initialize the object, see class documentation for details." - log.debug(u'FirewallHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._service_start = ('sh', path.join(self._config_writer_cfg_dir, - self._config_writer_files)) - self._service_stop = ('iptables', '-t', 'filter', '-F') - self._service_restart = self._service_start - self._service_reload = self._service_start - self._config_build_templates() - ServiceHandler.__init__(self) - self.rule = RuleHandler(self) - - def _get_config_vars(self, config_file): - return dict(rules=self.rules) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - import os - - fw_handler = FirewallHandler() - - def dump(): - print '-' * 80 - print 'Rules:' - print fw_handler.rule.show() - print '-' * 80 - - dump() - - fw_handler.rule.add('input', 'drop', protocol='icmp') - - fw_handler.rule.update(0, dst='192.168.0.188/32') - - fw_handler.rule.add('output', 'accept', '192.168.1.0/24') - - fw_handler.commit() - - fw_handler.stop() - - dump() - - os.system('rm -f *.pkl iptables.sh') - diff --git a/services/firewall/handler.py b/services/firewall/handler.py new file mode 100644 index 0000000..50a8dfb --- /dev/null +++ b/services/firewall/handler.py @@ -0,0 +1,145 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +# TODO See if it's better (more secure) to execute commands via python instead +# of using script templates. + +from os import path +from formencode import Invalid +from formencode.validators import OneOf, CIDR, Int +import logging ; log = logging.getLogger('pymin.services.firewall') + +from pymin.item import Item +from pymin.validatedclass import Field +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, ServiceHandler, \ + TransactionalHandler, ListSubHandler + +__all__ = ('FirewallHandler', 'get_service') + + +def get_service(config): + return FirewallHandler(config.firewall.pickle_dir, config.firewall.config_dir) + + +class UpOneOf(OneOf): + def validate_python(self, value, state): + value = value.upper() + return OneOf.validate_python(self, value, state) + +class Rule(Item): + r"""Rule(chain, target[, src[, dst[, ...]]]) -> Rule instance. + + chain - INPUT, OUTPUT or FORWARD. + target - ACCEPT, REJECT or DROP. + src - Source subnet as IP/mask. + dst - Destination subnet as IP/mask. + protocol - ICMP, UDP, TCP or ALL. + src_port - Source port (only for UDP or TCP protocols). + dst_port - Destination port (only for UDP or TCP protocols). + """ + chain = Field(UpOneOf(['INPUT', 'OUTPUT', 'FORWARD'], not_empty=True)) + target = Field(UpOneOf(['ACCEPT', 'REJECT', 'DROP'], not_empty=True)) + src = Field(CIDR(if_empty=None, if_missing=None)) + dst = Field(CIDR(if_empty=None, if_missing=None)) + protocol = Field(UpOneOf(['ICMP', 'UDP', 'TCP', 'ALL'], if_missing=None)) + src_port = Field(Int(min=0, max=65535, if_empty=None, if_missing=None)) + dst_port = Field(Int(min=0, max=65535, if_empty=None, if_missing=None)) + def chained_validator(self, fields, state): + errors = dict() + if fields['protocol'] not in ('TCP', 'UDP'): + for name in ('src_port', 'dst_port'): + if fields[name] is not None: + errors[name] = u"Should be None if protocol " \ + "(%(protocol)s) is not TCP or UDP" % fields + if errors: + raise Invalid(u"You can't specify any ports if the protocol " + u'is not TCP or UDP', fields, state, error_dict=errors) + +class RuleHandler(ListSubHandler): + r"""RuleHandler(parent) -> RuleHandler instance :: Handle a list of rules. + + This class is a helper for FirewallHandler to do all the work related to rules + administration. + + parent - The parent service handler. + """ + + handler_help = u"Manage firewall rules" + + _cont_subhandler_attr = 'rules' + _cont_subhandler_class = Rule + +class FirewallHandler(Restorable, ConfigWriter, ServiceHandler, + TransactionalHandler): + r"""FirewallHandler([pickle_dir[, config_dir]]) -> FirewallHandler instance. + + Handles firewall commands using iptables. + + pickle_dir - Directory where to write the persistent configuration data. + + config_dir - Directory where to store de generated configuration files. + + Both defaults to the current working directory. + """ + + handler_help = u"Manage firewall service" + + _persistent_attrs = ['rules'] + + _restorable_defaults = dict(rules=list()) + + _config_writer_files = 'iptables.sh' + _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') + + def __init__(self, pickle_dir='.', config_dir='.'): + r"Initialize the object, see class documentation for details." + log.debug(u'FirewallHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._service_start = ('sh', path.join(self._config_writer_cfg_dir, + self._config_writer_files)) + self._service_stop = ('iptables', '-t', 'filter', '-F') + self._service_restart = self._service_start + self._service_reload = self._service_start + self._config_build_templates() + ServiceHandler.__init__(self) + self.rule = RuleHandler(self) + + def _get_config_vars(self, config_file): + return dict(rules=self.rules) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + import os + + fw_handler = FirewallHandler() + + def dump(): + print '-' * 80 + print 'Rules:' + print fw_handler.rule.show() + print '-' * 80 + + dump() + + fw_handler.rule.add('input', 'drop', protocol='icmp') + + fw_handler.rule.update(0, dst='192.168.0.188/32') + + fw_handler.rule.add('output', 'accept', '192.168.1.0/24') + + fw_handler.commit() + + fw_handler.stop() + + dump() + + os.system('rm -f *.pkl iptables.sh') + diff --git a/services/ip/__init__.py b/services/ip/__init__.py index 2bbc288..8840e22 100644 --- a/services/ip/__init__.py +++ b/services/ip/__init__.py @@ -1,376 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from subprocess import Popen, PIPE -from os import path -import logging ; log = logging.getLogger('pymin.services.ip') - -from pymin.seqtools import Sequence -from pymin.dispatcher import handler, HandlerError, Handler -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, SubHandler, call, \ - get_network_devices, ListComposedSubHandler, \ - DictComposedSubHandler, ListSubHandler, \ - Device, Address, ExecutionError - -__all__ = ('IpHandler', 'get_service') - +from handler import IpHandler def get_service(config): return IpHandler(config.ip.pickle_dir, config.ip.config_dir) - -class Hop(Sequence): - - def __init__(self, gateway, device): - self.gateway = gateway - self.device = device - - def as_tuple(self): - return (self.gateway, self.device) - - def __cmp__(self, other): - if self.gateway == other.gateway \ - and self.device == other.device: - return 0 - return cmp(id(self), id(other)) - -class HopHandler(ListSubHandler): - handler_help = u"Manage IP hops" - _cont_subhandler_attr = 'hops' - _cont_subhandler_class = Hop - - @handler('Add a hop: add ') - def add(self, dev, gw): - if not dev in self.parent.devices: - raise DeviceNotFoundError(device) - return ListSubHandler.add(self, dev, gw) - - -class Route(Sequence): - def __init__(self, net_addr, prefix, gateway): - self.net_addr = net_addr - self.prefix = prefix - self.gateway = gateway - - def update(self, net_addr=None, prefix=None, gateway=None): - if net_addr is not None: self.net_addr = net_addr - if prefix is not None: self.prefix = prefix - if gateway is not None: self.gateway = gateway - - def as_tuple(self): - return(self.net_addr, self.prefix, self.gateway) - - def __cmp__(self, other): - if self.net_addr == other.net_addr \ - and self.prefix == other.prefix \ - and self.gateway == other.gateway: - return 0 - return cmp(id(self), id(other)) - -class RouteHandler(ListComposedSubHandler): - handler_help = u"Manage IP routes" - _comp_subhandler_cont = 'devices' - _comp_subhandler_attr = 'routes' - _comp_subhandler_class = Route - - @handler(u'Adds a route to : ip route add [device]') - def add(self, net_addr, prefix, gateway, dev=None): - if dev is not None: - ListComposedSubHandler.add(self, dev, net_addr, prefix, gateway) - else: - r = Route(net_addr, prefix, gateway) - if not r in self.parent.no_device_routes: - self.parent.no_device_routes.append(r) - - @handler("Deletes a route : ip route delete [dev]") - def delete(self, index, dev=None): - if dev is not None: - ListComposedSubHandler.delete(self, dev, index) - else: - i = int(index) - del self.parent.no_device_routes[i] - - @handler("Shows routes : ip route show [dev]") - def show(self, dev=None): - if dev is not None: - return ListComposedSubHandler.show(self, dev) - else: - return self.parent.no_device_routes - -class AddressHandler(DictComposedSubHandler): - handler_help = u"Manage IP addresses" - _comp_subhandler_cont = 'devices' - _comp_subhandler_attr = 'addrs' - _comp_subhandler_class = Address - - -class DeviceHandler(SubHandler): - - handler_help = u"Manage network devices" - - def __init__(self, parent): - log.debug(u'DeviceHandler(%r)', parent) - # FIXME remove templates to execute commands - from mako.template import Template - self.parent = parent - template_dir = path.join(path.dirname(__file__), 'templates') - dev_fn = path.join(template_dir, 'device') - self.device_template = Template(filename=dev_fn) - - @handler(u'Bring the device up') - def up(self, name): - log.debug(u'DeviceHandler.up(%r)', name) - if name in self.parent.devices: - call(self.device_template.render(dev=name, action='up'), shell=True) - #bring up all the route asocitaed to the device - for route in self.parent.devices[name].routes: - try: - log.debug(u'IpHandler.up: adding %r', route) - call(self.parent._render_config('route_add', dict( - dev = name, - net_addr = route.net_addr, - prefix = route.prefix, - gateway = route.gateway, - ) - ), shell=True) - except ExecutionError, e: - log.debug(u'IpHandler.up: error adding %r -> %r', route, e) - self.parent._bring_up_no_dev_routes() - self.parent._restart_services() - else: - log.debug(u'DeviceHandler.up: device not found') - raise DeviceNotFoundError(name) - - @handler(u'Bring the device down') - def down(self, name): - log.debug(u'DeviceHandler.down(%r)', name) - if name in self.parent.devices: - call(self.device_template.render(dev=name, action='down'), shell=True) - self.parent._bring_up_no_dev_routes() - self.parent._restart_services() - else: - log.debug(u'DeviceHandler.up: device not found') - raise DeviceNotFoundError(name) - - @handler(u'List all devices') - def list(self): - log.debug(u'DeviceHandler.list()') - return self.parent.devices.keys() - - @handler(u'Get information about a device') - def show(self): - log.debug(u'DeviceHandler.show()') - return self.parent.devices.items() - -class IpHandler(Restorable, ConfigWriter, TransactionalHandler): - - handler_help = u"Manage IP devices, addresses, routes and hops" - - _persistent_attrs = ('devices','hops','no_device_routes') - - _restorable_defaults = dict( - devices=get_network_devices(), - hops = list(), - no_device_routes = list(), - ) - - _config_writer_files = ('device', 'ip_add', 'ip_del', 'ip_flush', - 'route_add', 'route_del', 'route_flush', 'hop') - _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." - log.debug(u'IpHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._config_build_templates() - self._restore() - self._write_config() - self.addr = AddressHandler(self) - self.route = RouteHandler(self) - self.dev = DeviceHandler(self) - self.hop = HopHandler(self) - self.no_device_routes = list() - self.services = list() - - def _write_config(self): - r"_write_config() -> None :: Execute all commands." - log.debug(u'IpHandler._write_config()') - for device in self.devices.values(): - log.debug(u'IpHandler._write_config: processing device %s', device) - if device.active: - self._write_config_for_device(device) - self._bring_up_no_dev_routes() - self._write_hops() - - def _bring_up_no_dev_routes(self): - log.debug(u'IpHandler._bring_up_no_dev_routes()') - for route in self.no_device_routes: - try: - log.debug(u'IpHandler._bring_up_no_dev_routes: add %r', route) - call(self._render_config('route_add', dict( - dev = None, - net_addr = route.net_addr, - prefix = route.prefix, - gateway = route.gateway, - ) - ), shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_config: error flushing -> %r', e) - - def _write_hops(self): - r"_write_hops() -> None :: Execute all hops." - log.debug(u'IpHandler._write_hops()') - if self.hops: - log.debug(u'IpHandler._write_hops: we have hops: %r', self.hops) - try: - log.debug(u'IpHandler._write_hops: flushing default hops') - call('ip route del default', shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_hops: error adding -> %r', e) - try: - log.debug(u'IpHandler._write_hops: configuring hops') - #get hops for active devices - active_hops = dict() - for h in self.hops: - if h.device in self.devices: - if self.devices[h.device].active: - active_hops.append(h) - call(self._render_config('hop', dict( - hops = active_hops, - ) - ), shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_hops: error adding -> %r', e) - - def _write_config_for_device(self, device): - r"_write_config_for_device(self, device) -> None :: Execute commands." - log.debug(u'IpHandler._write_config_for_device()') - try: - log.debug(u'IpHandler._write_config_for_device: flushing routes...') - call(self._render_config('route_flush', dict(dev=device.name)), - shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_config_for_device: error flushing ' - u'-> %r', e) - try: - log.debug(u'IpHandler._write_config_for_device: flushing addrs...') - call(self._render_config('ip_flush', dict(dev=device.name)), - shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_config_for_device: error flushing ' - u'-> %r', e) - for address in device.addrs.values(): - broadcast = address.broadcast - if broadcast is None: - broadcast = '+' - try: - log.debug(u'IpHandler._write_config_for_device: adding %r', - address) - call(self._render_config('ip_add', dict( - dev = device.name, - addr = address.ip, - netmask = address.netmask, - peer = address.peer, - broadcast = broadcast, - ) - ), shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_config_for_device: error adding ' - u'-> %r', e) - for route in device.routes: - try: - log.debug(u'IpHandler._write_config_for_device: adding %r', - route) - call(self._render_config('route_add', dict( - dev = device.name, - net_addr = route.net_addr, - prefix = route.prefix, - gateway = route.gateway, - ) - ), shell=True) - except ExecutionError, e: - log.debug(u'IpHandler._write_config_for_device: error adding ' - u'-> %r', e) - - def handle_timer(self): - log.debug(u'IpHandler.handle_timer()') - self.refresh_devices() - - def refresh_devices(self): - log.debug(u'IpHandler.update_devices()') - devices = get_network_devices() - #add not registered and active devices - go_active = False - for k,v in devices.items(): - if k not in self.devices: - log.debug(u'IpHandler.update_devices: adding %r', v) - self.devices[k] = v - elif not self.devices[k].active: - self.active = True - go_active = True - self._write_config_for_device(self.devices[k]) - if go_active: - self._write_hops() - self._bring_up_no_dev_routes() - self._restart_services() - - #mark inactive devices - for k in self.devices.keys(): - go_down = False - if k not in devices: - log.debug(u'IpHandler.update_devices: removing %s', k) - self.devices[k].active = False - go_down = True - if go_down: - self._bring_up_no_dev_routes() - - def _restart_services(self): - for s in self.services: - if s._service_running: - try: - s.stop() - except ExecutionError: - pass - try: - s.start() - except ExecutionError: - pass - - #hooks a service to the ip handler, so when - #a device is brought up one can restart the service - #that need to refresh their device list - def device_up_hook(self, serv): - if hasattr(serv, 'stop') and hasattr(serv, 'start'): - self.services.append(serv) - - - - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - ip = IpHandler() - print '----------------------' - ip.hop.add('201.21.32.53','eth0') - ip.hop.add('205.65.65.25','eth1') - ip.commit() - ip.dev.up('eth0') - ip.addr.add('eth0','192.168.0.23','24','192.168.255.255') - ip.addr.add('eth0','192.168.0.26','24') - ip.commit() - ip.route.add('eth0','192.168.0.0','24','192.168.0.1') - ip.route.add('eth0','192.168.0.5','24','192.168.0.1') - ip.commit() - ip.hop.delete('201.21.32.53','eth0') - ip.route.clear('eth0') - ip.commit() - - - diff --git a/services/ip/handler.py b/services/ip/handler.py new file mode 100644 index 0000000..2bbc288 --- /dev/null +++ b/services/ip/handler.py @@ -0,0 +1,376 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from subprocess import Popen, PIPE +from os import path +import logging ; log = logging.getLogger('pymin.services.ip') + +from pymin.seqtools import Sequence +from pymin.dispatcher import handler, HandlerError, Handler +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, SubHandler, call, \ + get_network_devices, ListComposedSubHandler, \ + DictComposedSubHandler, ListSubHandler, \ + Device, Address, ExecutionError + +__all__ = ('IpHandler', 'get_service') + + +def get_service(config): + return IpHandler(config.ip.pickle_dir, config.ip.config_dir) + + +class Hop(Sequence): + + def __init__(self, gateway, device): + self.gateway = gateway + self.device = device + + def as_tuple(self): + return (self.gateway, self.device) + + def __cmp__(self, other): + if self.gateway == other.gateway \ + and self.device == other.device: + return 0 + return cmp(id(self), id(other)) + +class HopHandler(ListSubHandler): + handler_help = u"Manage IP hops" + _cont_subhandler_attr = 'hops' + _cont_subhandler_class = Hop + + @handler('Add a hop: add ') + def add(self, dev, gw): + if not dev in self.parent.devices: + raise DeviceNotFoundError(device) + return ListSubHandler.add(self, dev, gw) + + +class Route(Sequence): + def __init__(self, net_addr, prefix, gateway): + self.net_addr = net_addr + self.prefix = prefix + self.gateway = gateway + + def update(self, net_addr=None, prefix=None, gateway=None): + if net_addr is not None: self.net_addr = net_addr + if prefix is not None: self.prefix = prefix + if gateway is not None: self.gateway = gateway + + def as_tuple(self): + return(self.net_addr, self.prefix, self.gateway) + + def __cmp__(self, other): + if self.net_addr == other.net_addr \ + and self.prefix == other.prefix \ + and self.gateway == other.gateway: + return 0 + return cmp(id(self), id(other)) + +class RouteHandler(ListComposedSubHandler): + handler_help = u"Manage IP routes" + _comp_subhandler_cont = 'devices' + _comp_subhandler_attr = 'routes' + _comp_subhandler_class = Route + + @handler(u'Adds a route to : ip route add [device]') + def add(self, net_addr, prefix, gateway, dev=None): + if dev is not None: + ListComposedSubHandler.add(self, dev, net_addr, prefix, gateway) + else: + r = Route(net_addr, prefix, gateway) + if not r in self.parent.no_device_routes: + self.parent.no_device_routes.append(r) + + @handler("Deletes a route : ip route delete [dev]") + def delete(self, index, dev=None): + if dev is not None: + ListComposedSubHandler.delete(self, dev, index) + else: + i = int(index) + del self.parent.no_device_routes[i] + + @handler("Shows routes : ip route show [dev]") + def show(self, dev=None): + if dev is not None: + return ListComposedSubHandler.show(self, dev) + else: + return self.parent.no_device_routes + +class AddressHandler(DictComposedSubHandler): + handler_help = u"Manage IP addresses" + _comp_subhandler_cont = 'devices' + _comp_subhandler_attr = 'addrs' + _comp_subhandler_class = Address + + +class DeviceHandler(SubHandler): + + handler_help = u"Manage network devices" + + def __init__(self, parent): + log.debug(u'DeviceHandler(%r)', parent) + # FIXME remove templates to execute commands + from mako.template import Template + self.parent = parent + template_dir = path.join(path.dirname(__file__), 'templates') + dev_fn = path.join(template_dir, 'device') + self.device_template = Template(filename=dev_fn) + + @handler(u'Bring the device up') + def up(self, name): + log.debug(u'DeviceHandler.up(%r)', name) + if name in self.parent.devices: + call(self.device_template.render(dev=name, action='up'), shell=True) + #bring up all the route asocitaed to the device + for route in self.parent.devices[name].routes: + try: + log.debug(u'IpHandler.up: adding %r', route) + call(self.parent._render_config('route_add', dict( + dev = name, + net_addr = route.net_addr, + prefix = route.prefix, + gateway = route.gateway, + ) + ), shell=True) + except ExecutionError, e: + log.debug(u'IpHandler.up: error adding %r -> %r', route, e) + self.parent._bring_up_no_dev_routes() + self.parent._restart_services() + else: + log.debug(u'DeviceHandler.up: device not found') + raise DeviceNotFoundError(name) + + @handler(u'Bring the device down') + def down(self, name): + log.debug(u'DeviceHandler.down(%r)', name) + if name in self.parent.devices: + call(self.device_template.render(dev=name, action='down'), shell=True) + self.parent._bring_up_no_dev_routes() + self.parent._restart_services() + else: + log.debug(u'DeviceHandler.up: device not found') + raise DeviceNotFoundError(name) + + @handler(u'List all devices') + def list(self): + log.debug(u'DeviceHandler.list()') + return self.parent.devices.keys() + + @handler(u'Get information about a device') + def show(self): + log.debug(u'DeviceHandler.show()') + return self.parent.devices.items() + +class IpHandler(Restorable, ConfigWriter, TransactionalHandler): + + handler_help = u"Manage IP devices, addresses, routes and hops" + + _persistent_attrs = ('devices','hops','no_device_routes') + + _restorable_defaults = dict( + devices=get_network_devices(), + hops = list(), + no_device_routes = list(), + ) + + _config_writer_files = ('device', 'ip_add', 'ip_del', 'ip_flush', + 'route_add', 'route_del', 'route_flush', 'hop') + _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." + log.debug(u'IpHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._config_build_templates() + self._restore() + self._write_config() + self.addr = AddressHandler(self) + self.route = RouteHandler(self) + self.dev = DeviceHandler(self) + self.hop = HopHandler(self) + self.no_device_routes = list() + self.services = list() + + def _write_config(self): + r"_write_config() -> None :: Execute all commands." + log.debug(u'IpHandler._write_config()') + for device in self.devices.values(): + log.debug(u'IpHandler._write_config: processing device %s', device) + if device.active: + self._write_config_for_device(device) + self._bring_up_no_dev_routes() + self._write_hops() + + def _bring_up_no_dev_routes(self): + log.debug(u'IpHandler._bring_up_no_dev_routes()') + for route in self.no_device_routes: + try: + log.debug(u'IpHandler._bring_up_no_dev_routes: add %r', route) + call(self._render_config('route_add', dict( + dev = None, + net_addr = route.net_addr, + prefix = route.prefix, + gateway = route.gateway, + ) + ), shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_config: error flushing -> %r', e) + + def _write_hops(self): + r"_write_hops() -> None :: Execute all hops." + log.debug(u'IpHandler._write_hops()') + if self.hops: + log.debug(u'IpHandler._write_hops: we have hops: %r', self.hops) + try: + log.debug(u'IpHandler._write_hops: flushing default hops') + call('ip route del default', shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_hops: error adding -> %r', e) + try: + log.debug(u'IpHandler._write_hops: configuring hops') + #get hops for active devices + active_hops = dict() + for h in self.hops: + if h.device in self.devices: + if self.devices[h.device].active: + active_hops.append(h) + call(self._render_config('hop', dict( + hops = active_hops, + ) + ), shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_hops: error adding -> %r', e) + + def _write_config_for_device(self, device): + r"_write_config_for_device(self, device) -> None :: Execute commands." + log.debug(u'IpHandler._write_config_for_device()') + try: + log.debug(u'IpHandler._write_config_for_device: flushing routes...') + call(self._render_config('route_flush', dict(dev=device.name)), + shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_config_for_device: error flushing ' + u'-> %r', e) + try: + log.debug(u'IpHandler._write_config_for_device: flushing addrs...') + call(self._render_config('ip_flush', dict(dev=device.name)), + shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_config_for_device: error flushing ' + u'-> %r', e) + for address in device.addrs.values(): + broadcast = address.broadcast + if broadcast is None: + broadcast = '+' + try: + log.debug(u'IpHandler._write_config_for_device: adding %r', + address) + call(self._render_config('ip_add', dict( + dev = device.name, + addr = address.ip, + netmask = address.netmask, + peer = address.peer, + broadcast = broadcast, + ) + ), shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_config_for_device: error adding ' + u'-> %r', e) + for route in device.routes: + try: + log.debug(u'IpHandler._write_config_for_device: adding %r', + route) + call(self._render_config('route_add', dict( + dev = device.name, + net_addr = route.net_addr, + prefix = route.prefix, + gateway = route.gateway, + ) + ), shell=True) + except ExecutionError, e: + log.debug(u'IpHandler._write_config_for_device: error adding ' + u'-> %r', e) + + def handle_timer(self): + log.debug(u'IpHandler.handle_timer()') + self.refresh_devices() + + def refresh_devices(self): + log.debug(u'IpHandler.update_devices()') + devices = get_network_devices() + #add not registered and active devices + go_active = False + for k,v in devices.items(): + if k not in self.devices: + log.debug(u'IpHandler.update_devices: adding %r', v) + self.devices[k] = v + elif not self.devices[k].active: + self.active = True + go_active = True + self._write_config_for_device(self.devices[k]) + if go_active: + self._write_hops() + self._bring_up_no_dev_routes() + self._restart_services() + + #mark inactive devices + for k in self.devices.keys(): + go_down = False + if k not in devices: + log.debug(u'IpHandler.update_devices: removing %s', k) + self.devices[k].active = False + go_down = True + if go_down: + self._bring_up_no_dev_routes() + + def _restart_services(self): + for s in self.services: + if s._service_running: + try: + s.stop() + except ExecutionError: + pass + try: + s.start() + except ExecutionError: + pass + + #hooks a service to the ip handler, so when + #a device is brought up one can restart the service + #that need to refresh their device list + def device_up_hook(self, serv): + if hasattr(serv, 'stop') and hasattr(serv, 'start'): + self.services.append(serv) + + + + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + ip = IpHandler() + print '----------------------' + ip.hop.add('201.21.32.53','eth0') + ip.hop.add('205.65.65.25','eth1') + ip.commit() + ip.dev.up('eth0') + ip.addr.add('eth0','192.168.0.23','24','192.168.255.255') + ip.addr.add('eth0','192.168.0.26','24') + ip.commit() + ip.route.add('eth0','192.168.0.0','24','192.168.0.1') + ip.route.add('eth0','192.168.0.5','24','192.168.0.1') + ip.commit() + ip.hop.delete('201.21.32.53','eth0') + ip.route.clear('eth0') + ip.commit() + + + diff --git a/services/nat/__init__.py b/services/nat/__init__.py index 32e2608..0deab9a 100644 --- a/services/nat/__init__.py +++ b/services/nat/__init__.py @@ -1,285 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from os import path -import logging ; log = logging.getLogger('pymin.services.nat') - -from pymin.seqtools import Sequence -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, RestartHandler, \ - ReloadHandler, TransactionalHandler, \ - ServiceHandler, ListSubHandler, call - -__all__ = ('NatHandler', 'get_service') - +from handler import NatHandler def get_service(config): return NatHandler(config.nat.pickle_dir) - -class PortForward(Sequence): - r"""PortForward(dev, protocol, port, dst[, dst_port[, ...]]) -> PortForward. - - dev - Netword device to use. - protocol - TCP or UDP. - port - Port to forward. - dst - Destination IP address. - dst_port - Destination port (at dst). - src_net - Source network to apply the forward (as IP/mask). - dst_net - Source network to apply the forward (as IP/mask). - """ - - def __init__(self, dev, protocol, port, dst, dst_port=None, src_net=None, - dst_net=None): - r"Initialize object, see class documentation for details." - # TODO Validate - self.dev = dev - self.protocol = protocol - self.port = port - self.dst = dst - self.dst_port = dst_port - self.src_net = src_net - self.dst_net = dst_net - - def update(self, dev=None, protocol=None, port=None, dst=None, - dst_port=None, src_net=None, dst_net=None): - r"update([dev[, ...]]) -> Update the values of a port (see class doc)." - # TODO Validate - if dev is not None: self.dev = dev - if protocol is not None: self.protocol = protocol - if port is not None: self.port = port - if dst is not None: self.dst = dst - if dst_port is not None: self.dst_port = dst_port - if src_net is not None: self.src_net = src_net - if dst_net is not None: self.dst_net = dst_net - - def as_tuple(self): - r"Return a tuple representing the port forward." - return (self.dev, self.protocol, self.port, self.dst, self.dst_port, - self.src_net, self.dst_net) - - def as_call_list(self, index=None): - if self.dst_port is not None: - self.dst = self.dst + ':' + self.dst_port - cmd = ['-t', 'nat', '-I', 'PREROUTING'] - if index is not None: - cmd.append(str(index)) - cmd.extend(('-i', self.dev, '-j', 'DNAT', '--to', self.dst, - '-p', self.protocol, '--dport', self.port)) - if self.src_net is not None: - cmd.extend(('-s', self.src_net)) - if self.dst_net is not None: - cmd.extend(('-d', self.dst_net)) - return cmd - -class PortForwardHandler(ListSubHandler): - r"""PortForwardHandler(parent) -> PortForwardHandler instance. - - This class is a helper for NatHandler to do all the work related to port - forwarding. - - parent - The parent service handler. - """ - - handler_help = u"Manage NAT port forwarding." - - _cont_subhandler_attr = 'ports' - _cont_subhandler_class = PortForward - -class SNat(Sequence): - r"""SNat(dev, src[, src_net]) -> SNat instance. - - dev - Netword device to use. - src - Source IP address. - src_net - Source network to apply the NAT (as IP/mask). - """ - - def __init__(self, dev, src, src_net=None): - r"Initialize object, see class documentation for details." - # TODO Validate - self.dev = dev - self.src = src - self.src_net = src_net - - def update(self, dev=None, src=None, src_net=None): - r"update([dev[, ...]]) -> Update the values of a snat (see class doc)." - # TODO Validate - if dev is not None: self.dev = dev - if src is not None: self.src = src - if src_net is not None: self.src_net = src_net - - def __cmp__(self, other): - r"Compares two SNat objects." - return cmp(self.as_tuple(), other.as_tuple()) - - def as_tuple(self): - r"Return a tuple representing the snat." - return (self.dev, self.src, self.src_net) - - def as_call_list(self, index=None): - cmd = ['-t', 'nat', '-I', 'POSTROUTING'] - if index is not None: - cmd.append(str(index)) - cmd.extend(('-o', self.dev, '-j', 'SNAT', '--to', self.src)) - if self.src_net is not None: - cmd.extend(('-s', self.src_net)) - return cmd - -class SNatHandler(ListSubHandler): - r"""SNatHandler(parent) -> SNatHandler instance. - - This class is a helper for NatHandler to do all the work related to - Source NAT. - - parent - The parent service handler. - """ - - handler_help = u"Manage source NAT." - - _cont_subhandler_attr = 'snats' - _cont_subhandler_class = SNat - -class Masq(Sequence): - r"""Masq(dev, src_net) -> Masq instance. - - dev - Netword device to use. - src_net - Source network to apply the masquerade (as IP/mask). - """ - - def __init__(self, dev, src_net): - r"Initialize object, see class documentation for details." - # TODO Validate - self.dev = dev - self.src_net = src_net - - def update(self, dev=None, src_net=None): - r"update([dev[, ...]]) -> Update the values of a masq (see class doc)." - # TODO Validate - if dev is not None: self.dev = dev - if src_net is not None: self.src_net = src_net - - def __cmp__(self, other): - r"Compares two Masq objects." - return cmp(self.as_tuple(), other.as_tuple()) - - def as_tuple(self): - r"Return a tuple representing the masquerade." - return (self.dev, self.src_net) - - def as_call_list(self, index=None): - cmd = ['-t', 'nat', '-I', 'POSTROUTING'] - if index is not None: - cmd.append(str(index)) - cmd.extend(('-o', self.dev, '-j', 'MASQUERADE', '-s', self.src_net)) - return cmd - -class MasqHandler(ListSubHandler): - r"""MasqHandler(parent) -> MasqHandler instance. - - This class is a helper for NatHandler to do all the work related to - masquerading. - - parent - The parent service handler. - """ - - handler_help = u"Manage NAT masquerading." - - _cont_subhandler_attr = 'masqs' - _cont_subhandler_class = Masq - -class NatHandler(Restorable, ConfigWriter, ReloadHandler, ServiceHandler, - TransactionalHandler): - r"""NatHandler([pickle_dir[, config_dir]]) -> NatHandler instance. - - Handles NAT commands using iptables. - - pickle_dir - Directory where to write the persistent configuration data. - - config_dir - Directory where to store de generated configuration files. - - Both defaults to the current working directory. - """ - - handler_help = u"Manage NAT (Network Address Translation) service." - - _persistent_attrs = ('ports', 'snats', 'masqs') - - _restorable_defaults = dict( - ports=list(), - snats=list(), - masqs=list(), - ) - - def _service_start(self): - log.debug(u'NatHandler._service_start(): flushing nat table') - call(('iptables', '-t', 'nat', '-F')) - for (index, port) in enumerate(self.ports): - log.debug(u'NatHandler._service_start: adding port %r', port) - call(['iptables'] + port.as_call_list(index+1)) - for (index, snat) in enumerate(self.snats): - log.debug(u'NatHandler._service_start: adding snat %r', snat) - call(['iptables'] + snat.as_call_list(index+1)) - for (index, masq) in enumerate(self.masqs): - log.debug(u'NatHandler._service_start: adding masq %r', masq) - call(['iptables'] + masq.as_call_list(index+1)) - - def _service_stop(self): - log.debug(u'NatHandler._service_stop(): flushing nat table') - call(('iptables', '-t', 'nat', '-F')) - - _service_restart = _service_start - - def __init__(self, pickle_dir='.'): - r"Initialize the object, see class documentation for details." - log.debug(u'NatHandler(%r)', pickle_dir) - self._persistent_dir = pickle_dir - ServiceHandler.__init__(self) - self.forward = PortForwardHandler(self) - self.snat = SNatHandler(self) - self.masq = MasqHandler(self) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - import os - - handler = NatHandler() - - def dump(): - print '-' * 80 - print 'Forwarded ports:' - print handler.forward.show() - print '-' * 10 - print 'SNat:' - print handler.snat.show() - print '-' * 10 - print 'Masq:' - print handler.masq.show() - print '-' * 80 - - dump() - handler.forward.add('eth0','tcp','80', '192.168.0.9', '8080') - handler.forward.update(0, dst_net='192.168.0.188/32') - handler.forward.add('eth0', 'udp', '53', '192.168.1.0') - handler.commit() - handler.stop() - dump() - handler.snat.add('eth0', '192.168.0.9') - handler.snat.update(0, src_net='192.168.0.188/32') - handler.snat.add('eth0', '192.168.1.0') - handler.commit() - dump() - dump() - handler.masq.add('eth0', '192.168.0.9/24') - handler.masq.update(0, src_net='192.168.0.188/30') - handler.masq.add('eth1', '192.168.1.0/24') - handler.commit() - dump() - - os.system('rm -f *.pkl') - diff --git a/services/nat/handler.py b/services/nat/handler.py new file mode 100644 index 0000000..32e2608 --- /dev/null +++ b/services/nat/handler.py @@ -0,0 +1,285 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from os import path +import logging ; log = logging.getLogger('pymin.services.nat') + +from pymin.seqtools import Sequence +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, RestartHandler, \ + ReloadHandler, TransactionalHandler, \ + ServiceHandler, ListSubHandler, call + +__all__ = ('NatHandler', 'get_service') + + +def get_service(config): + return NatHandler(config.nat.pickle_dir) + + +class PortForward(Sequence): + r"""PortForward(dev, protocol, port, dst[, dst_port[, ...]]) -> PortForward. + + dev - Netword device to use. + protocol - TCP or UDP. + port - Port to forward. + dst - Destination IP address. + dst_port - Destination port (at dst). + src_net - Source network to apply the forward (as IP/mask). + dst_net - Source network to apply the forward (as IP/mask). + """ + + def __init__(self, dev, protocol, port, dst, dst_port=None, src_net=None, + dst_net=None): + r"Initialize object, see class documentation for details." + # TODO Validate + self.dev = dev + self.protocol = protocol + self.port = port + self.dst = dst + self.dst_port = dst_port + self.src_net = src_net + self.dst_net = dst_net + + def update(self, dev=None, protocol=None, port=None, dst=None, + dst_port=None, src_net=None, dst_net=None): + r"update([dev[, ...]]) -> Update the values of a port (see class doc)." + # TODO Validate + if dev is not None: self.dev = dev + if protocol is not None: self.protocol = protocol + if port is not None: self.port = port + if dst is not None: self.dst = dst + if dst_port is not None: self.dst_port = dst_port + if src_net is not None: self.src_net = src_net + if dst_net is not None: self.dst_net = dst_net + + def as_tuple(self): + r"Return a tuple representing the port forward." + return (self.dev, self.protocol, self.port, self.dst, self.dst_port, + self.src_net, self.dst_net) + + def as_call_list(self, index=None): + if self.dst_port is not None: + self.dst = self.dst + ':' + self.dst_port + cmd = ['-t', 'nat', '-I', 'PREROUTING'] + if index is not None: + cmd.append(str(index)) + cmd.extend(('-i', self.dev, '-j', 'DNAT', '--to', self.dst, + '-p', self.protocol, '--dport', self.port)) + if self.src_net is not None: + cmd.extend(('-s', self.src_net)) + if self.dst_net is not None: + cmd.extend(('-d', self.dst_net)) + return cmd + +class PortForwardHandler(ListSubHandler): + r"""PortForwardHandler(parent) -> PortForwardHandler instance. + + This class is a helper for NatHandler to do all the work related to port + forwarding. + + parent - The parent service handler. + """ + + handler_help = u"Manage NAT port forwarding." + + _cont_subhandler_attr = 'ports' + _cont_subhandler_class = PortForward + +class SNat(Sequence): + r"""SNat(dev, src[, src_net]) -> SNat instance. + + dev - Netword device to use. + src - Source IP address. + src_net - Source network to apply the NAT (as IP/mask). + """ + + def __init__(self, dev, src, src_net=None): + r"Initialize object, see class documentation for details." + # TODO Validate + self.dev = dev + self.src = src + self.src_net = src_net + + def update(self, dev=None, src=None, src_net=None): + r"update([dev[, ...]]) -> Update the values of a snat (see class doc)." + # TODO Validate + if dev is not None: self.dev = dev + if src is not None: self.src = src + if src_net is not None: self.src_net = src_net + + def __cmp__(self, other): + r"Compares two SNat objects." + return cmp(self.as_tuple(), other.as_tuple()) + + def as_tuple(self): + r"Return a tuple representing the snat." + return (self.dev, self.src, self.src_net) + + def as_call_list(self, index=None): + cmd = ['-t', 'nat', '-I', 'POSTROUTING'] + if index is not None: + cmd.append(str(index)) + cmd.extend(('-o', self.dev, '-j', 'SNAT', '--to', self.src)) + if self.src_net is not None: + cmd.extend(('-s', self.src_net)) + return cmd + +class SNatHandler(ListSubHandler): + r"""SNatHandler(parent) -> SNatHandler instance. + + This class is a helper for NatHandler to do all the work related to + Source NAT. + + parent - The parent service handler. + """ + + handler_help = u"Manage source NAT." + + _cont_subhandler_attr = 'snats' + _cont_subhandler_class = SNat + +class Masq(Sequence): + r"""Masq(dev, src_net) -> Masq instance. + + dev - Netword device to use. + src_net - Source network to apply the masquerade (as IP/mask). + """ + + def __init__(self, dev, src_net): + r"Initialize object, see class documentation for details." + # TODO Validate + self.dev = dev + self.src_net = src_net + + def update(self, dev=None, src_net=None): + r"update([dev[, ...]]) -> Update the values of a masq (see class doc)." + # TODO Validate + if dev is not None: self.dev = dev + if src_net is not None: self.src_net = src_net + + def __cmp__(self, other): + r"Compares two Masq objects." + return cmp(self.as_tuple(), other.as_tuple()) + + def as_tuple(self): + r"Return a tuple representing the masquerade." + return (self.dev, self.src_net) + + def as_call_list(self, index=None): + cmd = ['-t', 'nat', '-I', 'POSTROUTING'] + if index is not None: + cmd.append(str(index)) + cmd.extend(('-o', self.dev, '-j', 'MASQUERADE', '-s', self.src_net)) + return cmd + +class MasqHandler(ListSubHandler): + r"""MasqHandler(parent) -> MasqHandler instance. + + This class is a helper for NatHandler to do all the work related to + masquerading. + + parent - The parent service handler. + """ + + handler_help = u"Manage NAT masquerading." + + _cont_subhandler_attr = 'masqs' + _cont_subhandler_class = Masq + +class NatHandler(Restorable, ConfigWriter, ReloadHandler, ServiceHandler, + TransactionalHandler): + r"""NatHandler([pickle_dir[, config_dir]]) -> NatHandler instance. + + Handles NAT commands using iptables. + + pickle_dir - Directory where to write the persistent configuration data. + + config_dir - Directory where to store de generated configuration files. + + Both defaults to the current working directory. + """ + + handler_help = u"Manage NAT (Network Address Translation) service." + + _persistent_attrs = ('ports', 'snats', 'masqs') + + _restorable_defaults = dict( + ports=list(), + snats=list(), + masqs=list(), + ) + + def _service_start(self): + log.debug(u'NatHandler._service_start(): flushing nat table') + call(('iptables', '-t', 'nat', '-F')) + for (index, port) in enumerate(self.ports): + log.debug(u'NatHandler._service_start: adding port %r', port) + call(['iptables'] + port.as_call_list(index+1)) + for (index, snat) in enumerate(self.snats): + log.debug(u'NatHandler._service_start: adding snat %r', snat) + call(['iptables'] + snat.as_call_list(index+1)) + for (index, masq) in enumerate(self.masqs): + log.debug(u'NatHandler._service_start: adding masq %r', masq) + call(['iptables'] + masq.as_call_list(index+1)) + + def _service_stop(self): + log.debug(u'NatHandler._service_stop(): flushing nat table') + call(('iptables', '-t', 'nat', '-F')) + + _service_restart = _service_start + + def __init__(self, pickle_dir='.'): + r"Initialize the object, see class documentation for details." + log.debug(u'NatHandler(%r)', pickle_dir) + self._persistent_dir = pickle_dir + ServiceHandler.__init__(self) + self.forward = PortForwardHandler(self) + self.snat = SNatHandler(self) + self.masq = MasqHandler(self) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + import os + + handler = NatHandler() + + def dump(): + print '-' * 80 + print 'Forwarded ports:' + print handler.forward.show() + print '-' * 10 + print 'SNat:' + print handler.snat.show() + print '-' * 10 + print 'Masq:' + print handler.masq.show() + print '-' * 80 + + dump() + handler.forward.add('eth0','tcp','80', '192.168.0.9', '8080') + handler.forward.update(0, dst_net='192.168.0.188/32') + handler.forward.add('eth0', 'udp', '53', '192.168.1.0') + handler.commit() + handler.stop() + dump() + handler.snat.add('eth0', '192.168.0.9') + handler.snat.update(0, src_net='192.168.0.188/32') + handler.snat.add('eth0', '192.168.1.0') + handler.commit() + dump() + dump() + handler.masq.add('eth0', '192.168.0.9/24') + handler.masq.update(0, src_net='192.168.0.188/30') + handler.masq.add('eth1', '192.168.1.0/24') + handler.commit() + dump() + + os.system('rm -f *.pkl') + diff --git a/services/ppp/__init__.py b/services/ppp/__init__.py index 9c6bdff..cc53450 100644 --- a/services/ppp/__init__.py +++ b/services/ppp/__init__.py @@ -1,245 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -import os -import subprocess -from os import path -from signal import SIGTERM -import logging ; log = logging.getLogger('pymin.services.ppp') - -from pymin.seqtools import Sequence -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, ReloadHandler, \ - TransactionalHandler, DictSubHandler, call - -__all__ = ('PppHandler', 'get_service') - +from handler import PppHandler def get_service(config): return PppHandler(config.ppp.pickle_dir, config.ppp.config_dir) - -class ConnectionError(HandlerError, KeyError): - r""" - ConnectionError(hostname) -> ConnectionError instance - - This is the base exception for all connection related errors. - """ - - def __init__(self, connection): - r"Initialize the object. See class documentation for more info." - self.message = u'Connection error: "%s"' % connection - -class ConnectionNotFoundError(ConnectionError): - def __init__(self, connection): - r"Initialize the object. See class documentation for more info." - self.message = u'Connection not found error: "%s"' % connection - -class Connection(Sequence): - - def __init__(self, name, username, password, type, **kw): - self.name = name - self.username = username - self.password = password - self.type = type - self._running = False - if type == 'OE': - if not 'device' in kw: - raise ConnectionError('Bad arguments for type=OE') - self.device = kw['device'] - elif type == 'TUNNEL': - if not 'server' in kw: - raise ConnectionError('Bad arguments for type=TUNNEL') - self.server = kw['server'] - self.username = self.username.replace('\\','\\\\') - elif type == 'PPP': - if not 'device' in kw: - raise ConnectionError('Bad arguments for type=PPP') - self.device = kw['device'] - else: - raise ConnectionError('Bad arguments, unknown or unspecified type') - - def as_tuple(self): - if self.type == 'TUNNEL': - return (self.name, self.username, self.password, self.type, self.server) - elif self.type == 'PPP' or self.type == 'OE': - return (self.name, self.username, self.password, self.type, self.device) - - def update(self, device=None, username=None, password=None): - if device is not None: - self.device = device - if username is not None: - self.username = username - if password is not None: - self.password = password - - -class ConnectionHandler(DictSubHandler): - - handler_help = u"Manages connections for the ppp service" - - _cont_subhandler_attr = 'conns' - _cont_subhandler_class = Connection - -class PppHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler): - - handler_help = u"Manage ppp service" - - _persistent_attrs = ['conns'] - - _restorable_defaults = dict( - conns = dict(), - ) - - _config_writer_files = ('options.X','pap-secrets','chap-secrets','nameX') - _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') - - def __init__(self, pickle_dir='.', config_dir='.'): - r"Initialize Ppphandler object, see class documentation for details." - log.debug(u'PppHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._config_build_templates() - self._restore() - log.debug(u'PppHandler(): restoring connections...') - for conn in self.conns.values(): - if conn._running: - log.debug(u'PppHandler(): starting connection %r', conn.name) - conn._running = False - self.start(conn.name) - self.conn = ConnectionHandler(self) - - @handler(u'Start one or all the connections.') - def start(self, name=None): - log.debug(u'PppHandler.start(%r)', name) - names = [name] - if name is None: - names = self.conns.keys() - for name in names: - if name in self.conns: - if not self.conns[name]._running: - log.debug(u'PppHandler.start: starting connection %r', name) - call(('pppd', 'call', name)) - self.conns[name]._running = True - self._dump_attr('conns') - else: - log.debug(u'PppHandler.start: connection not found') - raise ConnectionNotFoundError(name) - - @handler(u'Stop one or all the connections.') - def stop(self, name=None): - log.debug(u'PppHandler.stop(%r)', name) - names = [name] - names = [name] - if name is None: - names = self.conns.keys() - for name in names: - if name in self.conns: - if self.conns[name]._running: - pid_file = '/var/run/ppp-' + name + '.pid' - log.debug(u'PppHandler.stop: getting pid from %r', pid_file) - if path.exists(pid_file): - pid = file(pid_file).readline() - pid = int(pid.strip()) - try: - log.debug(u'PppHandler.stop: killing pid %r', pid) - os.kill(pid, SIGTERM) - except OSError, e: - log.debug(u'PppHandler.stop: error killing: %r', e) - else: - log.debug(u'PppHandler.stop: pid file not found') - self.conns[name]._running = False - self._dump_attr('conns') - else: - log.debug(u'PppHandler.stop: connection not running') - else: - log.debug(u'PppHandler.stop: connection not found') - raise ConnectionNotFoundError(name) - - @handler(u'Restart one or all the connections (even disconnected ones).') - def restart(self, name=None): - log.debug(u'PppHandler.restart(%r)', name) - names = [name] - if name is None: - names = self.conns.keys() - for name in names: - self.stop(name) - self.start(name) - - @handler(u'Restart only one or all the already running connections.') - def reload(self, name=None): - r"reload() -> None :: Reload the configuration of the service." - log.debug(u'PppHandler.reload(%r)', name) - names = [name] - if name is None: - names = self.conns.keys() - for name in names: - if self.conns[name]._running: - self.stop(name) - self.start(name) - - @handler(u'Tell if the service is running.') - def running(self, name=None): - r"reload() -> None :: Reload the configuration of the service." - log.debug(u'PppHandler.running(%r)', name) - if name is None: - return [c.name for c in self.conns.values() if c._running] - if name in self.conns: - return int(self.conns[name]._running) - else: - log.debug(u'PppHandler.running: connection not found') - raise ConnectionNotFoundError(name) - - def handle_timer(self): - log.debug(u'PppHandler.handle_timer()') - for c in self.conns.values(): - log.debug(u'PppHandler.handle_timer: processing connection %r', c) - p = subprocess.Popen(('pgrep', '-f', 'pppd call ' + c.name), - stdout=subprocess.PIPE) - pid = p.communicate()[0] - if p.returncode == 0 and len(pid) > 0: - log.debug(u'PppHandler.handle_timer: pid present, running') - c._running = True - else: - log.debug(u'PppHandler.handle_timer: pid absent, NOT running') - c._running = False - - def _write_config(self): - r"_write_config() -> None :: Generate all the configuration files." - log.debug(u'PppHandler._write_config()') - #guardo los pass que van el pap-secrets - vars_pap = dict() - for conn in self.conns.values(): - if conn.type == 'OE' or conn.type == 'PPP': - vars_pap[conn.name] = conn - vars = dict(conns=vars_pap) - self._write_single_config('pap-secrets','pap-secrets',vars) - #guardo los pass que van el chap-secrets - vars_chap = dict() - for conn in self.conns.values(): - if conn.type == 'TUNNEL' : - vars_chap[conn.name] = conn - vars = dict(conns=vars_chap) - self._write_single_config('chap-secrets','chap-secrets',vars) - #guard las conns - for conn in self.conns.values(): - vars = dict(conn=conn) - self._write_single_config('nameX',conn.name, vars) - self._write_single_config('options.X','options.' + conn.name, vars) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - p = PppHandler() - p.conn.add('ppp_c','nico','nico',type='PPP',device='tty0') - p.conn.add('pppoe_c','fede','fede',type='OE',device='tty1') - p.conn.add('ppptunnel_c','dominio\luca','luca',type='TUNNEL',server='192.168.0.23') - p.commit() - print p.conn.list() - print p.conn.show() - diff --git a/services/ppp/handler.py b/services/ppp/handler.py new file mode 100644 index 0000000..9c6bdff --- /dev/null +++ b/services/ppp/handler.py @@ -0,0 +1,245 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +import os +import subprocess +from os import path +from signal import SIGTERM +import logging ; log = logging.getLogger('pymin.services.ppp') + +from pymin.seqtools import Sequence +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, ReloadHandler, \ + TransactionalHandler, DictSubHandler, call + +__all__ = ('PppHandler', 'get_service') + + +def get_service(config): + return PppHandler(config.ppp.pickle_dir, config.ppp.config_dir) + + +class ConnectionError(HandlerError, KeyError): + r""" + ConnectionError(hostname) -> ConnectionError instance + + This is the base exception for all connection related errors. + """ + + def __init__(self, connection): + r"Initialize the object. See class documentation for more info." + self.message = u'Connection error: "%s"' % connection + +class ConnectionNotFoundError(ConnectionError): + def __init__(self, connection): + r"Initialize the object. See class documentation for more info." + self.message = u'Connection not found error: "%s"' % connection + +class Connection(Sequence): + + def __init__(self, name, username, password, type, **kw): + self.name = name + self.username = username + self.password = password + self.type = type + self._running = False + if type == 'OE': + if not 'device' in kw: + raise ConnectionError('Bad arguments for type=OE') + self.device = kw['device'] + elif type == 'TUNNEL': + if not 'server' in kw: + raise ConnectionError('Bad arguments for type=TUNNEL') + self.server = kw['server'] + self.username = self.username.replace('\\','\\\\') + elif type == 'PPP': + if not 'device' in kw: + raise ConnectionError('Bad arguments for type=PPP') + self.device = kw['device'] + else: + raise ConnectionError('Bad arguments, unknown or unspecified type') + + def as_tuple(self): + if self.type == 'TUNNEL': + return (self.name, self.username, self.password, self.type, self.server) + elif self.type == 'PPP' or self.type == 'OE': + return (self.name, self.username, self.password, self.type, self.device) + + def update(self, device=None, username=None, password=None): + if device is not None: + self.device = device + if username is not None: + self.username = username + if password is not None: + self.password = password + + +class ConnectionHandler(DictSubHandler): + + handler_help = u"Manages connections for the ppp service" + + _cont_subhandler_attr = 'conns' + _cont_subhandler_class = Connection + +class PppHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler): + + handler_help = u"Manage ppp service" + + _persistent_attrs = ['conns'] + + _restorable_defaults = dict( + conns = dict(), + ) + + _config_writer_files = ('options.X','pap-secrets','chap-secrets','nameX') + _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') + + def __init__(self, pickle_dir='.', config_dir='.'): + r"Initialize Ppphandler object, see class documentation for details." + log.debug(u'PppHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._config_build_templates() + self._restore() + log.debug(u'PppHandler(): restoring connections...') + for conn in self.conns.values(): + if conn._running: + log.debug(u'PppHandler(): starting connection %r', conn.name) + conn._running = False + self.start(conn.name) + self.conn = ConnectionHandler(self) + + @handler(u'Start one or all the connections.') + def start(self, name=None): + log.debug(u'PppHandler.start(%r)', name) + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if name in self.conns: + if not self.conns[name]._running: + log.debug(u'PppHandler.start: starting connection %r', name) + call(('pppd', 'call', name)) + self.conns[name]._running = True + self._dump_attr('conns') + else: + log.debug(u'PppHandler.start: connection not found') + raise ConnectionNotFoundError(name) + + @handler(u'Stop one or all the connections.') + def stop(self, name=None): + log.debug(u'PppHandler.stop(%r)', name) + names = [name] + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if name in self.conns: + if self.conns[name]._running: + pid_file = '/var/run/ppp-' + name + '.pid' + log.debug(u'PppHandler.stop: getting pid from %r', pid_file) + if path.exists(pid_file): + pid = file(pid_file).readline() + pid = int(pid.strip()) + try: + log.debug(u'PppHandler.stop: killing pid %r', pid) + os.kill(pid, SIGTERM) + except OSError, e: + log.debug(u'PppHandler.stop: error killing: %r', e) + else: + log.debug(u'PppHandler.stop: pid file not found') + self.conns[name]._running = False + self._dump_attr('conns') + else: + log.debug(u'PppHandler.stop: connection not running') + else: + log.debug(u'PppHandler.stop: connection not found') + raise ConnectionNotFoundError(name) + + @handler(u'Restart one or all the connections (even disconnected ones).') + def restart(self, name=None): + log.debug(u'PppHandler.restart(%r)', name) + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + self.stop(name) + self.start(name) + + @handler(u'Restart only one or all the already running connections.') + def reload(self, name=None): + r"reload() -> None :: Reload the configuration of the service." + log.debug(u'PppHandler.reload(%r)', name) + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if self.conns[name]._running: + self.stop(name) + self.start(name) + + @handler(u'Tell if the service is running.') + def running(self, name=None): + r"reload() -> None :: Reload the configuration of the service." + log.debug(u'PppHandler.running(%r)', name) + if name is None: + return [c.name for c in self.conns.values() if c._running] + if name in self.conns: + return int(self.conns[name]._running) + else: + log.debug(u'PppHandler.running: connection not found') + raise ConnectionNotFoundError(name) + + def handle_timer(self): + log.debug(u'PppHandler.handle_timer()') + for c in self.conns.values(): + log.debug(u'PppHandler.handle_timer: processing connection %r', c) + p = subprocess.Popen(('pgrep', '-f', 'pppd call ' + c.name), + stdout=subprocess.PIPE) + pid = p.communicate()[0] + if p.returncode == 0 and len(pid) > 0: + log.debug(u'PppHandler.handle_timer: pid present, running') + c._running = True + else: + log.debug(u'PppHandler.handle_timer: pid absent, NOT running') + c._running = False + + def _write_config(self): + r"_write_config() -> None :: Generate all the configuration files." + log.debug(u'PppHandler._write_config()') + #guardo los pass que van el pap-secrets + vars_pap = dict() + for conn in self.conns.values(): + if conn.type == 'OE' or conn.type == 'PPP': + vars_pap[conn.name] = conn + vars = dict(conns=vars_pap) + self._write_single_config('pap-secrets','pap-secrets',vars) + #guardo los pass que van el chap-secrets + vars_chap = dict() + for conn in self.conns.values(): + if conn.type == 'TUNNEL' : + vars_chap[conn.name] = conn + vars = dict(conns=vars_chap) + self._write_single_config('chap-secrets','chap-secrets',vars) + #guard las conns + for conn in self.conns.values(): + vars = dict(conn=conn) + self._write_single_config('nameX',conn.name, vars) + self._write_single_config('options.X','options.' + conn.name, vars) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + p = PppHandler() + p.conn.add('ppp_c','nico','nico',type='PPP',device='tty0') + p.conn.add('pppoe_c','fede','fede',type='OE',device='tty1') + p.conn.add('ppptunnel_c','dominio\luca','luca',type='TUNNEL',server='192.168.0.23') + p.commit() + print p.conn.list() + print p.conn.show() + diff --git a/services/proxy/__init__.py b/services/proxy/__init__.py index 7a52724..ab2e84c 100644 --- a/services/proxy/__init__.py +++ b/services/proxy/__init__.py @@ -1,108 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from os import path -import logging ; log = logging.getLogger('pymin.services.proxy') - -from pymin.seqtools import Sequence -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, ParametersHandler, \ - DictSubHandler - -import crypt - - -__all__ = ('ProxyHandler', 'get_service') - +from handler import ProxyHandler def get_service(config): return ProxyHandler(config.proxy.pickle_dir, config.proxy.config_dir) - -class Host(Sequence): - def __init__(self,ip): - self.ip = ip - def as_tuple(self): - return (self.ip,) - -# TODO convert to a SetSubHandler - -class HostHandler(DictSubHandler): - - handler_help = u"Manage proxy hosts" - - _cont_subhandler_attr = 'hosts' - _cont_subhandler_class = Host - -class User(Sequence): - def __init__(self, name, password): - self.name = name - self.password = crypt.crypt(password,'BA') - def as_tuple(self): - return (self.name, self.password) - def update(self, password=None): - if password is not None: - self.password = crypt.crypt(password,'BA') - -class UserHandler(DictSubHandler): - - handler_help = u"Manage proxy users" - - _cont_subhandler_attr = 'users' - _cont_subhandler_class = User - -class ProxyHandler(Restorable, ConfigWriter, InitdHandler, - TransactionalHandler, ParametersHandler): - - handler_help = u"Manage proxy service" - - _initd_name = 'squid' - - _persistent_attrs = ('params', 'hosts', 'users') - - _restorable_defaults = dict( - hosts = dict(), - params = dict( - ip = '192.168.0.1', - port = '8080', - ), - users = dict(), - ) - - _config_writer_files = ('squid.conf','users.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." - log.debug(u'ProxyHandler(%r, %r)', pickle_dir, config_dir) - self._persistent_dir = pickle_dir - self._config_writer_cfg_dir = config_dir - self._config_build_templates() - InitdHandler.__init__(self) - self.host = HostHandler(self) - self.user = UserHandler(self) - - def _get_config_vars(self, config_file): - if config_file == 'squid.conf': - return dict(hosts=self.hosts.values(), **self.params) - return dict(users=self.users) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - px = ProxyHandler() - px.set('ip','192.66.66.66') - px.set('port','666') - px.host.add('192.168.0.25.25') - px.host.add('192.168.0.25.26') - px.host.add('192.168.0.25.27') - px.host.delete('192.168.0.25.27') - px.user.add('lala','soronga') - px.user.add('culo','sarasa') - px.commit() diff --git a/services/proxy/handler.py b/services/proxy/handler.py new file mode 100644 index 0000000..7a52724 --- /dev/null +++ b/services/proxy/handler.py @@ -0,0 +1,108 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from os import path +import logging ; log = logging.getLogger('pymin.services.proxy') + +from pymin.seqtools import Sequence +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, ParametersHandler, \ + DictSubHandler + +import crypt + + +__all__ = ('ProxyHandler', 'get_service') + + +def get_service(config): + return ProxyHandler(config.proxy.pickle_dir, config.proxy.config_dir) + + +class Host(Sequence): + def __init__(self,ip): + self.ip = ip + def as_tuple(self): + return (self.ip,) + +# TODO convert to a SetSubHandler + +class HostHandler(DictSubHandler): + + handler_help = u"Manage proxy hosts" + + _cont_subhandler_attr = 'hosts' + _cont_subhandler_class = Host + +class User(Sequence): + def __init__(self, name, password): + self.name = name + self.password = crypt.crypt(password,'BA') + def as_tuple(self): + return (self.name, self.password) + def update(self, password=None): + if password is not None: + self.password = crypt.crypt(password,'BA') + +class UserHandler(DictSubHandler): + + handler_help = u"Manage proxy users" + + _cont_subhandler_attr = 'users' + _cont_subhandler_class = User + +class ProxyHandler(Restorable, ConfigWriter, InitdHandler, + TransactionalHandler, ParametersHandler): + + handler_help = u"Manage proxy service" + + _initd_name = 'squid' + + _persistent_attrs = ('params', 'hosts', 'users') + + _restorable_defaults = dict( + hosts = dict(), + params = dict( + ip = '192.168.0.1', + port = '8080', + ), + users = dict(), + ) + + _config_writer_files = ('squid.conf','users.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." + log.debug(u'ProxyHandler(%r, %r)', pickle_dir, config_dir) + self._persistent_dir = pickle_dir + self._config_writer_cfg_dir = config_dir + self._config_build_templates() + InitdHandler.__init__(self) + self.host = HostHandler(self) + self.user = UserHandler(self) + + def _get_config_vars(self, config_file): + if config_file == 'squid.conf': + return dict(hosts=self.hosts.values(), **self.params) + return dict(users=self.users) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + px = ProxyHandler() + px.set('ip','192.66.66.66') + px.set('port','666') + px.host.add('192.168.0.25.25') + px.host.add('192.168.0.25.26') + px.host.add('192.168.0.25.27') + px.host.delete('192.168.0.25.27') + px.user.add('lala','soronga') + px.user.add('culo','sarasa') + px.commit() diff --git a/services/qos/__init__.py b/services/qos/__init__.py index c175049..78e8134 100644 --- a/services/qos/__init__.py +++ b/services/qos/__init__.py @@ -1,291 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from subprocess import Popen, PIPE -from os import path - -from pymin.seqtools import Sequence -from pymin.dispatcher import handler, HandlerError, Handler -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, SubHandler, call, \ - get_network_devices, ListComposedSubHandler, \ - DictComposedSubHandler, ExecutionError - -__all__ = ('QoSHandler', 'get_service') - +from handler import QoSHandler def get_service(config): return QoSHandler(config.qos.pickle_dir, config.qos.config_dir) - -class DeviceError(HandlerError): - - def __init__(self, dev): - self.message = u'Devive error : "%s"' % dev - - -class DeviceNotFoundError(DeviceError): - - def __init__(self, dev): - self.message = u'Device not found : "%s"' % dev - - -class ClassError(HandlerError): - - def __init__(self, cls): - self.message = u'Class error : "%s"' % cls - - -class ClassNotFoundError(ClassError): - - def __init__(self, cls): - self.message = u'Class not found : "%s"' % cls - - -class ClassAlreadyExistsError(ClassError): - - def __init__(self, cls): - self.message = u'Class already exists : "%s"' % cls - - -class HostError(HandlerError): - - def __init__(self, host): - self.message = u'Host error : "%s"' % host - - -class HostNotFoundError(HostError): - - def __init__(self, ip): - self.message = u'Host not found : "%s"' % host - - -class HostAlreadyExistsError(HostError): - - def __init__(self, ip): - self.message = u'Host already exists : "%s"' % host - - -class Class(Sequence): - - def __init__(self, cid, rate=None): - self.cid = cid - self.rate = rate - self.hosts = dict() - - def as_tuple(self): - return (self.cid, self.rate) - - def __cmp__(self, other): - if self.cid == other.cid: - return 0 - return cmp(id(self), id(other)) - - -class ClassHandler(Handler): - - def __init__(self, parent): - self.parent = parent - - @handler('Adds a class : add ') - def add(self, dev, cid, rate): - if not dev in self.parent.devices: - raise DeviceNotFoundError(dev) - - try: - self.parent.devices[dev].classes[cid] = Class(cid, rate) - except ValueError: - raise ClassAlreadyExistsError(cid + ' -> ' + dev) - - @handler(u'Deletes a class : delete ') - def delete(self, dev, cid): - if not dev in self.parent.devices: - raise DeviceNotFoundError(dev) - - try: - del self.parent.devices[dev].classes[cid] - except KeyError: - raise ClassNotFoundError(cid + ' -> ' + dev) - - @handler(u'Lists classes : list ') - def list(self, dev): - try: - k = self.parent.devices[dev].classes.items() - except KeyError: - k = dict() - return k - - -class Host(Sequence): - - def __init__(self, ip): - self.ip = ip - - def as_tuple(self): - return (self.ip) - - def __cmp__(self, other): - if self.ip == other.ip: - return 0 - return cmp(id(self), id(other)) - - -class HostHandler(SubHandler): - - def __init__(self, parent): - self.parent = parent - - @handler('Adds a host to a class : add ') - def add(self, dev, cid, ip): - if not dev in self.parent.devices: - raise DeviceNotFoundError(dev) - - if not cid in self.parent.devices[dev].classes: - raise ClassNotFoundError(cid) - - try: - self.parent.devices[dev].classes[cid].hosts[ip] = Host(ip) - except ValueError: - raise HostAlreadyExistsError(h + ' -> ' + dev) - - @handler(u'Lists hosts : list ') - def list(self, dev, cid): - try: - k = self.parent.devices[dev].classes[cid].hosts.keys() - except KeyError: - k = dict() - return k - - -class Device(Sequence): - - def __init__(self, name, mac): - self.name = name - self.mac = mac - self.classes = dict() - - def as_tuple(self): - return (self.name, self.mac) - - -class DeviceHandler(SubHandler): - - handler_help = u"Manage network devices" - - def __init__(self, parent): - # FIXME remove templates to execute commands - from mako.template import Template - self.parent = parent - template_dir = path.join(path.dirname(__file__), 'templates') - dev_fn = path.join(template_dir, 'device') - self.device_template = Template(filename=dev_fn) - - @handler(u'Bring the device up') - def up(self, name): - if name in self.parent.devices: - try: - call(self.device_template.render(dev=name, action='add'), shell=True) - except ExecutionError: - pass - else: - raise DeviceNotFoundError(name) - - @handler(u'Bring the device down') - def down(self, name): - if name in self.parent.devices: - try: - call(self.device_template.render(dev=name, action='del'), shell=True) - except ExecutionError: - pass - else: - raise DeviceNotFoundError(name) - - @handler(u'List all devices') - def list(self): - return self.parent.devices.keys() - - @handler(u'Get information about a device') - def show(self): - return self.parent.devices.items() - - -class QoSHandler(Restorable, ConfigWriter, TransactionalHandler): - - handler_help = u"Manage QoS devices, classes and hosts" - - _persistent_attrs = ('devices') - - _restorable_defaults = dict( - devices=dict((dev, Device(dev, mac)) - for (dev, mac) in get_network_devices().items()) - ) - - _config_writer_files = ('device', 'class_add', 'class_del', 'host_add') - - _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') - - def __init__(self, pickle_dir='.', config_dir='.'): - r"Initialize QoSHandler 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._write_config() - self.dev = DeviceHandler(self) - self.cls = ClassHandler(self) - self.host = HostHandler(self) - - def _write_config(self): - r"_write_config() -> None :: Execute all commands." - for device in self.devices.values(): - try: - call(self._render_config('device', dict(dev=device.name, action='del')), shell=True) - except ExecutionError: - pass - - try: - call(self._render_config('device', dict(dev=device.name, action='add')), shell=True) - except ExecutionError: - pass - - for cls in device.classes.values(): - try: - call(self._render_config('class_add', dict( - dev = device.name, - cid = cls.cid, - rate = cls.rate - ) - ), shell=True) - except ExecutionError: - pass - - for host in cls.hosts.values(): - try: - call(self._render_config('host_add', dict( - dev = device.name, - ip = host.ip, - cid = cls.cid - ) - ), shell=True) - except ExecutionError: - pass - - def handle_timer(self): - self.refresh_devices() - - def refresh_devices(self): - devices = get_network_devices() - #add not registered devices - for k, v in devices.items(): - if k not in self.devices: - self.devices[k] = Device(k, v) - #delete dead devices - for k in self.devices.keys(): - if k not in devices: - del self.devices[k] - - -if __name__ == '__main__': - - qos = QoSHandler() - print '----------------------' - qos.commit() diff --git a/services/qos/handler.py b/services/qos/handler.py new file mode 100644 index 0000000..c175049 --- /dev/null +++ b/services/qos/handler.py @@ -0,0 +1,291 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from subprocess import Popen, PIPE +from os import path + +from pymin.seqtools import Sequence +from pymin.dispatcher import handler, HandlerError, Handler +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, SubHandler, call, \ + get_network_devices, ListComposedSubHandler, \ + DictComposedSubHandler, ExecutionError + +__all__ = ('QoSHandler', 'get_service') + + +def get_service(config): + return QoSHandler(config.qos.pickle_dir, config.qos.config_dir) + + +class DeviceError(HandlerError): + + def __init__(self, dev): + self.message = u'Devive error : "%s"' % dev + + +class DeviceNotFoundError(DeviceError): + + def __init__(self, dev): + self.message = u'Device not found : "%s"' % dev + + +class ClassError(HandlerError): + + def __init__(self, cls): + self.message = u'Class error : "%s"' % cls + + +class ClassNotFoundError(ClassError): + + def __init__(self, cls): + self.message = u'Class not found : "%s"' % cls + + +class ClassAlreadyExistsError(ClassError): + + def __init__(self, cls): + self.message = u'Class already exists : "%s"' % cls + + +class HostError(HandlerError): + + def __init__(self, host): + self.message = u'Host error : "%s"' % host + + +class HostNotFoundError(HostError): + + def __init__(self, ip): + self.message = u'Host not found : "%s"' % host + + +class HostAlreadyExistsError(HostError): + + def __init__(self, ip): + self.message = u'Host already exists : "%s"' % host + + +class Class(Sequence): + + def __init__(self, cid, rate=None): + self.cid = cid + self.rate = rate + self.hosts = dict() + + def as_tuple(self): + return (self.cid, self.rate) + + def __cmp__(self, other): + if self.cid == other.cid: + return 0 + return cmp(id(self), id(other)) + + +class ClassHandler(Handler): + + def __init__(self, parent): + self.parent = parent + + @handler('Adds a class : add ') + def add(self, dev, cid, rate): + if not dev in self.parent.devices: + raise DeviceNotFoundError(dev) + + try: + self.parent.devices[dev].classes[cid] = Class(cid, rate) + except ValueError: + raise ClassAlreadyExistsError(cid + ' -> ' + dev) + + @handler(u'Deletes a class : delete ') + def delete(self, dev, cid): + if not dev in self.parent.devices: + raise DeviceNotFoundError(dev) + + try: + del self.parent.devices[dev].classes[cid] + except KeyError: + raise ClassNotFoundError(cid + ' -> ' + dev) + + @handler(u'Lists classes : list ') + def list(self, dev): + try: + k = self.parent.devices[dev].classes.items() + except KeyError: + k = dict() + return k + + +class Host(Sequence): + + def __init__(self, ip): + self.ip = ip + + def as_tuple(self): + return (self.ip) + + def __cmp__(self, other): + if self.ip == other.ip: + return 0 + return cmp(id(self), id(other)) + + +class HostHandler(SubHandler): + + def __init__(self, parent): + self.parent = parent + + @handler('Adds a host to a class : add ') + def add(self, dev, cid, ip): + if not dev in self.parent.devices: + raise DeviceNotFoundError(dev) + + if not cid in self.parent.devices[dev].classes: + raise ClassNotFoundError(cid) + + try: + self.parent.devices[dev].classes[cid].hosts[ip] = Host(ip) + except ValueError: + raise HostAlreadyExistsError(h + ' -> ' + dev) + + @handler(u'Lists hosts : list ') + def list(self, dev, cid): + try: + k = self.parent.devices[dev].classes[cid].hosts.keys() + except KeyError: + k = dict() + return k + + +class Device(Sequence): + + def __init__(self, name, mac): + self.name = name + self.mac = mac + self.classes = dict() + + def as_tuple(self): + return (self.name, self.mac) + + +class DeviceHandler(SubHandler): + + handler_help = u"Manage network devices" + + def __init__(self, parent): + # FIXME remove templates to execute commands + from mako.template import Template + self.parent = parent + template_dir = path.join(path.dirname(__file__), 'templates') + dev_fn = path.join(template_dir, 'device') + self.device_template = Template(filename=dev_fn) + + @handler(u'Bring the device up') + def up(self, name): + if name in self.parent.devices: + try: + call(self.device_template.render(dev=name, action='add'), shell=True) + except ExecutionError: + pass + else: + raise DeviceNotFoundError(name) + + @handler(u'Bring the device down') + def down(self, name): + if name in self.parent.devices: + try: + call(self.device_template.render(dev=name, action='del'), shell=True) + except ExecutionError: + pass + else: + raise DeviceNotFoundError(name) + + @handler(u'List all devices') + def list(self): + return self.parent.devices.keys() + + @handler(u'Get information about a device') + def show(self): + return self.parent.devices.items() + + +class QoSHandler(Restorable, ConfigWriter, TransactionalHandler): + + handler_help = u"Manage QoS devices, classes and hosts" + + _persistent_attrs = ('devices') + + _restorable_defaults = dict( + devices=dict((dev, Device(dev, mac)) + for (dev, mac) in get_network_devices().items()) + ) + + _config_writer_files = ('device', 'class_add', 'class_del', 'host_add') + + _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') + + def __init__(self, pickle_dir='.', config_dir='.'): + r"Initialize QoSHandler 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._write_config() + self.dev = DeviceHandler(self) + self.cls = ClassHandler(self) + self.host = HostHandler(self) + + def _write_config(self): + r"_write_config() -> None :: Execute all commands." + for device in self.devices.values(): + try: + call(self._render_config('device', dict(dev=device.name, action='del')), shell=True) + except ExecutionError: + pass + + try: + call(self._render_config('device', dict(dev=device.name, action='add')), shell=True) + except ExecutionError: + pass + + for cls in device.classes.values(): + try: + call(self._render_config('class_add', dict( + dev = device.name, + cid = cls.cid, + rate = cls.rate + ) + ), shell=True) + except ExecutionError: + pass + + for host in cls.hosts.values(): + try: + call(self._render_config('host_add', dict( + dev = device.name, + ip = host.ip, + cid = cls.cid + ) + ), shell=True) + except ExecutionError: + pass + + def handle_timer(self): + self.refresh_devices() + + def refresh_devices(self): + devices = get_network_devices() + #add not registered devices + for k, v in devices.items(): + if k not in self.devices: + self.devices[k] = Device(k, v) + #delete dead devices + for k in self.devices.keys(): + if k not in devices: + del self.devices[k] + + +if __name__ == '__main__': + + qos = QoSHandler() + print '----------------------' + qos.commit() diff --git a/services/vpn/__init__.py b/services/vpn/__init__.py index ebfa0ab..0e2747a 100644 --- a/services/vpn/__init__.py +++ b/services/vpn/__init__.py @@ -1,205 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -import os -import errno -import signal -from os import path -import logging ; log = logging.getLogger('pymin.services.vpn') - - -from pymin.seqtools import Sequence -from pymin.dispatcher import Handler, handler, HandlerError -from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, DictSubHandler, DictComposedSubHandler, call, ExecutionError - -__all__ = ('VpnHandler', 'get_service') - +from handler import VpnHandler def get_service(config): return VpnHandler(config.vpn.pickle_dir, config.vpn.config_dir) - -class Host(Sequence): - def __init__(self, vpn_src, ip, vpn_src_net, key): - self.name = vpn_src - self.ip = ip - self.src_net = vpn_src_net - self.pub_key = key - self._delete = False - - def as_tuple(self): - return(self.name, self.ip, self.src_net, self.pub_key) - -class HostHandler(DictComposedSubHandler): - - handler_help = u"Manage hosts for a vpn" - _comp_subhandler_cont = 'vpns' - _comp_subhandler_attr = 'hosts' - _comp_subhandler_class = Host - - -class Vpn(Sequence): - def __init__(self, vpn_src, vpn_dst, vpn_src_ip, vpn_src_mask, - pub_key=None, priv_key=None): - self.vpn_src = vpn_src - self.vpn_dst = vpn_dst - self.vpn_src_ip = vpn_src_ip - self.vpn_src_mask = vpn_src_mask - self.pub_key = pub_key - self.priv_key = priv_key - self.hosts = dict() - self._delete = False - - def as_tuple(self): - return(self.vpn_src, self.vpn_dst, self.vpn_src_ip, self.vpn_src_mask, self.pub_key, self.priv_key) - - def update(self, vpn_dst=None, vpn_src_ip=None, vpn_src_mask=None): - if vpn_dst is not None: - self.vpn_dst = vpn_dst - if vpn_src_ip is not None: - self.vpn_src_ip = vpn_src_ip - if vpn_src_mask is not None: - self.vpn_src_mask = vpn_src_mask - - -class VpnHandler(Restorable, ConfigWriter, - TransactionalHandler, DictSubHandler): - - handler_help = u"Manage vpn service" - - _cont_subhandler_attr = 'vpns' - _cont_subhandler_class = Vpn - - _persistent_attrs = ('vpns','hosts') - - _restorable_defaults = dict( - vpns = dict(), - hosts = dict(), - ) - - _config_writer_files = ('tinc.conf','tinc-up','host') - _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') - - def __init__(self, pickle_dir='.', config_dir='/etc/tinc'): - log.debug(u'VpnHandler(%r, %r)', pickle_dir, config_dir) - DictSubHandler.__init__(self, self) - self._config_writer_cfg_dir = config_dir - self._persistent_dir = pickle_dir - self._config_build_templates() - self._restore() - self.host = HostHandler(self) - - @handler('usage: start ') - def start(self, vpn_src): - log.debug(u'VpnHandler.start(%r)', vpn_src) - if vpn_src in self.vpns: - call(('tincd','--net='+ vpn_src)) - - @handler('usage: stop ') - def stop(self, vpn_src): - log.debug(u'VpnHandler.stop(%r)', vpn_src) - if vpn_src in self.vpns: - pid_file = '/var/run/tinc.' + vpn_src + '.pid' - log.debug(u'VpnHandler.stop: getting pid from %r', pid_file) - if path.exists(pid_file): - pid = file(pid_file).readline() - pid = int(pid.strip()) - try: - log.debug(u'VpnHandler.stop: killing pid %r', pid) - os.kill(pid, signal.SIGTERM) - except OSError: - log.debug(u'VpnHandler.stop: error killing: %r', e) - else: - log.debug(u'VpnHandler.stop: pid file not found') - - def _write_config(self): - log.debug(u'VpnHandler._write_config()') - for v in self.vpns.values(): - log.debug(u'VpnHandler._write_config: processing %r', v) - #chek whether it's been created or not. - if not v._delete: - if v.pub_key is None: - log.debug(u'VpnHandler._write_config: new VPN, generating ' - 'key...') - try: - log.debug(u'VpnHandler._write_config: creating dir %r', - path.join(self._config_writer_cfg_dir, - v.vpn_src ,'hosts')) - #first create the directory for the vpn - try: - os.makedirs(path.join(self._config_writer_cfg_dir, - v.vpn_src, 'hosts')) - except (IOError, OSError), e: - if e.errno != errno.EEXIST: - raise HandlerError(u"Can't create VPN config " - "directory '%s' (%s)'" - % (e.filename, e.strerror)) - #this command should generate 2 files inside the vpn - #dir, one rsa_key.priv and one rsa_key.pub - #for some reason debian does not work like this - # FIXME if the < /dev/null works, is magic! - log.debug(u'VpnHandler._write_config: creating key...') - call(('tincd', '-n', v.vpn_src, '-K', '<', '/dev/null')) - #open the created files and load the keys - try: - f = file(path.join(self._config_writer_cfg_dir, - v.vpn_src, 'rsa_key.pub'), - 'r') - pub = f.read() - f.close() - except (IOError, OSError), e: - raise HandlerError(u"Can't read VPN key '%s' (%s)'" - % (e.filename, e.strerror)) - - v.pub_key = pub - v.priv_key = priv - except ExecutionError, e: - log.debug(u'VpnHandler._write_config: error executing ' - 'the command: %r', e) - - vars = dict( - vpn = v, - ) - self._write_single_config('tinc.conf', - path.join(v.vpn_src, 'tinc.conf'), vars) - self._write_single_config('tinc-up', - path.join(v.vpn_src, 'tinc-up'), vars) - for h in v.hosts.values(): - if not h._delete: - vars = dict( - host = h, - ) - self._write_single_config('host', - path.join(v.vpn_src, 'hosts', h.name), vars) - else: - log.debug(u'VpnHandler._write_config: removing...') - try: - # FIXME use os.unlink() - call(('rm','-f', - path.join(v.vpn_src, 'hosts', h.name))) - del v.hosts[h.name] - except ExecutionError, e: - log.debug(u'VpnHandler._write_config: error ' - 'removing files: %r', e) - else: - #delete the vpn root at tinc dir - if path.exists('/etc/tinc/' + v.vpn_src): - self.stop(v.vpn_src) - call(('rm','-rf','/etc/tinc/' + v.vpn_src)) - del self.vpns[v.vpn_src] - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - v = VpnHandler() - v.add('prueba','sarasa','192.168.0.188','255.255.255.0') - v.host.add('prueba', 'azazel' ,'192.168.0.77', '192.168.0.0', - 'kjdhfkbdskljvkjblkbjeslkjbvkljbselvslberjhbvslbevlhb') - v.commit() - diff --git a/services/vpn/handler.py b/services/vpn/handler.py new file mode 100644 index 0000000..ebfa0ab --- /dev/null +++ b/services/vpn/handler.py @@ -0,0 +1,205 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +import os +import errno +import signal +from os import path +import logging ; log = logging.getLogger('pymin.services.vpn') + + +from pymin.seqtools import Sequence +from pymin.dispatcher import Handler, handler, HandlerError +from pymin.service.util import Restorable, ConfigWriter, InitdHandler, \ + TransactionalHandler, DictSubHandler, DictComposedSubHandler, call, ExecutionError + +__all__ = ('VpnHandler', 'get_service') + + +def get_service(config): + return VpnHandler(config.vpn.pickle_dir, config.vpn.config_dir) + + +class Host(Sequence): + def __init__(self, vpn_src, ip, vpn_src_net, key): + self.name = vpn_src + self.ip = ip + self.src_net = vpn_src_net + self.pub_key = key + self._delete = False + + def as_tuple(self): + return(self.name, self.ip, self.src_net, self.pub_key) + +class HostHandler(DictComposedSubHandler): + + handler_help = u"Manage hosts for a vpn" + _comp_subhandler_cont = 'vpns' + _comp_subhandler_attr = 'hosts' + _comp_subhandler_class = Host + + +class Vpn(Sequence): + def __init__(self, vpn_src, vpn_dst, vpn_src_ip, vpn_src_mask, + pub_key=None, priv_key=None): + self.vpn_src = vpn_src + self.vpn_dst = vpn_dst + self.vpn_src_ip = vpn_src_ip + self.vpn_src_mask = vpn_src_mask + self.pub_key = pub_key + self.priv_key = priv_key + self.hosts = dict() + self._delete = False + + def as_tuple(self): + return(self.vpn_src, self.vpn_dst, self.vpn_src_ip, self.vpn_src_mask, self.pub_key, self.priv_key) + + def update(self, vpn_dst=None, vpn_src_ip=None, vpn_src_mask=None): + if vpn_dst is not None: + self.vpn_dst = vpn_dst + if vpn_src_ip is not None: + self.vpn_src_ip = vpn_src_ip + if vpn_src_mask is not None: + self.vpn_src_mask = vpn_src_mask + + +class VpnHandler(Restorable, ConfigWriter, + TransactionalHandler, DictSubHandler): + + handler_help = u"Manage vpn service" + + _cont_subhandler_attr = 'vpns' + _cont_subhandler_class = Vpn + + _persistent_attrs = ('vpns','hosts') + + _restorable_defaults = dict( + vpns = dict(), + hosts = dict(), + ) + + _config_writer_files = ('tinc.conf','tinc-up','host') + _config_writer_tpl_dir = path.join(path.dirname(__file__), 'templates') + + def __init__(self, pickle_dir='.', config_dir='/etc/tinc'): + log.debug(u'VpnHandler(%r, %r)', pickle_dir, config_dir) + DictSubHandler.__init__(self, self) + self._config_writer_cfg_dir = config_dir + self._persistent_dir = pickle_dir + self._config_build_templates() + self._restore() + self.host = HostHandler(self) + + @handler('usage: start ') + def start(self, vpn_src): + log.debug(u'VpnHandler.start(%r)', vpn_src) + if vpn_src in self.vpns: + call(('tincd','--net='+ vpn_src)) + + @handler('usage: stop ') + def stop(self, vpn_src): + log.debug(u'VpnHandler.stop(%r)', vpn_src) + if vpn_src in self.vpns: + pid_file = '/var/run/tinc.' + vpn_src + '.pid' + log.debug(u'VpnHandler.stop: getting pid from %r', pid_file) + if path.exists(pid_file): + pid = file(pid_file).readline() + pid = int(pid.strip()) + try: + log.debug(u'VpnHandler.stop: killing pid %r', pid) + os.kill(pid, signal.SIGTERM) + except OSError: + log.debug(u'VpnHandler.stop: error killing: %r', e) + else: + log.debug(u'VpnHandler.stop: pid file not found') + + def _write_config(self): + log.debug(u'VpnHandler._write_config()') + for v in self.vpns.values(): + log.debug(u'VpnHandler._write_config: processing %r', v) + #chek whether it's been created or not. + if not v._delete: + if v.pub_key is None: + log.debug(u'VpnHandler._write_config: new VPN, generating ' + 'key...') + try: + log.debug(u'VpnHandler._write_config: creating dir %r', + path.join(self._config_writer_cfg_dir, + v.vpn_src ,'hosts')) + #first create the directory for the vpn + try: + os.makedirs(path.join(self._config_writer_cfg_dir, + v.vpn_src, 'hosts')) + except (IOError, OSError), e: + if e.errno != errno.EEXIST: + raise HandlerError(u"Can't create VPN config " + "directory '%s' (%s)'" + % (e.filename, e.strerror)) + #this command should generate 2 files inside the vpn + #dir, one rsa_key.priv and one rsa_key.pub + #for some reason debian does not work like this + # FIXME if the < /dev/null works, is magic! + log.debug(u'VpnHandler._write_config: creating key...') + call(('tincd', '-n', v.vpn_src, '-K', '<', '/dev/null')) + #open the created files and load the keys + try: + f = file(path.join(self._config_writer_cfg_dir, + v.vpn_src, 'rsa_key.pub'), + 'r') + pub = f.read() + f.close() + except (IOError, OSError), e: + raise HandlerError(u"Can't read VPN key '%s' (%s)'" + % (e.filename, e.strerror)) + + v.pub_key = pub + v.priv_key = priv + except ExecutionError, e: + log.debug(u'VpnHandler._write_config: error executing ' + 'the command: %r', e) + + vars = dict( + vpn = v, + ) + self._write_single_config('tinc.conf', + path.join(v.vpn_src, 'tinc.conf'), vars) + self._write_single_config('tinc-up', + path.join(v.vpn_src, 'tinc-up'), vars) + for h in v.hosts.values(): + if not h._delete: + vars = dict( + host = h, + ) + self._write_single_config('host', + path.join(v.vpn_src, 'hosts', h.name), vars) + else: + log.debug(u'VpnHandler._write_config: removing...') + try: + # FIXME use os.unlink() + call(('rm','-f', + path.join(v.vpn_src, 'hosts', h.name))) + del v.hosts[h.name] + except ExecutionError, e: + log.debug(u'VpnHandler._write_config: error ' + 'removing files: %r', e) + else: + #delete the vpn root at tinc dir + if path.exists('/etc/tinc/' + v.vpn_src): + self.stop(v.vpn_src) + call(('rm','-rf','/etc/tinc/' + v.vpn_src)) + del self.vpns[v.vpn_src] + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + v = VpnHandler() + v.add('prueba','sarasa','192.168.0.188','255.255.255.0') + v.host.add('prueba', 'azazel' ,'192.168.0.77', '192.168.0.0', + 'kjdhfkbdskljvkjblkbjeslkjbvkljbselvslberjhbvslbevlhb') + v.commit() + diff --git a/services/vrrp/__init__.py b/services/vrrp/__init__.py index 5ddbbaf..15cf061 100644 --- a/services/vrrp/__init__.py +++ b/services/vrrp/__init__.py @@ -1,81 +1,7 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : -from pymin import procman -from pymin.service.util import Restorable, TransactionalHandler, \ - ReloadHandler, RestartHandler, \ - ServiceHandler, ParametersHandler - -# Logger -import logging ; log = logging.getLogger('pymin.services.vrrp') - -__all__ = ('VrrpHandler', 'get_service') - +from handler import VrrpHandler def get_service(config): return VrrpHandler(config.vrrp.pickle_dir, config.vrrp.config_dir) - -# FIXME the the command should not use new parameters unless commit where called -# i.e. integrate commit with procman to update internal procman parameters. -class VrrpHandler(Restorable, ParametersHandler, ReloadHandler, RestartHandler, - ServiceHandler, TransactionalHandler): - - handler_help = u"Manage VRRP service" - - _persistent_attrs = ['params'] - - _restorable_defaults = dict( - params = dict( - ipaddress = '192.168.0.1', - id = '1', - prio = '', - dev = 'eth0', - persist = True, - ), - ) - - @property - def _command(self): - command = ['vrrpd', '-i', self.params['dev'], '-v', self.params['id']] - if self.params['prio']: - command.extend(('-p', self.params['prio'])) - command.append(self.params['ipaddress']) - return command - - def _service_start(self): - log.debug(u'VrrpHandler._service_start()') - procinfo = procman.get('vrrp') - procinfo.command = self._command - procinfo.persist = self.params['persist'] - procman.start('vrrp') - - def _service_stop(self): - log.debug(u'VrrpHandler._service_stop()') - procman.stop('vrrp') - - def _service_restart(self): - procinfo = procman.get('vrrp') - procinfo.command = self._command - procinfo.persist = self.params['persist'] - procman.restart('vrrp') - - def __init__(self, pickle_dir='.', config_dir='.', pid_dir='.'): - log.debug(u'VrrpHandler(%r, %r, %r)', pickle_dir, config_dir, pid_dir) - self._persistent_dir = pickle_dir - self._pid_dir = pid_dir - procman.register('vrrp', None) - ServiceHandler.__init__(self) - - -if __name__ == '__main__': - - logging.basicConfig( - level = logging.DEBUG, - format = '%(asctime)s %(levelname)-8s %(message)s', - datefmt = '%H:%M:%S', - ) - - v = VrrpHandler() - v.set('prio', '10') - v.commit() - diff --git a/services/vrrp/handler.py b/services/vrrp/handler.py new file mode 100644 index 0000000..5ddbbaf --- /dev/null +++ b/services/vrrp/handler.py @@ -0,0 +1,81 @@ +# vim: set encoding=utf-8 et sw=4 sts=4 : + +from pymin import procman +from pymin.service.util import Restorable, TransactionalHandler, \ + ReloadHandler, RestartHandler, \ + ServiceHandler, ParametersHandler + +# Logger +import logging ; log = logging.getLogger('pymin.services.vrrp') + +__all__ = ('VrrpHandler', 'get_service') + + +def get_service(config): + return VrrpHandler(config.vrrp.pickle_dir, config.vrrp.config_dir) + + +# FIXME the the command should not use new parameters unless commit where called +# i.e. integrate commit with procman to update internal procman parameters. +class VrrpHandler(Restorable, ParametersHandler, ReloadHandler, RestartHandler, + ServiceHandler, TransactionalHandler): + + handler_help = u"Manage VRRP service" + + _persistent_attrs = ['params'] + + _restorable_defaults = dict( + params = dict( + ipaddress = '192.168.0.1', + id = '1', + prio = '', + dev = 'eth0', + persist = True, + ), + ) + + @property + def _command(self): + command = ['vrrpd', '-i', self.params['dev'], '-v', self.params['id']] + if self.params['prio']: + command.extend(('-p', self.params['prio'])) + command.append(self.params['ipaddress']) + return command + + def _service_start(self): + log.debug(u'VrrpHandler._service_start()') + procinfo = procman.get('vrrp') + procinfo.command = self._command + procinfo.persist = self.params['persist'] + procman.start('vrrp') + + def _service_stop(self): + log.debug(u'VrrpHandler._service_stop()') + procman.stop('vrrp') + + def _service_restart(self): + procinfo = procman.get('vrrp') + procinfo.command = self._command + procinfo.persist = self.params['persist'] + procman.restart('vrrp') + + def __init__(self, pickle_dir='.', config_dir='.', pid_dir='.'): + log.debug(u'VrrpHandler(%r, %r, %r)', pickle_dir, config_dir, pid_dir) + self._persistent_dir = pickle_dir + self._pid_dir = pid_dir + procman.register('vrrp', None) + ServiceHandler.__init__(self) + + +if __name__ == '__main__': + + logging.basicConfig( + level = logging.DEBUG, + format = '%(asctime)s %(levelname)-8s %(message)s', + datefmt = '%H:%M:%S', + ) + + v = VrrpHandler() + v.set('prio', '10') + v.commit() +