From a234f616b9fff24b74f2b33dc83cf7c7edef3342 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 21 Mar 2021 13:33:06 +0100 Subject: isis: T3417: add VRF support VRF support can be tested using: set vrf name red table 1000 set vrf name red protocols isis domain FOOO set vrf name red protocols isis net 49.0001.1921.6800.1002.00 set vrf name red protocols isis interface eth1 --- data/templates/frr/isis.frr.tmpl | 4 +- .../include/isis/isis-common-config.xml.i | 764 +++++++++++++++++++++ interface-definitions/protocols-isis.xml.in | 763 +------------------- interface-definitions/vrf.xml.in | 29 +- smoketest/scripts/cli/test_protocols_isis.py | 40 +- src/conf_mode/protocols_isis.py | 72 +- 6 files changed, 887 insertions(+), 785 deletions(-) create mode 100644 interface-definitions/include/isis/isis-common-config.xml.i diff --git a/data/templates/frr/isis.frr.tmpl b/data/templates/frr/isis.frr.tmpl index d4500403c..da098abac 100644 --- a/data/templates/frr/isis.frr.tmpl +++ b/data/templates/frr/isis.frr.tmpl @@ -1,5 +1,5 @@ ! -router isis {{ domain }} +router isis {{ domain }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} net {{ net }} {% if dynamic_hostname is defined %} hostname dynamic @@ -133,7 +133,7 @@ router isis {{ domain }} ! {% if interface is defined and interface is not none %} {% for iface, iface_config in interface.items() %} -interface {{ iface }} +interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }} ip router isis {{ domain }} {% if iface_config.bfd is defined %} isis bfd diff --git a/interface-definitions/include/isis/isis-common-config.xml.i b/interface-definitions/include/isis/isis-common-config.xml.i new file mode 100644 index 000000000..94a6e3592 --- /dev/null +++ b/interface-definitions/include/isis/isis-common-config.xml.i @@ -0,0 +1,764 @@ + + + + Configure the authentication password for an area + + + + + Plain-text authentication type + + txt + Level-wide password + + + + + + MD5 authentication type + + txt + Level-wide password + + + + + + + + Control distribution of default information + + + + + Distribute a default route + + + + + Distribute default route for IPv4 + + + + + Distribute default route into level-1 + + + + + + Distribute default route into level-2 + + + + + + + + Distribute default route for IPv6 + + + + + Distribute default route into level-1 + + always + + + always + Always advertise default route + + + + + + Distribute default route into level-2 + + always + + + always + Always advertise default route + + + + + + + + + + + + Domain name used for this instance + + txt + IS-IS routing area domain/tag + + + + + + Set the authentication password for a routing domain + + + + + Plain-text authentication type + + txt + Level-wide password + + + + + + + + + Dynamic hostname for IS-IS + + + + + + IS-IS level number + + level-1 level-1-2 level-2 + + + level-1 + Act as a station router + + + level-1-2 + Act as both a station and an area router + + + level-2 + Act as an area router + + + (level-1|level-1-2|level-2) + + + + + + Minimum interval between regenerating same LSP + + u32:1-120 + Minimum interval in seconds + + + + + + + + + Configure the maximum size of generated LSPs + + u32:128-4352 + Maximum size of generated LSPs + + + + + + + + + LSP refresh interval + + u32:1-65235 + LSP refresh interval in seconds + + + + + + + + + Maximum LSP lifetime + + u32:350-65535 + LSP lifetime in seconds + + + + + + + + + Use old-style (ISO 10589) or new-style packet formats + + narrow transition wide + + + narrow + Use old style of TLVs with narrow metric + + + transition + Send and accept both styles of TLVs during transition + + + wide + Use new style of TLVs to carry wider metric + + + (narrow|transition|wide) + + + + + + A Network Entity Title for this process (ISO only) + + XX.XXXX. ... .XXX.XX + Network entity title (NET) + + + [a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2} + + + + + + Use the RFC 6232 purge-originator + + + + + + Show IS-IS neighbor adjacencies + + + + + Enable MPLS traffic engineering extensions + + + + + + + MPLS traffic engineering router ID + + ipv4 + IPv4 address + + + + + + + + + + + Segment-Routing (SPRING) settings + + + + + Enable segment-routing functionality + + + + + + Global block label range + + + + + The lower bound of the global block + + u32:16-1048575 + MPLS label value + + + + + + + + + The upper bound of the global block + + u32:16-1048575 + MPLS label value + + + + + + + + + + + + Maximum MPLS labels allowed for this router + + u32:1-16 + MPLS label depth + + + + + + + + + Static IPv4/IPv6 prefix segment/label mapping + + ipv4net + IPv4 prefix segment + + + ipv6net + IPv6 prefix segment + + + + + + + + + + Specify the absolute value of prefix segment/label ID + + + + + Specify the absolute value of prefix segment/label ID + + u32:16-1048575 + The absolute segment/label ID value + + + + + + + + + Request upstream neighbor to replace segment/label with explicit null label + + + + + + Do not request penultimate hop popping for segment/label + + + + + + + + Specify the index value of prefix segment/label ID + + + + + Specify the index value of prefix segment/label ID + + u32:0-65535 + The index segment/label ID value + + + + + + + + + Request upstream neighbor to replace segment/label with explicit null label + + + + + + Do not request penultimate hop popping for segment/label + + + + + + + + + + + + Redistribute information from another routing protocol + + + + + Redistribute IPv4 routes + + + + + Border Gateway Protocol (BGP) + + + #include + + + + + Redistribute connected routes into IS-IS + + + #include + + + + + Redistribute kernel routes into IS-IS + + + #include + + + + + Redistribute OSPF routes into IS-IS + + + #include + + + + + Redistribute RIP routes into IS-IS + + + #include + + + + + Redistribute static routes into IS-IS + + + #include + + + + + + + + + Set attached bit to identify as L1/L2 router for inter-area traffic + + + + + + Set overload bit to avoid any transit traffic + + + + + + IETF SPF delay algorithm + + + + + Delay used while in QUIET state + + u32:0-60000 + Delay used while in QUIET state (in ms) + + + + + + + + + Delay used while in SHORT_WAIT state + + u32:0-60000 + Delay used while in SHORT_WAIT state (in ms) + + + + + + + + + Delay used while in LONG_WAIT + + u32:0-60000 + Delay used while in LONG_WAIT state (in ms) + + + + + + + + + Time with no received IGP events before considering IGP stable + + u32:0-60000 + Time with no received IGP events before considering IGP stable (in ms) + + + + + + + + + Maximum duration needed to learn all the events related to a single failure + + u32:0-60000 + Maximum duration needed to learn all the events related to a single failure (in ms) + + + + + + + + + + + Minimum interval between SPF calculations + + u32:1-120 + Minimum interval between consecutive SPFs in seconds + + + + + + + + + + Interface params + + + + + + #include + + + Configure circuit type for interface + + level-1 level-1-2 level-2-only + + + level-1 + Level-1 only adjacencies are formed + + + level-1-2 + Level-1-2 adjacencies are formed + + + level-2-only + Level-2 only adjacencies are formed + + + (level-1|level-1-2|level-2-only) + + + + + + Add padding to IS-IS hello packets + + + + + + Set Hello interval + + u32:1-600 + Set Hello interval + + + + + + + + + Set Hello interval + + u32:2-100 + Set multiplier for Hello holding time + + + + + + + + + Set default metric for circuit + + u32:0-16777215 + Default metric value + + + + + + + + + Set network type + + + + + point-to-point network type + + + + + + #include + + + Configure the authentication password for a circuit + + + + + Plain-text authentication type + + txt + Circuit password + + + + + + + + Set priority for Designated Router election + + u32:0-127 + Priority value + + + + + + + + + Set PSNP interval in seconds + + u32:0-127 + Priority value + + + + + + + + + Disable three-way handshake + + + + + + diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in index e6982aca3..1bc890446 100644 --- a/interface-definitions/protocols-isis.xml.in +++ b/interface-definitions/protocols-isis.xml.in @@ -8,768 +8,7 @@ 610 - - - Configure the authentication password for an area - - - - - Plain-text authentication type - - txt - Level-wide password - - - - - - MD5 authentication type - - txt - Level-wide password - - - - - - - - Control distribution of default information - - - - - Distribute a default route - - - - - Distribute default route for IPv4 - - - - - Distribute default route into level-1 - - - - - - Distribute default route into level-2 - - - - - - - - Distribute default route for IPv6 - - - - - Distribute default route into level-1 - - always - - - always - Always advertise default route - - - - - - Distribute default route into level-2 - - always - - - always - Always advertise default route - - - - - - - - - - - - Domain name used for this instance - - txt - IS-IS routing area domain/tag - - - - - - Set the authentication password for a routing domain - - - - - Plain-text authentication type - - txt - Level-wide password - - - - - - - - - Dynamic hostname for IS-IS - - - - - - IS-IS level number - - level-1 level-1-2 level-2 - - - level-1 - Act as a station router - - - level-1-2 - Act as both a station and an area router - - - level-2 - Act as an area router - - - (level-1|level-1-2|level-2) - - - - - - Minimum interval between regenerating same LSP - - u32:1-120 - Minimum interval in seconds - - - - - - - - - Configure the maximum size of generated LSPs - - u32:128-4352 - Maximum size of generated LSPs - - - - - - - - - LSP refresh interval - - u32:1-65235 - LSP refresh interval in seconds - - - - - - - - - Maximum LSP lifetime - - u32:350-65535 - LSP lifetime in seconds - - - - - - - - - Use old-style (ISO 10589) or new-style packet formats - - narrow transition wide - - - narrow - Use old style of TLVs with narrow metric - - - transition - Send and accept both styles of TLVs during transition - - - wide - Use new style of TLVs to carry wider metric - - - (narrow|transition|wide) - - - - - - A Network Entity Title for this process (ISO only) - - XX.XXXX. ... .XXX.XX - Network entity title (NET) - - - [a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2} - - - - - - Use the RFC 6232 purge-originator - - - - - - Show IS-IS neighbor adjacencies - - - - - Enable MPLS traffic engineering extensions - - - - - - - MPLS traffic engineering router ID - - ipv4 - IPv4 address - - - - - - - - - - - Segment-Routing (SPRING) settings - - - - - Enable segment-routing functionality - - - - - - Global block label range - - - - - The lower bound of the global block - - u32:16-1048575 - MPLS label value - - - - - - - - - The upper bound of the global block - - u32:16-1048575 - MPLS label value - - - - - - - - - - - - Maximum MPLS labels allowed for this router - - u32:1-16 - MPLS label depth - - - - - - - - - Static IPv4/IPv6 prefix segment/label mapping - - ipv4net - IPv4 prefix segment - - - ipv6net - IPv6 prefix segment - - - - - - - - - - Specify the absolute value of prefix segment/label ID - - - - - Specify the absolute value of prefix segment/label ID - - u32:16-1048575 - The absolute segment/label ID value - - - - - - - - - Request upstream neighbor to replace segment/label with explicit null label - - - - - - Do not request penultimate hop popping for segment/label - - - - - - - - Specify the index value of prefix segment/label ID - - - - - Specify the index value of prefix segment/label ID - - u32:0-65535 - The index segment/label ID value - - - - - - - - - Request upstream neighbor to replace segment/label with explicit null label - - - - - - Do not request penultimate hop popping for segment/label - - - - - - - - - - - - Redistribute information from another routing protocol - - - - - Redistribute IPv4 routes - - - - - Border Gateway Protocol (BGP) - - - #include - - - - - Redistribute connected routes into IS-IS - - - #include - - - - - Redistribute kernel routes into IS-IS - - - #include - - - - - Redistribute OSPF routes into IS-IS - - - #include - - - - - Redistribute RIP routes into IS-IS - - - #include - - - - - Redistribute static routes into IS-IS - - - #include - - - - - - - - - Set attached bit to identify as L1/L2 router for inter-area traffic - - - - - - Set overload bit to avoid any transit traffic - - - - - - IETF SPF delay algorithm - - - - - Delay used while in QUIET state - - u32:0-60000 - Delay used while in QUIET state (in ms) - - - - - - - - - Delay used while in SHORT_WAIT state - - u32:0-60000 - Delay used while in SHORT_WAIT state (in ms) - - - - - - - - - Delay used while in LONG_WAIT - - u32:0-60000 - Delay used while in LONG_WAIT state (in ms) - - - - - - - - - Time with no received IGP events before considering IGP stable - - u32:0-60000 - Time with no received IGP events before considering IGP stable (in ms) - - - - - - - - - Maximum duration needed to learn all the events related to a single failure - - u32:0-60000 - Maximum duration needed to learn all the events related to a single failure (in ms) - - - - - - - - - - - Minimum interval between SPF calculations - - u32:1-120 - Minimum interval between consecutive SPFs in seconds - - - - - - - - - - Interface params - - - - - - #include - - - Configure circuit type for interface - - level-1 level-1-2 level-2-only - - - level-1 - Level-1 only adjacencies are formed - - - level-1-2 - Level-1-2 adjacencies are formed - - - level-2-only - Level-2 only adjacencies are formed - - - (level-1|level-1-2|level-2-only) - - - - - - Add padding to IS-IS hello packets - - - - - - Set Hello interval - - u32:1-600 - Set Hello interval - - - - - - - - - Set Hello interval - - u32:2-100 - Set multiplier for Hello holding time - - - - - - - - - Set default metric for circuit - - u32:0-16777215 - Default metric value - - - - - - - - - Set network type - - - - - point-to-point network type - - - - - - #include - - - Configure the authentication password for a circuit - - - - - Plain-text authentication type - - txt - Circuit password - - - - - - - - Set priority for Designated Router election - - u32:0-127 - Priority value - - - - - - - - - Set PSNP interval in seconds - - u32:0-127 - Priority value - - - - - - - - - Disable three-way handshake - - - - - + #include diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index de605e89b..95428eb94 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -33,16 +33,6 @@ Routing protocol parameters - - - Static route parameters - 481 - - - #include - #include - - Border Gateway Protocol (BGP) @@ -59,6 +49,15 @@ #include + + + Intermediate System to Intermediate System (IS-IS) + 611 + + + #include + + Open Shortest Path First (OSPF) @@ -68,6 +67,16 @@ #include + + + Static route parameters + 481 + + + #include + #include + + diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index 765e0ef4e..79d497186 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -36,7 +36,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) - def test_isis_redistribute(self): + def test_isis_01_redistribute(self): prefix_list = 'EXPORT-ISIS' route_map = 'EXPORT-ISIS' rule = '10' @@ -57,7 +57,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify all changes - tmp = self.getFRRconfig(f'router isis {domain}', end='') + # XXX: FRR represents router isis with a trailing whitespace :/ + tmp = self.getFRRconfig(f'router isis {domain} ') self.assertIn(f' net {net}', tmp) self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp) @@ -67,5 +68,40 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_delete(['policy']) + + def test_isis_02_vrfs(self): + vrfs = ['red', 'green', 'blue'] + # It is safe to assume that when the basic VRF test works, all other + # IS-IS related features work, as we entirely inherit the CLI templates + # and Jinja2 FRR template. + table = '1000' + vrf = 'red' + vrf_base = ['vrf', 'name', vrf] + vrf_iface = 'eth1' + self.cli_set(vrf_base + ['table', table]) + self.cli_set(vrf_base + ['protocols', 'isis', 'domain', domain]) + self.cli_set(vrf_base + ['protocols', 'isis', 'net', net]) + self.cli_set(vrf_base + ['protocols', 'isis', 'interface', vrf_iface]) + self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf]) + + # Also set a default VRF IS-IS config + self.cli_set(base_path + ['domain', domain]) + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['interface', 'eth0']) + self.cli_commit() + + # Verify FRR isisd configuration + # XXX: FRR represents router isis with a trailing whitespace :/ + tmp = self.getFRRconfig(f'router isis {domain} ') + self.assertIn(f'router isis {domain}', tmp) + self.assertIn(f' net {net}', tmp) + + tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}') + self.assertIn(f'router isis {domain} vrf {vrf}', tmp) + self.assertIn(f' net {net}', tmp) + + self.cli_delete(['vrf', 'name', vrf]) + self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index 24db52567..8acff58cc 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -17,30 +17,61 @@ import os from sys import exit +from sys import argv from vyos.config import Config from vyos.configdict import node_changed -from vyos import ConfigError from vyos.util import call from vyos.util import dict_search +from vyos.util import get_interface_config from vyos.template import render_to_string +from vyos import ConfigError from vyos import frr from vyos import airbag airbag.enable() +frr_daemon = 'isisd' + def get_config(config=None): if config: conf = config else: conf = Config() - base = ['protocols', 'isis'] + + vrf = None + if len(argv) > 1: + vrf = argv[1] + + base_path = ['protocols', 'isis'] + + # eqivalent of the C foo ? 'a' : 'b' statement + base = vrf and ['vrf', 'name', vrf, 'protocols', 'isis'] or base_path isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Assign the name of our VRF context. This MUST be done before the return + # statement below, else on deletion we will delete the default instance + # instead of the VRF instance. + if vrf: isis['vrf'] = vrf + + # As we no re-use this Python handler for both VRF and non VRF instances for + # IS-IS we need to find out if any interfaces changed so properly adjust + # the FRR configuration and not by acctident change interfaces from a + # different VRF. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + isis['interface_removed'] = list(interfaces_removed) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + isis.update({'deleted' : ''}) + return isis + return isis def verify(isis): # bail out early - looks like removal from running config - if not isis: + if not isis or 'deleted' in isis: return None if 'domain' not in isis: @@ -53,6 +84,17 @@ def verify(isis): if 'interface' not in isis: raise ConfigError('Interface used for routing updates is mandatory!') + for interface in isis['interface']: + if 'vrf' in isis: + # If interface specific options are set, we must ensure that the + # interface is bound to our requesting VRF. Due to the VyOS + # priorities the interface is bound to the VRF after creation of + # the VRF itself, and before any routing protocol is configured. + vrf = isis['vrf'] + tmp = get_interface_config(interface) + if 'master' not in tmp or tmp['master'] != vrf: + raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!') + # If md5 and plaintext-password set at the same time if 'area_password' in isis: if {'md5', 'plaintext_password'} <= set(isis['encryption']): @@ -111,7 +153,7 @@ def verify(isis): return None def generate(isis): - if not isis: + if not isis or 'deleted' in isis: isis['new_frr_config'] = '' return None @@ -121,17 +163,29 @@ def generate(isis): def apply(isis): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(daemon='isisd') - frr_cfg.modify_section('^interface \S+$', '') - frr_cfg.modify_section('^router isis \S+$', '') + frr_cfg.load_configuration(frr_daemon) + + # Generate empty helper string which can be ammended to FRR commands, + # it will be either empty (default VRF) or contain the "vrf