X-Git-Url: https://git.llucax.com/software/pymin.git/blobdiff_plain/b55f9ac5e9bd1a7404ded132c1e15a4da5cc8bba..7c760d8e7832391621b67cabe629f46e6b5959d1:/pymin/services/dns/__init__.py?ds=sidebyside diff --git a/pymin/services/dns/__init__.py b/pymin/services/dns/__init__.py index cf44ea4..f57386c 100644 --- a/pymin/services/dns/__init__.py +++ b/pymin/services/dns/__init__.py @@ -3,316 +3,57 @@ # TODO COMMENT from os import path from os import unlink -from new import instancemethod +import logging ; log = logging.getLogger('pymin.services.dns') from pymin.seqtools import Sequence from pymin.dispatcher import handler, HandlerError, Handler from pymin.services.util import Restorable, ConfigWriter, InitdHandler, \ - TransactionalHandler, ParametersHandler, call - -__ALL__ = ('DnsHandler', 'Error', - 'ZoneError', 'ZoneNotFoundError', 'ZoneAlreadyExistsError', - 'HostError', 'HostAlreadyExistsError', 'HostNotFoundError', - 'MailExchangeError', 'MailExchangeAlreadyExistsError', - 'MailExchangeNotFoundError', 'NameServerError', - 'NameServerAlreadyExistsError', 'NameServerNotFoundError') - -template_dir = path.join(path.dirname(__file__), 'templates') - - -class Error(HandlerError): - r""" - Error(command) -> Error instance :: Base DnsHandler exception class. - - All exceptions raised by the DnsHandler inherits from this one, so you can - easily catch any DnsHandler exception. - - message - A descriptive error message. - """ - pass - -class ZoneError(Error, KeyError): - r""" - ZoneError(zonename) -> ZoneError instance - - This is the base exception for all zone related errors. - """ - - def __init__(self, zonename): - r"Initialize the object. See class documentation for more info." - self.message = u'Zone error: "%s"' % zonename - -class ZoneNotFoundError(ZoneError): - r""" - ZoneNotFoundError(hostname) -> ZoneNotFoundError instance - - This exception is raised when trying to operate on a zone that doesn't - exists. - """ - - def __init__(self, zonename): - r"Initialize the object. See class documentation for more info." - self.message = u'zone not found: "%s"' % zonename - -class ZoneAlreadyExistsError(ZoneError): - r""" - ZoneAlreadyExistsError(hostname) -> ZoneAlreadyExistsError instance - - This exception is raised when trying to add a zonename that already exists. - """ - - def __init__(self, zonename): - r"Initialize the object. See class documentation for more info." - self.message = u'Zone already exists: "%s"' % zonename - -class HostError(Error, KeyError): - r""" - HostError(hostname) -> HostError instance - - This is the base exception for all host related errors. - """ - - def __init__(self, hostname): - r"Initialize the object. See class documentation for more info." - self.message = u'Host error: "%s"' % hostname - -class HostAlreadyExistsError(HostError): - r""" - HostAlreadyExistsError(hostname) -> HostAlreadyExistsError instance - - This exception is raised when trying to add a hostname that already exists. - """ - - def __init__(self, hostname): - r"Initialize the object. See class documentation for more info." - self.message = u'Host already exists: "%s"' % hostname - -class HostNotFoundError(HostError): - r""" - HostNotFoundError(hostname) -> HostNotFoundError instance. - - This exception is raised when trying to operate on a hostname that doesn't - exists. - """ - - def __init__(self, hostname): - r"Initialize the object. See class documentation for more info." - self.message = u'Host not found: "%s"' % hostname - -class MailExchangeError(Error, KeyError): - r""" - MailExchangeError(hostname) -> MailExchangeError instance. - - This is the base exception for all mail exchange related errors. - """ - - def __init__(self, mx): - r"Initialize the object. See class documentation for more info." - self.message = u'Mail Exchange error: "%s"' % mx - -class MailExchangeAlreadyExistsError(MailExchangeError): - r""" - MailExchangeAlreadyExistsError(hostname) -> MailExchangeAlreadyExistsError. - - This exception is raised when trying to add a mail exchange that already exists. - """ - - def __init__(self, mx): - r"Initialize the object. See class documentation for more info." - self.message = u'Mail Exchange already exists: "%s"' % mx - -class MailExchangeNotFoundError(MailExchangeError): - r""" - MailExchangeNotFoundError(hostname) -> MailExchangeNotFoundError instance. - - This exception is raised when trying to operate on a mail exchange that - doesn't exists. - """ - - def __init__(self, mx): - r"Initialize the object. See class documentation for more info." - self.message = 'Mail Exchange not found: "%s"' % mx - -class NameServerError(Error, KeyError): - r""" - NameServerError(ns) -> NameServerError instance - - This is the base exception for all name server related errors. - """ - - def __init__(self, ns): - r"Initialize the object. See class documentation for more info." - self.message = 'Name Server error: "%s"' % ns - -class NameServerAlreadyExistsError(NameServerError): - r""" - NameServerAlreadyExistsError(hostname) -> NameServerAlreadyExistsError. - - This exception is raised when trying to add a name server that already - exists. - """ - - def __init__(self, ns): - r"Initialize the object. See class documentation for more info." - self.message = 'Name server already exists: "%s"' % ns - -class NameServerNotFoundError(NameServerError): - r""" - NameServerNotFoundError(hostname) -> NameServerNotFoundError instance. - - This exception is raised when trying to operate on a name server that - doesn't exists. - """ - - def __init__(self, ns): - r"Initialize the object. See class documentation for more info." - self.message = 'Mail Exchange not found: "%s"' % ns + TransactionalHandler, ParametersHandler, \ + DictComposedSubHandler, DictSubHandler, call +__ALL__ = ('DnsHandler',) 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(Handler): - +class HostHandler(DictComposedSubHandler): handler_help = u"Manage DNS hosts" - - def __init__(self,zones): - self.zones = zones - - @handler(u'Adds a host to a zone') - def add(self, name, hostname, ip): - if not name in self.zones: - raise ZoneNotFoundError(name) - if hostname in self.zones[name].hosts: - raise HostAlreadyExistsError(hostname) - self.zones[name].hosts[hostname] = Host(hostname, ip) - self.zones[name].mod = True - - @handler(u'Updates a host ip in a zone') - def update(self, name, hostname, ip): - if not name in self.zones: - raise ZoneNotFoundError(name) - if not hostname in self.zones[name].hosts: - raise HostNotFoundError(name) - self.zones[name].hosts[hostname].ip = ip - self.zones[name].mod = True - - @handler(u'Deletes a host from a zone') - def delete(self, name, hostname): - if not name in self.zones: - raise ZoneNotFoundError(name) - if not hostname in self.zones[name].hosts: - raise HostNotFoundError(name) - del self.zones[name].hosts[hostname] - self.zones[name].mod = True - - @handler(u'Lists hosts') - def list(self): - return self.zones.keys() - - @handler(u'Get insormation about all hosts') - def show(self): - return self.zones.values() - + _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(Handler): - +class MailExchangeHandler(DictComposedSubHandler): handler_help = u"Manage DNS mail exchangers (MX)" - - def __init__(self, zones): - self.zones = zones - - @handler(u'Adds a mail exchange to a zone') - def add(self, zonename, mx, prio): - if not zonename in self.zones: - raise ZoneNotFoundError(zonename) - if mx in self.zones[zonename].mxs: - raise MailExchangeAlreadyExistsError(mx) - self.zones[zonename].mxs[mx] = MailExchange(mx, prio) - self.zones[zonename].mod = True - - @handler(u'Updates a mail exchange priority') - def update(self, zonename, mx, prio): - if not zonename in self.zones: - raise ZoneNotFoundError(zonename) - if not mx in self.zones[zonename].mxs: - raise MailExchangeNotFoundError(mx) - self.zones[zonename].mxs[mx].prio = prio - self.zones[zonename].mod = True - - @handler(u'Deletes a mail exchange from a zone') - def delete(self, zonename, mx): - if not zonename in self.zones: - raise ZoneNotFoundError(zonename) - if not mx in self.zones[zonename].mxs: - raise MailExchangeNotFoundError(mx) - del self.zones[zonename].mxs[mx] - self.zones[zonename].mod = True - - @handler(u'Lists mail exchangers') - def list(self): - return self.zones.keys() - - @handler(u'Get information about all mail exchangers') - def show(self): - return self.zones.values() - + _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(Handler): + return (self.name,) +class NameServerHandler(DictComposedSubHandler): handler_help = u"Manage DNS name servers (NS)" - - def __init__(self, zones): - self.zones = zones - - @handler(u'Adds a name server to a zone') - def add(self, zone, ns): - if not zone in self.zones: - raise ZoneNotFoundError(zone) - if ns in self.zones[zone].nss: - raise NameServerAlreadyExistsError(ns) - self.zones[zone].nss[ns] = NameServer(ns) - self.zones[zone].mod = True - - @handler(u'Deletes a name server from a zone') - def delete(self, zone, ns): - if not zone in self.zones: - raise ZoneNotFoundError(zone) - if not ns in self.zones[zone].nss: - raise NameServerNotFoundError(ns) - del self.zones[zone].nss[ns] - self.zones[zone].mod = True - - @handler(u'Lists name servers') - def list(self): - return self.zones.keys() - - @handler(u'Get information about all name servers') - def show(self): - return self.zones.values() - + _comp_subhandler_cont = 'zones' + _comp_subhandler_attr = 'nss' + _comp_subhandler_class = NameServer class Zone(Sequence): def __init__(self, name): @@ -320,54 +61,16 @@ class Zone(Sequence): self.hosts = dict() self.mxs = dict() self.nss = dict() - self.new = False - self.mod = False - self.dele = False - + self._add = False + self._update = False + self._delete = False def as_tuple(self): return (self.name, self.hosts, self.mxs, self.nss) -class ZoneHandler(Handler): - r"""ZoneHandler(zones) -> ZoneHandler instance :: Handle a list of zones. - - This class is a helper for DnsHandler to do all the work related to zone - administration. - - zones - A dictionary with string keys (zone name) and Zone instances values. - """ - +class ZoneHandler(DictSubHandler): handler_help = u"Manage DNS zones" - - def __init__(self, zones): - self.zones = zones - - @handler(u'Adds a zone') - def add(self, name): - if name in self.zones: - if self.zones[name].dele == True: - self.zones[name].dele = False - else: - raise ZoneAlreadyExistsError(name) - self.zones[name] = Zone(name) - self.zones[name].mod = True - self.zones[name].new = True - - - @handler(u'Deletes a zone') - def delete(self, name): - r"delete(name) -> None :: Delete a zone from the zone list." - if not name in self.zones: - raise ZoneNotFoundError(name) - self.zones[name].dele = True - - @handler(u'Lists zones') - def list(self): - return self.zones.keys() - - @handler(u'Get information about all zones') - def show(self): - return self.zones.values() - + _cont_subhandler_attr = 'zones' + _cont_subhandler_class = Zone class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, ParametersHandler): @@ -384,7 +87,7 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, handler_help = u"Manage DNS service" - _initd_name = 'bind' + _initd_name = 'named' _persistent_attrs = ('params', 'zones') @@ -403,15 +106,16 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, 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.mod = False + self._update = False self._config_build_templates() - self._restore() - self.host = HostHandler(self.zones) - self.zone = ZoneHandler(self.zones) - self.mx = MailExchangeHandler(self.zones) - self.ns = NameServerHandler(self.zones) + 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' @@ -421,12 +125,15 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, 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(): - if a_zone.mod: - if not a_zone.new: - # TODO freeze de la zona - call(('dns', 'freeze', a_zone.name)) + 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(), @@ -435,35 +142,60 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, ) self._write_single_config('zoneX.zone', self._zone_filename(a_zone), vars) - a_zone.mod = False - if not a_zone.new: - # TODO unfreeze de la zona - call(('dns', 'unfreeze', a_zone.name)) + 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.mod = True - a_zone.new = False - if a_zone.dele: + 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.mod = True + 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.mod: + if self._update: self._write_single_config('named.conf') - self.mod = False - self.reload() + 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') @@ -503,7 +235,8 @@ if __name__ == '__main__': dns.commit() print 'ZONAS :', dns.zone.show() - print 'HOSTS :', dns.host.show() + for z in dns.zones: + print 'HOSTS from', z, ':', dns.host.show(z) #test zone errors #try: @@ -511,9 +244,12 @@ if __name__ == '__main__': #except ZoneNotFoundError, inst: # print 'Error: ', inst + from pymin.services.util import ItemNotFoundError, ItemAlreadyExistsError, \ + ContainerNotFoundError + try: dns.zone.delete('zone-sarasa') - except ZoneNotFoundError, inst: + except ItemNotFoundError, inst: print 'Error: ', inst #try: @@ -521,33 +257,34 @@ if __name__ == '__main__': #except ZoneAlreadyExistsError, inst: # print 'Error: ', inst + #test hosts errors try: dns.host.update('zone-sarasa','kuak','192.68') - except ZoneNotFoundError, inst: + except ContainerNotFoundError, inst: print 'Error: ', inst try: dns.host.update('zona_loca.com','kuak','192.68') - except HostNotFoundError, inst: + except ItemNotFoundError, inst: print 'Error: ', inst try: dns.host.delete('zone-sarasa','lala') - except ZoneNotFoundError, inst: + except ContainerNotFoundError, inst: print 'Error: ', inst try: dns.host.delete('zona_loca.com','lala') - except HostNotFoundError, inst: + except ItemNotFoundError, inst: print 'Error: ', inst try: dns.host.add('zona','hostname_loco','192.168.0.23') - except ZoneNotFoundError, inst: + except ContainerNotFoundError, inst: print 'Error: ', inst try: dns.host.add('zona_loca.com','hostname_loco','192.168.0.23') - except HostAlreadyExistsError, inst: + except ItemAlreadyExistsError, inst: print 'Error: ', inst