def __init__(self, config, auto_load=True, connectors_dir='/connectors'): self.logger = logging.getLogger('dns_update.dispatcher') # List of registered connectors self.__connectors = [] # List of modified PTRs self.unsaved_ptrs = [] # Dict of devices. Keyed by hostname (FQDN) self.devices = {} # Config self.config = config # DNS self.dns = DnsCheck(self.config) if auto_load: # Autoload all connectors path = os.path.dirname(os.path.abspath(__file__)) + connectors_dir self.logger.info("Autoload enabled. Searching: '%s'" % path) for dirname in [d for d in os.listdir(path) if os.path.isdir(path + '/' + d)]: filename = dirname + '_connector.py' py = filename[:-3] class_name = ''.join([x.capitalize() for x in py.split('_')]) try: mod = imp.load_source(class_name, path + '/' + dirname + '/' + filename) # Instantiate class if hasattr(mod, class_name): getattr(mod, class_name)(self) self.logger.info("Connector '%s' successfully loaded" % class_name) else: self.logger.error("Connector '%s' couldn't be loaded" % class_name) except IOError: self.logger.error("Connector's '%s' directory doesn't have '%s' file" % (class_name, filename)) continue
def setUp(self): logging.basicConfig( filename='test/unittest.log', format= "%(asctime)s - %(levelname)s - %(name)s:%(funcName)s - %(message)s", level=logging.DEBUG, filemode='w') self.dns = DnsCheck( config=Config('test/configuration_examples/configuration.json'))
class Dispatcher: """ Registers connectors (interfaces) to various input and output methods Maintains the dict of devices and ptrs """ def __init__(self, config, auto_load=True, connectors_dir='/connectors'): self.logger = logging.getLogger('dns_update.dispatcher') # List of registered connectors self.__connectors = [] # List of modified PTRs self.unsaved_ptrs = [] # Dict of devices. Keyed by hostname (FQDN) self.devices = {} # Config self.config = config # DNS self.dns = DnsCheck(self.config) if auto_load: # Autoload all connectors path = os.path.dirname(os.path.abspath(__file__)) + connectors_dir self.logger.info("Autoload enabled. Searching: '%s'" % path) for dirname in [d for d in os.listdir(path) if os.path.isdir(path + '/' + d)]: filename = dirname + '_connector.py' py = filename[:-3] class_name = ''.join([x.capitalize() for x in py.split('_')]) try: mod = imp.load_source(class_name, path + '/' + dirname + '/' + filename) # Instantiate class if hasattr(mod, class_name): getattr(mod, class_name)(self) self.logger.info("Connector '%s' successfully loaded" % class_name) else: self.logger.error("Connector '%s' couldn't be loaded" % class_name) except IOError: self.logger.error("Connector's '%s' directory doesn't have '%s' file" % (class_name, filename)) continue def register_connector(self, connector): """ Register connector. This is called from Connector's __init__ method :param connector: Connector object :return: """ self.logger.debug("Register connector '%s; to dispatcher" % connector.__class__.__name__) self.__connectors.append(connector) def get_connector_list(self): return [x.__class__.__name__ for x in self.__connectors] def get_connector_config(self, connector): connector_name = connector.get_connector_name() self.logger.debug("Search for ['%s'] in configuration file" % connector_name) return self.config.get_connector_config(connector_name) def save_ptr(self, ptr): """ Issue save command on each connector :param ptr: PTR dict :return: """ self.logger.info("Dispatch save PTR command for %s to all (%d) connectors" % (ptr, len(self.__connectors))) for connector in self.__connectors: connector.save_ptr(ptr) def save_ptrs(self, ptrs): """ Issue save multiple PTRs command on each connector :param ptrs: PTR dict :return: """ self.logger.info("Dispatch save PTRs command for %d PTRs to all (%d) connectors" % ( len(ptrs), len(self.__connectors) )) for connector in self.__connectors: connector.save_ptrs(ptrs) def load(self): """ Load list of devices from each connector Since devices dict is keyed by hostnames, there are no duplicates :return: """ # Temporary list device_list = [] self.logger.info("Dispatch load command to all (%d) connectors" % len(self.__connectors)) # Concatenate device list from each connector to temporary list for connector in self.__connectors: device_list += filter(lambda x: len(x) > 0, connector.load_devices()) # Populate devices dict from temporary list for device in device_list: hostname = self.dns.get_fqdn(device) if hostname: if hostname not in self.devices: self.devices[hostname] = Device(hostname, self.config, self.dns) else: self.logger.warning("Hostname '%s' couldn't be resolved. SKipping..." % device) pass self.logger.info("Loaded %d device(s) from %d connectors" % (len(device_list), len(self.__connectors)))
action="store_true") args = parser.parse_args() hostname = args.hostname check_only = args.check diff_only = args.diff terse = args.terse config = Config(check_only=check_only, diff_only=diff_only, terse=terse) dispatcher = Dispatcher(config) print "Loaded connectors: %s" % ', '.join(dispatcher.get_connector_list()) dns = DnsCheck(config=config) output = TabularUtf8Output() fqdn = dns.get_fqdn(hostname) if fqdn: d = Device(hostname=fqdn, config=config) if d.get_interfaces(): d.check_ptrs() print output.display_device_detailed(d) # Filter all PTRs that don't have status equal to STATUS_NOT_UPDATED or STATUS_NOT_CREATED ptrs_for_update = { k: v for k, v in d.get_ptrs().iteritems() if v.status in (DnsCheck.STATUS_NOT_UPDATED, DnsCheck.STATUS_NOT_CREATED)
def test_get_ptr_zone(self): self.assertRaises(ipaddress.AddressValueError, DnsCheck.get_ptr_zone, 'x.x.x.x') self.assertEqual('2.0.192.in-addr.arpa.', DnsCheck.get_ptr_zone('192.0.2.1')) self.assertRaises(ValueError, DnsCheck.get_ptr_zone, '192.0.2')
class TestDnsCheck(unittest.TestCase): def setUp(self): logging.basicConfig( filename='test/unittest.log', format= "%(asctime)s - %(levelname)s - %(name)s:%(funcName)s - %(message)s", level=logging.DEBUG, filemode='w') self.dns = DnsCheck( config=Config('test/configuration_examples/configuration.json')) def test_fqdn_query(self): self.assertFalse(self.dns.get_fqdn('asdasd')) self.assertFalse(self.dns.get_fqdn('')) self.assertFalse(self.dns.get_fqdn('.')) self.assertEquals('host1.domain.example.', self.dns.get_fqdn('host1')) self.assertEquals('host2.domain.example.', self.dns.get_fqdn('host2')) self.assertEquals('host3.domain.example.', self.dns.get_fqdn('host3')) def test_a_query(self): self.assertFalse(self.dns.get_a('asdasd')) self.assertFalse(self.dns.get_a('')) self.assertFalse(self.dns.get_a('.')) self.assertEquals('192.0.2.1', self.dns.get_a('host1')) self.assertEquals('192.0.2.2', self.dns.get_a('host2')) self.assertEquals('192.0.2.3', self.dns.get_a('host3')) def test_ptr_query(self): self.assertRaises(ipaddress.AddressValueError, self.dns.get_ptr, 'x.x.x.x') self.assertEquals('host1.domain.example.', self.dns.get_ptr('192.0.2.1')) self.assertEquals('host2.domain.example.', self.dns.get_ptr('192.0.2.2')) self.assertEquals('host3.domain.example.', self.dns.get_ptr('192.0.2.3')) self.assertRaises(ValueError, DnsCheck.get_ptr_zone, '192.0.2') def test_get_ptr_zone(self): self.assertRaises(ipaddress.AddressValueError, DnsCheck.get_ptr_zone, 'x.x.x.x') self.assertEqual('2.0.192.in-addr.arpa.', DnsCheck.get_ptr_zone('192.0.2.1')) self.assertRaises(ValueError, DnsCheck.get_ptr_zone, '192.0.2') def test_is_authoritative(self): self.assertRaises(ipaddress.AddressValueError, self.dns.is_authoritative, 'x.x.x.x') self.assertTrue(self.dns.is_authoritative('192.0.2.1')) self.assertTrue(self.dns.is_authoritative('192.0.2.255')) self.assertFalse(self.dns.is_authoritative('89.216.119.169')) self.assertFalse(self.dns.is_authoritative('109.122.98.1')) self.assertFalse(self.dns.is_authoritative('1.1.1.1')) def test_check_status(self): self.assertRaises(ipaddress.AddressValueError, self.dns.get_status, 'x.x.x.x', 'test') # OK PTR self.assertEqual( DnsCheck.STATUS_OK, self.dns.get_status('192.0.2.1', 'host1.domain.example.')[1]) # Authoritative # Wrong PTR self.assertEqual( DnsCheck.STATUS_NOT_UPDATED, self.dns.get_status('192.0.2.22', 'localhost.domain.example.')[1]) # No PTR self.assertEqual( DnsCheck.STATUS_NOT_CREATED, self.dns.get_status('192.0.2.4', 'host4.domain.example.')[1]) # Not authoritative # Wrong PTR self.assertEqual( DnsCheck.STATUS_NOT_AUTHORITATIVE, self.dns.get_status('8.8.8.8', 'wrong.domain.example.')[1]) # No PTR self.assertEqual( DnsCheck.STATUS_NOT_AUTHORITATIVE, self.dns.get_status('8.8.8.255', 'empty.domain.example.')[1]) # Wrong IP self.assertRaises(ValueError, self.dns.get_status, '192.0.2', 'something.domain.example.')
class TestDnsConnector(unittest.TestCase): def setUp(self): logging.basicConfig( filename='test/unittest.log', format="%(asctime)s - %(levelname)s - %(name)s:%(funcName)s - %(message)s", level=logging.DEBUG, filemode='w' ) self.dispatcher = Dispatcher(Config(filename='test/configuration_examples/configuration.json')) self.connector = DnsConnector(self.dispatcher) self.dns = DnsCheck(config=Config('test/configuration_examples/configuration.json')) def test_create(self): ptr_dict = { 'ip_address': u'192.0.2.255', 'hostname': 'host255.domain.example', 'if_name': 'Ethernet2/5/5', 'ptr': 'host255-et2-5-5.domain.example.' } ptr = Ptr(**ptr_dict) self.connector.create_ptr(ptr) self.assertEquals('host255-et2-5-5.domain.example.', self.dns.get_ptr('192.0.2.255')) def test_update(self): ptr_dict = { 'ip_address': u'192.0.2.255', 'hostname': 'host255.domain.example', 'if_name': 'Ethernet2/5/5', 'ptr': 'host255-et2-5-5.domain.example.' } ptr = Ptr(**ptr_dict) self.connector.create_ptr(ptr) self.assertEquals('host255-et2-5-5.domain.example.', self.dns.get_ptr('192.0.2.255')) ptr_dict['ptr'] = 'host-et2-5-5.domain.example.' ptr = Ptr(**ptr_dict) self.connector.create_ptr(ptr) self.assertEquals('host-et2-5-5.domain.example.', self.dns.get_ptr('192.0.2.255')) def test_delete(self): self.connector.delete_ptr('192.0.2.255') self.assertNotEquals('host-et2-5-5.domain.example.', self.dns.get_ptr('192.0.2.255')) self.assertNotEquals('host255-et2-5-5.domain.example.', self.dns.get_ptr('192.0.2.255')) def test_multiple_delete(self): ptr1_dict = { 'ip_address': u'192.0.2.201', 'hostname': 'host201.domain.example', 'if_name': 'Ethernet2/0/1', 'ptr': 'host201-et2-0-1.domain.example.' } ptr2_dict = { 'ip_address': u'192.0.2.202', 'hostname': 'host202.domain.example', 'if_name': 'Ethernet2/0/2', 'ptr': 'host202-et2-0-2.domain.example.' } ptr3_dict = { 'ip_address': u'192.0.2.203', 'hostname': 'host203.domain.example', 'if_name': 'Ethernet2/0/3', 'ptr': 'host203-et2-0-3.domain.example.' } ptr1 = Ptr(**ptr1_dict) ptr2 = Ptr(**ptr2_dict) ptr3 = Ptr(**ptr3_dict) self.connector.create_ptr(ptr1) self.connector.create_ptr(ptr2) self.connector.create_ptr(ptr3) self.assertEquals('host201-et2-0-1.domain.example.', self.dns.get_ptr('192.0.2.201')) self.assertEquals('host202-et2-0-2.domain.example.', self.dns.get_ptr('192.0.2.202')) self.assertEquals('host203-et2-0-3.domain.example.', self.dns.get_ptr('192.0.2.203')) self.connector.delete_ptrs(['192.0.2.201','192.0.2.202','192.0.2.203']) self.assertNotEquals('host201-et2-0-1.domain.example.', self.dns.get_ptr('192.0.2.201')) self.assertNotEquals('host202-et2-0-2.domain.example.', self.dns.get_ptr('192.0.2.202')) self.assertNotEquals('host203-et2-0-3.domain.example.', self.dns.get_ptr('192.0.2.203'))