def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute( "DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute( "UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0" ) self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn")
def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn")
def clear_database(cls): cfg = NipapConfig('/etc/nipap/nipap.conf') n = Nipap() # have to delete hosts before we can delete the rest n._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest n._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 n._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 n._execute( "UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0" ) n._execute("DELETE FROM ip_net_pool") n._execute("DELETE FROM ip_net_asn")
def clear_database(cls): cfg = NipapConfig('/etc/nipap/nipap.conf') n = Nipap() # have to delete hosts before we can delete the rest n._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest n._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 n._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 n._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") n._execute("DELETE FROM ip_net_pool") n._execute("DELETE FROM ip_net_asn")
class NipapCliTest(unittest.TestCase): """ Tests the NIPAP CLI We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _mangle_prefix_result(self, res): """ Mangle prefix result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. """ if isinstance(res, list): # res from list_prefix for p in res: self.assertIn('added', p) self.assertIn('last_modified', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('added', p) self.assertIn('last_modified', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict): # just one single prefix self.assertIn('added', p) self.assertIn('last_modified', p) del(p['added']) del(p['last_modified']) del(res['total_addresses']) del(res['used_addresses']) del(res['free_addresses']) return res def _run_cmd(self, cmd): """ Run a command """ import subprocess return subprocess.check_output(cmd) def test_prefix_add_list(self): """ Add a prefix and verify result in database """ ref = { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'foo description', 'comment': 'comment bar', 'country': 'AB', 'alarm_priority': 'high', 'monitor': 'true', 'order_id': '123', 'customer_id': '66' } cmd = [nipap_bin, 'address', 'add'] for key in ref: cmd.append(key) cmd.append(ref[key]) ref['display_prefix'] = '1.3.3.0/24' ref['indent'] = 0 ref['family'] = 4 ref['monitor'] = True ref['pool_id'] = None ref['pool_name'] = None ref['vrf_id'] = 0 ref['vrf_name'] = 'default' ref['vrf_rt'] = None ref['external_key'] = None ref['node'] = None ref['authoritative_source'] = 'nipap' ref['vlan'] = None ref['inherited_tags'] = [] ref['tags'] = [] self._run_cmd(cmd) res = self._mangle_prefix_result(s.list_prefix({ 'auth': ad, 'spec': {} })) del(res[0]['id']) self.assertEqual(res, [ ref, ])
class NipapCliTest(unittest.TestCase): """ Tests the NIPAP CLI We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute( "DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute( "UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0" ) self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _mangle_prefix_result(self, res): """ Mangle prefix result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. """ if isinstance(res, list): # res from list_prefix for p in res: self.assertIn('added', p) self.assertIn('last_modified', p) del (p['added']) del (p['last_modified']) del (p['total_addresses']) del (p['used_addresses']) del (p['free_addresses']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('added', p) self.assertIn('last_modified', p) del (p['added']) del (p['last_modified']) del (p['total_addresses']) del (p['used_addresses']) del (p['free_addresses']) elif isinstance(res, dict): # just one single prefix self.assertIn('added', p) self.assertIn('last_modified', p) del (p['added']) del (p['last_modified']) del (res['total_addresses']) del (res['used_addresses']) del (res['free_addresses']) return res def _run_cmd(self, cmd): """ Run a command """ import subprocess return subprocess.check_output(cmd) def test_prefix_add_list(self): """ Add a prefix and verify result in database """ ref = { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'status': 'assigned', 'description': 'foo description', 'comment': 'comment bar', 'country': 'AB', 'alarm_priority': 'high', 'monitor': 'true', 'order_id': '123', 'customer_id': '66' } cmd = [nipap_bin, 'address', 'add'] for key in ref: cmd.append(key) cmd.append(ref[key]) ref['display_prefix'] = '1.3.3.0/24' ref['indent'] = 0 ref['family'] = 4 ref['monitor'] = True ref['pool_id'] = None ref['pool_name'] = None ref['vrf_id'] = 0 ref['vrf_name'] = 'default' ref['vrf_rt'] = None ref['external_key'] = None ref['node'] = None ref['authoritative_source'] = 'nipap' ref['vlan'] = None ref['inherited_tags'] = [] ref['tags'] = [] ref['avps'] = {} ref['expires'] = None self._run_cmd(cmd) res = self._mangle_prefix_result( s.list_prefix({ 'auth': ad, 'spec': {} })) del (res[0]['id']) self.assertEqual(res, [ ref, ])
def setUp(self): """ Better start from a clean slate! """ cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' self.nipap._execute("TRUNCATE ip_net_plan, ip_net_pool, ip_net_vrf, ip_net_log, ip_net_asn") self.schema_attrs = { 'name': 'test-schema1', 'description': 'Test schema numero uno!' } self.schema_attrs['id'] = self.nipap.add_schema(self.auth, self.schema_attrs) self.schema_attrs2 = { 'name': 'test-schema2', 'description': 'Test schema numero dos!' } self.schema_attrs2['id'] = self.nipap.add_schema(self.auth, self.schema_attrs2) self.pool_attrs = { 'name': 'test-pool1', 'description': 'Test pool numero uno!', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } self.pool_attrs['id'] = self.nipap.add_pool(self.auth, {'id': self.schema_attrs['id']}, self.pool_attrs) self.prefix_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': '' } self.prefix_attrs1['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs1) self.prefix_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix_attrs['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs) self.prefix_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.2.0/23', 'type': 'reservation', 'description': '' } self.prefix_attrs2['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs2) self.prefix_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': '' } self.prefix_attrs3['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs3) self.prefix_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/17', 'type': 'reservation', 'description': '' } self.prefix_attrs4['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs2['id']}, self.prefix_attrs4) self.prefix6_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/112', 'type': 'assignment', 'description': '' } self.prefix6_attrs1['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs1) self.prefix6_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::1/128', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix6_attrs['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs) self.prefix6_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/64', 'type': 'reservation', 'description': '' } self.prefix6_attrs2['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs2) self.prefix6_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/48', 'type': 'reservation', 'description': '' } self.prefix6_attrs3['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs3) self.prefix6_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/56', 'type': 'reservation', 'description': '' } self.prefix6_attrs4['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs2['id']}, self.prefix6_attrs4)
class NipapRestTest(unittest.TestCase): """ Tests the NIPAP REST API We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None server_url = "http://*****:*****@127.0.0.1:1337/rest/v1/prefixes" headers = { "NIPAP-Authoritative-Source": "nipap", "NIPAP-Username": "******", "NIPAP-Full-Name": "unit tester" } def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute( "DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute( "UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0" ) self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _mangle_prefix_result(self, res): """ Mangle prefix result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_prefix for p in res: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) self.assertIn('expires', p) del (p['added']) del (p['last_modified']) del (p['total_addresses']) del (p['used_addresses']) del (p['free_addresses']) del (p['expires']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) self.assertIn('expires', p) del (p['added']) del (p['last_modified']) del (p['total_addresses']) del (p['used_addresses']) del (p['free_addresses']) del (res['expires']) if isinstance(res, dict): # just one single prefix self.assertIn('added', res) self.assertIn('last_modified', res) self.assertIn('total_addresses', res) self.assertIn('used_addresses', res) self.assertIn('free_addresses', res) self.assertIn('expires', res) del (res['added']) del (res['last_modified']) del (res['total_addresses']) del (res['used_addresses']) del (res['free_addresses']) del (res['expires']) return res def _convert_list_of_unicode_to_str(self, list_of_items): """ Converts list of unicode values to string This helper function converts keys and values in unicode to string for a list containing nested dictionaries. When converting JSON respons back to Python dict the keys and values are added as unicode. This helper function handles the problem, but all types get replaced to strings. Is used for assertEqual. """ result = [] for item in list_of_items: item = dict([(str(k), str(v)) for k, v in item.items()]) result.append(item) return result def test_prefix_add(self): """ Add a prefix and list """ # check that some error / sanity checking is there attr = {} request = requests.post(self.server_url, headers=self.headers, json=attr) text = request.text self.assertRegexpMatches(text, "'attr' must be a dict") attr['prefix'] = '1.3.3.0/24' request = requests.post(self.server_url, headers=self.headers, json=attr) text = request.text self.assertRegexpMatches( text, "Either description or node must be specified.") attr['description'] = 'test prefix' request = requests.post(self.server_url, headers=self.headers, json=attr) text = request.text self.assertRegexpMatches(text, "Unknown prefix type") attr['type'] = 'assignment' attr['order_id'] = 'test' # add 1.3.3.0/24 request = requests.post(self.server_url, headers=self.headers, json=attr) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) attr['id'] = result['id'] self.assertGreater(attr['id'], 0) # what we expect the above prefix to look like expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'avps': {}, 'comment': None, 'country': None, 'description': 'test prefix', 'display_prefix': '1.3.3.0/24', 'external_key': None, 'family': 4, 'id': int(attr['id']), 'indent': 0, 'inherited_tags': [], 'monitor': None, 'node': None, 'order_id': 'test', 'customer_id': None, 'pool_name': None, 'pool_id': None, 'tags': [], 'status': 'assigned', 'vrf_rt': None, 'vrf_id': 0, 'vrf_name': 'default', 'vlan': None } expected = dict([(str(k), str(v)) for k, v in expected.items()]) expected.update(attr) # list of prefixes through GET request parameters = {'order_id': 'test'} list_prefix_request = request = requests.get(self.server_url, headers=self.headers, params=parameters) list_prefix = json.loads(list_prefix_request.text) list_prefix = self._convert_list_of_unicode_to_str(list_prefix) self.assertEqual(self._mangle_prefix_result(list_prefix), [expected]) attr = {} attr['description'] = 'test for from-prefix 1.3.3.0/24' attr['type'] = 'host' attr['order_id'] = 'test' parameters = {'fromPrefix': '1.3.3.0/24', 'prefixLength': 32} # add a host in 1.3.3.0/24 request = requests.post(self.server_url, headers=self.headers, json=attr, params=parameters) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) # copy expected from 1.3.3.0/24 since we expect most things to look the # same for the new prefix (1.3.3.1/32) from 1.3.3.0/24 expected_host = expected.copy() expected_host.update(attr) expected_host['id'] = result['id'] expected_host['prefix'] = '1.3.3.1/32' expected_host['display_prefix'] = '1.3.3.1/24' expected_host['indent'] = 1 # build list of expected expected_list = [] expected_list.append(expected) expected_list.append(expected_host) expected_list = self._convert_list_of_unicode_to_str(expected_list) # add another prefix, try with vrf_id = None attr['vrf_id'] = None request = requests.post(self.server_url, headers=self.headers, json=attr, params=parameters) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) # update expected list expected_host2 = expected_host.copy() expected_host2['id'] = result['id'] expected_host2['prefix'] = '1.3.3.2/32' expected_host2['display_prefix'] = '1.3.3.2/24' expected_list.append(expected_host2) expected_list = self._convert_list_of_unicode_to_str(expected_list) # add another prefix, this time completely without VRF info del (attr['vrf_id']) request = requests.post(self.server_url, headers=self.headers, json=attr, params=parameters) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) # update expected list expected_host3 = expected_host.copy() expected_host3['id'] = result['id'] expected_host3['prefix'] = '1.3.3.3/32' expected_host3['display_prefix'] = '1.3.3.3/24' expected_list.append(expected_host3) expected_list = self._convert_list_of_unicode_to_str(expected_list) # list of prefixes through GET request parameters = {'order_id': 'test'} list_prefix_request = request = requests.get(self.server_url, headers=self.headers, params=parameters) list_prefix = json.loads(list_prefix_request.text) list_prefix = self._convert_list_of_unicode_to_str(list_prefix) mangled_result = self._mangle_prefix_result(list_prefix) # make sure the result looks like we expect it too! :D self.assertEqual(mangled_result, expected_list) def test_prefix_remove(self): """ Removes a prefix """ # add 1.3.3.4/24 attr = {} attr['prefix'] = '1.3.4.0/24' attr['description'] = 'test delete prefix' attr['type'] = 'assignment' attr['order_id'] = 'test' request = requests.post(self.server_url, headers=self.headers, json=attr) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) prefix_id = result['id'] # delete prefix parameters = {'id': prefix_id} request = requests.delete(self.server_url, headers=self.headers, params=parameters) text = request.text result = json.loads(text) result = dict([(str(k), str(v)) for k, v in result.items()]) expected = { 'prefix': '1.3.4.0/24', 'vrf_id': '0', } self.assertEqual(result, expected)
class NipapSql(unittest.TestCase): nipap = Nipap() def clean_up(self): self.nipap._execute("TRUNCATE ip_net_plan CASCADE") self.nipap._execute("TRUNCATE ip_net_pool CASCADE") self.nipap._execute("TRUNCATE ip_net_schema CASCADE") def setUp(self): self.clean_up() self.schema_id = self.nipap.add_schema({ 'name': 'test-schema', 'description': '' }) def test_calc_indent(self): """ Test automatic calculation of indent level Insert baseline data, namely 192.0.2.0/24 Verify indent level is 0, since it should be a root prefix Insert 192.0.2.0/27 Verify indent level is 1 for 192.0.2.0/27 Insert 192.0.2.0/32 Insert 192.0.2.1/32 Insert 192.0.2.2/32 Insert 192.0.2.3/32 Verify indent level is 2 for 192.0.2.[0-3]/32 Insert 192.0.2.32/32 Insert 192.0.2.33/32 Insert 192.0.2.34/32 Insert 192.0.2.35/32 Verify indent level is 1 for 192.0.2.3[2-5]/32 Insert 192.0.2.32/27 Verify indent level is 1 for 192.0.2.32/27 Verify indent level is 2 for 192.0.2.3[2-5]/32 Remove 192.0.2.0/27 Verify indent level is 1 for 192.0.2.[0-3]/32 """ def test_db_constraints(self): """ INSERT 1.3.0.0/16 r allow INSERT 1.3.0.0/16 r deny duplicate INSERT 1.3.3.0/24 r allow INSERT 1.3.3.0/27 a allow INSERT 1.3.3.0/32 h allow INSERT 1.3.3.1/32 h allow INSERT 1.3.3.2/32 a deny assignment within assignment not allowed DELETE 1.3.3.0/27 a deny hosts inside assignment """ self.assertEqual(self._inspre('1.3.0.0/16', 'reservation'), True, 'Unable to insert prefix 1.3.0.0/16') self.assertRaises(NipapDuplicateError, self._inspre, '1.3.0.0/16', 'reservation') # Duplicate prefix detection not working self.assertEqual(self._inspre('1.3.3.0/24', 'reservation'), True) self.assertEqual(self._inspre('1.3.3.0/27', 'assignment'), True) self.assertEqual(self._inspre('1.3.3.0/32', 'host'), True) self.assertEqual(self._inspre('1.3.3.1/32', 'host'), True) self.assertRaises(NipapValueError, self._inspre, '1.3.3.2/31', 'host') # do not allow /31 as type 'host' self.assertRaises(NipapValueError, self._inspre, '1.3.3.3/32', 'assignment') # Able to create assignment within assignment - we should not self.assertRaises(NipapValueError, self._delpre, '1.3.3.0/27') # Able to delete assignment containing hosts - we should not self.assertRaises(NipapValueError, self._updpre, '1.3.3.0/24', 'assignment') def _inspre(self, prefix, prefix_type): """ Insert a prefix Return true on success, exception otherwise """ self.nipap._execute("INSERT INTO ip_net_plan (authoritative_source, schema, prefix, type) VALUES ('nipaptest', %(schema)s, %(prefix)s, %(prefix_type)s)", { 'schema': self.schema_id, 'prefix': prefix, 'prefix_type': prefix_type }) return True def _updpre(self, prefix, prefix_type): """ Update a prefix Return true on success, exception otherwise """ self.nipap._execute("UPDATE ip_net_plan SET type=%(prefix_type)s WHERE schema = %(schema)s AND prefix = %(prefix)s", { 'schema': self.schema_id, 'prefix': prefix, 'prefix_type': prefix_type }) return True def _delpre(self, prefix): """ Delete a prefix Return true on success, exception otherwise """ self.nipap._execute("DELETE FROM ip_net_plan WHERE schema = %(schema)s AND prefix = %(prefix)s", { 'schema': self.schema_id, 'prefix': prefix }) return True
class NipapTest(unittest.TestCase): """ Tests the NIPAP class """ nipap = None def setUp(self): """ Better start from a clean slate! """ cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' self.nipap._execute("TRUNCATE ip_net_plan, ip_net_pool, ip_net_vrf, ip_net_log, ip_net_asn") self.schema_attrs = { 'name': 'test-schema1', 'description': 'Test schema numero uno!' } self.schema_attrs['id'] = self.nipap.add_schema(self.auth, self.schema_attrs) self.schema_attrs2 = { 'name': 'test-schema2', 'description': 'Test schema numero dos!' } self.schema_attrs2['id'] = self.nipap.add_schema(self.auth, self.schema_attrs2) self.pool_attrs = { 'name': 'test-pool1', 'description': 'Test pool numero uno!', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } self.pool_attrs['id'] = self.nipap.add_pool(self.auth, {'id': self.schema_attrs['id']}, self.pool_attrs) self.prefix_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': '' } self.prefix_attrs1['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs1) self.prefix_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix_attrs['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs) self.prefix_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.2.0/23', 'type': 'reservation', 'description': '' } self.prefix_attrs2['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs2) self.prefix_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': '' } self.prefix_attrs3['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs3) self.prefix_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/17', 'type': 'reservation', 'description': '' } self.prefix_attrs4['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs2['id']}, self.prefix_attrs4) self.prefix6_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/112', 'type': 'assignment', 'description': '' } self.prefix6_attrs1['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs1) self.prefix6_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::1/128', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix6_attrs['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs) self.prefix6_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/64', 'type': 'reservation', 'description': '' } self.prefix6_attrs2['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs2) self.prefix6_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/48', 'type': 'reservation', 'description': '' } self.prefix6_attrs3['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs3) self.prefix6_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/56', 'type': 'reservation', 'description': '' } self.prefix6_attrs4['id'] = self.nipap.add_prefix(self.auth, {'id': self.schema_attrs2['id']}, self.prefix6_attrs4) def test_schema_basic(self): """ Basic schema test 1. Add a new schema 2. List with filters to get newly created schema 3. Verify listed schema coincides with input args for added schema 4. Remove schema """ attrs = { 'name': 'test-schema-wrong', 'description': 'A simple test schema with incorrect name!' } attrs['id'] = self.nipap.add_schema(self.auth, attrs) schema = self.nipap.list_schema(self.auth, { 'id': attrs['id'] }) for a in attrs: self.assertEqual(schema[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_schema_add_crap_input(self): """ Try to input junk into add_schema and expect error """ attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!', 'crap': 'this is just some crap' } # missing everything self.assertRaises(NipapMissingInputError, self.nipap.add_schema, self.auth, { }) # missing description self.assertRaises(NipapMissingInputError, self.nipap.add_schema, self.auth, { 'name': 'crapson' }) # have required and extra crap self.assertRaises(NipapExtraneousInputError, self.nipap.add_schema, self.auth, attrs) def test_expand_schema_spec(self): """ Test the expand_schema_spec() The _expand_schema_spec() function is used throughout the schema functions to expand the schema specification input and so we test the separately. """ # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, 'string') # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, 1) # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, []) # missing keys self.assertRaises(NipapMissingInputError, self.nipap._expand_schema_spec, { }) # crap key self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, { 'crap': self.schema_attrs['name'] }) # required keys and extra crap self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, { 'name': self.schema_attrs['name'], 'crap': 'crap' }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_schema_spec, { 'id': '3' }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_schema_spec, { 'name': 3 }) # both id and name self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, { 'id': 3, 'name': '3' }) # proper key - id where, params = self.nipap._expand_schema_spec({ 'id': 3 }) self.assertEqual(where, 'id = %(spec_id)s', "Improperly expanded WHERE clause") self.assertEqual(params, {'spec_id': 3}, "Improperly expanded params dict") # proper spec - name where, params = self.nipap._expand_schema_spec({ 'name': 'test' }) def test_schema_edit_crap_input(self): """ Try to input junk into edit_schema and expect error """ attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!' } crap_attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!', 'crap': 'this is just some crap' } # spec is tested elsewhere, just test attrs part self.assertRaises(NipapExtraneousInputError, self.nipap.edit_schema, self.auth, { 'name': self.schema_attrs['name'] }, crap_attrs) def test_schema_list_crap_input(self): """ Try to input junk into list_schema and expect error """ # TODO: what do we really expect? self.assertRaises(NipapExtraneousInputError, self.nipap.list_schema, self.auth, { 'crap': 'crap crap' }) def test_schema_dupe(self): """ Check so we can't create duplicate schemas There are unique indices in the database that should prevent us from creating duplicate schema (ie, with the same name). """ schema_attrs = { 'name': 'test-schema-dupe', 'description': 'Testing dupe' } self.nipap.add_schema(self.auth, schema_attrs) self.assertRaises(NipapDuplicateError, self.nipap.add_schema, self.auth, schema_attrs) def test_schema_rename(self): """ Rename a schema Uses the edit_schema() functionality to rename our previously created and incorrectly named schema so it hereafter has the correct name. Also tests the list_schema() functionality since we use that to list the modified schema. """ spec = { 'name': 'test-schema1' } attrs = { 'name': 'test-schema', 'description': 'A simple test schema with correct name!' } self.nipap.edit_schema(self.auth, spec, attrs) # check that search for old record doesn't return anything schema = self.nipap.list_schema(self.auth, spec) self.assertEqual(schema, [], 'Old entry still exists') schema = self.nipap.list_schema(self.auth, { 'name': 'test-schema' }) for a in attrs: self.assertEqual(schema[0][a], attrs[a], 'Modified schema differ from listed on attribute: ' + a) def test_schema_remove(self): """ Remove a schema Remove the schema previously modified and make sure it's not there. """ spec = { 'name': 'test-schema' } self.nipap.remove_schema(self.auth, spec) # check that search for old record doesn't return anything schema = self.nipap.list_schema(self.auth, spec) self.assertEqual(schema, [], 'Old entry still exists') def test_expand_pool_spec(self): """ Test the function which expands pool spec to SQL. """ schema = {'id': self.schema_attrs['id']} # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, 'string') # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, 1) # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, []) # missing keys self.assertRaises(NipapMissingInputError, self.nipap._expand_pool_spec, { }) # crap key self.assertRaises(NipapExtraneousInputError, self.nipap._expand_pool_spec, { 'crap': self.pool_attrs['name'] }) # required keys and extra crap self.assertRaises(NipapExtraneousInputError, self.nipap._expand_pool_spec, { 'id': self.pool_attrs['id'], 'schema': self.schema_attrs['id'], 'crap': 'crap' }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_pool_spec, { 'id': '3', 'schema': self.schema_attrs['id'] }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_pool_spec, { 'name': 3, 'schema': self.schema_attrs['id'] }) # both id and name self.assertRaises(NipapExtraneousInputError, self.nipap._expand_pool_spec, { 'id': 3, 'name': '3', 'schema': self.schema_attrs['id'] }) # proper key - id where, params = self.nipap._expand_pool_spec({ 'id': 3, 'schema': self.schema_attrs['id'] }) self.assertEqual(where, 'po.id = %(spec_id)s AND po.schema = %(spec_schema)s', "Improperly expanded WHERE clause") self.assertEqual(params, {'spec_id': 3, 'spec_schema': self.schema_attrs['id']}, "Improperly expanded params dict") # proper spec - name where, params = self.nipap._expand_pool_spec({ 'name': 'test', 'schema': self.schema_attrs['id'] }) self.assertEqual(where, 'po.name = %(spec_name)s AND po.schema = %(spec_schema)s', "Improperly expanded WHERE clause") self.assertEqual(params, {'spec_name': 'test', 'spec_schema': self.schema_attrs['id'] }, "Improperly expanded params dict") def test_pool_add1(self): """ Add a pool and check it's there using list functions Refer to schema by id """ attrs = { 'name': 'test-pool-wrong', 'description': 'A simple test pool with incorrect name!', 'default_type': 'reservation', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } schema = {'id': self.schema_attrs['id']} pool_id = self.nipap.add_pool(self.auth, schema, attrs) pool = self.nipap.list_pool(self.auth, schema, { 'id': pool_id }) for a in attrs: self.assertEqual(pool[0][a], attrs[a], 'Added object differ from listed on attribute: %s %s!=%s' % (a, attrs[a], pool[0][a])) def test_pool_add2(self): """ Add a pool and check it's there using list functions Refer to schema by name """ schema = {'id': self.schema_attrs['id']} attrs = { 'name': 'test-pool-wrong', 'default_type': 'reservation', 'description': 'A simple test pool with incorrect name!' } pool_id = self.nipap.add_pool(self.auth, schema, attrs) pool = self.nipap.list_pool(self.auth, schema, { 'id': pool_id }) for a in attrs: self.assertEqual(pool[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_edit_pool_by_name(self): """ Try to rename a pool using edit_pool() function Pool is not uniquely identified (empty spec) so this should raise an error """ schema = {'id': self.schema_attrs['id']} spec = { } attrs = { 'name': self.pool_attrs['name'], 'default_type': 'assignment', 'description': 'A simple test pool with correct name!' } self.assertRaises(NipapInputError, self.nipap.edit_pool, self.auth, schema, spec, attrs) def test_edit_pool(self): """ Rename a pool using edit_pool() function """ schema = {'id': self.schema_attrs['id']} spec = { 'id': self.pool_attrs['id'] } attrs = { 'name': 'test-pool', 'default_type': 'assignment', 'description': 'A simple test pool with correct name!', 'ipv4_default_prefix_length': 32, 'ipv6_default_prefix_length': 128 } self.nipap.edit_pool(self.auth, schema, spec, attrs) # check that search for old record doesn't return anything pool = self.nipap.list_pool(self.auth, schema, { 'name': self.pool_attrs['name'] }) self.assertEqual(pool, [], 'Old entry still exists') pool = self.nipap.list_pool(self.auth, schema, { 'name': attrs['name'] }) for a in attrs: self.assertEqual(pool[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_remove_pool_by_id(self): """ Remove a pool by id """ schema = {'id': self.schema_attrs['id']} pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Record must exist before we can delete it') for a in self.pool_attrs: self.assertEqual(pool[0][a], self.pool_attrs[a], 'Listed attribute differ from original') # remove the pool self.nipap.remove_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) # check that search for old record doesn't return anything pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) self.assertEqual(pool, [], 'Old entry still exists') def test_prefix_in_a_pool(self): """ Add prefixes to a poll and list! """ schema = {'id': self.schema_attrs['id']} pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '1.2.2.0/32', '1.2.2.1/32', '1.2.2.2/32', '1.2.2.3/32', '1.2.2.4/32', '1.2.2.5/32' ] for p in pfxs: prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': p, 'type': 'host', 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # list again pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) self.assertNotEqual(pool[0], [], 'Pool must exist!') self.assertEqual(set(pfxs), set(pool[0]['prefixes']), 'Returned prefixes do not match added ones') def test_prefix_basic(self): """ Test basic prefix functions """ schema = {'id': self.schema_attrs['id']} prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '1.3.3.7/32', 'type': 'host', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) prefix = self.nipap.list_prefix(self.auth, schema, { 'prefix': prefix_attrs['prefix'] }) for a in prefix_attrs: self.assertEqual(prefix[0][a], prefix_attrs[a], 'Added object differ from listed on attribute: ' + a) # fetch many prefixes - all in a schema prefix = self.nipap.list_prefix(self.auth, schema, {}) self.assertNotEqual(len(prefix), 0, 'Found 0 prefixes in schema ' + self.schema_attrs['name']) def test_add_prefix(self): """ Test add_prefix in a bit more detail """ schema = {'id': self.schema_attrs['id']} # we need a bloody pool first! pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '10.0.0.0/24', '10.0.1.0/24', '10.0.2.0/24', '10.0.3.0/24', '10.0.4.0/24' ] for p in pfxs: prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': p, 'type': 'reservation', 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # get an address based on from-prefix prefix_attrs = { 'type': 'assignment', 'authoritative_source': 'nipap-test', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } res = self.nipap.add_prefix(self.auth, schema, prefix_attrs, { 'from-prefix': ['10.0.0.0/24'], 'prefix_length': 30 }) p = self.nipap.list_prefix(self.auth, schema, { 'id': res }) self.assertEqual(p[0]['prefix'], '10.0.0.0/30', "New prefix differ from what it should be!") self.nipap.add_schema(self.auth, { 'name': 'testtest', 'description': 'another test schema!' }) # pass different schemas in attr and args # TODO: Find something similar? #self.assertRaises(NipapInputError, self.nipap.add_prefix, schema, { 'authoritative_source': 'nipap-test', 'description': 'tjong' }, { 'from-prefix': ['10.0.0.0/24'], 'prefix_length': 30 }) def test_prefix_search_simple(self): """ Test the simple prefix search function. """ schema = {'id': self.schema_attrs['id']} # First, perform e few tests to verify search string expansion. query_keys = dict() query_keys['testing testing'] = "description" query_keys['1.2.3.4'] = "prefix" # build query string query_str = "" for key, val in query_keys.items(): if val == "description": query_str += "\"%s\" " % key else: query_str += "%s " % key res = self.nipap.smart_search_prefix(self.auth, schema, query_str) for interp in res['interpretation']: self.assertEqual(interp['string'] in query_keys, True, "Function returned unknown interpreted string %s" % interp['string']) prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '1.3.3.77/32', 'type': 'host', 'description': 'test-ish prefix', 'comment': 'Test prefix #77! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) res = self.nipap.smart_search_prefix(self.auth, schema, r"""1.3.3.77 "-ish" """) self.assertEqual(res['result'][-1]['prefix'], '1.3.3.77/32', 'Prefix not found') def test_prefix_search_smart(self): """ Test the smart prefix search function. """ schema = {'id': self.schema_attrs['id']} # test full ipv4 address res = self.nipap.smart_search_prefix(self.auth, schema, '1.3.3.7') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv4 address') res = self.nipap.smart_search_prefix(self.auth, schema, '1.1') self.assertEqual(res['interpretation'][0]['interpretation'], 'text', "Incorrectly interpreted '1.1' as : " + res['interpretation'][0]['interpretation']) res = self.nipap.smart_search_prefix(self.auth, schema, '10/8') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv4 prefix') res = self.nipap.smart_search_prefix(self.auth, schema, '2000:0::01') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv6 address') def test_prefix_remove(self): """ Remove a prefix """ schema = {'id': self.schema_attrs['id']} prefix = self.nipap.list_prefix(self.auth, schema, { 'id': self.prefix_attrs['id'] }) # first make sure our prefix exists self.assertEqual(prefix[0]['id'], self.prefix_attrs['id'], 'Record must exist before we can delete it') # remove the prefix, by id self.nipap.remove_prefix(self.auth, schema, { 'id': self.prefix_attrs['id'] }) # check that search for old record doesn't return anything prefix = self.nipap.list_prefix(self.auth, schema, { 'id': self.prefix_attrs['id'] }) self.assertEqual(prefix, [], 'Old entry still exists') def test_prefix_indent_ipv4(self): """ Check that our indentation calculation is working for IPv4 Prefixes gets an indent value automatically assigned to help in displaying prefix information. The indent value is written on updates to the table and this test is to make sure it is correctly calculated. """ schema = {'id': self.schema_attrs['id']} p1 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '1.3.3.1/32' })[0] p2 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '1.3.3.0/24' })[0] p3 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '1.3.0.0/16' })[0] self.assertEqual(p1['indent'], 4, "Indent calc on add failed") self.assertEqual(p2['indent'], 3, "Indent calc on add failed") self.assertEqual(p3['indent'], 0, "Indent calc on add failed") # remove middle prefix self.nipap.remove_prefix(self.auth, schema, { 'id': self.prefix_attrs2['id'] }) # check that child prefix indent level has decreased p1 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '1.3.3.1/32' })[0] p3 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '1.3.0.0/16' })[0] self.assertEqual(p1['indent'], 3, "Indent calc on remove failed") self.assertEqual(p3['indent'], 0, "Indent calc on remove failed") def test_prefix_indent_ipv6(self): """ Check that our indentation calculation is working for IPv6 Prefixes gets an indent value automatically assigned to help in displaying prefix information. The indent value is written on updates to the table and this test is to make sure it is correctly calculated. """ schema = {'id': self.schema_attrs['id']} p1 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '2001:0db8:3:3::1/128' })[0] p2 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '2001:0db8:3:3::/64' })[0] p3 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '2001:0db8:3:0::/48' })[0] self.assertEqual(p1['indent'], 4, "Indent calc on add failed") self.assertEqual(p2['indent'], 2, "Indent calc on add failed") self.assertEqual(p3['indent'], 0, "Indent calc on add failed") # remove middle prefix self.nipap.remove_prefix(self.auth, schema, { 'id': self.prefix6_attrs2['id'] }) # check that child prefix indent level has decreased p1 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '2001:0db8:3:3::1/128' })[0] p3 = self.nipap.list_prefix(self.auth, schema, { 'prefix': '2001:0db8:3:0::/48' })[0] self.assertEqual(p1['indent'], 3, "Indent calc on remove failed for " + p1['prefix'] + " indent: " + str(p1['indent'])) self.assertEqual(p3['indent'], 0, "Indent calc on remove failed for " + p3['prefix'] + " indent: " + str(p3['indent'])) def test_find_free_prefix_input(self): """ Mostly input testing of find_free_prefix Try to stress find_free_prefix and send a lot of junk.. """ schema = {'id': self.schema_attrs['id']} # set up a prefix not used elsewhere so we have a known good state prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '100.0.0.0/16', 'type': 'reservation', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # no schema, should raise error! self.assertRaises(NipapInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16'] }) # incorrect from-prefix type, string instead of list of strings (looking like an IP address) self.assertRaises(NipapInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': '100.0.0.0/16' }) # missing prefix_length self.assertRaises(NipapMissingInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '100.0.0.0/16' ], 'count': 1 }) # try giving both IPv4 and IPv6 in from-prefix which shouldn't work self.assertRaises(NipapInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '100.0.0.0/16', '2a00:800::0/25' ], 'prefix_length': 24, 'count': 1 }) # try giving non-integer as wanted prefix length self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '100.0.0.0/16'], 'prefix_length': '24', 'count': 1 }) # try giving to high a number as wanted prefix length for IPv4 self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '100.0.0.0/16'], 'prefix_length': 35, 'count': 1 }) # try giving to high a number as wanted prefix length for IPv6 self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '2a00:800::1/25'], 'prefix_length': 150, 'count': 1 }) # try giving a high number for result count (max is 1000) self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': [ '100.0.0.0/16'], 'prefix_length': 30, 'count': 55555 }) # don't pass 'family', which is required when specifying 'from-pool' self.assertRaises(NipapMissingInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1 }) # pass crap as family, wrong type even self.assertRaises(ValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1, 'family': 'crap' }) # pass 7 as family self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1, 'family': 7 }) # pass non existent pool self.assertRaises(NipapNonExistentError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': 'crap' }, 'prefix_length': 24, 'count': 1, 'family': 4 }) def test_find_free_prefix1(self): """ Functionality testing of find_free_prefix Mostly based on 'from-prefix' """ schema = { 'id': self.schema_attrs['id'] } # set up a prefix not used elsewhere so we have a known good state prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '100.0.0.0/16', 'type': 'assignment', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # simple test res = self.nipap.find_free_prefix(self.auth, schema, { 'from-prefix': [ '100.0.0.0/16', '1.3.3.0/24' ], 'prefix_length': 24, 'count': 1 }) self.assertEqual(res, ['100.0.0.0/24'], "Incorrect prefix set returned") # simple test - only one input prefix (which did cause a bug, thus keeping it) res = self.nipap.find_free_prefix(self.auth, schema, { 'from-prefix': [ '100.0.0.0/16' ], 'prefix_length': 24, 'count': 1 }) self.assertEqual(res, ['100.0.0.0/24'], "Incorrect prefix set returned") res = self.nipap.find_free_prefix(self.auth, schema, { 'from-prefix': [ '100.0.0.0/16', '1.3.3.0/24' ], 'prefix_length': 24, 'count': 999 }) self.assertEqual(len(res), 256, "Incorrect prefix set returned") def test_find_free_prefix2(self): """ Functionality testing of find_free_prefix Mostly based on 'from-pool' """ schema = { 'id': self.schema_attrs['id'] } # we need a bloody pool first! pool = self.nipap.list_pool(self.auth, schema, { 'id': self.pool_attrs['id'] }) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '10.0.0.0/24', '10.0.1.0/24', '10.0.2.0/24', '10.0.3.0/24', '10.0.4.0/24' ] for p in pfxs: prefix_attrs = { 'type': 'reservation', 'authoritative_source': 'nipap-test', 'prefix': p, 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # from-pool test res = self.nipap.find_free_prefix(self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'count': 1, 'family': 4}) self.assertEqual(res, ['10.0.1.0/30'], "Incorrect prefix set returned when requesting default prefix-length") # from-pool test, specify wanted prefix length res = self.nipap.find_free_prefix(self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'count': 1, 'family': 4, 'prefix_length': 31}) self.assertEqual(res, ['10.0.1.0/31'], "Incorrect prefix set returned with explicit prefix-length") def test_edit_prefix(self): """ Functionality testing of edit_prefix. """ schema = { 'id': self.schema_attrs['id'] } data = { 'prefix': '192.0.2.0/24', 'description': 'foo', 'comment': 'bar', 'order_id': '0xBEEF', 'customer_id': 'CUST-EEF-DERP', 'alarm_priority': 'low', 'type': 'assignment', 'node': 'TOK-CORE-1', 'country': 'EE', 'authoritative_source': 'unittest', 'pool': self.pool_attrs['id'] } # basic edit self.nipap.edit_prefix(self.auth, schema, { 'id': self.prefix_attrs['id'] }, data) p = self.nipap.list_prefix(self.auth, schema, {'id': self.prefix_attrs['id']})[0] # remove what we did not touch for k, v in data.keys(): if k not in p: del p[k] self.assertEqual(data, p, "Prefix data incorrect after edit.") # create a collision self.assertRaises(NipapError, self.nipap.edit_prefix, self.auth, schema, {'id': self.prefix_attrs2['id']}, {'prefix': data['prefix']}) # try to change schema - disallowed self.assertRaises(NipapExtraneousInputError, self.nipap_edit_prefix, self.auth, schema, {'id': self.prefix_attrs2['id']}, {'schema': self.schema_attrs2['id']}) def test_add_asn(self): """ Test adding ASNs to NIPAP. """ data = { 'asn': 1, 'name': 'Test ASN #1' } self.assertEqual(self.nipap.add_asn(self.auth, data), 1, "add_asn did not return correct ASN.") asn = self.nipap.list_asn(self.auth, { 'asn': 1 })[0] self.assertEquals(data, asn, "ASN in database not equal to what was added.") self.assertRaises(NipapDuplicateError, self.nipap.add_asn, self.auth, data) def test_remove_asn(self): """ Test removing ASNs from NIPAP. """ data = { 'asn': 2, 'name': 'Test ASN #2' } asn = self.nipap.add_asn(self.auth, data) self.nipap.remove_asn(self.auth, asn) self.assertEquals(0, len(self.nipap.list_asn(self.auth, { 'asn': 2 })), "Removed ASN still in database") def test_edit_asn(self): """ Test editing ASNs. """ data = { 'asn': 3, 'name': 'Test ASN #3' } asn = self.nipap.add_asn(self.auth, data) self.nipap.edit_asn(self.auth, data['asn'], { 'name': 'b0rk' }) self.assertEquals(self.nipap.list_asn(self.auth, { 'asn': 3 })[0]['name'], 'b0rk', "Edited ASN still has it's old name.") self.assertRaises(NipapExtraneousInputError, self.nipap.edit_asn, self.auth, {'asn': 3}, {'asn': 4, 'name': 'Test ASN #4'}) def test_search_asn(self): """ Test searching ASNs. """ data = { 'asn': 4, 'name': 'This is AS number 4' } asn = self.nipap.add_asn(self.auth, data) q = { 'operator': 'equals', 'val1': 'asn', 'val2': data['asn'] } res = self.nipap.search_asn(self.auth, q) self.assertEquals(len(res['result']), 1, "equal search resulted in wrong number of hits") self.assertEquals(res['result'][0]['name'], data['name'], "search hit got wrong name") q = { 'operator': 'regex_match', 'val1': 'name', 'val2': 'number' } res = self.nipap.search_asn(self.auth, q) self.assertEquals(len(res['result']), 1, "regex search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") def test_smart_search_asn(self): """ Test smart_search_asn function. """ data = { 'asn': 5, 'name': 'Autonomous System Number 5' } asn = self.nipap.add_asn(self.auth, data) res = self.nipap.smart_search_asn(self.auth, "Autonomous") self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'name', "search term interpretated as wrong type") res = self.nipap.smart_search_asn(self.auth, "5") self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'asn', "search term interpretated as wrong type")
class NipapXmlTest(unittest.TestCase): """ Tests the NIPAP XML-RPC daemon We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _mangle_pool_result(self, res): """ Mangle pool result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_pool for p in res: self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('member_prefixes_v4', p) self.assertIn('member_prefixes_v6', p) self.assertIn('used_prefixes_v4', p) self.assertIn('used_prefixes_v6', p) self.assertIn('free_prefixes_v4', p) self.assertIn('free_prefixes_v6', p) self.assertIn('total_prefixes_v4', p) self.assertIn('total_prefixes_v6', p) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['member_prefixes_v4']) del(p['member_prefixes_v6']) del(p['used_prefixes_v4']) del(p['used_prefixes_v6']) del(p['free_prefixes_v4']) del(p['free_prefixes_v6']) del(p['total_prefixes_v4']) del(p['total_prefixes_v6']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('member_prefixes_v4', p) self.assertIn('member_prefixes_v6', p) self.assertIn('used_prefixes_v4', p) self.assertIn('used_prefixes_v6', p) self.assertIn('free_prefixes_v4', p) self.assertIn('free_prefixes_v6', p) self.assertIn('total_prefixes_v4', p) self.assertIn('total_prefixes_v6', p) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['member_prefixes_v4']) del(p['member_prefixes_v6']) del(p['used_prefixes_v4']) del(p['used_prefixes_v6']) del(p['free_prefixes_v4']) del(p['free_prefixes_v6']) del(p['total_prefixes_v4']) del(p['total_prefixes_v6']) elif isinstance(res, dict): # just one single pool self.assertIn('total_addresses_v4', res) self.assertIn('total_addresses_v6', res) self.assertIn('used_addresses_v4', res) self.assertIn('used_addresses_v6', res) self.assertIn('free_addresses_v4', res) self.assertIn('free_addresses_v6', res) self.assertIn('member_prefixes_v4', res) self.assertIn('member_prefixes_v6', res) self.assertIn('used_prefixes_v4', res) self.assertIn('used_prefixes_v6', res) self.assertIn('free_prefixes_v4', res) self.assertIn('free_prefixes_v6', res) self.assertIn('total_prefixes_v4', res) self.assertIn('total_prefixes_v6', res) del(res['total_addresses_v4']) del(res['total_addresses_v6']) del(res['used_addresses_v4']) del(res['used_addresses_v6']) del(res['free_addresses_v4']) del(res['free_addresses_v6']) del(res['member_prefixes_v4']) del(res['member_prefixes_v6']) del(res['used_prefixes_v4']) del(res['used_prefixes_v6']) del(res['free_prefixes_v4']) del(res['free_prefixes_v6']) del(res['total_prefixes_v4']) del(res['total_prefixes_v6']) return res def _mangle_prefix_result(self, res): """ Mangle prefix result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_prefix for p in res: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict): # just one single prefix self.assertIn('added', res) self.assertIn('last_modified', res) self.assertIn('total_addresses', res) self.assertIn('used_addresses', res) self.assertIn('free_addresses', res) del(res['added']) del(res['last_modified']) del(res['total_addresses']) del(res['used_addresses']) del(res['free_addresses']) return res def _mangle_vrf_result(self, res): """ Mangle vrf result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_vrf for p in res: #self.assertIn('added', p) #self.assertIn('last_modified', p) self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('num_prefixes_v4', p) self.assertIn('num_prefixes_v6', p) #del(p['added']) #del(p['last_modified']) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['num_prefixes_v4']) del(p['num_prefixes_v6']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: #self.assertIn('added', p) #self.assertIn('last_modified', p) self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('num_prefixes_v4', p) self.assertIn('num_prefixes_v6', p) #del(p['added']) #del(p['last_modified']) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['num_prefixes_v4']) del(p['num_prefixes_v6']) elif isinstance(res, dict): # just one single vrf #self.assertIn('added', res) #self.assertIn('last_modified', res) self.assertIn('total_addresses_v4', res) self.assertIn('total_addresses_v6', res) self.assertIn('used_addresses_v4', res) self.assertIn('used_addresses_v6', res) self.assertIn('free_addresses_v4', res) self.assertIn('free_addresses_v6', res) self.assertIn('num_prefixes_v4', res) self.assertIn('num_prefixes_v6', res) #del(res['added']) #del(res['last_modified']) del(res['total_addresses_v4']) del(res['total_addresses_v6']) del(res['used_addresses_v4']) del(res['used_addresses_v6']) del(res['free_addresses_v4']) del(res['free_addresses_v6']) del(res['num_prefixes_v4']) del(res['num_prefixes_v6']) return res def test_vrf_add_list(self): """ Add a VRF and verify result in database """ attr = {} with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute rt'): s.add_vrf({ 'auth': ad, 'attr': attr }) attr['rt'] = '123:456' with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute name'): s.add_vrf({ 'auth': ad, 'attr': attr }) attr['name'] = 'test' attr['tags'] = [] attr['description'] = 'my test vrf' vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) self.assertGreater(vrf['id'], 0) ref = attr.copy() ref['id'] = vrf['id'] self.assertEqual(self._mangle_vrf_result(vrf), ref) self.assertEqual(self._mangle_vrf_result(s.list_vrf({ 'auth': ad, 'vrf': { 'id': vrf['id'] } })), [ ref, ]) attr['rt'] = '123:abc' with self.assertRaisesRegexp(xmlrpclib.Fault, '.'): # TODO: specify exception string s.add_vrf({ 'auth': ad, 'attr': attr }) def test_vrf_edit_default(self): """ Edit the default VRF and verify the change """ # try to set an RT, which should fail on the default VRF with self.assertRaisesRegexp(xmlrpclib.Fault, 'Invalid input for column rt, must be NULL for VRF id 0'): s.edit_vrf({ 'auth': ad, 'vrf': { 'id': 0 }, 'attr': { 'rt': '123:456a' }}) res_edit = s.edit_vrf({ 'auth': ad, 'vrf': { 'id': 0 }, 'attr': { 'name': 'FOO', 'description': 'BAR', 'tags': [] }}) res_list = s.list_vrf({ 'auth': ad, 'vrf': { } })[0] del(res_list['id']) self.assertEqual(self._mangle_vrf_result(res_list), { 'rt': None, 'name': 'FOO', 'description': 'BAR', 'tags': [] }, 'VRF change incorrect') def test_vrf_edit(self): """ Edit VRF and verify the change """ attr = { 'rt': '65000:123', 'name': '65k:123', 'description': 'VRF 65000:123', 'tags': [] } spec = { 'rt': '65000:123' } vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) # omitting VRF spec with self.assertRaisesRegexp(xmlrpclib.Fault, 'vrf specification must be a dict'): s.edit_vrf({ 'auth': ad, 'attr': { 'name': 'test_vrf_edit' } }) # omitting VRF attributes with self.assertRaisesRegexp(xmlrpclib.Fault, 'invalid input type, must be dict'): s.edit_vrf({ 'auth': ad, 'vrf': spec }) # specifying too many attributes in spec with self.assertRaisesRegexp(xmlrpclib.Fault, 'specification contains too many keys'): s.edit_vrf({ 'auth': ad, 'vrf': { 'rt': '65000:123', 'name': '65k:123' }, 'attr': {} }) # test changing ID with self.assertRaisesRegexp(xmlrpclib.Fault, 'extraneous attribute'): s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': { 'id': 1337 } }) # empty attribute list with self.assertRaisesRegexp(xmlrpclib.Fault, "'attr' must not be empty."): s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': {} }) res = s.list_vrf({ 'auth': ad, 'vrf': spec }) self.assertEquals(len(res), 1, 'wrong number of VRFs returned') res = res[0] del(res['id']) self.assertEqual(self._mangle_vrf_result(res), attr) # valid change attr['rt'] = '65000:1234' attr['name'] = '65k:1234' attr['description'] = 'VRF 65000:1234' attr['tags'] = [] s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': attr }) # verify result of valid change res = s.list_vrf({ 'auth': ad, 'vrf': { 'rt': attr['rt'] } }) self.assertEquals(len(res), 1, 'wrong number of VRFs returned') res = res[0] # ignore the ID del(res['id']) self.assertEqual(self._mangle_vrf_result(res), attr, 'VRF change incorrect') def test_vrf_add_search(self): """ Add VRF and search for it """ # add VRF attr = { 'rt': '65000:1235', 'name': '65k:1235', 'description': 'Virtual Routing and Forwarding instance 65000:123', 'tags': [] } vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) attr['id'] = vrf['id'] # equal match q = { 'operator': 'equals', 'val1': 'rt', 'val2': attr['rt'] } res = self._mangle_vrf_result(s.search_vrf({ 'auth': ad, 'query': q })) self.assertEquals(res['result'], [ attr, ], 'Search result from equal match did not match') # regex match q = { 'operator': 'regex_match', 'val1': 'description', 'val2': 'instance 65000' } res = self._mangle_vrf_result(s.search_vrf({ 'auth': ad, 'query': q })) self.assertEquals(res['result'], [ attr, ], 'Search result from regex match did not match') # smart search res = self._mangle_vrf_result(s.smart_search_vrf({ 'auth': ad, 'query_string': 'forwarding instance' })) self.assertEquals(res['result'], [ attr, ], 'Smart search result did not match') def test_prefix_add(self): """ Add a prefix and list """ # check that some error / sanity checking is there attr = {} with self.assertRaisesRegexp(xmlrpclib.Fault, "specify 'prefix' or 'from-prefix' or 'from-pool'"): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['prefix'] = '1.3.3.0/24' with self.assertRaisesRegexp(xmlrpclib.Fault, "Either description or node must be specified."): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['description'] = 'test prefix' with self.assertRaisesRegexp(xmlrpclib.Fault, "Unknown prefix type"): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['type'] = 'assignment' # add 1.3.3.0/24 prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) attr['id'] = prefix['id'] self.assertGreater(attr['id'], 0) # what we expect the above prefix to look like expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'description': 'test prefix', 'display_prefix': '1.3.3.0/24', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'inherited_tags': [], 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_name': None, 'pool_id': None, 'tags': [], 'vrf_rt': None, 'vrf_id': 0, 'vrf_name': 'default', 'vlan': None } expected.update(attr) self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), [expected]) attr = { 'description': 'test for from-prefix 1.3.3.0/24', 'type': 'host' } args = { 'from-prefix': ['1.3.3.0/24'], 'prefix_length': 32 } # add a host in 1.3.3.0/24 res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # copy expected from 1.3.3.0/24 since we expect most things to look the # same for the new prefix (1.3.3.1/32) from 1.3.3.0/24 expected_host = expected.copy() expected_host.update(attr) expected_host['id'] = res['id'] expected_host['prefix'] = '1.3.3.1/32' expected_host['display_prefix'] = '1.3.3.1/24' expected_host['indent'] = 1 # build list of expected expected_list = [] expected_list.append(expected) expected_list.append(expected_host) # add another prefix, try with vrf_id = None attr['vrf_id'] = None res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host2 = expected_host.copy() expected_host2['id'] = res['id'] expected_host2['prefix'] = '1.3.3.2/32' expected_host2['display_prefix'] = '1.3.3.2/24' expected_list.append(expected_host2) # add another prefix, this time completely without VRF info del(attr['vrf_id']) res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host3 = expected_host.copy() expected_host3['id'] = res['id'] expected_host3['prefix'] = '1.3.3.3/32' expected_host3['display_prefix'] = '1.3.3.3/24' expected_list.append(expected_host3) # add another prefix, based on VRF name attr['vrf_name'] = 'default' res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host4 = expected_host.copy() expected_host4['id'] = res['id'] expected_host4['prefix'] = '1.3.3.4/32' expected_host4['display_prefix'] = '1.3.3.4/24' expected_list.append(expected_host4) # make sure the result looks like we expect it too! :D self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected_list) def test_prefix_add_vrf(self): """ Test adding prefixes to VRF """ args = { 'from-prefix': ['1.3.3.0/24'], 'prefix_length': 32 } expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'description': 'test prefix', 'display_prefix': '1.3.3.0/24', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': None, 'vlan': None, 'inherited_tags': [], 'tags': [] } # add VRF vrf_attr = { 'rt': '123:4567', 'name': 'test_prefix_add_vrf1', } vrf = s.add_vrf({ 'auth': ad, 'attr': vrf_attr }) vrf_attr['id'] = vrf['id'] # add prefix to VRF by specifying ID vrf_pref_attr = { 'prefix': '1.3.3.0/24', 'vrf_id': vrf['id'], 'type': 'assignment', 'description': 'Test prefix 1.3.3.0/24 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr }) expected['id'] = prefix['id'] expected['vrf_rt'] = vrf_attr['rt'] expected['vrf_name'] = vrf_attr['name'] expected['display_prefix'] = '1.3.3.0/24' expected['indent'] = 0 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF ID reference not equal') # add prefix to VRF by specifying VRF vrf_pref_attr = { 'vrf_rt': vrf_attr['rt'], 'type': 'host', 'description': 'Test host 1.3.3.1/32 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr, 'args': args }) expected['id'] = prefix['id'] expected['vrf_id'] = vrf_attr['id'] expected['vrf_name'] = vrf_attr['name'] expected['display_prefix'] = '1.3.3.1/24' expected['prefix'] = '1.3.3.1/32' expected['indent'] = 1 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF reference not equal') # add prefix to VRF by specifying VRF name vrf_pref_attr = { 'vrf_name': vrf_attr['name'], 'type': 'host', 'description': 'Test host 1.3.3.2/32 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr, 'args': args }) expected['id'] = prefix['id'] expected['vrf_rt'] = vrf_attr['rt'] expected['vrf_id'] = vrf_attr['id'] expected['display_prefix'] = '1.3.3.2/24' expected['prefix'] = '1.3.3.2/32' expected['indent'] = 1 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF name reference not equal') def test_prefix_add_tags(self): """ Verify tag inheritance works correctly """ expected = [] expected_top = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'display_prefix': '1.0.0.0/8', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'inherited_tags': [], 'vlan': None } # add the "top" prefix - 1.0.0.0/8 attr = { 'prefix': '1.0.0.0/8', 'description': 'top prefix', 'type': 'reservation', 'tags': ['top'] } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_top.update(attr) expected_top['id'] = prefix['id'] # add the "bottom" prefix 1.3.3.0/24 attr = { 'prefix': '1.3.3.0/24', 'description': 'bottom prefix', 'type': 'assignment', 'tags': ['bottom'], } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_bottom = expected_top.copy() expected_bottom.update(attr) expected_bottom['id'] = prefix['id'] expected_bottom['display_prefix'] = '1.3.3.0/24' expected_bottom['inherited_tags'] = ['top'] expected_bottom['indent'] = 1 # check the list is correct! expected = [ expected_top, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # add the "middle" prefix 1.3.0.0/16 attr = { 'prefix': '1.3.0.0/16', 'description': 'middle prefix', 'type': 'reservation', 'tags': ['middle'], } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_middle = expected_top.copy() expected_middle.update(attr) expected_middle['id'] = prefix['id'] expected_middle['display_prefix'] = '1.3.0.0/16' expected_middle['inherited_tags'] = ['top'] expected_middle['indent'] = 1 expected_bottom['inherited_tags'] = ['middle', 'top'] expected_bottom['indent'] = 2 # check the list is correct! expected = [ expected_top, expected_middle, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # remove middle prefix s.remove_prefix({ 'auth': ad, 'prefix': { 'id': expected_middle['id'] } }) expected_bottom['inherited_tags'] = ['top'] expected_bottom['indent'] = 1 # check the list is correct! expected = [ expected_top, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # remove top prefix s.remove_prefix({ 'auth': ad, 'prefix': { 'id': expected_top['id'] } }) expected_bottom['inherited_tags'] = [] expected_bottom['indent'] = 0 # check the list is correct! expected = [ expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) def test_prefix_node(self): """ Test node constraints Setting the node value is not allowed for all prefix types. Make sure the constraints are working correctly. """ expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'type': 'assignment', 'description': 'test prefix', 'display_prefix': '1.3.2.0/24', 'external_key': None, 'family': 4, 'indent': 0, 'inherited_tags': [], 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_name': None, 'pool_id': None, 'tags': [], 'vrf_rt': None, 'vrf_id': 0, 'vrf_name': 'default', 'vlan': None } expected_list = [] attr = {} attr['prefix'] = '1.3.2.0/24' attr['description'] = 'test prefix' attr['type'] = 'assignment' # add an assignment which we need later on res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp1 = expected.copy() exp1.update(attr) exp1['id'] = res['id'] attr['prefix'] = '1.3.3.0/24' # set node attr['node'] = 'test' # node value is not allowed at all for prefixes of type reservation attr['type'] = 'reservation' with self.assertRaisesRegexp(xmlrpclib.Fault, "Not allowed to set 'node' value for prefixes of type 'reservation'."): s.add_prefix({ 'auth': ad, 'attr': attr }) # node value is only allowed for assignments when prefix-length is max # (/24 for IPv4 or /128 for IPv6). attr['type'] = 'assignment' with self.assertRaisesRegexp(xmlrpclib.Fault, "Not allowed to set 'node' value for prefixes of type 'assignment' which do not have all bits set in netmask."): s.add_prefix({ 'auth': ad, 'attr': attr }) # correct prefix length attr['prefix'] = '1.3.3.0/32' res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp2 = expected.copy() exp2.update(attr) exp2['id'] = res['id'] exp2['display_prefix'] = '1.3.3.0' # let's add a host too attr['type'] = 'host' attr['prefix'] = '1.3.2.1/32' res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp3 = expected.copy() exp3.update(attr) exp3['id'] = res['id'] exp3['display_prefix'] = '1.3.2.1/24' exp3['indent'] = 1 # note the non-intuitive order expected_list.append(exp1) expected_list.append(exp3) expected_list.append(exp2) res = self._mangle_prefix_result(s.list_prefix({ 'auth': ad })) self.assertEqual(res, expected_list) def test_prefix_add_to_pool(self): """ Test adding prefixes to a pool """ # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool #1', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool prefix_attr = { 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # Add prefix to pool prefix_attr = { 'prefix': '1.4.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_name': 'pool_1' } s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # add a prefix prefix_attr = { 'prefix': '1.5.0.0/16', 'type': 'reservation', 'description': 'FOO' } prefix = s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # modify prefix so that it's part of pool s.edit_prefix({ 'auth': ad, 'prefix': { 'id': prefix['id'] }, 'attr': { 'pool_id': pool['id'] } }) # add a prefix prefix_attr = { 'prefix': '1.6.0.0/16', 'type': 'reservation', 'description': 'FOO' } prefix = s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # modify prefix so that it's part of pool s.edit_prefix({ 'auth': ad, 'prefix': { 'id': prefix['id'] }, 'attr': { 'pool_name': 'pool_1' } }) res = s.list_pool({ 'auth': ad, 'pool': { 'id': pool['id'] } }) self.assertEquals(res[0]['prefixes'], ['1.3.0.0/16', '1.4.0.0/16', '1.5.0.0/16', '1.6.0.0/16']) def test_prefix_from_pool(self): """ Add a prefix from a pool """ # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool #1', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool parent_prefix_attr = { 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': parent_prefix_attr }) args = { 'from-pool': { 'name': 'pool_1' }, 'family': 4 } prefix_attr = { 'description': 'BAR' } expected = { 'prefix': '1.3.0.0/24', 'display_prefix': '1.3.0.0/24', 'description': 'BAR', 'type': 'assignment', 'comment': None, 'country': None, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'external_key': None, 'family': 4, 'indent': 1, 'alarm_priority': None, 'authoritative_source': 'nipap' } child = s.add_prefix({ 'auth': ad, 'attr': prefix_attr, 'args': args }) #expected['id'] = child['id'] #p = s.list_prefix({ 'auth': ad, 'attr': { 'id': child['id'] } })[1] #self.assertEquals(p, expected) def test_prefix_from_pool_vrf(self): """ Add a prefix from a pool in a VRF """ # Add a VRF vrf_attr = { 'name' : 'vrf_1', 'description' : 'Test VRF #1', 'rt' : '123:123' } vrf = s.add_vrf({ 'auth': ad, 'attr': vrf_attr }) # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool for from-pool test', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool parent_prefix_attr = { 'prefix': '1.3.0.0/16', 'vrf_rt': '123:123', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': parent_prefix_attr }) args = { 'from-pool': { 'name': 'pool_1' }, 'family': 4 } prefix_attr = { 'description': 'BAR' } expected = { 'prefix': '1.3.0.0/24', 'display_prefix': '1.3.0.0/24', 'description': 'BAR', 'type': 'assignment', 'comment': None, 'country': None, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': vrf['id'], 'vrf_rt': '123:123', 'vrf_name': 'vrf_1', 'external_key': None, 'family': 4, 'indent': 1, 'alarm_priority': None, 'authoritative_source': 'nipap', 'vlan': None, 'inherited_tags': [], 'tags': [] } child = s.add_prefix({ 'auth': ad, 'attr': prefix_attr, 'args': args }) expected['id'] = child['id'] p = s.list_prefix({ 'auth': ad, 'attr': { 'id': child['id'] } })[1] p = self._mangle_prefix_result(p) self.assertEquals(p, expected) def test_prefix_edit_return(self): """ Check return value of edit_prefix """ p1 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'FOO' } }) p2 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'child 1' } }) p3 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.2/32', 'type': 'host', 'description': 'child 2' } }) p4 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.3/32', 'type': 'host', 'description': 'child 3' } }) ss_res = s.smart_search_prefix({ 'auth': ad, 'query_string': 'child 2' })['result'][0] edit_res = s.edit_prefix({ 'auth': ad, 'prefix': { 'prefix': '1.3.3.2/32' }, 'attr': { 'description': 'Kid 2' } })[0] del(edit_res['added']) del(edit_res['last_modified']) del(ss_res['added']) del(ss_res['last_modified']) ss_res['description'] = 'Kid 2' self.assertEqual(ss_res, edit_res) def test_prefix_smart_search(self): """ Test the prefix smart search """ p1 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'FOO' } }) s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/32', 'type': 'host', 'description': 'BAR' } }) res = s.smart_search_prefix({ 'auth': ad, 'query_string': 'F' }) expected = { 'interpretation': [{'operator': 'regex', 'attribute': 'description or comment or node or order_id or customer_id', 'interpretation': 'text', 'string': 'F'}], 'search_options': {'include_all_children': False, 'max_result': 50, 'include_all_parents': False, 'parents_depth': 0, 'offset': 0, 'children_depth': 0, 'parent_prefix': None, 'include_neighbors': False }, 'result': [ {'comment': None, 'external_key': None, 'family': 4, 'prefix': '1.3.3.0/24', 'authoritative_source': 'nipap', 'id': p1['id'], 'display_prefix': '1.3.3.0/24', 'monitor': None, 'children': 1, 'prefix_length': 24, 'type': 'assignment', 'match': True, 'node': None, 'description': 'FOO', 'order_id': None, 'customer_id': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'pool_id': None, 'pool_name': None, 'alarm_priority': None, 'indent': 0, 'country': None, 'display': True, 'vlan': None, 'inherited_tags': [], 'tags': [] } ] } res = self._mangle_prefix_result(res) self.assertEqual(res, expected) def test_asn_add_list(self): """ Add ASN to NIPAP and list it """ attr = { 'asn': 1, 'name': 'Test ASN #1' } # add ASN self.assertEqual(s.add_asn({ 'auth': ad, 'attr': attr}), attr, "add_asn did not return correct ASN.") # make sure that it got added asn = s.list_asn({ 'auth': ad, 'asn': { 'asn': 1 } }) self.assertEqual(len(asn), 1, "Wrong number of ASNs returned.") asn = asn[0] self.assertEquals(attr, asn, "ASN in database not equal to what was added.") # adding the same ASN again should result in duplicate key error with self.assertRaisesRegexp(xmlrpclib.Fault, 'Duplicate value for'): s.add_asn({ 'auth': ad, 'attr': attr }) def test_remove_asn(self): """ Remove ASN from NIPAP """ attr = { 'asn': 2, 'name': 'Test ASN #2' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) s.remove_asn({ 'auth': ad, 'asn': { 'asn': asn['asn'] } }) self.assertEquals(0, len(s.list_asn({ 'auth': ad, 'asn': { 'asn': 2 } })), "Removed ASN still in database") def test_edit_asn(self): """ Edit ASNs """ attr = { 'asn': 3, 'name': 'Test ASN #3' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) s.edit_asn({ 'auth': ad, 'asn': { 'asn': attr['asn'] }, 'attr': { 'name': 'b0rk' } }) self.assertEquals(s.list_asn({ 'auth': ad, 'asn': { 'asn': 3 } })[0]['name'], 'b0rk', "Edited ASN still has it's old name.") with self.assertRaisesRegexp(xmlrpclib.Fault, 'extraneous attribute'): s.edit_asn({ 'auth': ad, 'asn': { 'asn': 3 }, 'attr': {'asn': 4, 'name': 'Test ASN #4'} }) def test_search_asn(self): """ Search ASNs """ attr = { 'asn': 4, 'name': 'This is AS number 4' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) # equal match q = { 'operator': 'equals', 'val1': 'asn', 'val2': attr['asn'] } res = s.search_asn({ 'auth': ad, 'query': q }) self.assertEquals(len(res['result']), 1, "equal search resulted in wrong number of hits") self.assertEquals(res['result'][0]['name'], attr['name'], "search hit got wrong name") # regexp match q = { 'operator': 'regex_match', 'val1': 'name', 'val2': 'number' } res = s.search_asn({ 'auth': ad, 'query': q }) self.assertEquals(len(res['result']), 1, "regex search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") def test_smart_search_asn(self): """ Test smart_search_asn function. """ attr = { 'asn': 5, 'name': 'Autonomous System Number 5' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) res = s.smart_search_asn({ 'auth': ad, 'query_string': "Autonomous" }) self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'name', "search term interpretated as wrong type") res = s.smart_search_asn({ 'auth': ad, 'query_string': "5" }) self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'asn', "search term interpretated as wrong type") def test_pool_add_list(self): """ Test adding a pool and verifying it """ # Add a pool attr = { 'description': 'Test pool #1', 'default_type': 'assignment', 'ipv4_default_prefix_length': 31, 'ipv6_default_prefix_length': 112 } with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute name'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['name'] = 'pool_1' attr['ipv4_default_prefix_length'] = 50 with self.assertRaisesRegexp(xmlrpclib.Fault, '1200: \'Default IPv4 prefix length must be an integer between 1 and 32.'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['ipv4_default_prefix_length'] = 31 attr['ipv6_default_prefix_length'] = 'over 9000' with self.assertRaisesRegexp(xmlrpclib.Fault, '1200: \'Default IPv6 prefix length must be an integer between 1 and 128.'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['ipv6_default_prefix_length'] = 112 res = s.add_pool({ 'auth': ad, 'attr': attr }) expected = attr.copy() expected['id'] = res['id'] expected['prefixes'] = [] expected['vrf_id'] = None expected['vrf_rt'] = None expected['vrf_name'] = None expected['tags'] = [] # list pool and verify data in NIPAP p = s.list_pool({ 'auth': ad, 'pool': { 'id': expected['id'] } }) self.assertEquals(1, len(p), 'Wrong number of pools returned') p = p[0] self.assertEquals(self._mangle_pool_result(p), expected, 'Received pool differs from added pool') def test_edit_pool(self): """ Test editing a pool """ # add a pool, we need something to edit attr = { 'name': 'test_pool_2', 'description': 'Test pool #2', 'default_type': 'reservation', 'ipv4_default_prefix_length': 31, 'ipv6_default_prefix_length': 112 } attr2 = { 'name': 'test_pool_2_edit', 'description': 'Test pool #2 edit', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 96 } res = s.add_pool({ 'auth': ad, 'attr': attr }) s.edit_pool({ 'auth': ad, 'pool': { 'id': res['id'] }, 'attr': attr2 }) expected = attr2.copy() expected['id'] = res['id'] expected['prefixes'] = [] expected['vrf_id'] = None expected['vrf_rt'] = None expected['vrf_name'] = None expected['tags'] = [] self.assertEquals(self._mangle_pool_result(s.list_pool({ 'auth': ad, 'pool': { 'id': res['id'] } })[0]), expected) def test_search_pool(self): """ Test searching pools """ def test_smart_search_pool(self): """ Test smart searching among pools """ def test_remove_pool(self): """ Test removing pools
class NipapXmlTest(unittest.TestCase): """ Tests the NIPAP XML-RPC daemon We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _mangle_pool_result(self, res): """ Mangle pool result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_pool for p in res: self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('member_prefixes_v4', p) self.assertIn('member_prefixes_v6', p) self.assertIn('used_prefixes_v4', p) self.assertIn('used_prefixes_v6', p) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['member_prefixes_v4']) del(p['member_prefixes_v6']) del(p['used_prefixes_v4']) del(p['used_prefixes_v6']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('member_prefixes_v4', p) self.assertIn('member_prefixes_v6', p) self.assertIn('used_prefixes_v4', p) self.assertIn('used_prefixes_v6', p) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['member_prefixes_v4']) del(p['member_prefixes_v6']) del(p['used_prefixes_v4']) del(p['used_prefixes_v6']) elif isinstance(res, dict): # just one single pool self.assertIn('total_addresses_v4', res) self.assertIn('total_addresses_v6', res) self.assertIn('used_addresses_v4', res) self.assertIn('used_addresses_v6', res) self.assertIn('free_addresses_v4', res) self.assertIn('free_addresses_v6', res) self.assertIn('member_prefixes_v4', res) self.assertIn('member_prefixes_v6', res) self.assertIn('used_prefixes_v4', res) self.assertIn('used_prefixes_v6', res) del(res['total_addresses_v4']) del(res['total_addresses_v6']) del(res['used_addresses_v4']) del(res['used_addresses_v6']) del(res['free_addresses_v4']) del(res['free_addresses_v6']) del(res['member_prefixes_v4']) del(res['member_prefixes_v6']) del(res['used_prefixes_v4']) del(res['used_prefixes_v6']) return res def _mangle_prefix_result(self, res): """ Mangle prefix result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_prefix for p in res: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: self.assertIn('added', p) self.assertIn('last_modified', p) self.assertIn('total_addresses', p) self.assertIn('used_addresses', p) self.assertIn('free_addresses', p) del(p['added']) del(p['last_modified']) del(p['total_addresses']) del(p['used_addresses']) del(p['free_addresses']) elif isinstance(res, dict): # just one single prefix self.assertIn('added', res) self.assertIn('last_modified', res) self.assertIn('total_addresses', res) self.assertIn('used_addresses', res) self.assertIn('free_addresses', res) del(res['added']) del(res['last_modified']) del(res['total_addresses']) del(res['used_addresses']) del(res['free_addresses']) return res def _mangle_vrf_result(self, res): """ Mangle vrf result for easier testing We can never predict the values of things like the ID (okay, that one is actually kind of doable) or the added and last_modified timestamp. This function will make sure the values are present but then strip them to make it easier to test against an expected result. All testing of statistics is done in nipaptest.py so we strip that from the result here to make things simple. """ if isinstance(res, list): # res from list_vrf for p in res: #self.assertIn('added', p) #self.assertIn('last_modified', p) self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('num_prefixes_v4', p) self.assertIn('num_prefixes_v6', p) #del(p['added']) #del(p['last_modified']) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['num_prefixes_v4']) del(p['num_prefixes_v6']) elif isinstance(res, dict) and 'result' in res: # res from smart search for p in res['result']: #self.assertIn('added', p) #self.assertIn('last_modified', p) self.assertIn('total_addresses_v4', p) self.assertIn('total_addresses_v6', p) self.assertIn('used_addresses_v4', p) self.assertIn('used_addresses_v6', p) self.assertIn('free_addresses_v4', p) self.assertIn('free_addresses_v6', p) self.assertIn('num_prefixes_v4', p) self.assertIn('num_prefixes_v6', p) #del(p['added']) #del(p['last_modified']) del(p['total_addresses_v4']) del(p['total_addresses_v6']) del(p['used_addresses_v4']) del(p['used_addresses_v6']) del(p['free_addresses_v4']) del(p['free_addresses_v6']) del(p['num_prefixes_v4']) del(p['num_prefixes_v6']) elif isinstance(res, dict): # just one single vrf #self.assertIn('added', res) #self.assertIn('last_modified', res) self.assertIn('total_addresses_v4', res) self.assertIn('total_addresses_v6', res) self.assertIn('used_addresses_v4', res) self.assertIn('used_addresses_v6', res) self.assertIn('free_addresses_v4', res) self.assertIn('free_addresses_v6', res) self.assertIn('num_prefixes_v4', res) self.assertIn('num_prefixes_v6', res) #del(res['added']) #del(res['last_modified']) del(res['total_addresses_v4']) del(res['total_addresses_v6']) del(res['used_addresses_v4']) del(res['used_addresses_v6']) del(res['free_addresses_v4']) del(res['free_addresses_v6']) del(res['num_prefixes_v4']) del(res['num_prefixes_v6']) return res def test_vrf_add_list(self): """ Add a VRF and verify result in database """ attr = {} with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute rt'): s.add_vrf({ 'auth': ad, 'attr': attr }) attr['rt'] = '123:456' with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute name'): s.add_vrf({ 'auth': ad, 'attr': attr }) attr['name'] = 'test' attr['description'] = 'my test vrf' vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) self.assertGreater(vrf['id'], 0) ref = attr.copy() ref['id'] = vrf['id'] self.assertEqual(self._mangle_vrf_result(vrf), ref) self.assertEqual(self._mangle_vrf_result(s.list_vrf({ 'auth': ad, 'vrf': { 'id': vrf['id'] } })), [ ref, ]) attr['rt'] = '123:abc' with self.assertRaisesRegexp(xmlrpclib.Fault, '.'): # TODO: specify exception string s.add_vrf({ 'auth': ad, 'attr': attr }) def test_vrf_edit_default(self): """ Edit the default VRF and verify the change """ # try to set an RT, which should fail on the default VRF with self.assertRaisesRegexp(xmlrpclib.Fault, 'Invalid input for column rt, must be NULL for VRF id 0'): s.edit_vrf({ 'auth': ad, 'vrf': { 'id': 0 }, 'attr': { 'rt': '123:456a' }}) res_edit = s.edit_vrf({ 'auth': ad, 'vrf': { 'id': 0 }, 'attr': { 'name': 'FOO', 'description': 'BAR' }}) res_list = s.list_vrf({ 'auth': ad, 'vrf': { } })[0] del(res_list['id']) self.assertEqual(self._mangle_vrf_result(res_list), { 'rt': None, 'name': 'FOO', 'description': 'BAR' }, 'VRF change incorrect') def test_vrf_edit(self): """ Edit VRF and verify the change """ attr = { 'rt': '65000:123', 'name': '65k:123', 'description': 'VRF 65000:123' } spec = { 'rt': '65000:123' } vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) # omitting VRF spec with self.assertRaisesRegexp(xmlrpclib.Fault, 'vrf specification must be a dict'): s.edit_vrf({ 'auth': ad, 'attr': { 'name': 'test_vrf_edit' } }) # omitting VRF attributes with self.assertRaisesRegexp(xmlrpclib.Fault, 'invalid input type, must be dict'): s.edit_vrf({ 'auth': ad, 'vrf': spec }) # specifying too many attributes in spec with self.assertRaisesRegexp(xmlrpclib.Fault, 'specification contains too many keys'): s.edit_vrf({ 'auth': ad, 'vrf': { 'rt': '65000:123', 'name': '65k:123' }, 'attr': {} }) # test changing ID with self.assertRaisesRegexp(xmlrpclib.Fault, 'extraneous attribute'): s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': { 'id': 1337 } }) # empty attribute list with self.assertRaisesRegexp(xmlrpclib.Fault, "'attr' must not be empty."): s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': {} }) res = s.list_vrf({ 'auth': ad, 'vrf': spec }) self.assertEquals(len(res), 1, 'wrong number of VRFs returned') res = res[0] del(res['id']) self.assertEqual(self._mangle_vrf_result(res), attr) # valid change attr['rt'] = '65000:1234' attr['name'] = '65k:1234' attr['description'] = 'VRF 65000:1234' s.edit_vrf({ 'auth': ad, 'vrf': spec, 'attr': attr }) # verify result of valid change res = s.list_vrf({ 'auth': ad, 'vrf': { 'rt': attr['rt'] } }) self.assertEquals(len(res), 1, 'wrong number of VRFs returned') res = res[0] # ignore the ID del(res['id']) self.assertEqual(self._mangle_vrf_result(res), attr, 'VRF change incorrect') def test_vrf_add_search(self): """ Add VRF and search for it """ # add VRF attr = { 'rt': '65000:1235', 'name': '65k:1235', 'description': 'Virtual Routing and Forwarding instance 65000:123' } vrf = s.add_vrf({ 'auth': ad, 'attr': attr }) attr['id'] = vrf['id'] # equal match q = { 'operator': 'equals', 'val1': 'rt', 'val2': attr['rt'] } res = self._mangle_vrf_result(s.search_vrf({ 'auth': ad, 'query': q })) self.assertEquals(res['result'], [ attr, ], 'Search result from equal match did not match') # regex match q = { 'operator': 'regex_match', 'val1': 'description', 'val2': 'instance 65000' } res = self._mangle_vrf_result(s.search_vrf({ 'auth': ad, 'query': q })) self.assertEquals(res['result'], [ attr, ], 'Search result from regex match did not match') # smart search res = self._mangle_vrf_result(s.smart_search_vrf({ 'auth': ad, 'query_string': 'forwarding instance' })) self.assertEquals(res['result'], [ attr, ], 'Smart search result did not match') def test_prefix_add(self): """ Add a prefix and list """ # check that some error / sanity checking is there attr = {} with self.assertRaisesRegexp(xmlrpclib.Fault, "specify 'prefix' or 'from-prefix' or 'from-pool'"): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['prefix'] = '1.3.3.0/24' with self.assertRaisesRegexp(xmlrpclib.Fault, "Either description or node must be specified."): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['description'] = 'test prefix' with self.assertRaisesRegexp(xmlrpclib.Fault, "Unknown prefix type"): s.add_prefix({ 'auth': ad, 'attr': attr }) attr['type'] = 'assignment' # add 1.3.3.0/24 prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) attr['id'] = prefix['id'] self.assertGreater(attr['id'], 0) # what we expect the above prefix to look like expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'description': 'test prefix', 'display_prefix': '1.3.3.0/24', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'inherited_tags': [], 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_name': None, 'pool_id': None, 'tags': [], 'vrf_rt': None, 'vrf_id': 0, 'vrf_name': 'default', 'vlan': None } expected.update(attr) self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), [expected]) attr = { 'description': 'test for from-prefix 1.3.3.0/24', 'type': 'host' } args = { 'from-prefix': ['1.3.3.0/24'], 'prefix_length': 32 } # add a host in 1.3.3.0/24 res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # copy expected from 1.3.3.0/24 since we expect most things to look the # same for the new prefix (1.3.3.1/32) from 1.3.3.0/24 expected_host = expected.copy() expected_host.update(attr) expected_host['id'] = res['id'] expected_host['prefix'] = '1.3.3.1/32' expected_host['display_prefix'] = '1.3.3.1/24' expected_host['indent'] = 1 # build list of expected expected_list = [] expected_list.append(expected) expected_list.append(expected_host) # add another prefix, try with vrf_id = None attr['vrf_id'] = None res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host2 = expected_host.copy() expected_host2['id'] = res['id'] expected_host2['prefix'] = '1.3.3.2/32' expected_host2['display_prefix'] = '1.3.3.2/24' expected_list.append(expected_host2) # add another prefix, this time completely without VRF info del(attr['vrf_id']) res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host3 = expected_host.copy() expected_host3['id'] = res['id'] expected_host3['prefix'] = '1.3.3.3/32' expected_host3['display_prefix'] = '1.3.3.3/24' expected_list.append(expected_host3) # add another prefix, based on VRF name attr['vrf_name'] = 'default' res = s.add_prefix({ 'auth': ad, 'attr': attr, 'args': args }) # update expected list expected_host4 = expected_host.copy() expected_host4['id'] = res['id'] expected_host4['prefix'] = '1.3.3.4/32' expected_host4['display_prefix'] = '1.3.3.4/24' expected_list.append(expected_host4) # make sure the result looks like we expect it too! :D self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected_list) def test_prefix_add_vrf(self): """ Test adding prefixes to VRF """ args = { 'from-prefix': ['1.3.3.0/24'], 'prefix_length': 32 } expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'description': 'test prefix', 'display_prefix': '1.3.3.0/24', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': None, 'vlan': None, 'inherited_tags': [], 'tags': [] } # add VRF vrf_attr = { 'rt': '123:4567', 'name': 'test_prefix_add_vrf1', } vrf = s.add_vrf({ 'auth': ad, 'attr': vrf_attr }) vrf_attr['id'] = vrf['id'] # add prefix to VRF by specifying ID vrf_pref_attr = { 'prefix': '1.3.3.0/24', 'vrf_id': vrf['id'], 'type': 'assignment', 'description': 'Test prefix 1.3.3.0/24 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr }) expected['id'] = prefix['id'] expected['vrf_rt'] = vrf_attr['rt'] expected['vrf_name'] = vrf_attr['name'] expected['display_prefix'] = '1.3.3.0/24' expected['indent'] = 0 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF ID reference not equal') # add prefix to VRF by specifying VRF vrf_pref_attr = { 'vrf_rt': vrf_attr['rt'], 'type': 'host', 'description': 'Test host 1.3.3.1/32 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr, 'args': args }) expected['id'] = prefix['id'] expected['vrf_id'] = vrf_attr['id'] expected['vrf_name'] = vrf_attr['name'] expected['display_prefix'] = '1.3.3.1/24' expected['prefix'] = '1.3.3.1/32' expected['indent'] = 1 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF reference not equal') # add prefix to VRF by specifying VRF name vrf_pref_attr = { 'vrf_name': vrf_attr['name'], 'type': 'host', 'description': 'Test host 1.3.3.2/32 in vrf 123:4567' } prefix = s.add_prefix({ 'auth': ad, 'attr': vrf_pref_attr, 'args': args }) expected['id'] = prefix['id'] expected['vrf_rt'] = vrf_attr['rt'] expected['vrf_id'] = vrf_attr['id'] expected['display_prefix'] = '1.3.3.2/24' expected['prefix'] = '1.3.3.2/32' expected['indent'] = 1 expected.update(vrf_pref_attr) vrf_pref = s.list_prefix({ 'auth': ad, 'prefix': { 'id': expected['id'] } })[0] vrf_pref = self._mangle_prefix_result(vrf_pref) self.assertEqual(vrf_pref, expected, 'Prefix added with VRF name reference not equal') def test_prefix_add_tags(self): """ Verify tag inheritance works correctly """ expected = [] expected_top = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'display_prefix': '1.0.0.0/8', 'external_key': None, 'family': 4, 'id': 131, 'indent': 0, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'inherited_tags': [], 'vlan': None } # add the "top" prefix - 1.0.0.0/8 attr = { 'prefix': '1.0.0.0/8', 'description': 'top prefix', 'type': 'reservation', 'tags': ['top'] } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_top.update(attr) expected_top['id'] = prefix['id'] # add the "bottom" prefix 1.3.3.0/24 attr = { 'prefix': '1.3.3.0/24', 'description': 'bottom prefix', 'type': 'assignment', 'tags': ['bottom'], } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_bottom = expected_top.copy() expected_bottom.update(attr) expected_bottom['id'] = prefix['id'] expected_bottom['display_prefix'] = '1.3.3.0/24' expected_bottom['inherited_tags'] = ['top'] expected_bottom['indent'] = 1 # check the list is correct! expected = [ expected_top, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # add the "middle" prefix 1.3.0.0/16 attr = { 'prefix': '1.3.0.0/16', 'description': 'middle prefix', 'type': 'reservation', 'tags': ['middle'], } prefix = s.add_prefix({ 'auth': ad, 'attr': attr }) expected_middle = expected_top.copy() expected_middle.update(attr) expected_middle['id'] = prefix['id'] expected_middle['display_prefix'] = '1.3.0.0/16' expected_middle['inherited_tags'] = ['top'] expected_middle['indent'] = 1 expected_bottom['inherited_tags'] = ['middle', 'top'] expected_bottom['indent'] = 2 # check the list is correct! expected = [ expected_top, expected_middle, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # remove middle prefix s.remove_prefix({ 'auth': ad, 'prefix': { 'id': expected_middle['id'] } }) expected_bottom['inherited_tags'] = ['top'] expected_bottom['indent'] = 1 # check the list is correct! expected = [ expected_top, expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) # remove top prefix s.remove_prefix({ 'auth': ad, 'prefix': { 'id': expected_top['id'] } }) expected_bottom['inherited_tags'] = [] expected_bottom['indent'] = 0 # check the list is correct! expected = [ expected_bottom ] self.assertEqual( self._mangle_prefix_result(s.list_prefix({ 'auth': ad })), expected) def test_prefix_node(self): """ Test node constraints Setting the node value is not allowed for all prefix types. Make sure the constraints are working correctly. """ expected = { 'alarm_priority': None, 'authoritative_source': 'nipap', 'comment': None, 'country': None, 'type': 'assignment', 'description': 'test prefix', 'display_prefix': '1.3.2.0/24', 'external_key': None, 'family': 4, 'indent': 0, 'inherited_tags': [], 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_name': None, 'pool_id': None, 'tags': [], 'vrf_rt': None, 'vrf_id': 0, 'vrf_name': 'default', 'vlan': None } expected_list = [] attr = {} attr['prefix'] = '1.3.2.0/24' attr['description'] = 'test prefix' attr['type'] = 'assignment' # add an assignment which we need later on res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp1 = expected.copy() exp1.update(attr) exp1['id'] = res['id'] attr['prefix'] = '1.3.3.0/24' # set node attr['node'] = 'test' # node value is not allowed at all for prefixes of type reservation attr['type'] = 'reservation' with self.assertRaisesRegexp(xmlrpclib.Fault, "Not allowed to set 'node' value for prefixes of type 'reservation'."): s.add_prefix({ 'auth': ad, 'attr': attr }) # node value is only allowed for assignments when prefix-length is max # (/24 for IPv4 or /128 for IPv6). attr['type'] = 'assignment' with self.assertRaisesRegexp(xmlrpclib.Fault, "Not allowed to set 'node' value for prefixes of type 'assignment' which do not have all bits set in netmask."): s.add_prefix({ 'auth': ad, 'attr': attr }) # correct prefix length attr['prefix'] = '1.3.3.0/32' res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp2 = expected.copy() exp2.update(attr) exp2['id'] = res['id'] exp2['display_prefix'] = '1.3.3.0' # let's add a host too attr['type'] = 'host' attr['prefix'] = '1.3.2.1/32' res = s.add_prefix({ 'auth': ad, 'attr': attr }) exp3 = expected.copy() exp3.update(attr) exp3['id'] = res['id'] exp3['display_prefix'] = '1.3.2.1/24' exp3['indent'] = 1 # note the non-intuitive order expected_list.append(exp1) expected_list.append(exp3) expected_list.append(exp2) res = self._mangle_prefix_result(s.list_prefix({ 'auth': ad })) self.assertEqual(res, expected_list) def test_prefix_add_to_pool(self): """ Test adding prefixes to a pool """ # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool #1', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool prefix_attr = { 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # Add prefix to pool prefix_attr = { 'prefix': '1.4.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_name': 'pool_1' } s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # add a prefix prefix_attr = { 'prefix': '1.5.0.0/16', 'type': 'reservation', 'description': 'FOO' } prefix = s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # modify prefix so that it's part of pool s.edit_prefix({ 'auth': ad, 'prefix': { 'id': prefix['id'] }, 'attr': { 'pool_id': pool['id'] } }) # add a prefix prefix_attr = { 'prefix': '1.6.0.0/16', 'type': 'reservation', 'description': 'FOO' } prefix = s.add_prefix({ 'auth': ad, 'attr': prefix_attr }) # modify prefix so that it's part of pool s.edit_prefix({ 'auth': ad, 'prefix': { 'id': prefix['id'] }, 'attr': { 'pool_name': 'pool_1' } }) res = s.list_pool({ 'auth': ad, 'pool': { 'id': pool['id'] } }) self.assertEquals(res[0]['prefixes'], ['1.3.0.0/16', '1.4.0.0/16', '1.5.0.0/16', '1.6.0.0/16']) def test_prefix_from_pool(self): """ Add a prefix from a pool """ # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool #1', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool parent_prefix_attr = { 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': parent_prefix_attr }) args = { 'from-pool': { 'name': 'pool_1' }, 'family': 4 } prefix_attr = { 'description': 'BAR' } expected = { 'prefix': '1.3.0.0/24', 'display_prefix': '1.3.0.0/24', 'description': 'BAR', 'type': 'assignment', 'comment': None, 'country': None, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'external_key': None, 'family': 4, 'indent': 1, 'alarm_priority': None, 'authoritative_source': 'nipap' } child = s.add_prefix({ 'auth': ad, 'attr': prefix_attr, 'args': args }) #expected['id'] = child['id'] #p = s.list_prefix({ 'auth': ad, 'attr': { 'id': child['id'] } })[1] #self.assertEquals(p, expected) def test_prefix_from_pool_vrf(self): """ Add a prefix from a pool in a VRF """ # Add a VRF vrf_attr = { 'name' : 'vrf_1', 'description' : 'Test VRF #1', 'rt' : '123:123' } vrf = s.add_vrf({ 'auth': ad, 'attr': vrf_attr }) # Add a pool pool_attr = { 'name' : 'pool_1', 'description' : 'Test pool for from-pool test', 'default_type' : 'assignment', 'ipv4_default_prefix_length' : 24 } pool = s.add_pool({ 'auth': ad, 'attr': pool_attr }) # Add prefix to pool parent_prefix_attr = { 'prefix': '1.3.0.0/16', 'vrf_rt': '123:123', 'type': 'reservation', 'description': 'FOO', 'pool_id': pool['id'] } s.add_prefix({ 'auth': ad, 'attr': parent_prefix_attr }) args = { 'from-pool': { 'name': 'pool_1' }, 'family': 4 } prefix_attr = { 'description': 'BAR' } expected = { 'prefix': '1.3.0.0/24', 'display_prefix': '1.3.0.0/24', 'description': 'BAR', 'type': 'assignment', 'comment': None, 'country': None, 'monitor': None, 'node': None, 'order_id': None, 'customer_id': None, 'pool_id': None, 'pool_name': None, 'vrf_id': vrf['id'], 'vrf_rt': '123:123', 'vrf_name': 'vrf_1', 'external_key': None, 'family': 4, 'indent': 1, 'alarm_priority': None, 'authoritative_source': 'nipap', 'vlan': None, 'inherited_tags': [], 'tags': [] } child = s.add_prefix({ 'auth': ad, 'attr': prefix_attr, 'args': args }) expected['id'] = child['id'] p = s.list_prefix({ 'auth': ad, 'attr': { 'id': child['id'] } })[1] p = self._mangle_prefix_result(p) self.assertEquals(p, expected) def test_prefix_edit_return(self): """ Check return value of edit_prefix """ p1 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'FOO' } }) p2 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'child 1' } }) p3 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.2/32', 'type': 'host', 'description': 'child 2' } }) p4 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.3/32', 'type': 'host', 'description': 'child 3' } }) ss_res = s.smart_search_prefix({ 'auth': ad, 'query_string': 'child 2' })['result'][0] edit_res = s.edit_prefix({ 'auth': ad, 'prefix': { 'prefix': '1.3.3.2/32' }, 'attr': { 'description': 'Kid 2' } })[0] del(edit_res['added']) del(edit_res['last_modified']) del(ss_res['added']) del(ss_res['last_modified']) ss_res['description'] = 'Kid 2' self.assertEqual(ss_res, edit_res) def test_prefix_smart_search(self): """ Test the prefix smart search """ p1 = s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'FOO' } }) s.add_prefix({ 'auth': ad, 'attr': { 'prefix': '1.3.3.0/32', 'type': 'host', 'description': 'BAR' } }) res = s.smart_search_prefix({ 'auth': ad, 'query_string': 'F' }) expected = { 'interpretation': [{'operator': 'regex', 'attribute': 'description or comment or node or order_id or customer_id', 'interpretation': 'text', 'string': 'F'}], 'search_options': {'include_all_children': False, 'max_result': 50, 'include_all_parents': False, 'parents_depth': 0, 'offset': 0, 'children_depth': 0, 'parent_prefix': None, 'include_neighbors': False }, 'result': [ {'comment': None, 'external_key': None, 'family': 4, 'prefix': '1.3.3.0/24', 'authoritative_source': 'nipap', 'id': p1['id'], 'display_prefix': '1.3.3.0/24', 'monitor': None, 'children': 1, 'prefix_length': 24, 'type': 'assignment', 'match': True, 'node': None, 'description': 'FOO', 'order_id': None, 'customer_id': None, 'vrf_id': 0, 'vrf_rt': None, 'vrf_name': 'default', 'pool_id': None, 'pool_name': None, 'alarm_priority': None, 'indent': 0, 'country': None, 'display': True, 'vlan': None, 'inherited_tags': [], 'tags': [] } ] } res = self._mangle_prefix_result(res) self.assertEqual(res, expected) def test_asn_add_list(self): """ Add ASN to NIPAP and list it """ attr = { 'asn': 1, 'name': 'Test ASN #1' } # add ASN self.assertEqual(s.add_asn({ 'auth': ad, 'attr': attr}), attr, "add_asn did not return correct ASN.") # make sure that it got added asn = s.list_asn({ 'auth': ad, 'asn': { 'asn': 1 } }) self.assertEqual(len(asn), 1, "Wrong number of ASNs returned.") asn = asn[0] self.assertEquals(attr, asn, "ASN in database not equal to what was added.") # adding the same ASN again should result in duplicate key error with self.assertRaisesRegexp(xmlrpclib.Fault, 'Duplicate value for'): s.add_asn({ 'auth': ad, 'attr': attr }) def test_remove_asn(self): """ Remove ASN from NIPAP """ attr = { 'asn': 2, 'name': 'Test ASN #2' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) s.remove_asn({ 'auth': ad, 'asn': { 'asn': asn['asn'] } }) self.assertEquals(0, len(s.list_asn({ 'auth': ad, 'asn': { 'asn': 2 } })), "Removed ASN still in database") def test_edit_asn(self): """ Edit ASNs """ attr = { 'asn': 3, 'name': 'Test ASN #3' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) s.edit_asn({ 'auth': ad, 'asn': { 'asn': attr['asn'] }, 'attr': { 'name': 'b0rk' } }) self.assertEquals(s.list_asn({ 'auth': ad, 'asn': { 'asn': 3 } })[0]['name'], 'b0rk', "Edited ASN still has it's old name.") with self.assertRaisesRegexp(xmlrpclib.Fault, 'extraneous attribute'): s.edit_asn({ 'auth': ad, 'asn': { 'asn': 3 }, 'attr': {'asn': 4, 'name': 'Test ASN #4'} }) def test_search_asn(self): """ Search ASNs """ attr = { 'asn': 4, 'name': 'This is AS number 4' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) # equal match q = { 'operator': 'equals', 'val1': 'asn', 'val2': attr['asn'] } res = s.search_asn({ 'auth': ad, 'query': q }) self.assertEquals(len(res['result']), 1, "equal search resulted in wrong number of hits") self.assertEquals(res['result'][0]['name'], attr['name'], "search hit got wrong name") # regexp match q = { 'operator': 'regex_match', 'val1': 'name', 'val2': 'number' } res = s.search_asn({ 'auth': ad, 'query': q }) self.assertEquals(len(res['result']), 1, "regex search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") def test_smart_search_asn(self): """ Test smart_search_asn function. """ attr = { 'asn': 5, 'name': 'Autonomous System Number 5' } asn = s.add_asn({ 'auth': ad, 'attr': attr }) res = s.smart_search_asn({ 'auth': ad, 'query_string': "Autonomous" }) self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'name', "search term interpretated as wrong type") res = s.smart_search_asn({ 'auth': ad, 'query_string': "5" }) self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], attr['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'asn', "search term interpretated as wrong type") def test_pool_add_list(self): """ Test adding a pool and verifying it """ # Add a pool attr = { 'description': 'Test pool #1', 'default_type': 'assignment', 'ipv4_default_prefix_length': 31, 'ipv6_default_prefix_length': 112 } with self.assertRaisesRegexp(xmlrpclib.Fault, 'missing attribute name'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['name'] = 'pool_1' attr['ipv4_default_prefix_length'] = 50 with self.assertRaisesRegexp(xmlrpclib.Fault, '1200: \'Default IPv4 prefix length must be an integer between 1 and 32.'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['ipv4_default_prefix_length'] = 31 attr['ipv6_default_prefix_length'] = 'over 9000' with self.assertRaisesRegexp(xmlrpclib.Fault, '1200: \'Default IPv6 prefix length must be an integer between 1 and 128.'): s.add_pool({ 'auth': ad, 'attr': attr }) attr['ipv6_default_prefix_length'] = 112 res = s.add_pool({ 'auth': ad, 'attr': attr }) expected = attr.copy() expected['id'] = res['id'] expected['prefixes'] = [] expected['vrf_id'] = None expected['vrf_rt'] = None expected['vrf_name'] = None # list pool and verify data in NIPAP p = s.list_pool({ 'auth': ad, 'pool': { 'id': expected['id'] } }) self.assertEquals(1, len(p), 'Wrong number of pools returned') p = p[0] self.assertEquals(self._mangle_pool_result(p), expected, 'Received pool differs from added pool') def test_edit_pool(self): """ Test editing a pool """ # add a pool, we need something to edit attr = { 'name': 'test_pool_2', 'description': 'Test pool #2', 'default_type': 'reservation', 'ipv4_default_prefix_length': 31, 'ipv6_default_prefix_length': 112 } attr2 = { 'name': 'test_pool_2_edit', 'description': 'Test pool #2 edit', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 96 } res = s.add_pool({ 'auth': ad, 'attr': attr }) s.edit_pool({ 'auth': ad, 'pool': { 'id': res['id'] }, 'attr': attr2 }) expected = attr2.copy() expected['id'] = res['id'] expected['prefixes'] = [] expected['vrf_id'] = None expected['vrf_rt'] = None expected['vrf_name'] = None self.assertEquals(self._mangle_pool_result(s.list_pool({ 'auth': ad, 'pool': { 'id': res['id'] } })[0]), expected) def test_search_pool(self): """ Test searching pools """ def test_smart_search_pool(self): """ Test smart searching among pools """ def test_remove_pool(self): """ Test removing pools
def setUp(self): """ Better start from a clean slate! """ cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' self.nipap._execute( "TRUNCATE ip_net_plan, ip_net_pool, ip_net_vrf, ip_net_log, ip_net_asn" ) self.schema_attrs = { 'name': 'test-schema1', 'description': 'Test schema numero uno!' } self.schema_attrs['id'] = self.nipap.add_schema( self.auth, self.schema_attrs) self.schema_attrs2 = { 'name': 'test-schema2', 'description': 'Test schema numero dos!' } self.schema_attrs2['id'] = self.nipap.add_schema( self.auth, self.schema_attrs2) self.pool_attrs = { 'name': 'test-pool1', 'description': 'Test pool numero uno!', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } self.pool_attrs['id'] = self.nipap.add_pool( self.auth, {'id': self.schema_attrs['id']}, self.pool_attrs) self.prefix_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': '' } self.prefix_attrs1['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs1) self.prefix_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix_attrs['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs) self.prefix_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.2.0/23', 'type': 'reservation', 'description': '' } self.prefix_attrs2['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs2) self.prefix_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': '' } self.prefix_attrs3['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs3) self.prefix_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/17', 'type': 'reservation', 'description': '' } self.prefix_attrs4['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs2['id']}, self.prefix_attrs4) self.prefix6_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/112', 'type': 'assignment', 'description': '' } self.prefix6_attrs1['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs1) self.prefix6_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::1/128', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix6_attrs['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs) self.prefix6_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/64', 'type': 'reservation', 'description': '' } self.prefix6_attrs2['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs2) self.prefix6_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/48', 'type': 'reservation', 'description': '' } self.prefix6_attrs3['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs3) self.prefix6_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/56', 'type': 'reservation', 'description': '' } self.prefix6_attrs4['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs2['id']}, self.prefix6_attrs4)
class NipapTest(unittest.TestCase): """ Tests the NIPAP class """ nipap = None def setUp(self): """ Better start from a clean slate! """ cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' self.nipap._execute( "TRUNCATE ip_net_plan, ip_net_pool, ip_net_vrf, ip_net_log, ip_net_asn" ) self.schema_attrs = { 'name': 'test-schema1', 'description': 'Test schema numero uno!' } self.schema_attrs['id'] = self.nipap.add_schema( self.auth, self.schema_attrs) self.schema_attrs2 = { 'name': 'test-schema2', 'description': 'Test schema numero dos!' } self.schema_attrs2['id'] = self.nipap.add_schema( self.auth, self.schema_attrs2) self.pool_attrs = { 'name': 'test-pool1', 'description': 'Test pool numero uno!', 'default_type': 'assignment', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } self.pool_attrs['id'] = self.nipap.add_pool( self.auth, {'id': self.schema_attrs['id']}, self.pool_attrs) self.prefix_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': '' } self.prefix_attrs1['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs1) self.prefix_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.3.1/32', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix_attrs['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs) self.prefix_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.2.0/23', 'type': 'reservation', 'description': '' } self.prefix_attrs2['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs2) self.prefix_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/16', 'type': 'reservation', 'description': '' } self.prefix_attrs3['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix_attrs3) self.prefix_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '1.3.0.0/17', 'type': 'reservation', 'description': '' } self.prefix_attrs4['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs2['id']}, self.prefix_attrs4) self.prefix6_attrs1 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/112', 'type': 'assignment', 'description': '' } self.prefix6_attrs1['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs1) self.prefix6_attrs = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::1/128', 'type': 'host', 'description': 'Test prefix numero uno!' } self.prefix6_attrs['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs) self.prefix6_attrs2 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:3::/64', 'type': 'reservation', 'description': '' } self.prefix6_attrs2['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs2) self.prefix6_attrs3 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/48', 'type': 'reservation', 'description': '' } self.prefix6_attrs3['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs['id']}, self.prefix6_attrs3) self.prefix6_attrs4 = { 'authoritative_source': 'nipaptest', 'prefix': '2001:0db8:3:0::/56', 'type': 'reservation', 'description': '' } self.prefix6_attrs4['id'] = self.nipap.add_prefix( self.auth, {'id': self.schema_attrs2['id']}, self.prefix6_attrs4) def test_schema_basic(self): """ Basic schema test 1. Add a new schema 2. List with filters to get newly created schema 3. Verify listed schema coincides with input args for added schema 4. Remove schema """ attrs = { 'name': 'test-schema-wrong', 'description': 'A simple test schema with incorrect name!' } attrs['id'] = self.nipap.add_schema(self.auth, attrs) schema = self.nipap.list_schema(self.auth, {'id': attrs['id']}) for a in attrs: self.assertEqual( schema[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_schema_add_crap_input(self): """ Try to input junk into add_schema and expect error """ attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!', 'crap': 'this is just some crap' } # missing everything self.assertRaises(NipapMissingInputError, self.nipap.add_schema, self.auth, {}) # missing description self.assertRaises(NipapMissingInputError, self.nipap.add_schema, self.auth, {'name': 'crapson'}) # have required and extra crap self.assertRaises(NipapExtraneousInputError, self.nipap.add_schema, self.auth, attrs) def test_expand_schema_spec(self): """ Test the expand_schema_spec() The _expand_schema_spec() function is used throughout the schema functions to expand the schema specification input and so we test the separately. """ # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, 'string') # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, 1) # wrong type self.assertRaises(NipapInputError, self.nipap._expand_schema_spec, []) # missing keys self.assertRaises(NipapMissingInputError, self.nipap._expand_schema_spec, {}) # crap key self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, {'crap': self.schema_attrs['name']}) # required keys and extra crap self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, { 'name': self.schema_attrs['name'], 'crap': 'crap' }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_schema_spec, {'id': '3'}) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_schema_spec, {'name': 3}) # both id and name self.assertRaises(NipapExtraneousInputError, self.nipap._expand_schema_spec, { 'id': 3, 'name': '3' }) # proper key - id where, params = self.nipap._expand_schema_spec({'id': 3}) self.assertEqual(where, 'id = %(spec_id)s', "Improperly expanded WHERE clause") self.assertEqual(params, {'spec_id': 3}, "Improperly expanded params dict") # proper spec - name where, params = self.nipap._expand_schema_spec({'name': 'test'}) def test_schema_edit_crap_input(self): """ Try to input junk into edit_schema and expect error """ attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!' } crap_attrs = { 'name': 'test-schema-crap', 'description': 'A simple test schema with incorrect name!', 'crap': 'this is just some crap' } # spec is tested elsewhere, just test attrs part self.assertRaises(NipapExtraneousInputError, self.nipap.edit_schema, self.auth, {'name': self.schema_attrs['name']}, crap_attrs) def test_schema_list_crap_input(self): """ Try to input junk into list_schema and expect error """ # TODO: what do we really expect? self.assertRaises(NipapExtraneousInputError, self.nipap.list_schema, self.auth, {'crap': 'crap crap'}) def test_schema_dupe(self): """ Check so we can't create duplicate schemas There are unique indices in the database that should prevent us from creating duplicate schema (ie, with the same name). """ schema_attrs = { 'name': 'test-schema-dupe', 'description': 'Testing dupe' } self.nipap.add_schema(self.auth, schema_attrs) self.assertRaises(NipapDuplicateError, self.nipap.add_schema, self.auth, schema_attrs) def test_schema_rename(self): """ Rename a schema Uses the edit_schema() functionality to rename our previously created and incorrectly named schema so it hereafter has the correct name. Also tests the list_schema() functionality since we use that to list the modified schema. """ spec = {'name': 'test-schema1'} attrs = { 'name': 'test-schema', 'description': 'A simple test schema with correct name!' } self.nipap.edit_schema(self.auth, spec, attrs) # check that search for old record doesn't return anything schema = self.nipap.list_schema(self.auth, spec) self.assertEqual(schema, [], 'Old entry still exists') schema = self.nipap.list_schema(self.auth, {'name': 'test-schema'}) for a in attrs: self.assertEqual( schema[0][a], attrs[a], 'Modified schema differ from listed on attribute: ' + a) def test_schema_remove(self): """ Remove a schema Remove the schema previously modified and make sure it's not there. """ spec = {'name': 'test-schema'} self.nipap.remove_schema(self.auth, spec) # check that search for old record doesn't return anything schema = self.nipap.list_schema(self.auth, spec) self.assertEqual(schema, [], 'Old entry still exists') def test_expand_pool_spec(self): """ Test the function which expands pool spec to SQL. """ schema = {'id': self.schema_attrs['id']} # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, 'string') # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, 1) # wrong type self.assertRaises(NipapInputError, self.nipap._expand_pool_spec, []) # missing keys self.assertRaises(NipapMissingInputError, self.nipap._expand_pool_spec, {}) # crap key self.assertRaises(NipapExtraneousInputError, self.nipap._expand_pool_spec, {'crap': self.pool_attrs['name']}) # required keys and extra crap self.assertRaises( NipapExtraneousInputError, self.nipap._expand_pool_spec, { 'id': self.pool_attrs['id'], 'schema': self.schema_attrs['id'], 'crap': 'crap' }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_pool_spec, { 'id': '3', 'schema': self.schema_attrs['id'] }) # proper key but incorrect value (int vs string) self.assertRaises(NipapValueError, self.nipap._expand_pool_spec, { 'name': 3, 'schema': self.schema_attrs['id'] }) # both id and name self.assertRaises(NipapExtraneousInputError, self.nipap._expand_pool_spec, { 'id': 3, 'name': '3', 'schema': self.schema_attrs['id'] }) # proper key - id where, params = self.nipap._expand_pool_spec({ 'id': 3, 'schema': self.schema_attrs['id'] }) self.assertEqual( where, 'po.id = %(spec_id)s AND po.schema = %(spec_schema)s', "Improperly expanded WHERE clause") self.assertEqual(params, { 'spec_id': 3, 'spec_schema': self.schema_attrs['id'] }, "Improperly expanded params dict") # proper spec - name where, params = self.nipap._expand_pool_spec({ 'name': 'test', 'schema': self.schema_attrs['id'] }) self.assertEqual( where, 'po.name = %(spec_name)s AND po.schema = %(spec_schema)s', "Improperly expanded WHERE clause") self.assertEqual(params, { 'spec_name': 'test', 'spec_schema': self.schema_attrs['id'] }, "Improperly expanded params dict") def test_pool_add1(self): """ Add a pool and check it's there using list functions Refer to schema by id """ attrs = { 'name': 'test-pool-wrong', 'description': 'A simple test pool with incorrect name!', 'default_type': 'reservation', 'ipv4_default_prefix_length': 30, 'ipv6_default_prefix_length': 112 } schema = {'id': self.schema_attrs['id']} pool_id = self.nipap.add_pool(self.auth, schema, attrs) pool = self.nipap.list_pool(self.auth, schema, {'id': pool_id}) for a in attrs: self.assertEqual( pool[0][a], attrs[a], 'Added object differ from listed on attribute: %s %s!=%s' % (a, attrs[a], pool[0][a])) def test_pool_add2(self): """ Add a pool and check it's there using list functions Refer to schema by name """ schema = {'id': self.schema_attrs['id']} attrs = { 'name': 'test-pool-wrong', 'default_type': 'reservation', 'description': 'A simple test pool with incorrect name!' } pool_id = self.nipap.add_pool(self.auth, schema, attrs) pool = self.nipap.list_pool(self.auth, schema, {'id': pool_id}) for a in attrs: self.assertEqual( pool[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_edit_pool_by_name(self): """ Try to rename a pool using edit_pool() function Pool is not uniquely identified (empty spec) so this should raise an error """ schema = {'id': self.schema_attrs['id']} spec = {} attrs = { 'name': self.pool_attrs['name'], 'default_type': 'assignment', 'description': 'A simple test pool with correct name!' } self.assertRaises(NipapInputError, self.nipap.edit_pool, self.auth, schema, spec, attrs) def test_edit_pool(self): """ Rename a pool using edit_pool() function """ schema = {'id': self.schema_attrs['id']} spec = {'id': self.pool_attrs['id']} attrs = { 'name': 'test-pool', 'default_type': 'assignment', 'description': 'A simple test pool with correct name!', 'ipv4_default_prefix_length': 32, 'ipv6_default_prefix_length': 128 } self.nipap.edit_pool(self.auth, schema, spec, attrs) # check that search for old record doesn't return anything pool = self.nipap.list_pool(self.auth, schema, {'name': self.pool_attrs['name']}) self.assertEqual(pool, [], 'Old entry still exists') pool = self.nipap.list_pool(self.auth, schema, {'name': attrs['name']}) for a in attrs: self.assertEqual( pool[0][a], attrs[a], 'Added object differ from listed on attribute: ' + a) def test_remove_pool_by_id(self): """ Remove a pool by id """ schema = {'id': self.schema_attrs['id']} pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Record must exist before we can delete it') for a in self.pool_attrs: self.assertEqual(pool[0][a], self.pool_attrs[a], 'Listed attribute differ from original') # remove the pool self.nipap.remove_pool(self.auth, schema, {'id': self.pool_attrs['id']}) # check that search for old record doesn't return anything pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) self.assertEqual(pool, [], 'Old entry still exists') def test_prefix_in_a_pool(self): """ Add prefixes to a poll and list! """ schema = {'id': self.schema_attrs['id']} pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '1.2.2.0/32', '1.2.2.1/32', '1.2.2.2/32', '1.2.2.3/32', '1.2.2.4/32', '1.2.2.5/32' ] for p in pfxs: prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': p, 'type': 'host', 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # list again pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) self.assertNotEqual(pool[0], [], 'Pool must exist!') self.assertEqual(set(pfxs), set(pool[0]['prefixes']), 'Returned prefixes do not match added ones') def test_prefix_basic(self): """ Test basic prefix functions """ schema = {'id': self.schema_attrs['id']} prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '1.3.3.7/32', 'type': 'host', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) prefix = self.nipap.list_prefix(self.auth, schema, {'prefix': prefix_attrs['prefix']}) for a in prefix_attrs: self.assertEqual( prefix[0][a], prefix_attrs[a], 'Added object differ from listed on attribute: ' + a) # fetch many prefixes - all in a schema prefix = self.nipap.list_prefix(self.auth, schema, {}) self.assertNotEqual( len(prefix), 0, 'Found 0 prefixes in schema ' + self.schema_attrs['name']) def test_add_prefix(self): """ Test add_prefix in a bit more detail """ schema = {'id': self.schema_attrs['id']} # we need a bloody pool first! pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '10.0.0.0/24', '10.0.1.0/24', '10.0.2.0/24', '10.0.3.0/24', '10.0.4.0/24' ] for p in pfxs: prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': p, 'type': 'reservation', 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # get an address based on from-prefix prefix_attrs = { 'type': 'assignment', 'authoritative_source': 'nipap-test', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } res = self.nipap.add_prefix(self.auth, schema, prefix_attrs, { 'from-prefix': ['10.0.0.0/24'], 'prefix_length': 30 }) p = self.nipap.list_prefix(self.auth, schema, {'id': res}) self.assertEqual(p[0]['prefix'], '10.0.0.0/30', "New prefix differ from what it should be!") self.nipap.add_schema(self.auth, { 'name': 'testtest', 'description': 'another test schema!' }) # pass different schemas in attr and args # TODO: Find something similar? #self.assertRaises(NipapInputError, self.nipap.add_prefix, schema, { 'authoritative_source': 'nipap-test', 'description': 'tjong' }, { 'from-prefix': ['10.0.0.0/24'], 'prefix_length': 30 }) def test_prefix_search_simple(self): """ Test the simple prefix search function. """ schema = {'id': self.schema_attrs['id']} # First, perform e few tests to verify search string expansion. query_keys = dict() query_keys['testing testing'] = "description" query_keys['1.2.3.4'] = "prefix" # build query string query_str = "" for key, val in query_keys.items(): if val == "description": query_str += "\"%s\" " % key else: query_str += "%s " % key res = self.nipap.smart_search_prefix(self.auth, schema, query_str) for interp in res['interpretation']: self.assertEqual( interp['string'] in query_keys, True, "Function returned unknown interpreted string %s" % interp['string']) prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '1.3.3.77/32', 'type': 'host', 'description': 'test-ish prefix', 'comment': 'Test prefix #77! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) res = self.nipap.smart_search_prefix(self.auth, schema, r"""1.3.3.77 "-ish" """) self.assertEqual(res['result'][-1]['prefix'], '1.3.3.77/32', 'Prefix not found') def test_prefix_search_smart(self): """ Test the smart prefix search function. """ schema = {'id': self.schema_attrs['id']} # test full ipv4 address res = self.nipap.smart_search_prefix(self.auth, schema, '1.3.3.7') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv4 address') res = self.nipap.smart_search_prefix(self.auth, schema, '1.1') self.assertEqual( res['interpretation'][0]['interpretation'], 'text', "Incorrectly interpreted '1.1' as : " + res['interpretation'][0]['interpretation']) res = self.nipap.smart_search_prefix(self.auth, schema, '10/8') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv4 prefix') res = self.nipap.smart_search_prefix(self.auth, schema, '2000:0::01') self.assertEqual(res['interpretation'][0]['interpretation'], 'IPv6 address') def test_prefix_remove(self): """ Remove a prefix """ schema = {'id': self.schema_attrs['id']} prefix = self.nipap.list_prefix(self.auth, schema, {'id': self.prefix_attrs['id']}) # first make sure our prefix exists self.assertEqual(prefix[0]['id'], self.prefix_attrs['id'], 'Record must exist before we can delete it') # remove the prefix, by id self.nipap.remove_prefix(self.auth, schema, {'id': self.prefix_attrs['id']}) # check that search for old record doesn't return anything prefix = self.nipap.list_prefix(self.auth, schema, {'id': self.prefix_attrs['id']}) self.assertEqual(prefix, [], 'Old entry still exists') def test_prefix_indent_ipv4(self): """ Check that our indentation calculation is working for IPv4 Prefixes gets an indent value automatically assigned to help in displaying prefix information. The indent value is written on updates to the table and this test is to make sure it is correctly calculated. """ schema = {'id': self.schema_attrs['id']} p1 = self.nipap.list_prefix(self.auth, schema, {'prefix': '1.3.3.1/32'})[0] p2 = self.nipap.list_prefix(self.auth, schema, {'prefix': '1.3.3.0/24'})[0] p3 = self.nipap.list_prefix(self.auth, schema, {'prefix': '1.3.0.0/16'})[0] self.assertEqual(p1['indent'], 4, "Indent calc on add failed") self.assertEqual(p2['indent'], 3, "Indent calc on add failed") self.assertEqual(p3['indent'], 0, "Indent calc on add failed") # remove middle prefix self.nipap.remove_prefix(self.auth, schema, {'id': self.prefix_attrs2['id']}) # check that child prefix indent level has decreased p1 = self.nipap.list_prefix(self.auth, schema, {'prefix': '1.3.3.1/32'})[0] p3 = self.nipap.list_prefix(self.auth, schema, {'prefix': '1.3.0.0/16'})[0] self.assertEqual(p1['indent'], 3, "Indent calc on remove failed") self.assertEqual(p3['indent'], 0, "Indent calc on remove failed") def test_prefix_indent_ipv6(self): """ Check that our indentation calculation is working for IPv6 Prefixes gets an indent value automatically assigned to help in displaying prefix information. The indent value is written on updates to the table and this test is to make sure it is correctly calculated. """ schema = {'id': self.schema_attrs['id']} p1 = self.nipap.list_prefix(self.auth, schema, {'prefix': '2001:0db8:3:3::1/128'})[0] p2 = self.nipap.list_prefix(self.auth, schema, {'prefix': '2001:0db8:3:3::/64'})[0] p3 = self.nipap.list_prefix(self.auth, schema, {'prefix': '2001:0db8:3:0::/48'})[0] self.assertEqual(p1['indent'], 4, "Indent calc on add failed") self.assertEqual(p2['indent'], 2, "Indent calc on add failed") self.assertEqual(p3['indent'], 0, "Indent calc on add failed") # remove middle prefix self.nipap.remove_prefix(self.auth, schema, {'id': self.prefix6_attrs2['id']}) # check that child prefix indent level has decreased p1 = self.nipap.list_prefix(self.auth, schema, {'prefix': '2001:0db8:3:3::1/128'})[0] p3 = self.nipap.list_prefix(self.auth, schema, {'prefix': '2001:0db8:3:0::/48'})[0] self.assertEqual( p1['indent'], 3, "Indent calc on remove failed for " + p1['prefix'] + " indent: " + str(p1['indent'])) self.assertEqual( p3['indent'], 0, "Indent calc on remove failed for " + p3['prefix'] + " indent: " + str(p3['indent'])) def test_find_free_prefix_input(self): """ Mostly input testing of find_free_prefix Try to stress find_free_prefix and send a lot of junk.. """ schema = {'id': self.schema_attrs['id']} # set up a prefix not used elsewhere so we have a known good state prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '100.0.0.0/16', 'type': 'reservation', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # no schema, should raise error! self.assertRaises(NipapInputError, self.nipap.find_free_prefix, self.auth, schema, {'from-prefix': ['100.0.0.0/16']}) # incorrect from-prefix type, string instead of list of strings (looking like an IP address) self.assertRaises(NipapInputError, self.nipap.find_free_prefix, self.auth, schema, {'from-prefix': '100.0.0.0/16'}) # missing prefix_length self.assertRaises(NipapMissingInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16'], 'count': 1 }) # try giving both IPv4 and IPv6 in from-prefix which shouldn't work self.assertRaises( NipapInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16', '2a00:800::0/25'], 'prefix_length': 24, 'count': 1 }) # try giving non-integer as wanted prefix length self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16'], 'prefix_length': '24', 'count': 1 }) # try giving to high a number as wanted prefix length for IPv4 self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16'], 'prefix_length': 35, 'count': 1 }) # try giving to high a number as wanted prefix length for IPv6 self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['2a00:800::1/25'], 'prefix_length': 150, 'count': 1 }) # try giving a high number for result count (max is 1000) self.assertRaises(NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-prefix': ['100.0.0.0/16'], 'prefix_length': 30, 'count': 55555 }) # don't pass 'family', which is required when specifying 'from-pool' self.assertRaises( NipapMissingInputError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1 }) # pass crap as family, wrong type even self.assertRaises( ValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1, 'family': 'crap' }) # pass 7 as family self.assertRaises( NipapValueError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'prefix_length': 24, 'count': 1, 'family': 7 }) # pass non existent pool self.assertRaises( NipapNonExistentError, self.nipap.find_free_prefix, self.auth, schema, { 'from-pool': { 'name': 'crap' }, 'prefix_length': 24, 'count': 1, 'family': 4 }) def test_find_free_prefix1(self): """ Functionality testing of find_free_prefix Mostly based on 'from-prefix' """ schema = {'id': self.schema_attrs['id']} # set up a prefix not used elsewhere so we have a known good state prefix_attrs = { 'authoritative_source': 'nipap-test', 'prefix': '100.0.0.0/16', 'type': 'assignment', 'description': 'test prefix', 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # simple test res = self.nipap.find_free_prefix( self.auth, schema, { 'from-prefix': ['100.0.0.0/16', '1.3.3.0/24'], 'prefix_length': 24, 'count': 1 }) self.assertEqual(res, ['100.0.0.0/24'], "Incorrect prefix set returned") # simple test - only one input prefix (which did cause a bug, thus keeping it) res = self.nipap.find_free_prefix(self.auth, schema, { 'from-prefix': ['100.0.0.0/16'], 'prefix_length': 24, 'count': 1 }) self.assertEqual(res, ['100.0.0.0/24'], "Incorrect prefix set returned") res = self.nipap.find_free_prefix( self.auth, schema, { 'from-prefix': ['100.0.0.0/16', '1.3.3.0/24'], 'prefix_length': 24, 'count': 999 }) self.assertEqual(len(res), 256, "Incorrect prefix set returned") def test_find_free_prefix2(self): """ Functionality testing of find_free_prefix Mostly based on 'from-pool' """ schema = {'id': self.schema_attrs['id']} # we need a bloody pool first! pool = self.nipap.list_pool(self.auth, schema, {'id': self.pool_attrs['id']}) # first make sure our pool exists self.assertNotEqual(pool[0], [], 'Pool must exist!') pfxs = [ '10.0.0.0/24', '10.0.1.0/24', '10.0.2.0/24', '10.0.3.0/24', '10.0.4.0/24' ] for p in pfxs: prefix_attrs = { 'type': 'reservation', 'authoritative_source': 'nipap-test', 'prefix': p, 'description': 'test prefix', 'pool_id': self.pool_attrs['id'], 'comment': 'test comment, please remove! ;)' } self.nipap.add_prefix(self.auth, schema, prefix_attrs) # from-pool test res = self.nipap.find_free_prefix( self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'count': 1, 'family': 4 }) self.assertEqual( res, ['10.0.1.0/30'], "Incorrect prefix set returned when requesting default prefix-length" ) # from-pool test, specify wanted prefix length res = self.nipap.find_free_prefix( self.auth, schema, { 'from-pool': { 'name': self.pool_attrs['name'] }, 'count': 1, 'family': 4, 'prefix_length': 31 }) self.assertEqual( res, ['10.0.1.0/31'], "Incorrect prefix set returned with explicit prefix-length") def test_edit_prefix(self): """ Functionality testing of edit_prefix. """ schema = {'id': self.schema_attrs['id']} data = { 'prefix': '192.0.2.0/24', 'description': 'foo', 'comment': 'bar', 'order_id': '0xBEEF', 'customer_id': 'CUST-EEF-DERP', 'alarm_priority': 'low', 'type': 'assignment', 'node': 'TOK-CORE-1', 'country': 'EE', 'authoritative_source': 'unittest', 'pool': self.pool_attrs['id'] } # basic edit self.nipap.edit_prefix(self.auth, schema, {'id': self.prefix_attrs['id']}, data) p = self.nipap.list_prefix(self.auth, schema, {'id': self.prefix_attrs['id']})[0] # remove what we did not touch for k, v in data.keys(): if k not in p: del p[k] self.assertEqual(data, p, "Prefix data incorrect after edit.") # create a collision self.assertRaises(NipapError, self.nipap.edit_prefix, self.auth, schema, {'id': self.prefix_attrs2['id']}, {'prefix': data['prefix']}) # try to change schema - disallowed self.assertRaises(NipapExtraneousInputError, self.nipap_edit_prefix, self.auth, schema, {'id': self.prefix_attrs2['id']}, {'schema': self.schema_attrs2['id']}) def test_add_asn(self): """ Test adding ASNs to NIPAP. """ data = {'asn': 1, 'name': 'Test ASN #1'} self.assertEqual(self.nipap.add_asn(self.auth, data), 1, "add_asn did not return correct ASN.") asn = self.nipap.list_asn(self.auth, {'asn': 1})[0] self.assertEquals(data, asn, "ASN in database not equal to what was added.") self.assertRaises(NipapDuplicateError, self.nipap.add_asn, self.auth, data) def test_remove_asn(self): """ Test removing ASNs from NIPAP. """ data = {'asn': 2, 'name': 'Test ASN #2'} asn = self.nipap.add_asn(self.auth, data) self.nipap.remove_asn(self.auth, asn) self.assertEquals(0, len(self.nipap.list_asn(self.auth, {'asn': 2})), "Removed ASN still in database") def test_edit_asn(self): """ Test editing ASNs. """ data = {'asn': 3, 'name': 'Test ASN #3'} asn = self.nipap.add_asn(self.auth, data) self.nipap.edit_asn(self.auth, data['asn'], {'name': 'b0rk'}) self.assertEquals( self.nipap.list_asn(self.auth, {'asn': 3})[0]['name'], 'b0rk', "Edited ASN still has it's old name.") self.assertRaises(NipapExtraneousInputError, self.nipap.edit_asn, self.auth, {'asn': 3}, { 'asn': 4, 'name': 'Test ASN #4' }) def test_search_asn(self): """ Test searching ASNs. """ data = {'asn': 4, 'name': 'This is AS number 4'} asn = self.nipap.add_asn(self.auth, data) q = {'operator': 'equals', 'val1': 'asn', 'val2': data['asn']} res = self.nipap.search_asn(self.auth, q) self.assertEquals(len(res['result']), 1, "equal search resulted in wrong number of hits") self.assertEquals(res['result'][0]['name'], data['name'], "search hit got wrong name") q = {'operator': 'regex_match', 'val1': 'name', 'val2': 'number'} res = self.nipap.search_asn(self.auth, q) self.assertEquals(len(res['result']), 1, "regex search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") def test_smart_search_asn(self): """ Test smart_search_asn function. """ data = {'asn': 5, 'name': 'Autonomous System Number 5'} asn = self.nipap.add_asn(self.auth, data) res = self.nipap.smart_search_asn(self.auth, "Autonomous") self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'name', "search term interpretated as wrong type") res = self.nipap.smart_search_asn(self.auth, "5") self.assertEquals(len(res['result']), 1, "search resulted in wrong number of hits") self.assertEquals(res['result'][0]['asn'], data['asn'], "search hit got wrong asn") self.assertEquals(res['interpretation'][0]['attribute'], 'asn', "search term interpretated as wrong type")
class NipapCliTest(unittest.TestCase): """ Tests the NIPAP CLI We presume the database is empty """ maxDiff = None logger = None cfg = None nipap = None def setUp(self): # logging self.logger = logging.getLogger(self.__class__.__name__) # NIPAP self.cfg = NipapConfig('/etc/nipap/nipap.conf') self.nipap = Nipap() # create dummy auth object # As the authentication is performed before the query hits the Nipap # class, it does not matter what user we use here self.auth = SqliteAuth('local', 'unittest', 'unittest', 'unittest') self.auth.authenticated_as = 'unittest' self.auth.full_name = 'Unit test' # have to delete hosts before we can delete the rest self.nipap._execute("DELETE FROM ip_net_plan WHERE masklen(prefix) = 32") # the rest self.nipap._execute("DELETE FROM ip_net_plan") # delete all except for the default VRF with id 0 self.nipap._execute("DELETE FROM ip_net_vrf WHERE id > 0") # set default info for VRF 0 self.nipap._execute("UPDATE ip_net_vrf SET name = 'default', description = 'The default VRF, typically the Internet.' WHERE id = 0") self.nipap._execute("DELETE FROM ip_net_pool") self.nipap._execute("DELETE FROM ip_net_asn") def _run_cmd(self, cmd): """ Run a command """ import subprocess return subprocess.check_output(cmd) def test_prefix_add_list(self): """ Add a prefix and verify result in database """ ref = { 'prefix': '1.3.3.0/24', 'type': 'assignment', 'description': 'foo description', 'comment': 'comment bar', 'country': 'AB', 'alarm_priority': 'high', 'monitor': 'true', 'order_id': '123' } cmd = [nipap_bin, 'address', 'add'] for key in ref: cmd.append(key) cmd.append(ref[key]) ref['display_prefix'] = '1.3.3.0/24' ref['indent'] = 0 ref['family'] = 4 ref['monitor'] = True ref['pool_id'] = None ref['pool_name'] = None ref['vrf_id'] = 0 ref['vrf_name'] = 'default' ref['vrf_rt'] = None ref['external_key'] = None ref['node'] = None ref['authoritative_source'] = 'nipap' ref['vlan'] = None ref['inherited_tags'] = [] ref['tags'] = [] self._run_cmd(cmd) res = s.list_prefix({ 'auth': ad, 'spec': {} }) del(res[0]['id']) self.assertEqual(res, [ ref, ])