diff options
Diffstat (limited to 'smoketest/scripts/cli')
28 files changed, 1596 insertions, 268 deletions
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 8f21ec70e..1426e80c2 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -402,12 +402,13 @@ class BasicInterfaceTest: tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits') self.assertEqual(dad_transmits, tmp) - def test_dhcpv6_client_options(self): + def test_dhcpv6_clinet_options(self): if not self._test_ipv6_dhcpc6: self.skipTest('not supported') - duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:10' + duid_base = 10 for interface in self._interfaces: + duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) path = self._base_path + [interface] for option in self._options.get(interface, []): self.session.set(path + option.split()) @@ -417,10 +418,13 @@ class BasicInterfaceTest: self.session.set(path + ['dhcpv6-options', 'rapid-commit']) self.session.set(path + ['dhcpv6-options', 'parameters-only']) self.session.set(path + ['dhcpv6-options', 'duid', duid]) + duid_base += 1 self.session.commit() + duid_base = 10 for interface in self._interfaces: + duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') self.assertIn(f'interface {interface} ' + '{', dhcpc6_config) self.assertIn(f' request domain-name-servers;', dhcpc6_config) @@ -430,6 +434,7 @@ class BasicInterfaceTest: self.assertIn(f' send rapid-commit;', dhcpc6_config) self.assertIn(f' send client-id {duid};', dhcpc6_config) self.assertIn('};', dhcpc6_config) + duid_base += 1 # Check for running process self.assertTrue(process_named_running('dhcp6c')) diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py index 882d38760..b65d97d30 100755 --- a/smoketest/scripts/cli/test_interfaces_bonding.py +++ b/smoketest/scripts/cli/test_interfaces_bonding.py @@ -25,34 +25,32 @@ from vyos.configsession import ConfigSessionError from vyos.util import read_file class BondingInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_ipv6_pd = True - self._test_ipv6_dhcpc6 = True - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._base_path = ['interfaces', 'bonding'] - self._interfaces = ['bond0'] - self._mirror_interfaces = ['dum21354'] - self._members = [] + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'bonding'] + cls._interfaces = ['bond0'] + cls._mirror_interfaces = ['dum21354'] + cls._members = [] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: - self._members = os.environ['TEST_ETH'].split() + cls._members = os.environ['TEST_ETH'].split() else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._members.append(tmp) - - self._options['bond0'] = [] - for member in self._members: - self._options['bond0'].append(f'member interface {member}') - - super().setUp() + cls._members.append(tmp) + cls._options['bond0'] = [] + for member in cls._members: + cls._options['bond0'].append(f'member interface {member}') def test_add_single_ip_address(self): super().test_add_single_ip_address() diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py index 33c2e7dad..f64b527b3 100755 --- a/smoketest/scripts/cli/test_interfaces_bridge.py +++ b/smoketest/scripts/cli/test_interfaces_bridge.py @@ -28,31 +28,30 @@ from vyos.util import read_file from vyos.validate import is_intf_addr_assigned class BridgeInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_ipv6_pd = True - self._test_ipv6_dhcpc6 = True - self._test_vlan = True - self._base_path = ['interfaces', 'bridge'] - self._mirror_interfaces = ['dum21354'] - self._members = [] + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_vlan = True + cls._base_path = ['interfaces', 'bridge'] + cls._mirror_interfaces = ['dum21354'] + cls._members = [] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: - self._members = os.environ['TEST_ETH'].split() + cls._members = os.environ['TEST_ETH'].split() else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._members.append(tmp) + cls._members.append(tmp) - self._options['br0'] = [] - for member in self._members: - self._options['br0'].append(f'member interface {member}') - self._interfaces = list(self._options) - - super().setUp() + cls._options['br0'] = [] + for member in cls._members: + cls._options['br0'].append(f'member interface {member}') + cls._interfaces = list(cls._options) def tearDown(self): for intf in self._interfaces: diff --git a/smoketest/scripts/cli/test_interfaces_dummy.py b/smoketest/scripts/cli/test_interfaces_dummy.py index 60465a1d5..6e462bccf 100755 --- a/smoketest/scripts/cli/test_interfaces_dummy.py +++ b/smoketest/scripts/cli/test_interfaces_dummy.py @@ -19,10 +19,10 @@ import unittest from base_interfaces_test import BasicInterfaceTest class DummyInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._base_path = ['interfaces', 'dummy'] - self._interfaces = ['dum435', 'dum8677', 'dum0931', 'dum089'] - super().setUp() + @classmethod + def setUpClass(cls): + cls._base_path = ['interfaces', 'dummy'] + cls._interfaces = ['dum435', 'dum8677', 'dum0931', 'dum089'] if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_erspan.py b/smoketest/scripts/cli/test_interfaces_erspan.py new file mode 100755 index 000000000..c180f0a34 --- /dev/null +++ b/smoketest/scripts/cli/test_interfaces_erspan.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import unittest +import json + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd + +from base_interfaces_test import BasicInterfaceTest + +mtu = 1500 + +def erspan_conf(interface): + tmp = cmd(f'ip -d -j link show {interface}') + ''' + [ + { + "ifindex": 17, + "link": null, + "ifname": "ersp0", + "flags": [ + "BROADCAST", + "MULTICAST" + ], + "mtu": 1450, + "qdisc": "noop", + "operstate": "DOWN", + "linkmode": "DEFAULT", + "group": "default", + "txqlen": 1000, + "link_type": "ether", + "address": "22:27:14:7b:0d:79", + "broadcast": "ff:ff:ff:ff:ff:ff", + "promiscuity": 0, + "min_mtu": 68, + "max_mtu": 0, + "linkinfo": { + "info_kind": "erspan", + "info_data": { + "remote": "10.2.2.2", + "local": "10.1.1.1", + "ttl": 0, + "pmtudisc": true, + "ikey": "0.0.0.123", + "okey": "0.0.0.123", + "iseq": true, + "oseq": true, + "erspan_index": 0, + "erspan_ver": 1 + } + }, + "inet6_addr_gen_mode": "eui64", + "num_tx_queues": 1, + "num_rx_queues": 1, + "gso_max_size": 65536, + "gso_max_segs": 65535 + } + ] + ''' + return json.loads(tmp)[0] + +class ERSPanTunnelInterfaceTest(BasicInterfaceTest.BaseTest): + def setUp(self): + super().setUp() + + self._base_path = ['interfaces', 'erspan'] + self._test_mtu = True + + self.local_v4 = '10.1.1.1' + self.local_v6 = '2001:db8::1' + self.remote_v4 = '10.2.2.2' + self.remote_v6 = '2001:db9::1' + + def tearDown(self): + self.session.delete(['interfaces', 'erspan']) + super().tearDown() + + def test_erspan_ipv4(self): + interface = 'ersp100' + encapsulation = 'erspan' + key = 123 + + self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) + self.session.set(self._base_path + [interface, 'remote-ip', self.remote_v4]) + self.session.set(self._base_path + [interface, 'parameters', 'ip' , 'key', str(key)]) + + self.session.commit() + + conf = erspan_conf(interface) + self.assertEqual(interface, conf['ifname']) + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) + self.assertEqual(mtu, conf['mtu']) + + self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) + self.assertEqual(self.remote_v4, conf['linkinfo']['info_data']['remote']) + + + def test_erspan_ipv6(self): + interface = 'ersp1000' + encapsulation = 'ip6erspan' + key = 123 + + self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.session.set(self._base_path + [interface, 'local-ip', self.local_v6]) + self.session.set(self._base_path + [interface, 'remote-ip', self.remote_v6]) + self.session.set(self._base_path + [interface, 'parameters', 'ip' , 'key', str(key)]) + + self.session.commit() + + conf = erspan_conf(interface) + self.assertEqual(interface, conf['ifname']) + self.assertEqual(encapsulation, conf['linkinfo']['info_kind']) + self.assertEqual(mtu, conf['mtu']) + + self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) + self.assertEqual(self.remote_v6, conf['linkinfo']['info_data']['remote']) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index 6c6e66008..772ff248f 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -35,38 +35,31 @@ def get_wpa_supplicant_value(interface, key): return tmp[0] class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_ipv6_pd = True - self._test_ipv6_dhcpc6 = True - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._base_path = ['interfaces', 'ethernet'] - self._mirror_interfaces = ['dum21354'] + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'ethernet'] + cls._mirror_interfaces = ['dum21354'] # we need to filter out VLAN interfaces identified by a dot (.) # in their name - just in case! if 'TEST_ETH' in os.environ: tmp = os.environ['TEST_ETH'].split() - self._interfaces = tmp + cls._interfaces = tmp else: - for tmp in Section.interfaces("ethernet"): + for tmp in Section.interfaces('ethernet'): if not '.' in tmp: - self._interfaces.append(tmp) + cls._interfaces.append(tmp) - self._macs = {} - for interface in self._interfaces: - try: - mac = self.session.show_config(self._base_path + - [interface, 'hw-id']).split()[1] - except: - # during initial system startup there is no hw-id node - mac = read_file(f'/sys/class/net/{interface}/address') - self._macs[interface] = mac - - super().setUp() + cls._macs = {} + for interface in cls._interfaces: + cls._macs[interface] = read_file(f'/sys/class/net/{interface}/address') def tearDown(self): @@ -102,8 +95,7 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): # Validate interface state for interface in self._interfaces: - with open(f'/sys/class/net/{interface}/flags', 'r') as f: - flags = f.read() + flags = read_file(f'/sys/class/net/{interface}/flags') self.assertEqual(int(flags, 16) & 1, 0) def test_offloading_rps(self): diff --git a/smoketest/scripts/cli/test_interfaces_geneve.py b/smoketest/scripts/cli/test_interfaces_geneve.py index 12cded400..b708b5437 100755 --- a/smoketest/scripts/cli/test_interfaces_geneve.py +++ b/smoketest/scripts/cli/test_interfaces_geneve.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -20,16 +20,16 @@ from vyos.configsession import ConfigSession from base_interfaces_test import BasicInterfaceTest class GeneveInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._base_path = ['interfaces', 'geneve'] - self._options = { + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'geneve'] + cls._options = { 'gnv0': ['vni 10', 'remote 127.0.1.1'], 'gnv1': ['vni 20', 'remote 127.0.1.2'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_l2tpv3.py b/smoketest/scripts/cli/test_interfaces_l2tpv3.py index 81af6d7f4..a89895b92 100755 --- a/smoketest/scripts/cli/test_interfaces_l2tpv3.py +++ b/smoketest/scripts/cli/test_interfaces_l2tpv3.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -21,11 +21,12 @@ from base_interfaces_test import BasicInterfaceTest from vyos.util import cmd class GeneveInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._base_path = ['interfaces', 'l2tpv3'] - self._options = { + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'l2tpv3'] + cls._options = { 'l2tpeth10': ['local-ip 127.0.0.1', 'remote-ip 127.10.10.10', 'tunnel-id 100', 'peer-tunnel-id 10', 'session-id 100', 'peer-session-id 10', @@ -35,8 +36,7 @@ class GeneveInterfaceTest(BasicInterfaceTest.BaseTest): 'session-id 20', 'tunnel-id 200', 'source-port 2020', 'destination-port 20202'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) def test_add_single_ip_address(self): super().test_add_single_ip_address() diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py index 36000c3ff..77dd4c1b5 100755 --- a/smoketest/scripts/cli/test_interfaces_loopback.py +++ b/smoketest/scripts/cli/test_interfaces_loopback.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -21,13 +21,13 @@ from netifaces import interfaces from vyos.validate import is_intf_addr_assigned +loopbacks = ['127.0.0.1', '::1'] + class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - super().setUp() - # these addresses are never allowed to be removed from the system - self._loopback_addresses = ['127.0.0.1', '::1'] - self._base_path = ['interfaces', 'loopback'] - self._interfaces = ['lo'] + @classmethod + def setUpClass(cls): + cls._base_path = ['interfaces', 'loopback'] + cls._interfaces = ['lo'] def tearDown(self): self.session.delete(self._base_path) @@ -40,12 +40,12 @@ class LoopbackInterfaceTest(BasicInterfaceTest.BaseTest): def test_add_single_ip_address(self): super().test_add_single_ip_address() - for addr in self._loopback_addresses: + for addr in loopbacks: self.assertTrue(is_intf_addr_assigned('lo', addr)) def test_add_multiple_ip_addresses(self): super().test_add_multiple_ip_addresses() - for addr in self._loopback_addresses: + for addr in loopbacks: self.assertTrue(is_intf_addr_assigned('lo', addr)) def test_interface_disable(self): diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index 89743e5fd..3a3e7bff3 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -31,19 +31,19 @@ def get_config_value(interface, key): return tmp[0] class MACsecInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - super().setUp() - self._test_ip = True - self._test_ipv6 = True - self._base_path = ['interfaces', 'macsec'] - self._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._base_path = ['interfaces', 'macsec'] + cls._options = { 'macsec0': ['source-interface eth0', 'security cipher gcm-aes-128'] } # if we have a physical eth1 interface, add a second macsec instance - if 'eth1' in Section.interfaces("ethernet"): + if 'eth1' in Section.interfaces('ethernet'): macsec = { 'macsec1': [f'source-interface eth1', 'security cipher gcm-aes-128'] } - self._options.update(macsec) + cls._options.update(macsec) - self._interfaces = list(self._options) + cls._interfaces = list(cls._options) def test_macsec_encryption(self): # MACsec can be operating in authentication and encryption mode - both diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index 00db3f667..a2a1a85ec 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -625,27 +625,27 @@ if __name__ == '__main__': # Generate mandatory SSL certificate tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\ f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(ca_cert): # Generate "CA" tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(dh_pem): # Generate "DH" key tmp = f'openssl dhparam -out {dh_pem} 2048' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(s2s_key): # Generate site-2-site key tmp = f'openvpn --genkey --secret {s2s_key}' - print(cmd(tmp)) + cmd(tmp) if not os.path.isfile(auth_key): # Generate TLS auth key tmp = f'openvpn --genkey --secret {auth_key}' - print(cmd(tmp)) + cmd(tmp) for file in [ca_cert, ssl_cert, ssl_key, dh_pem, s2s_key, auth_key]: cmd(f'sudo chown openvpn:openvpn {file}') diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py index 6bfe35d86..285c756e2 100755 --- a/smoketest/scripts/cli/test_interfaces_pppoe.py +++ b/smoketest/scripts/cli/test_interfaces_pppoe.py @@ -97,6 +97,27 @@ class PPPoEInterfaceTest(unittest.TestCase): self.assertTrue(running) + + def test_pppoe_clent_disabled_interface(self): + # Check if PPPoE Client can be disabled + for interface in self._interfaces: + self.session.set(base_path + [interface, 'authentication', 'user', 'vyos']) + self.session.set(base_path + [interface, 'authentication', 'password', 'vyos']) + self.session.set(base_path + [interface, 'source-interface', self._source_interface]) + self.session.set(base_path + [interface, 'disable']) + + self.session.commit() + + # Validate PPPoE client process + running = False + for interface in self._interfaces: + for proc in process_iter(): + if interface in proc.cmdline(): + running = True + + self.assertFalse(running) + + def test_pppoe_dhcpv6pd(self): # Check if PPPoE dialer can be configured with DHCPv6-PD address = '1' diff --git a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py index 1a5debb79..f4cb4cdc9 100755 --- a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -19,21 +19,21 @@ import unittest from base_interfaces_test import BasicInterfaceTest class PEthInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_ipv6_pd = True - self._test_ipv6_dhcpc6 = True - self._test_mtu = True - self._test_vlan = True - self._test_qinq = True - self._base_path = ['interfaces', 'pseudo-ethernet'] - self._options = { + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_ipv6_pd = True + cls._test_ipv6_dhcpc6 = True + cls._test_mtu = True + cls._test_vlan = True + cls._test_qinq = True + cls._base_path = ['interfaces', 'pseudo-ethernet'] + cls._options = { 'peth0': ['source-interface eth1'], 'peth1': ['source-interface eth1'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py index 8405fc7d0..a9250e3e5 100755 --- a/smoketest/scripts/cli/test_interfaces_tunnel.py +++ b/smoketest/scripts/cli/test_interfaces_tunnel.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -61,22 +61,22 @@ def tunnel_conf(interface): return json.loads(tmp)[0] class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_mtu = True - self._base_path = ['interfaces', 'tunnel'] - self.local_v4 = '192.0.2.1' - self.local_v6 = '2001:db8::1' - - self._options = { - 'tun10': ['encapsulation ipip', 'remote-ip 192.0.2.10', 'local-ip ' + self.local_v4], - 'tun20': ['encapsulation gre', 'remote-ip 192.0.2.20', 'local-ip ' + self.local_v4], + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_mtu = True + cls._base_path = ['interfaces', 'tunnel'] + cls.local_v4 = '192.0.2.1' + cls.local_v6 = '2001:db8::1' + cls._options = { + 'tun10': ['encapsulation ipip', 'remote-ip 192.0.2.10', 'local-ip ' + cls.local_v4], + 'tun20': ['encapsulation gre', 'remote-ip 192.0.2.20', 'local-ip ' + cls.local_v4], } + cls._interfaces = list(cls._options) - self._interfaces = list(self._options) + def setUp(self): super().setUp() - self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32']) self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128']) diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py index a726aa610..fcc1b15ce 100755 --- a/smoketest/scripts/cli/test_interfaces_vxlan.py +++ b/smoketest/scripts/cli/test_interfaces_vxlan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -20,17 +20,17 @@ from vyos.configsession import ConfigSession, ConfigSessionError from base_interfaces_test import BasicInterfaceTest class VXLANInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._test_ipv6 = True - self._test_mtu = True - self._base_path = ['interfaces', 'vxlan'] - self._options = { + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._test_ipv6 = True + cls._test_mtu = True + cls._base_path = ['interfaces', 'vxlan'] + cls._options = { 'vxlan0': ['vni 10', 'remote 127.0.0.2'], 'vxlan1': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py index d9a51b146..e70324f96 100755 --- a/smoketest/scripts/cli/test_interfaces_wireguard.py +++ b/smoketest/scripts/cli/test_interfaces_wireguard.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py index 51d97f032..39e8cd5b8 100755 --- a/smoketest/scripts/cli/test_interfaces_wireless.py +++ b/smoketest/scripts/cli/test_interfaces_wireless.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -32,10 +32,11 @@ def get_config_value(interface, key): return tmp[0] class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): - def setUp(self): - self._test_ip = True - self._base_path = ['interfaces', 'wireless'] - self._options = { + @classmethod + def setUpClass(cls): + cls._test_ip = True + cls._base_path = ['interfaces', 'wireless'] + cls._options = { 'wlan0': ['physical-device phy0', 'ssid VyOS-WIFI-0', 'type station', 'address 192.0.2.1/30'], 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1', 'country-code se', @@ -45,8 +46,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest): 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3', 'country-code se', 'type access-point', 'address 192.0.2.13/30', 'channel 0'], } - self._interfaces = list(self._options) - super().setUp() + cls._interfaces = list(cls._options) def test_wireless_add_single_ip_address(self): # derived method to check if member interfaces are enslaved properly diff --git a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py index 696a6946b..023f57305 100755 --- a/smoketest/scripts/cli/test_interfaces_wirelessmodem.py +++ b/smoketest/scripts/cli/test_interfaces_wirelessmodem.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py new file mode 100755 index 000000000..80e5daa7c --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_bfd.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import process_named_running + +PROCESS_NAME = 'bfdd' +base_path = ['protocols', 'bfd'] + +dum_if = 'dum1001' +peers = { + '192.0.2.10' : { + 'intv_rx' : '500', + 'intv_tx' : '600', + 'multihop' : '', + 'source_addr': '192.0.2.254', + }, + '192.0.2.20' : { + 'echo_mode' : '', + 'intv_echo' : '100', + 'intv_mult' : '100', + 'intv_rx' : '222', + 'intv_tx' : '333', + 'shutdown' : '', + 'source_intf': dum_if, + }, + '2001:db8::a' : { + 'source_addr': '2001:db8::1', + 'source_intf': dum_if, + }, + '2001:db8::b' : { + 'source_addr': '2001:db8::1', + 'multihop' : '', + }, +} + +profiles = { + 'foo' : { + 'echo_mode' : '', + 'intv_echo' : '100', + 'intv_mult' : '101', + 'intv_rx' : '222', + 'intv_tx' : '333', + 'shutdown' : '', + }, + 'bar' : { + 'intv_mult' : '102', + 'intv_rx' : '444', + }, +} + +def getFRRconfig(): + return cmd('vtysh -c "show run" | sed -n "/^bfd/,/^!/p"') + +def getBFDPeerconfig(peer): + return cmd(f'vtysh -c "show run" | sed -n "/^ {peer}/,/^!/p"') + +def getBFDProfileconfig(profile): + return cmd(f'vtysh -c "show run" | sed -n "/^ {profile}/,/^!/p"') + +class TestProtocolsBFD(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + self.session.delete(base_path) + self.session.commit() + del self.session + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_bfd_peer(self): + for peer, peer_config in peers.items(): + if 'echo_mode' in peer_config: + self.session.set(base_path + ['peer', peer, 'echo-mode']) + if 'intv_echo' in peer_config: + self.session.set(base_path + ['peer', peer, 'interval', 'echo-interval', peer_config["intv_echo"]]) + if 'intv_mult' in peer_config: + self.session.set(base_path + ['peer', peer, 'interval', 'multiplier', peer_config["intv_mult"]]) + if 'intv_rx' in peer_config: + self.session.set(base_path + ['peer', peer, 'interval', 'receive', peer_config["intv_rx"]]) + if 'intv_tx' in peer_config: + self.session.set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]]) + if 'multihop' in peer_config: + self.session.set(base_path + ['peer', peer, 'multihop']) + if 'shutdown' in peer_config: + self.session.set(base_path + ['peer', peer, 'shutdown']) + if 'source_addr' in peer_config: + self.session.set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]]) + if 'source_intf' in peer_config: + self.session.set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + frrconfig = getFRRconfig() + for peer, peer_config in peers.items(): + tmp = f'peer {peer}' + if 'multihop' in peer_config: + tmp += f' multihop' + if 'source_addr' in peer_config: + tmp += f' local-address {peer_config["source_addr"]}' + if 'source_intf' in peer_config: + tmp += f' interface {peer_config["source_intf"]}' + + self.assertIn(tmp, frrconfig) + peerconfig = getBFDPeerconfig(tmp) + + if 'echo_mode' in peer_config: + self.assertIn(f' echo-mode', peerconfig) + if 'intv_echo' in peer_config: + self.assertIn(f' echo-interval {peer_config["intv_echo"]}', peerconfig) + if 'intv_mult' in peer_config: + self.assertIn(f' detect-multiplier {peer_config["intv_mult"]}', peerconfig) + if 'intv_rx' in peer_config: + self.assertIn(f' receive-interval {peer_config["intv_rx"]}', peerconfig) + if 'intv_tx' in peer_config: + self.assertIn(f' transmit-interval {peer_config["intv_tx"]}', peerconfig) + if 'shutdown' not in peer_config: + self.assertIn(f' no shutdown', peerconfig) + + def test_bfd_profile(self): + peer = '192.0.2.10' + + for profile, profile_config in profiles.items(): + if 'echo_mode' in profile_config: + self.session.set(base_path + ['profile', profile, 'echo-mode']) + if 'intv_echo' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'echo-interval', profile_config["intv_echo"]]) + if 'intv_mult' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'multiplier', profile_config["intv_mult"]]) + if 'intv_rx' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]]) + if 'intv_tx' in profile_config: + self.session.set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]]) + if 'shutdown' in profile_config: + self.session.set(base_path + ['profile', profile, 'shutdown']) + + self.session.set(base_path + ['peer', peer, 'profile', list(profiles)[0]]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + for profile, profile_config in profiles.items(): + config = getBFDProfileconfig(f'profile {profile}') + if 'echo_mode' in profile_config: + self.assertIn(f' echo-mode', config) + if 'intv_echo' in profile_config: + self.assertIn(f' echo-interval {profile_config["intv_echo"]}', config) + if 'intv_mult' in profile_config: + self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config) + if 'intv_rx' in profile_config: + self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config) + if 'intv_tx' in profile_config: + self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config) + if 'shutdown' not in profile_config: + self.assertIn(f' no shutdown', config) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 87d10eb85..b6a6a493c 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -19,6 +19,7 @@ import unittest from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.template import is_ipv6 from vyos.util import cmd from vyos.util import process_named_running @@ -30,6 +31,8 @@ route_map_in = 'foo-map-in' route_map_out = 'foo-map-out' prefix_list_in = 'pfx-foo-in' prefix_list_out = 'pfx-foo-out' +prefix_list_in6 = 'pfx-foo-in6' +prefix_list_out6 = 'pfx-foo-out6' neighbor_config = { '192.0.2.1' : { @@ -45,6 +48,8 @@ neighbor_config = { 'local_as' : '300', 'route_map_in' : route_map_in, 'route_map_out': route_map_out, + 'no_send_comm_ext' : '', + 'addpath_all' : '', }, '192.0.2.2' : { 'remote_as' : '200', @@ -54,6 +59,7 @@ neighbor_config = { 'cap_strict' : '', 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, + 'no_send_comm_std' : '', }, '192.0.2.3' : { 'description' : 'foo bar baz', @@ -62,6 +68,32 @@ neighbor_config = { 'multi_hop' : '5', 'update_src' : 'lo', }, + '2001:db8::1' : { + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '123', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, + 'no_send_comm_std' : '', + 'addpath_per_as' : '', + }, + '2001:db8::2' : { + 'remote_as' : '456', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'pfx_list_in' : prefix_list_in6, + 'pfx_list_out' : prefix_list_out6, + 'no_send_comm_ext' : '', + }, } peer_group_config = { @@ -82,6 +114,7 @@ peer_group_config = { 'local_as' : '300', 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, + 'no_send_comm_ext' : '', }, 'baz' : { 'cap_dynamic' : '', @@ -96,10 +129,13 @@ peer_group_config = { } def getFRRBGPconfig(): - return cmd(f'vtysh -c "show run" | sed -n "/router bgp {ASN}/,/^!/p"') + return cmd(f'vtysh -c "show run" | sed -n "/^router bgp {ASN}/,/^!/p"') + +def getFRRBGPVNIconfig(vni): + return cmd(f'vtysh -c "show run" | sed -n "/^ vni {vni}/,/^!/p"') def getFRRRPKIconfig(): - return cmd(f'vtysh -c "show run" | sed -n "/rpki/,/^!/p"') + return cmd(f'vtysh -c "show run" | sed -n "/^rpki/,/^!/p"') class TestProtocolsBGP(unittest.TestCase): def setUp(self): @@ -112,16 +148,26 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit']) self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25']) + self.session.set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64']) + self.session.set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny']) + self.session.set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64']) + def tearDown(self): self.session.delete(['policy', 'route-map', route_map_in]) self.session.delete(['policy', 'route-map', route_map_out]) self.session.delete(['policy', 'prefix-list', prefix_list_in]) self.session.delete(['policy', 'prefix-list', prefix_list_out]) + self.session.delete(['policy', 'prefix-list6', prefix_list_in6]) + self.session.delete(['policy', 'prefix-list6', prefix_list_out6]) self.session.delete(base_path) self.session.commit() del self.session + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + def verify_frr_config(self, peer, peer_config, frrconfig): # recurring patterns to verify for both a simple neighbor and a peer-group if 'cap_dynamic' in peer_config: @@ -158,11 +204,20 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig) if 'pfx_list_out' in peer_config: self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig) + if 'no_send_comm_std' in peer_config: + self.assertIn(f' no neighbor {peer} send-community', frrconfig) + if 'no_send_comm_ext' in peer_config: + self.assertIn(f' no neighbor {peer} send-community extended', frrconfig) + if 'addpath_all' in peer_config: + self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig) + if 'addpath_per_as' in peer_config: + self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig) def test_bgp_01_simple(self): router_id = '127.0.0.1' local_pref = '500' + stalepath_time = '60' self.session.set(base_path + ['parameters', 'router-id', router_id]) self.session.set(base_path + ['parameters', 'log-neighbor-changes']) @@ -170,6 +225,8 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['parameters', 'default', 'local-pref', local_pref]) # Deactivate IPv4 unicast for a peer by default self.session.set(base_path + ['parameters', 'default', 'no-ipv4-unicast']) + self.session.set(base_path + ['parameters', 'graceful-restart', 'stalepath-time', stalepath_time]) + self.session.set(base_path + ['parameters', 'graceful-shutdown']) # commit changes self.session.commit() @@ -181,54 +238,66 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' bgp log-neighbor-changes', frrconfig) self.assertIn(f' bgp default local-preference {local_pref}', frrconfig) self.assertIn(f' no bgp default ipv4-unicast', frrconfig) + self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig) + self.assertIn(f' bgp graceful-shutdown', frrconfig) - # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) def test_bgp_02_neighbors(self): # Test out individual neighbor configuration items, not all of them are # also available to a peer-group! - for neighbor, config in neighbor_config.items(): - if 'adv_interv' in config: - self.session.set(base_path + ['neighbor', neighbor, 'advertisement-interval', config["adv_interv"]]) - if 'cap_dynamic' in config: - self.session.set(base_path + ['neighbor', neighbor, 'capability', 'dynamic']) - if 'cap_ext_next' in config: - self.session.set(base_path + ['neighbor', neighbor, 'capability', 'extended-nexthop']) - if 'description' in config: - self.session.set(base_path + ['neighbor', neighbor, 'description', config["description"]]) - if 'no_cap_nego' in config: - self.session.set(base_path + ['neighbor', neighbor, 'disable-capability-negotiation']) - if 'multi_hop' in config: - self.session.set(base_path + ['neighbor', neighbor, 'ebgp-multihop', config["multi_hop"]]) - if 'local_as' in config: - self.session.set(base_path + ['neighbor', neighbor, 'local-as', config["local_as"]]) - if 'cap_over' in config: - self.session.set(base_path + ['neighbor', neighbor, 'override-capability']) - if 'passive' in config: - self.session.set(base_path + ['neighbor', neighbor, 'passive']) - if 'password' in config: - self.session.set(base_path + ['neighbor', neighbor, 'password', config["password"]]) - if 'port' in config: - self.session.set(base_path + ['neighbor', neighbor, 'port', config["port"]]) - if 'remote_as' in config: - self.session.set(base_path + ['neighbor', neighbor, 'remote-as', config["remote_as"]]) - if 'cap_strict' in config: - self.session.set(base_path + ['neighbor', neighbor, 'strict-capability-match']) - if 'shutdown' in config: - self.session.set(base_path + ['neighbor', neighbor, 'shutdown']) - if 'ttl_security' in config: - self.session.set(base_path + ['neighbor', neighbor, 'ttl-security', 'hops', config["ttl_security"]]) - if 'update_src' in config: - self.session.set(base_path + ['neighbor', neighbor, 'update-source', config["update_src"]]) - if 'route_map_in' in config: - self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]]) - if 'route_map_out' in config: - self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]]) - if 'pfx_list_in' in config: - self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) - if 'pfx_list_out' in config: - self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) + for peer, peer_config in neighbor_config.items(): + afi = 'ipv4-unicast' + if is_ipv6(peer): + afi = 'ipv6-unicast' + + if 'adv_interv' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]]) + if 'cap_dynamic' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'capability', 'dynamic']) + if 'cap_ext_next' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'capability', 'extended-nexthop']) + if 'description' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'description', peer_config["description"]]) + if 'no_cap_nego' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'disable-capability-negotiation']) + if 'multi_hop' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]]) + if 'local_as' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"]]) + if 'cap_over' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'override-capability']) + if 'passive' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'passive']) + if 'password' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'password', peer_config["password"]]) + if 'port' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'port', peer_config["port"]]) + if 'remote_as' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'remote-as', peer_config["remote_as"]]) + if 'cap_strict' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'strict-capability-match']) + if 'shutdown' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'shutdown']) + if 'ttl_security' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]]) + if 'update_src' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]]) + if 'route_map_in' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]]) + if 'route_map_out' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'export', peer_config["route_map_out"]]) + if 'pfx_list_in' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'import', peer_config["pfx_list_in"]]) + if 'pfx_list_out' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'prefix-list', 'export', peer_config["pfx_list_out"]]) + if 'no_send_comm_std' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'disable-send-community', 'extended']) + if 'addpath_all' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all']) + if 'addpath_per_as' in peer_config: + self.session.set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as']) # commit changes self.session.commit() @@ -238,11 +307,11 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f'router bgp {ASN}', frrconfig) for peer, peer_config in neighbor_config.items(): - if 'adv_interv' in config: + if 'adv_interv' in peer_config: self.assertIn(f' neighbor {peer} advertisement-interval {peer_config["adv_interv"]}', frrconfig) - if 'port' in config: + if 'port' in peer_config: self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig) - if 'cap_strict' in config: + if 'cap_strict' in peer_config: self.assertIn(f' neighbor {peer} strict-capability-match', frrconfig) self.verify_frr_config(peer, peer_config, frrconfig) @@ -284,6 +353,14 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) if 'pfx_list_out' in config: self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) + if 'no_send_comm_std' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'standard']) + if 'no_send_comm_ext' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'disable-send-community', 'extended']) + if 'addpath_all' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all']) + if 'addpath_per_as' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as']) # commit changes self.session.commit() @@ -311,7 +388,7 @@ class TestProtocolsBGP(unittest.TestCase): } # We want to redistribute ... - redistributes = ['connected', 'kernel', 'ospf', 'rip', 'static'] + redistributes = ['connected', 'isis', 'kernel', 'ospf', 'rip', 'static'] for redistribute in redistributes: self.session.set(base_path + ['address-family', 'ipv4-unicast', 'redistribute', redistribute]) @@ -348,12 +425,12 @@ class TestProtocolsBGP(unittest.TestCase): def test_bgp_05_afi_ipv6(self): networks = { '2001:db8:100::/48' : { - }, + }, '2001:db8:200::/48' : { - }, + }, '2001:db8:300::/48' : { 'summary_only' : '', - }, + }, } # We want to redistribute ... @@ -420,38 +497,33 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig) - def test_bgp_07_rpki(self): - rpki_path = ['protocols', 'rpki'] - init_tmo = '50' - polling = '400' - preference = '100' - timeout = '900' - - cache = { - 'foo' : { 'address' : '1.1.1.1', 'port' : '8080' }, -# T3253 only one peer supported -# 'bar' : { 'address' : '2.2.2.2', 'port' : '9090' }, - } - - self.session.set(rpki_path + ['polling-period', polling]) - self.session.set(rpki_path + ['preference', preference]) - - for name, config in cache.items(): - self.session.set(rpki_path + ['cache', name, 'address', config['address']]) - self.session.set(rpki_path + ['cache', name, 'port', config['port']]) + def test_bgp_07_l2vpn_evpn(self): + vnis = ['10010', '10020', '10030'] + neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30'] + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni']) + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw']) + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip']) + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable']) + for vni in vnis: + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw']) + self.session.set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip']) # commit changes self.session.commit() # Verify FRR bgpd configuration - frrconfig = getFRRRPKIconfig() - self.assertIn(f'rpki polling_period {polling}', frrconfig) - - for name, config in cache.items(): - self.assertIn('rpki cache {address} {port} preference 1'.format(**config), frrconfig) - - self.session.delete(rpki_path) - + frrconfig = getFRRBGPconfig() + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' address-family l2vpn evpn', frrconfig) + self.assertIn(f' advertise-all-vni', frrconfig) + self.assertIn(f' advertise-default-gw', frrconfig) + self.assertIn(f' advertise-svi-ip', frrconfig) + self.assertIn(f' flooding disable', frrconfig) + for vni in vnis: + vniconfig = getFRRBGPVNIconfig(vni) + self.assertIn(f'vni {vni}', vniconfig) + self.assertIn(f' advertise-default-gw', vniconfig) + self.assertIn(f' advertise-svi-ip', vniconfig) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index d47838d8c..0ca8bb3bd 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -30,6 +30,9 @@ route_map = 'foo-bar-baz10' def getFRROSPFconfig(): return cmd('vtysh -c "show run" | sed -n "/router ospf/,/^!/p"') +def getFRRInterfaceConfig(interface): + return cmd(f'vtysh -c "show run" | sed -n "/^interface {interface}$/,/^!/p"') + class TestProtocolsOSPF(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -85,7 +88,7 @@ class TestProtocolsOSPF(unittest.TestCase): def test_ospf_03_access_list(self): acl = '100' seq = '10' - protocols = ['bgp', 'connected', 'kernel', 'rip', 'static'] + protocols = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] self.session.set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit']) self.session.set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any']) @@ -212,7 +215,7 @@ class TestProtocolsOSPF(unittest.TestCase): def test_ospf_08_redistribute(self): metric = '15' metric_type = '1' - redistribute = ['bgp', 'connected', 'kernel', 'rip', 'static'] + redistribute = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static'] for protocol in redistribute: self.session.set(base_path + ['redistribute', protocol, 'metric', metric]) @@ -232,24 +235,8 @@ class TestProtocolsOSPF(unittest.TestCase): else: self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) - - def test_ospf_09_area(self): - area = '0' + def test_ospf_09_virtual_link(self): networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] - for network in networks: - self.session.set(base_path + ['area', area, 'network', network]) - - # commit changes - self.session.commit() - - # Verify FRR ospfd configuration - frrconfig = getFRROSPFconfig() - self.assertIn(f'router ospf', frrconfig) - for network in networks: - self.assertIn(f' network {network} area {area}', frrconfig) - - - def test_ospf_10_virtual_link(self): area = '10' shortcut = 'enable' virtual_link = '192.0.2.1' @@ -263,6 +250,8 @@ class TestProtocolsOSPF(unittest.TestCase): self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'retransmit-interval', retransmit]) self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'transmit-delay', transmit]) self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'dead-interval', dead]) + for network in networks: + self.session.set(base_path + ['area', area, 'network', network]) # commit changes self.session.commit() @@ -272,6 +261,39 @@ class TestProtocolsOSPF(unittest.TestCase): self.assertIn(f'router ospf', frrconfig) self.assertIn(f' area {area} shortcut {shortcut}', frrconfig) self.assertIn(f' area {area} virtual-link {virtual_link} hello-interval {hello} retransmit-interval {retransmit} transmit-delay {transmit} dead-interval {dead}', frrconfig) + for network in networks: + self.assertIn(f' network {network} area {area}', frrconfig) + + def test_ospf_10_interface_configureation(self): + interfaces = Section.interfaces('ethernet') + password = 'vyos1234' + bandwidth = '10000' + cost = '150' + network = 'point-to-point' + priority = '200' + + for interface in interfaces: + self.session.set(base_path + ['interface', interface, 'authentication', 'plaintext-password', password]) + self.session.set(base_path + ['interface', interface, 'bandwidth', bandwidth]) + self.session.set(base_path + ['interface', interface, 'bfd']) + self.session.set(base_path + ['interface', interface, 'cost', cost]) + self.session.set(base_path + ['interface', interface, 'mtu-ignore']) + self.session.set(base_path + ['interface', interface, 'network', network]) + self.session.set(base_path + ['interface', interface, 'priority', priority]) + + # commit changes + self.session.commit() + + for interface in interfaces: + config = getFRRInterfaceConfig(interface) + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf authentication-key {password}', config) + self.assertIn(f' ip ospf bfd', config) + self.assertIn(f' ip ospf cost {cost}', config) + self.assertIn(f' ip ospf mtu-ignore', config) + self.assertIn(f' ip ospf network {network}', config) + self.assertIn(f' ip ospf priority {priority}', config) + self.assertIn(f' bandwidth {bandwidth}', config) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index 297d5d996..754c4488f 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -25,10 +25,15 @@ from vyos.util import process_named_running PROCESS_NAME = 'ospf6d' base_path = ['protocols', 'ospfv3'] +router_id = '192.0.2.1' +default_area = '0' def getFRROSPFconfig(): return cmd('vtysh -c "show run" | sed -n "/router ospf6/,/^!/p"') +def getFRRIFconfig(iface): + return cmd(f'vtysh -c "show run" | sed -n "/^interface {iface}/,/^!/p"') + class TestProtocolsOSPFv3(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) @@ -43,23 +48,21 @@ class TestProtocolsOSPFv3(unittest.TestCase): def test_ospfv3_01_basic(self): - area = '0' seq = '10' prefix = '2001:db8::/32' acl_name = 'foo-acl-100' - router_id = '192.0.2.1' self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'action', 'permit']) self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'source', 'any']) self.session.set(base_path + ['parameters', 'router-id', router_id]) - self.session.set(base_path + ['area', area, 'range', prefix, 'advertise']) - self.session.set(base_path + ['area', area, 'export-list', acl_name]) - self.session.set(base_path + ['area', area, 'import-list', acl_name]) + self.session.set(base_path + ['area', default_area, 'range', prefix, 'advertise']) + self.session.set(base_path + ['area', default_area, 'export-list', acl_name]) + self.session.set(base_path + ['area', default_area, 'import-list', acl_name]) interfaces = Section.interfaces('ethernet') for interface in interfaces: - self.session.set(base_path + ['area', area, 'interface', interface]) + self.session.set(base_path + ['area', default_area, 'interface', interface]) # commit changes self.session.commit() @@ -67,13 +70,13 @@ class TestProtocolsOSPFv3(unittest.TestCase): # Verify FRR ospfd configuration frrconfig = getFRROSPFconfig() self.assertIn(f'router ospf6', frrconfig) - self.assertIn(f' area {area} range {prefix}', frrconfig) + self.assertIn(f' area {default_area} range {prefix}', frrconfig) self.assertIn(f' ospf6 router-id {router_id}', frrconfig) - self.assertIn(f' area {area} import-list {acl_name}', frrconfig) - self.assertIn(f' area {area} export-list {acl_name}', frrconfig) + self.assertIn(f' area {default_area} import-list {acl_name}', frrconfig) + self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig) for interface in interfaces: - self.assertIn(f' interface {interface} area {area}', frrconfig) + self.assertIn(f' interface {interface} area {default_area}', frrconfig) self.session.delete(['policy', 'access-list6', acl_name]) @@ -118,6 +121,46 @@ class TestProtocolsOSPFv3(unittest.TestCase): for protocol in redistribute: self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) + def test_ospfv3_0104_interfaces(self): + + self.session.set(base_path + ['parameters', 'router-id', router_id]) + self.session.set(base_path + ['area', default_area]) + + cost = '100' + priority = '10' + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + if_base = base_path + ['interface', interface] + self.session.set(if_base + ['bfd']) + self.session.set(if_base + ['cost', cost]) + self.session.set(if_base + ['instance-id', '0']) + self.session.set(if_base + ['mtu-ignore']) + self.session.set(if_base + ['network', 'point-to-point']) + self.session.set(if_base + ['passive']) + self.session.set(if_base + ['priority', priority]) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf6', frrconfig) + + cost = '100' + priority = '10' + for interface in interfaces: + if_config = getFRRIFconfig(interface) + self.assertIn(f'interface {interface}', if_config) + self.assertIn(f' ipv6 ospf6 bfd', if_config) + self.assertIn(f' ipv6 ospf6 cost {cost}', if_config) + self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config) + self.assertIn(f' ipv6 ospf6 network point-to-point', if_config) + self.assertIn(f' ipv6 ospf6 passive', if_config) + self.assertIn(f' ipv6 ospf6 priority {priority}', if_config) + cost = str(int(cost) + 10) + priority = str(int(priority) + 5) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py new file mode 100755 index 000000000..f42ea0c0a --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_rip.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import cmd +from vyos.util import process_named_running + +PROCESS_NAME = 'ripd' +acl_in = '198' +acl_out = '199' +prefix_list_in = 'foo-prefix' +prefix_list_out = 'bar-prefix' +route_map = 'FooBar123' + +base_path = ['protocols', 'rip'] + +def getFRRconfig(): + return cmd('vtysh -c "show run" | sed -n "/router rip/,/^!/p"') + +class TestProtocolsRIP(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + self.session.set(['policy', 'access-list', acl_in, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'access-list', acl_in, 'rule', '10', 'source', 'any']) + self.session.set(['policy', 'access-list', acl_in, 'rule', '10', 'destination', 'any']) + self.session.set(['policy', 'access-list', acl_out, 'rule', '20', 'action', 'deny']) + self.session.set(['policy', 'access-list', acl_out, 'rule', '20', 'source', 'any']) + self.session.set(['policy', 'access-list', acl_out, 'rule', '20', 'destination', 'any']) + self.session.set(['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'action', 'permit']) + self.session.set(['policy', 'prefix-list', prefix_list_in, 'rule', '100', 'prefix', '192.0.2.0/24']) + self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'action', 'deny']) + self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '200', 'prefix', '192.0.2.0/24']) + self.session.set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + + def tearDown(self): + self.session.delete(base_path) + self.session.delete(['policy', 'access-list', acl_in]) + self.session.delete(['policy', 'access-list', acl_out]) + self.session.delete(['policy', 'prefix-list', prefix_list_in]) + self.session.delete(['policy', 'prefix-list', prefix_list_out]) + self.session.delete(['policy', 'route-map', route_map]) + + self.session.commit() + del self.session + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_rip(self): + distance = '40' + network_distance = '66' + metric = '8' + interfaces = Section.interfaces('ethernet') + neighbors = ['1.2.3.4', '1.2.3.5', '1.2.3.6'] + networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] + redistribute = ['bgp', 'connected', 'isis', 'kernel', 'ospf', 'static'] + timer_garbage = '888' + timer_timeout = '1000' + timer_update = '90' + + self.session.set(base_path + ['default-distance', distance]) + self.session.set(base_path + ['default-information', 'originate']) + self.session.set(base_path + ['default-metric', metric]) + self.session.set(base_path + ['distribute-list', 'access-list', 'in', acl_in]) + self.session.set(base_path + ['distribute-list', 'access-list', 'out', acl_out]) + self.session.set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in]) + self.session.set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out]) + self.session.set(base_path + ['passive-interface', 'default']) + self.session.set(base_path + ['timers', 'garbage-collection', timer_garbage]) + self.session.set(base_path + ['timers', 'timeout', timer_timeout]) + self.session.set(base_path + ['timers', 'update', timer_update]) + for interface in interfaces: + self.session.set(base_path + ['interface', interface]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out]) + for neighbor in neighbors: + self.session.set(base_path + ['neighbor', neighbor]) + for network in networks: + self.session.set(base_path + ['network', network]) + self.session.set(base_path + ['network-distance', network, 'distance', network_distance]) + self.session.set(base_path + ['route', network]) + for proto in redistribute: + self.session.set(base_path + ['redistribute', proto, 'metric', metric]) + self.session.set(base_path + ['redistribute', proto, 'route-map', route_map]) + + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRRconfig() + self.assertIn(f'router rip', frrconfig) + self.assertIn(f' distance {distance}', frrconfig) + self.assertIn(f' default-information originate', frrconfig) + self.assertIn(f' default-metric {metric}', frrconfig) + self.assertIn(f' distribute-list {acl_in} in', frrconfig) + self.assertIn(f' distribute-list {acl_out} out', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_in} in', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_out} out', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig) + for interface in interfaces: + self.assertIn(f' network {interface}', frrconfig) + self.assertIn(f' distribute-list {acl_in} in {interface}', frrconfig) + self.assertIn(f' distribute-list {acl_out} out {interface}', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_in} in {interface}', frrconfig) + self.assertIn(f' distribute-list prefix {prefix_list_out} out {interface}', frrconfig) + for neighbor in neighbors: + self.assertIn(f' neighbor {neighbor}', frrconfig) + for network in networks: + self.assertIn(f' network {network}', frrconfig) + self.assertIn(f' distance {network_distance} {network}', frrconfig) + self.assertIn(f' route {network}', frrconfig) + for proto in redistribute: + self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py new file mode 100755 index 000000000..6850b60d3 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ripng.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import cmd +from vyos.util import process_named_running + +PROCESS_NAME = 'ripngd' +acl_in = '198' +acl_out = '199' +prefix_list_in = 'foo-prefix' +prefix_list_out = 'bar-prefix' +route_map = 'FooBar123' + +base_path = ['protocols', 'ripng'] + +def getFRRconfig(): + return cmd('vtysh -c "show run" | sed -n "/router ripng/,/^!/p"') + +class TestProtocolsRIPng(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + self.session.set(['policy', 'access-list6', acl_in, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'access-list6', acl_in, 'rule', '10', 'source', 'any']) + self.session.set(['policy', 'access-list6', acl_out, 'rule', '20', 'action', 'deny']) + self.session.set(['policy', 'access-list6', acl_out, 'rule', '20', 'source', 'any']) + self.session.set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'action', 'permit']) + self.session.set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'prefix', '2001:db8::/32']) + self.session.set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'action', 'deny']) + self.session.set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'prefix', '2001:db8::/32']) + self.session.set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + + def tearDown(self): + self.session.delete(base_path) + self.session.delete(['policy', 'access-list6', acl_in]) + self.session.delete(['policy', 'access-list6', acl_out]) + self.session.delete(['policy', 'prefix-list6', prefix_list_in]) + self.session.delete(['policy', 'prefix-list6', prefix_list_out]) + self.session.delete(['policy', 'route-map', route_map]) + + self.session.commit() + del self.session + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_ripng(self): + metric = '8' + interfaces = Section.interfaces('ethernet') + aggregates = ['2001:db8:1000::/48', '2001:db8:2000::/48', '2001:db8:3000::/48'] + networks = ['2001:db8:1000::/64', '2001:db8:1001::/64', '2001:db8:2000::/64', '2001:db8:2001::/64'] + redistribute = ['bgp', 'connected', 'kernel', 'ospfv3', 'static'] + timer_garbage = '888' + timer_timeout = '1000' + timer_update = '90' + + self.session.set(base_path + ['default-information', 'originate']) + self.session.set(base_path + ['default-metric', metric]) + self.session.set(base_path + ['distribute-list', 'access-list', 'in', acl_in]) + self.session.set(base_path + ['distribute-list', 'access-list', 'out', acl_out]) + self.session.set(base_path + ['distribute-list', 'prefix-list', 'in', prefix_list_in]) + self.session.set(base_path + ['distribute-list', 'prefix-list', 'out', prefix_list_out]) + self.session.set(base_path + ['passive-interface', 'default']) + self.session.set(base_path + ['timers', 'garbage-collection', timer_garbage]) + self.session.set(base_path + ['timers', 'timeout', timer_timeout]) + self.session.set(base_path + ['timers', 'update', timer_update]) + for aggregate in aggregates: + self.session.set(base_path + ['aggregate-address', aggregate]) + + for interface in interfaces: + self.session.set(base_path + ['interface', interface]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'in', acl_in]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'access-list', 'out', acl_out]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'in', prefix_list_in]) + self.session.set(base_path + ['distribute-list', 'interface', interface, 'prefix-list', 'out', prefix_list_out]) + for network in networks: + self.session.set(base_path + ['network', network]) + self.session.set(base_path + ['route', network]) + for proto in redistribute: + self.session.set(base_path + ['redistribute', proto, 'metric', metric]) + self.session.set(base_path + ['redistribute', proto, 'route-map', route_map]) + + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRRconfig() + self.assertIn(f'router ripng', frrconfig) + self.assertIn(f' default-information originate', frrconfig) + self.assertIn(f' default-metric {metric}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_out} out', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + self.assertIn(f' timers basic {timer_update} {timer_timeout} {timer_garbage}', frrconfig) + for aggregate in aggregates: + self.assertIn(f' aggregate-address {aggregate}', frrconfig) + for interface in interfaces: + self.assertIn(f' network {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_in} in {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list {acl_out} out {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_in} in {interface}', frrconfig) + self.assertIn(f' ipv6 distribute-list prefix {prefix_list_out} out {interface}', frrconfig) + for network in networks: + self.assertIn(f' network {network}', frrconfig) + self.assertIn(f' route {network}', frrconfig) + for proto in redistribute: + if proto == 'ospfv3': + proto = 'ospf6' + self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py new file mode 100755 index 000000000..bec4ef76f --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_rpki.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import process_named_running + +base_path = ['protocols', 'rpki'] +PROCESS_NAME = 'bgpd' + +rpki_known_hosts = '/config/auth/known_hosts' +rpki_ssh_key = '/config/auth/id_rsa_rpki' +rpki_ssh_pub = f'{rpki_ssh_key}.pub' + +def getFRRRPKIconfig(): + return cmd(f'vtysh -c "show run" | sed -n "/rpki/,/^!/p"') + +class TestProtocolsRPKI(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + self.session.delete(base_path) + self.session.commit() + del self.session + + # Nothing RPKI specific should be left over in the config + # + # Disabled until T3266 is resolved + # frrconfig = getFRRRPKIconfig() + # self.assertNotIn('rpki', frrconfig) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + def test_rpki(self): + polling = '7200' + cache = { + '192.0.2.1' : { + 'port' : '8080', + 'preference' : '1' + }, + '192.0.2.2' : { + 'port' : '9090', + 'preference' : '2' + }, + '2001:db8::1' : { + 'port' : '1234', + 'preference' : '3' + }, + '2001:db8::2' : { + 'port' : '5678', + 'preference' : '4' + }, + } + + self.session.set(base_path + ['polling-period', polling]) + for peer, peer_config in cache.items(): + self.session.set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.session.set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + + # commit changes + self.session.commit() + + # Verify FRR configuration + frrconfig = getFRRRPKIconfig() + self.assertIn(f'rpki polling_period {polling}', frrconfig) + + for peer, peer_config in cache.items(): + port = peer_config['port'] + preference = peer_config['preference'] + self.assertIn(f'rpki cache {peer} {port} preference {preference}', frrconfig) + + def test_rpki_ssh(self): + polling = '7200' + cache = { + '192.0.2.3' : { + 'port' : '1234', + 'username' : 'foo', + 'preference' : '10' + }, + '192.0.2.4' : { + 'port' : '5678', + 'username' : 'bar', + 'preference' : '20' + }, + } + + self.session.set(base_path + ['polling-period', polling]) + + for peer, peer_config in cache.items(): + self.session.set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.session.set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + self.session.set(base_path + ['cache', peer, 'ssh', 'username', peer_config['username']]) + self.session.set(base_path + ['cache', peer, 'ssh', 'public-key-file', rpki_ssh_pub]) + self.session.set(base_path + ['cache', peer, 'ssh', 'private-key-file', rpki_ssh_key]) + self.session.set(base_path + ['cache', peer, 'ssh', 'known-hosts-file', rpki_known_hosts]) + + # commit changes + self.session.commit() + + # Verify FRR configuration + frrconfig = getFRRRPKIconfig() + self.assertIn(f'rpki polling_period {polling}', frrconfig) + + for peer, peer_config in cache.items(): + port = peer_config['port'] + preference = peer_config['preference'] + username = peer_config['username'] + self.assertIn(f'rpki cache {peer} {port} {username} {rpki_ssh_key} {rpki_known_hosts} preference {preference}', frrconfig) + + + def test_rpki_verify_preference(self): + cache = { + '192.0.2.1' : { + 'port' : '8080', + 'preference' : '1' + }, + '192.0.2.2' : { + 'port' : '9090', + 'preference' : '1' + }, + } + + for peer, peer_config in cache.items(): + self.session.set(base_path + ['cache', peer, 'port', peer_config['port']]) + self.session.set(base_path + ['cache', peer, 'preference', peer_config['preference']]) + + # check validate() - preferences must be unique + with self.assertRaises(ConfigSessionError): + self.session.commit() + + +if __name__ == '__main__': + # Create OpenSSH keypair used in RPKI tests + if not os.path.isfile(rpki_ssh_key): + cmd(f'ssh-keygen -t rsa -f {rpki_ssh_key} -N ""') + + if not os.path.isfile(rpki_known_hosts): + cmd(f'touch {rpki_known_hosts}') + + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py new file mode 100755 index 000000000..cf591f060 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_static.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.template import is_ipv6 +from vyos.util import cmd + +base_path = ['protocols', 'static'] +vrf_path = ['protocols', 'vrf'] + +def getFRRCconfig(vrf=None): + if vrf: + return cmd(f'vtysh -c "show run" | sed -n "/^vrf {vrf}/,/^!/p"') + else: + return cmd(f'vtysh -c "show run" | sed -n "/^ip route/,/^!/p"') + +routes = { + '10.0.0.0/8' : { + 'next_hop' : { + '192.0.2.100' : { 'distance' : '100' }, + '192.0.2.110' : { 'distance' : '110', 'interface' : 'eth0' }, + '192.0.2.120' : { 'distance' : '120', 'disable' : '' }, + }, + 'interface' : { + 'eth0' : { 'distance' : '130' }, + 'eth1' : { 'distance' : '140' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '172.16.0.0/12' : { + 'interface' : { + 'eth0' : { 'distance' : '50', 'vrf' : 'black' }, + 'eth1' : { 'distance' : '60', 'vrf' : 'black' }, + }, + 'blackhole' : { 'distance' : '90' }, + }, + '192.0.2.0/24' : { + 'interface' : { + 'eth0' : { 'distance' : '50', 'vrf' : 'black' }, + 'eth1' : { 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '90' }, + }, + '100.64.0.0/10' : { + 'blackhole' : { }, + }, + '2001:db8:100::/40' : { + 'next_hop' : { + '2001:db8::1' : { 'distance' : '10' }, + '2001:db8::2' : { 'distance' : '20', 'interface' : 'eth0' }, + '2001:db8::3' : { 'distance' : '30', 'disable' : '' }, + }, + 'interface' : { + 'eth0' : { 'distance' : '40', 'vrf' : 'black' }, + 'eth1' : { 'distance' : '50', 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '2001:db8:200::/40' : { + 'interface' : { + 'eth0' : { 'distance' : '40' }, + 'eth1' : { 'distance' : '50', 'disable' : '' }, + }, + 'blackhole' : { 'distance' : '250', 'tag' : '500' }, + }, + '2001:db8::/32' : { + 'blackhole' : { 'distance' : '200', 'tag' : '600' }, + }, +} + +vrfs = ['red', 'green', 'blue'] +tables = ['80', '81', '82'] + +class StaticRouteTest(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + self.session.delete(base_path + [route_type, route]) + + for vrf in vrfs: + self.session.delete(vrf_path + [vrf]) + + for table in tables: + self.session.delete(base_path + ['table', table]) + + self.session.commit() + del self.session + + def test_protocols_static(self): + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + base = base_path + [route_type, route] + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.session.set(base + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.session.set(base + ['interface', interface]) + if 'disable' in interface_config: + self.session.set(base + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.session.set(base + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.session.set(base + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.session.set(base + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + frrconfig = getFRRCconfig() + + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + self.assertIn(tmp, frrconfig) + + def test_protocols_static_table(self): + for table in tables: + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + base = base_path + ['table', table, route_type, route] + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.session.set(base + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.session.set(base + ['interface', interface]) + if 'disable' in interface_config: + self.session.set(base + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.session.set(base + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.session.set(base + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.session.set(base + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + frrconfig = getFRRCconfig() + + for table in tables: + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + tmp += ' table ' + table + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + tmp += ' table ' + table + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + tmp += ' table ' + table + self.assertIn(tmp, frrconfig) + + + def test_protocols_vrf_static(self): + for vrf in vrfs: + for route, route_config in routes.items(): + route_type = 'route' + if is_ipv6(route): + route_type = 'route6' + base = vrf_path + [vrf, 'static', route_type, route] + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + self.session.set(base + ['next-hop', next_hop]) + if 'disable' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'disable']) + if 'distance' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'distance', next_hop_config['distance']]) + if 'interface' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'interface', next_hop_config['interface']]) + if 'vrf' in next_hop_config: + self.session.set(base + ['next-hop', next_hop, 'vrf', next_hop_config['vrf']]) + + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + self.session.set(base + ['interface', interface]) + if 'disable' in interface_config: + self.session.set(base + ['interface', interface, 'disable']) + if 'distance' in interface_config: + self.session.set(base + ['interface', interface, 'distance', interface_config['distance']]) + if 'vrf' in interface_config: + self.session.set(base + ['interface', interface, 'vrf', interface_config['vrf']]) + + if 'blackhole' in route_config: + self.session.set(base + ['blackhole']) + if 'distance' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'distance', route_config['blackhole']['distance']]) + if 'tag' in route_config['blackhole']: + self.session.set(base + ['blackhole', 'tag', route_config['blackhole']['tag']]) + + # commit changes + self.session.commit() + + for vrf in vrfs: + # Verify FRR bgpd configuration + frrconfig = getFRRCconfig(vrf) + self.assertIn(f'vrf {vrf}', frrconfig) + + # Verify routes + for route, route_config in routes.items(): + ip_ipv6 = 'ip' + if is_ipv6(route): + ip_ipv6 = 'ipv6' + + if 'next_hop' in route_config: + for next_hop, next_hop_config in route_config['next_hop'].items(): + tmp = f'{ip_ipv6} route {route} {next_hop}' + if 'interface' in next_hop_config: + tmp += ' ' + next_hop_config['interface'] + if 'distance' in next_hop_config: + tmp += ' ' + next_hop_config['distance'] + if 'vrf' in next_hop_config: + tmp += ' nexthop-vrf ' + next_hop_config['vrf'] + + if 'disable' in next_hop_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'interface' in route_config: + for interface, interface_config in route_config['interface'].items(): + tmp = f'{ip_ipv6} route {route} {interface}' + if 'interface' in interface_config: + tmp += ' ' + interface_config['interface'] + if 'distance' in interface_config: + tmp += ' ' + interface_config['distance'] + if 'vrf' in interface_config: + tmp += ' nexthop-vrf ' + interface_config['vrf'] + + if 'disable' in interface_config: + self.assertNotIn(tmp, frrconfig) + else: + self.assertIn(tmp, frrconfig) + + if 'blackhole' in route_config: + tmp = f'{ip_ipv6} route {route} blackhole' + if 'tag' in route_config['blackhole']: + tmp += ' tag ' + route_config['blackhole']['tag'] + if 'distance' in route_config['blackhole']: + tmp += ' ' + route_config['blackhole']['distance'] + + self.assertIn(tmp, frrconfig) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_bcast-relay.py b/smoketest/scripts/cli/test_service_bcast-relay.py index c28509714..00d7750aa 100755 --- a/smoketest/scripts/cli/test_service_bcast-relay.py +++ b/smoketest/scripts/cli/test_service_bcast-relay.py @@ -30,7 +30,6 @@ class TestServiceBroadcastRelay(unittest.TestCase): self.session = ConfigSession(os.getpid()) self.session.set(['interfaces', 'dummy', 'dum1001', 'address', self._address1]) self.session.set(['interfaces', 'dummy', 'dum1002', 'address', self._address2]) - self.session.commit() def tearDown(self): self.session.delete(['interfaces', 'dummy', 'dum1001']) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 5270e758a..8e977d407 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -16,19 +16,44 @@ import re import os +import json import unittest + from netifaces import interfaces from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Interface +from vyos.ifconfig import Section +from vyos.template import is_ipv6 +from vyos.util import cmd from vyos.util import read_file from vyos.validate import is_intf_addr_assigned -from vyos.ifconfig import Interface base_path = ['vrf'] vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] +def get_vrf_ipv4_routes(vrf): + return json.loads(cmd(f'ip -4 -j route show vrf {vrf}')) + +def get_vrf_ipv6_routes(vrf): + return json.loads(cmd(f'ip -6 -j route show vrf {vrf}')) + class VRFTest(unittest.TestCase): + _interfaces = [] + + @classmethod + def setUpClass(cls): + # we need to filter out VLAN interfaces identified by a dot (.) + # in their name - just in case! + if 'TEST_ETH' in os.environ: + tmp = os.environ['TEST_ETH'].split() + cls._interfaces = tmp + else: + for tmp in Section.interfaces('ethernet'): + if not '.' in tmp: + cls._interfaces.append(tmp) + def setUp(self): self.session = ConfigSession(os.getpid()) @@ -120,5 +145,26 @@ class VRFTest(unittest.TestCase): with self.assertRaises(ConfigSessionError): self.session.commit() + def test_vrf_assign_interface(self): + vrf = vrfs[0] + table = '5000' + self.session.set(['vrf', 'name', vrf, 'table', table]) + + for interface in self._interfaces: + section = Section.section(interface) + self.session.set(['interfaces', section, interface, 'vrf', vrf]) + + # commit changes + self.session.commit() + + # Verify & cleanup + for interface in self._interfaces: + # os.readlink resolves to: '../../../../../virtual/net/foovrf' + tmp = os.readlink(f'/sys/class/net/{interface}/master').split('/')[-1] + self.assertEqual(tmp, vrf) + # cleanup + section = Section.section(interface) + self.session.delete(['interfaces', section, interface, 'vrf']) + if __name__ == '__main__': - unittest.main(verbosity=2, failfast=True) + unittest.main(verbosity=2) |