diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/protocols_isis.py | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py new file mode 100755 index 000000000..d5e5b64fb --- /dev/null +++ b/src/conf_mode/protocols_isis.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2017-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 os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import node_changed +from vyos import ConfigError +from vyos.util import call +from vyos.template import render +from vyos.template import render_to_string +from vyos import frr +from vyos import airbag +airbag.enable() + +config_file = r'/tmp/isis.frr' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'isis'] + + isis = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # determine which members have been removed + for instance in isis: + conf.set_level(base + [instance]) + tmp = node_changed(conf, ['interface']) + if tmp: + isis[instance].update({'interface_remove': tmp}) + + return isis + +def verify(isis): + # bail out early - looks like removal from running config + 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 definded') + + # 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 deley 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)) + + return None + +def generate(isis): + if not 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 + + import pprint + pprint.pprint(isis[process]) + + # render(config) not needed, its only for debug + render(config_file, 'frr/isis.frr.tmpl', isis[process], trim_blocks=True) + + isis['new_frr_config'] = render_to_string('frr/isis.frr.tmpl', + isis[process], trim_blocks=True) + + return None + +def apply(isis): + + # Save original configration prior to starting any commit actions + frr_cfg = {} + frr_cfg['original_config'] = frr.get_configuration(daemon='isisd') + frr_cfg['modified_config'] = frr.replace_section(frr_cfg['original_config'], isis['new_frr_config'], from_re='router isis .*') + + # 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') + + # FRR mark configuration will test for syntax errors and throws an + # exception if any syntax errors is detected + frr.mark_configuration(frr_cfg['modified_config']) + + # Commit resulting configuration to FRR, this will throw CommitError + # on failure + frr.reload_configuration(frr_cfg['modified_config'], daemon='isisd') + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) |