summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2021-03-21 11:35:21 +0100
committerChristian Poessinger <christian@poessinger.com>2021-03-21 11:35:23 +0100
commit3cb84afe3a6b9f22a35e0a887f7ca350644d6c8a (patch)
tree15d57b133aab018e4d0bf81d2af0ac07f5b13ea7
parentb666f8ba97dc792aac90fad4c4c99e47caca7baf (diff)
downloadvyos-1x-3cb84afe3a6b9f22a35e0a887f7ca350644d6c8a.tar.gz
vyos-1x-3cb84afe3a6b9f22a35e0a887f7ca350644d6c8a.zip
isis: T3417: move from cli tagNode to node
As there can only be one running IS-IS process (FRR limitation) there is no need in having a tagNode here. This adds artifical restrictions/limitations when moving on to support VRFs for IS-IS protocol.
-rw-r--r--data/templates/frr/isis.frr.tmpl4
-rw-r--r--interface-definitions/protocols-isis.xml.in17
-rwxr-xr-xsrc/conf_mode/protocols_isis.py162
-rwxr-xr-xsrc/migration-scripts/isis/0-to-160
4 files changed, 143 insertions, 100 deletions
diff --git a/data/templates/frr/isis.frr.tmpl b/data/templates/frr/isis.frr.tmpl
index 4460ab3b5..d4500403c 100644
--- a/data/templates/frr/isis.frr.tmpl
+++ b/data/templates/frr/isis.frr.tmpl
@@ -1,5 +1,5 @@
!
-router isis {{ process }}
+router isis {{ domain }}
net {{ net }}
{% if dynamic_hostname is defined %}
hostname dynamic
@@ -134,7 +134,7 @@ router isis {{ process }}
{% if interface is defined and interface is not none %}
{% for iface, iface_config in interface.items() %}
interface {{ iface }}
- ip router isis {{ process }}
+ ip router isis {{ domain }}
{% if iface_config.bfd is defined %}
isis bfd
{% endif %}
diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in
index 0afa8dcb1..e6982aca3 100644
--- a/interface-definitions/protocols-isis.xml.in
+++ b/interface-definitions/protocols-isis.xml.in
@@ -2,14 +2,10 @@
<interfaceDefinition>
<node name="protocols">
<children>
- <tagNode name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py">
+ <node name="isis" owner="${vyos_conf_scripts_dir}/protocols_isis.py">
<properties>
<help>Intermediate System to Intermediate System (IS-IS)</help>
<priority>610</priority>
- <valueHelp>
- <format>text(TAG)</format>
- <description>ISO Routing area tag</description>
- </valueHelp>
</properties>
<children>
<node name="area-password">
@@ -101,6 +97,15 @@
</node>
</children>
</node>
+ <leafNode name="domain">
+ <properties>
+ <help>Domain name used for this instance</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>IS-IS routing area domain/tag</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
<node name="domain-password">
<properties>
<help>Set the authentication password for a routing domain</help>
@@ -766,7 +771,7 @@
</children>
</tagNode>
</children>
- </tagNode>
+ </node>
</children>
</node>
</interfaceDefinition>
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index b7afad473..24db52567 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.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
@@ -23,7 +23,6 @@ from vyos.configdict import node_changed
from vyos import ConfigError
from vyos.util import call
from vyos.util import dict_search
-from vyos.template import render
from vyos.template import render_to_string
from vyos import frr
from vyos import airbag
@@ -35,9 +34,8 @@ def get_config(config=None):
else:
conf = Config()
base = ['protocols', 'isis']
-
- isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
+ isis = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True)
return isis
def verify(isis):
@@ -45,74 +43,70 @@ def verify(isis):
if not isis:
return None
- for process, isis_config in isis.items():
- # If more then one isis process is defined (Frr only supports one)
- # http://docs.frrouting.org/en/latest/isisd.html#isis-router
- if len(isis) > 1:
- raise ConfigError('Only one isis process can be defined')
-
- # If network entity title (net) not defined
- if 'net' not in isis_config:
- raise ConfigError('ISIS net format iso is mandatory!')
-
- # If interface not set
- if 'interface' not in isis_config:
- raise ConfigError('ISIS interface is mandatory!')
-
- # If md5 and plaintext-password set at the same time
- if 'area_password' in isis_config:
- if {'md5', 'plaintext_password'} <= set(isis_config['encryption']):
- raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!')
-
- # If one param from delay set, but not set others
- if 'spf_delay_ietf' in isis_config:
- required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
- exist_timers = []
- for elm_timer in required_timers:
- if elm_timer in isis_config['spf_delay_ietf']:
- exist_timers.append(elm_timer)
-
- exist_timers = set(required_timers).difference(set(exist_timers))
- if len(exist_timers) > 0:
- raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
-
- # If Redistribute set, but level don't set
- if 'redistribute' in isis_config:
- proc_level = isis_config.get('level','').replace('-','_')
- for proto, proto_config in isis_config.get('redistribute', {}).get('ipv4', {}).items():
- if 'level_1' not in proto_config and 'level_2' not in proto_config:
- raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto))
- for redistribute_level in proto_config.keys():
- if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
- raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level))
-
- # Segment routing checks
- if dict_search('segment_routing', isis_config):
- if dict_search('segment_routing.global_block', isis_config):
- high_label_value = dict_search('segment_routing.global_block.high_label_value', isis_config)
- low_label_value = dict_search('segment_routing.global_block.low_label_value', isis_config)
- # If segment routing global block high value is blank, throw error
- if low_label_value and not high_label_value:
- raise ConfigError('Segment routing global block high value must not be left blank')
- # If segment routing global block low value is blank, throw error
- if high_label_value and not low_label_value:
- raise ConfigError('Segment routing global block low value must not be left blank')
- # If segment routing global block low value is higher than the high value, throw error
- if int(low_label_value) > int(high_label_value):
- raise ConfigError('Segment routing global block low value must be lower than high value')
-
- if dict_search('segment_routing.local_block', isis_config):
- high_label_value = dict_search('segment_routing.local_block.high_label_value', isis_config)
- low_label_value = dict_search('segment_routing.local_block.low_label_value', isis_config)
- # If segment routing local block high value is blank, throw error
- if low_label_value and not high_label_value:
- raise ConfigError('Segment routing local block high value must not be left blank')
- # If segment routing local block low value is blank, throw error
- if high_label_value and not low_label_value:
- raise ConfigError('Segment routing local block low value must not be left blank')
- # If segment routing local block low value is higher than the high value, throw error
- if int(low_label_value) > int(high_label_value):
- raise ConfigError('Segment routing local block low value must be lower than high value')
+ if 'domain' not in isis:
+ raise ConfigError('Routing domain name/tag must be set!')
+
+ if 'net' not in isis:
+ raise ConfigError('Network entity is mandatory!')
+
+ # If interface not set
+ if 'interface' not in isis:
+ raise ConfigError('Interface used for routing updates is mandatory!')
+
+ # If md5 and plaintext-password set at the same time
+ if 'area_password' in isis:
+ if {'md5', 'plaintext_password'} <= set(isis['encryption']):
+ raise ConfigError('Can not use both md5 and plaintext-password for ISIS area-password!')
+
+ # If one param from delay set, but not set others
+ if 'spf_delay_ietf' in isis:
+ required_timers = ['holddown', 'init_delay', 'long_delay', 'short_delay', 'time_to_learn']
+ exist_timers = []
+ for elm_timer in required_timers:
+ if elm_timer in isis['spf_delay_ietf']:
+ exist_timers.append(elm_timer)
+
+ exist_timers = set(required_timers).difference(set(exist_timers))
+ if len(exist_timers) > 0:
+ raise ConfigError('All types of delay must be specified: ' + ', '.join(exist_timers).replace('_', '-'))
+
+ # If Redistribute set, but level don't set
+ if 'redistribute' in isis:
+ proc_level = isis.get('level','').replace('-','_')
+ for proto, proto_config in isis.get('redistribute', {}).get('ipv4', {}).items():
+ if 'level_1' not in proto_config and 'level_2' not in proto_config:
+ raise ConfigError('Redistribute level-1 or level-2 should be specified in \"protocols isis {} redistribute ipv4 {}\"'.format(process, proto))
+ for redistribute_level in proto_config.keys():
+ if proc_level and proc_level != 'level_1_2' and proc_level != redistribute_level:
+ raise ConfigError('\"protocols isis {0} redistribute ipv4 {2} {3}\" cannot be used with \"protocols isis {0} level {1}\"'.format(process, proc_level, proto, redistribute_level))
+
+ # Segment routing checks
+ if dict_search('segment_routing', isis):
+ if dict_search('segment_routing.global_block', isis):
+ high_label_value = dict_search('segment_routing.global_block.high_label_value', isis)
+ low_label_value = dict_search('segment_routing.global_block.low_label_value', isis)
+ # If segment routing global block high value is blank, throw error
+ if low_label_value and not high_label_value:
+ raise ConfigError('Segment routing global block high value must not be left blank')
+ # If segment routing global block low value is blank, throw error
+ if high_label_value and not low_label_value:
+ raise ConfigError('Segment routing global block low value must not be left blank')
+ # If segment routing global block low value is higher than the high value, throw error
+ if int(low_label_value) > int(high_label_value):
+ raise ConfigError('Segment routing global block low value must be lower than high value')
+
+ if dict_search('segment_routing.local_block', isis):
+ high_label_value = dict_search('segment_routing.local_block.high_label_value', isis)
+ low_label_value = dict_search('segment_routing.local_block.low_label_value', isis)
+ # If segment routing local block high value is blank, throw error
+ if low_label_value and not high_label_value:
+ raise ConfigError('Segment routing local block high value must not be left blank')
+ # If segment routing local block low value is blank, throw error
+ if high_label_value and not low_label_value:
+ raise ConfigError('Segment routing local block low value must not be left blank')
+ # If segment routing local block low value is higher than the high value, throw error
+ if int(low_label_value) > int(high_label_value):
+ raise ConfigError('Segment routing local block low value must be lower than high value')
return None
@@ -121,22 +115,15 @@ def generate(isis):
isis['new_frr_config'] = ''
return None
- # only one ISIS process is supported, so we can directly send the first key
- # of the config dict
- process = list(isis.keys())[0]
- isis[process]['process'] = process
-
- isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl',
- isis[process])
-
+ isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', isis)
return None
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(r'interface \S+', '')
- frr_cfg.modify_section(f'router isis \S+', '')
+ frr_cfg.modify_section('^interface \S+$', '')
+ frr_cfg.modify_section('^router isis \S+$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['new_frr_config'])
frr_cfg.commit_configuration(daemon='isisd')
@@ -146,15 +133,6 @@ def apply(isis):
for a in range(5):
frr_cfg.commit_configuration(daemon='isisd')
- # Debugging
- '''
- print('')
- print('--------- DEBUGGING ----------')
- print(f'Existing config:\n{frr_cfg["original_config"]}\n\n')
- print(f'Replacement config:\n{isis["new_frr_config"]}\n\n')
- print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n')
- '''
-
return None
if __name__ == '__main__':
diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1
new file mode 100755
index 000000000..5f51f986e
--- /dev/null
+++ b/src/migration-scripts/isis/0-to-1
@@ -0,0 +1,60 @@
+#!/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/>.
+
+# T3417: migrate IS-IS tagNode to node as we can only have one IS-IS process
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['protocols', 'isis']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# Only one IS-IS process is supported, thus this operation is save
+process = config.list_nodes(base)
+isis_base = base + process
+
+# We need a temporary copy of the config
+tmp_base = ['protocols', 'isis2']
+config.copy(isis_base, tmp_base)
+
+# Now it's save to delete the old configuration
+config.delete(base)
+
+# Rename temporary copy to new final config and set domain key
+config.rename(tmp_base, 'isis')
+config.set(base + ['domain'], process[0])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ sys.exit(1)