diff options
Diffstat (limited to 'src/conf_mode/protocols_ospf.py')
-rwxr-xr-x | src/conf_mode/protocols_ospf.py | 101 |
1 files changed, 32 insertions, 69 deletions
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 0582d32be..cddd3765e 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2023 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,6 +20,7 @@ from sys import exit from sys import argv from vyos.config import Config +from vyos.config import config_dict_merge from vyos.configdict import dict_merge from vyos.configdict import node_changed from vyos.configverify import verify_common_route_maps @@ -27,9 +28,8 @@ from vyos.configverify import verify_route_map from vyos.configverify import verify_interface_exists from vyos.configverify import verify_access_list from vyos.template import render_to_string -from vyos.util import dict_search -from vyos.util import get_interface_config -from vyos.xml import defaults +from vyos.utils.dict import dict_search +from vyos.utils.network import get_interface_config from vyos import ConfigError from vyos import frr from vyos import airbag @@ -65,17 +65,15 @@ def get_config(config=None): if interfaces_removed: ospf['interface_removed'] = list(interfaces_removed) - # Bail out early if configuration tree does not exist + # Bail out early if configuration tree does no longer exist. this must + # be done after retrieving the list of interfaces to be removed. if not conf.exists(base): ospf.update({'deleted' : ''}) return ospf # We have gathered the dict representation of the CLI, but there are default # options which we need to update into the dictionary retrived. - # XXX: Note that we can not call defaults(base), as defaults does not work - # on an instance of a tag node. As we use the exact same CLI definition for - # both the non-vrf and vrf version this is absolutely safe! - default_values = defaults(base_path) + default_values = conf.get_config_defaults(**ospf.kwargs, recursive=True) # We have to cleanup the default dict, as default values could enable features # which are not explicitly enabled on the CLI. Example: default-information @@ -84,60 +82,27 @@ def get_config(config=None): # need to check this first and probably drop that key. if dict_search('default_information.originate', ospf) is None: del default_values['default_information'] - if dict_search('area.area_type.nssa', ospf) is None: - del default_values['area']['area_type']['nssa'] if 'mpls_te' not in ospf: del default_values['mpls_te'] + if 'graceful_restart' not in ospf: + del default_values['graceful_restart'] + for area_num in default_values.get('area', []): + if dict_search(f'area.{area_num}.area_type.nssa', ospf) is None: + del default_values['area'][area_num]['area_type']['nssa'] - for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']: - # table is a tagNode thus we need to clean out all occurances for the - # default values and load them in later individually - if protocol == 'table': - del default_values['redistribute']['table'] - continue + for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static']: if dict_search(f'redistribute.{protocol}', ospf) is None: del default_values['redistribute'][protocol] - # XXX: T2665: we currently have no nice way for defaults under tag nodes, - # clean them out and add them manually :( - del default_values['neighbor'] - del default_values['area']['virtual_link'] - del default_values['interface'] - - # merge in remaining default values - ospf = dict_merge(default_values, ospf) - - if 'neighbor' in ospf: - default_values = defaults(base + ['neighbor']) - for neighbor in ospf['neighbor']: - ospf['neighbor'][neighbor] = dict_merge(default_values, ospf['neighbor'][neighbor]) - - if 'area' in ospf: - default_values = defaults(base + ['area', 'virtual-link']) - for area, area_config in ospf['area'].items(): - if 'virtual_link' in area_config: - for virtual_link in area_config['virtual_link']: - ospf['area'][area]['virtual_link'][virtual_link] = dict_merge( - default_values, ospf['area'][area]['virtual_link'][virtual_link]) + for interface in ospf.get('interface', []): + # We need to reload the defaults on every pass b/c of + # hello-multiplier dependency on dead-interval + # If hello-multiplier is set, we need to remove the default from + # dead-interval. + if 'hello_multiplier' in ospf['interface'][interface]: + del default_values['interface'][interface]['dead_interval'] - if 'interface' in ospf: - for interface in ospf['interface']: - # We need to reload the defaults on every pass b/c of - # hello-multiplier dependency on dead-interval - default_values = defaults(base + ['interface']) - # If hello-multiplier is set, we need to remove the default from - # dead-interval. - if 'hello_multiplier' in ospf['interface'][interface]: - del default_values['dead_interval'] - - ospf['interface'][interface] = dict_merge(default_values, - ospf['interface'][interface]) - - if 'redistribute' in ospf and 'table' in ospf['redistribute']: - default_values = defaults(base + ['redistribute', 'table']) - for table in ospf['redistribute']['table']: - ospf['redistribute']['table'][table] = dict_merge(default_values, - ospf['redistribute']['table'][table]) + ospf = config_dict_merge(default_values, ospf) # We also need some additional information from the config, prefix-lists # and route-maps for instance. They will be used in verify(). @@ -196,7 +161,7 @@ def verify(ospf): vrf = ospf['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}!') + raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!') # Segment routing checks if dict_search('segment_routing.global_block', ospf): @@ -234,7 +199,7 @@ def verify(ospf): if list(set(global_range) & set(local_range)): raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\ f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!') - + # Check for a blank or invalid value per prefix if dict_search('segment_routing.prefix', ospf): for prefix, prefix_config in ospf['segment_routing']['prefix'].items(): @@ -250,31 +215,28 @@ def verify(ospf): raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\ f'and no-php-flag configured at the same time.') + # Check route summarisation + if 'summary_address' in ospf: + for prefix, prefix_options in ospf['summary_address'].items(): + if {'tag', 'no_advertise'} <= set(prefix_options): + raise ConfigError(f'Can not set both "tag" and "no-advertise" for Type-5 '\ + f'and Type-7 route summarisation of "{prefix}"!') + return None def generate(ospf): if not ospf or 'deleted' in ospf: return None - ospf['protocol'] = 'ospf' # required for frr/vrf.route-map.frr.j2 - ospf['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.j2', ospf) ospf['frr_ospfd_config'] = render_to_string('frr/ospfd.frr.j2', ospf) return None def apply(ospf): ospf_daemon = 'ospfd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section('(\s+)?ip protocol ospf route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in ospf: - frr_cfg.add_before(frr.default_add_before, ospf['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' @@ -288,10 +250,11 @@ def apply(ospf): if key not in ospf: continue for interface in ospf[key]: - frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) if 'frr_ospfd_config' in ospf: frr_cfg.add_before(frr.default_add_before, ospf['frr_ospfd_config']) + frr_cfg.commit_configuration(ospf_daemon) return None |