diff --git a/unittest/TestInterfaceMethods.yaml b/unittest/TestInterfaceMethods.yaml new file mode 100644 index 0000000..c315b4d --- /dev/null +++ b/unittest/TestInterfaceMethods.yaml @@ -0,0 +1,62 @@ +interfaces: + GigabitEthernet1/0/0: + sub-interfaces: + 100: + description: "This sub-int is invalid because it has both dot1q and dot1ad" + encapsulation: + dot1q: 1000 + dot1ad: 1000 + 101: + description: "This sub-int is invalid because it has no outer dot1q and dot1ad" + encapsulation: + inner-dot1q: 1000 + + 102: + description: "This sub-int is has the same encap as 103" + 103: + description: "This sub-int is has the same encap as 102" + encapsulation: + dot1q: 102 + + GigabitEthernet1/0/1: + mtu: 9216 + lcp: "e1" + addresses: [ "192.0.2.1/30", "2001:db8:1::1/64" ] + sub-interfaces: + 100: + lcp: "foo" + addresses: [ "10.0.0.1/24", "10.0.0.2/24", "2001:db8:2::1/64" ] + 200: + mtu: 9000 + encapsulation: + dot1q: 1000 + addresses: [ "10.0.1.1/30" ] + 201: + encapsulation: + dot1ad: 1000 + addresses: [ "10.0.2.1/30" ] + 202: + encapsulation: + dot1q: 1000 + inner-dot1q: 1234 + addresses: [ "10.0.3.1/30" ] + 203: + encapsulation: + dot1ad: 1000 + inner-dot1q: 1000 + addresses: [ "10.0.4.1/30" ] + 204: + encapsulation: + dot1ad: 1000 + inner-dot1q: 1000 + exact-match: True + addresses: [ "10.0.5.1/30" ] + + GigabitEthernet2/0/0: + description: "This interface has no sub-ints" + lcp: "e2" + + GigabitEthernet3/0/0: + l2xc: GigabitEthernet3/0/1 + GigabitEthernet3/0/1: + l2xc: GigabitEthernet3/0/0 diff --git a/validator/interface.py b/validator/interface.py index ffc71d1..b2368f4 100644 --- a/validator/interface.py +++ b/validator/interface.py @@ -126,11 +126,11 @@ def get_l2xc_interfaces(yaml): return ret for ifname, iface in yaml['interfaces'].items(): if 'l2xc' in iface: - ret.extend(ifname) + ret.append(ifname) if 'sub-interfaces' in iface: for sub_ifname, sub_iface in iface['sub-interfaces'].items(): if 'l2xc' in sub_iface: - ret.extend(sub_ifname) + ret.append(sub_ifname) return ret @@ -239,6 +239,52 @@ def get_encapsulation(yaml, ifname): } +def get_interfaces(yaml): + """ Return a list of all interface and sub-interface names """ + ret = [] + if not 'interfaces' in yaml: + return ret + for ifname, iface in yaml['interfaces'].items(): + ret.append(ifname) + if not 'sub-interfaces' in iface: + continue + for subid, sub_iface in iface['sub-interfaces'].items(): + ret.append("%s.%d" % (ifname, subid)) + return ret + + +def get_sub_interfaces(yaml): + """ Return all interfaces which are a subinterface. """ + ret = [] + for ifname in get_interfaces(yaml): + if is_sub(yaml, ifname): + ret.append(ifname) + return ret + +def get_qinx_interfaces(yaml): + """ Return all interfaces which are double-tagged, either QinAD or QinQ. + These interfaces will always have a valid encapsulation with 'inner-dot1q' + set to non-zero. + + Note: this is always a strict subset of get_sub_interfaces() + """ + ret = [] + for ifname in get_interfaces(yaml): + if not is_sub(yaml, ifname): + continue + encap = get_encapsulation(yaml, ifname) + if not encap: + continue + if encap['inner-dot1q'] > 0: + ret.append(ifname) + return ret + + +def is_qinx(yaml, ifname): + """ Returns True if the interface is a double-tagged (QinQ or QinAD) interface """ + return ifname in get_qinx_interfaces(yaml) + + def unique_encapsulation(yaml, sub_ifname): """ Ensures that for the sub_ifname specified, there exist no other sub-ints on the parent with the same encapsulation. """ diff --git a/validator/test_interface.py b/validator/test_interface.py new file mode 100644 index 0000000..e2c6d1a --- /dev/null +++ b/validator/test_interface.py @@ -0,0 +1,122 @@ +import unittest +import yaml +import validator.interface as interface +import validator.lcp as lcp + +class TestInterfaceMethods(unittest.TestCase): + def setUp(self): + with open("unittest/TestInterfaceMethods.yaml", "r") as f: + self.cfg = yaml.load(f, Loader = yaml.FullLoader) + + def test_enumerators(self): + ifs = interface.get_interfaces(self.cfg) + self.assertEquals(len(ifs), 15) + self.assertIn("GigabitEthernet1/0/1", ifs) + self.assertIn("GigabitEthernet1/0/1.200", ifs) + self.assertIn("GigabitEthernet1/0/1.204", ifs) + + ifs = interface.get_sub_interfaces(self.cfg) + self.assertEquals(len(ifs), 10) + self.assertNotIn("GigabitEthernet1/0/1", ifs) + self.assertIn("GigabitEthernet1/0/1.200", ifs) + self.assertIn("GigabitEthernet1/0/1.204", ifs) + + ifs = interface.get_qinx_interfaces(self.cfg) + self.assertEquals(len(ifs), 3) + self.assertNotIn("GigabitEthernet1/0/1.200", ifs) + self.assertIn("GigabitEthernet1/0/1.204", ifs) + + def test_mtu(self): + self.assertEquals(interface.get_mtu(self.cfg, "GigabitEthernet1/0/1"), 9216) + self.assertEquals(interface.get_mtu(self.cfg, "GigabitEthernet1/0/1.200"), 9000) + self.assertEquals(interface.get_mtu(self.cfg, "GigabitEthernet1/0/1.201"), 1500) + + def test_encapsulation(self): + self.assertTrue(interface.valid_encapsulation(self.cfg, "GigabitEthernet1/0/1.200")) + self.assertTrue(interface.unique_encapsulation(self.cfg, "GigabitEthernet1/0/1.200")) + self.assertEqual(interface.get_encapsulation(self.cfg, "GigabitEthernet1/0/1.200"), + { 'dot1q': 1000, 'dot1ad': 0, 'inner-dot1q': 0, 'exact-match': False }) + self.assertEqual(interface.get_encapsulation(self.cfg, "GigabitEthernet1/0/1.201"), + { 'dot1q': 0, 'dot1ad': 1000, 'inner-dot1q': 0, 'exact-match': False }) + self.assertEqual(interface.get_encapsulation(self.cfg, "GigabitEthernet1/0/1.202"), + { 'dot1q': 1000, 'dot1ad': 0, 'inner-dot1q': 1234, 'exact-match': False }) + self.assertEqual(interface.get_encapsulation(self.cfg, "GigabitEthernet1/0/1.203"), + { 'dot1q': 0, 'dot1ad': 1000, 'inner-dot1q': 1000, 'exact-match': False }) + self.assertEqual(interface.get_encapsulation(self.cfg, "GigabitEthernet1/0/1.204"), + { 'dot1q': 0, 'dot1ad': 1000, 'inner-dot1q': 1000, 'exact-match': True }) + + self.assertFalse(interface.valid_encapsulation(self.cfg, "GigabitEthernet1/0/0.100")) + self.assertFalse(interface.valid_encapsulation(self.cfg, "GigabitEthernet1/0/0.101")) + + self.assertFalse(interface.unique_encapsulation(self.cfg, "GigabitEthernet1/0/0.102")) + self.assertFalse(interface.unique_encapsulation(self.cfg, "GigabitEthernet1/0/0.103")) + + def test_has_sub(self): + self.assertTrue(interface.has_sub(self.cfg, "GigabitEthernet1/0/1")) + self.assertFalse(interface.has_sub(self.cfg, "GigabitEthernet1/0/1.200")) + self.assertFalse(interface.has_sub(self.cfg, "GigabitEthernet2/0/0")) + self.assertFalse(interface.has_sub(self.cfg, "GigabitEthernet3/0/0")) + + def test_is_sub(self): + self.assertFalse(interface.is_sub(self.cfg, "GigabitEthernet1/0/1")) + self.assertTrue(interface.is_sub(self.cfg, "GigabitEthernet1/0/1.200")) + + def test_is_qinx(self): + self.assertTrue(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1.202")) + self.assertTrue(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1.203")) + self.assertTrue(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1.204")) + + self.assertFalse(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1")) + self.assertFalse(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1.200")) + self.assertFalse(interface.is_qinx(self.cfg, "GigabitEthernet1/0/1.201")) + + def test_lcp(self): + self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/0")) + self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/0.100")) + + self.assertEquals(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1"), "e1") + self.assertEquals(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.100"), "foo") + + self.assertTrue(lcp.is_unique(self.cfg, "e1")) + self.assertTrue(lcp.is_unique(self.cfg, "foo")) + + ## TODO(pim) - ensure that is_unique also takes synthesized LCPs into account + ## self.assertFalse(lcp.is_unique(self.cfg, "e1.1000")) + self.assertEquals(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.200"), "e1.1000") + self.assertEquals(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.201"), "e1.1000") + + ## TODO(pim) - sub 201-203 cannot have an LCP, their encap is not exact-match + ## self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.200")) + ## self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.201")) + ## self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.202")) + ## self.assertIsNone(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.203")) + self.assertEquals(interface.get_lcp(self.cfg, "GigabitEthernet1/0/1.204"), "e1.1000.1000") + + def test_address(self): + self.assertFalse(interface.has_address(self.cfg, "GigabitEthernet1/0/0")) + self.assertFalse(interface.has_address(self.cfg, "GigabitEthernet1/0/0.100")) + + self.assertTrue(interface.has_address(self.cfg, "GigabitEthernet1/0/1")) + self.assertTrue(interface.has_address(self.cfg, "GigabitEthernet1/0/1.100")) + + def test_lx2c(self): + l2xc_ifs = interface.get_l2xc_interfaces(self.cfg) + l2xc_target_ifs = interface.get_l2xc_target_interfaces(self.cfg) + + self.assertIn("GigabitEthernet3/0/0", l2xc_ifs) + self.assertIn("GigabitEthernet3/0/0", l2xc_target_ifs) + self.assertTrue(interface.is_l2xc_interface(self.cfg, "GigabitEthernet3/0/0")) + self.assertTrue(interface.is_l2xc_target_interface(self.cfg, "GigabitEthernet3/0/0")) + + self.assertNotIn("GigabitEthernet2/0/0", l2xc_ifs) + self.assertNotIn("GigabitEthernet2/0/0", l2xc_target_ifs) + self.assertFalse(interface.is_l2xc_interface(self.cfg, "GigabitEthernet2/0/0")) + self.assertFalse(interface.is_l2xc_target_interface(self.cfg, "GigabitEthernet2/0/0")) + + def test_l2(self): + self.assertTrue(interface.is_l2(self.cfg, "GigabitEthernet3/0/0")) + self.assertFalse(interface.is_l2(self.cfg, "GigabitEthernet1/0/0")) + + def test_l3(self): + self.assertTrue(interface.is_l3(self.cfg, "GigabitEthernet1/0/0")) + self.assertFalse(interface.is_l3(self.cfg, "GigabitEthernet3/0/0"))