diff options
Diffstat (limited to 'python/vyos/configdict.py')
-rw-r--r-- | python/vyos/configdict.py | 121 |
1 files changed, 69 insertions, 52 deletions
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index f50db0c99..04ddc10e9 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -104,6 +104,11 @@ def list_diff(first, second): second = set(second) return [item for item in first if item not in second] +def is_node_changed(conf, path): + from vyos.configdiff import get_config_diff + D = get_config_diff(conf, key_mangling=('-', '_')) + return D.is_node_changed(path) + def leaf_node_changed(conf, path): """ Check if a leaf node was altered. If it has been altered - values has been @@ -114,7 +119,6 @@ def leaf_node_changed(conf, path): """ from vyos.configdiff import get_config_diff D = get_config_diff(conf, key_mangling=('-', '_')) - D.set_level(conf.get_level()) (new, old) = D.get_value_diff(path) if new != old: if isinstance(old, dict): @@ -133,9 +137,6 @@ def leaf_node_changed(conf, path): elif isinstance(new, type(None)): new = [] return list_diff(old, new) - if old is None and new is not None: - # node was added to the CLI - return True return None @@ -147,12 +148,11 @@ def node_changed(conf, path, key_mangling=None, recursive=False): """ from vyos.configdiff import get_config_diff, Diff D = get_config_diff(conf, key_mangling) - D.set_level(conf.get_level()) # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE, recursive=recursive)['delete'].keys() return list(keys) -def get_removed_vlans(conf, dict): +def get_removed_vlans(conf, path, dict): """ Common function to parse a dictionary retrieved via get_config_dict() and determine any added/removed VLAN interfaces - be it 802.1q or Q-in-Q. @@ -162,16 +162,17 @@ def get_removed_vlans(conf, dict): # Check vif, vif-s/vif-c VLAN interfaces for removal D = get_config_diff(conf, key_mangling=('-', '_')) D.set_level(conf.get_level()) + # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 - keys = D.get_child_nodes_diff(['vif'], expand_nodes=Diff.DELETE)['delete'].keys() + keys = D.get_child_nodes_diff(path + ['vif'], expand_nodes=Diff.DELETE)['delete'].keys() if keys: dict['vif_remove'] = [*keys] # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 - keys = D.get_child_nodes_diff(['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys() + keys = D.get_child_nodes_diff(path + ['vif-s'], expand_nodes=Diff.DELETE)['delete'].keys() if keys: dict['vif_s_remove'] = [*keys] for vif in dict.get('vif_s', {}).keys(): - keys = D.get_child_nodes_diff(['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys() + keys = D.get_child_nodes_diff(path + ['vif-s', vif, 'vif-c'], expand_nodes=Diff.DELETE)['delete'].keys() if keys: dict['vif_s'][vif]['vif_c_remove'] = [*keys] return dict @@ -215,10 +216,6 @@ def is_member(conf, interface, intftype=None): intftype = intftypes if intftype == None else [intftype] - # set config level to root - old_level = conf.get_level() - conf.set_level([]) - for iftype in intftype: base = ['interfaces', iftype] for intf in conf.list_nodes(base): @@ -228,7 +225,6 @@ def is_member(conf, interface, intftype=None): get_first_key=True, no_tag_node_value_mangle=True) ret_val.update({intf : tmp}) - old_level = conf.set_level(old_level) return ret_val def is_mirror_intf(conf, interface, direction=None): @@ -250,8 +246,6 @@ def is_mirror_intf(conf, interface, direction=None): direction = directions if direction == None else [direction] ret_val = None - old_level = conf.get_level() - conf.set_level([]) base = ['interfaces'] for dir in direction: @@ -265,7 +259,6 @@ def is_mirror_intf(conf, interface, direction=None): get_first_key=True) ret_val = {intf : tmp} - old_level = conf.set_level(old_level) return ret_val def has_vlan_subinterface_configured(conf, intf): @@ -279,15 +272,11 @@ def has_vlan_subinterface_configured(conf, intf): from vyos.ifconfig import Section ret = False - old_level = conf.get_level() - conf.set_level([]) - intfpath = ['interfaces', Section.section(intf), intf] if ( conf.exists(intfpath + ['vif']) or conf.exists(intfpath + ['vif-s'])): ret = True - conf.set_level(old_level) return ret def is_source_interface(conf, interface, intftype=None): @@ -309,11 +298,6 @@ def is_source_interface(conf, interface, intftype=None): 'have a source-interface') intftype = intftypes if intftype == None else [intftype] - - # set config level to root - old_level = conf.get_level() - conf.set_level([]) - for it in intftype: base = ['interfaces', it] for intf in conf.list_nodes(base): @@ -322,7 +306,6 @@ def is_source_interface(conf, interface, intftype=None): ret_val = intf break - old_level = conf.set_level(old_level) return ret_val def get_dhcp_interfaces(conf, vrf=None): @@ -333,40 +316,67 @@ def get_dhcp_interfaces(conf, vrf=None): if not dict: return dhcp_interfaces - def check_dhcp(config, ifname): + def check_dhcp(config): + ifname = config['ifname'] tmp = {} if 'address' in config and 'dhcp' in config['address']: options = {} - if 'dhcp_options' in config and 'default_route_distance' in config['dhcp_options']: - options.update({'distance' : config['dhcp_options']['default_route_distance']}) + if dict_search('dhcp_options.default_route_distance', config) != None: + options.update({'dhcp_options' : config['dhcp_options']}) if 'vrf' in config: if vrf is config['vrf']: tmp.update({ifname : options}) else: tmp.update({ifname : options}) + return tmp for section, interface in dict.items(): for ifname in interface: + # always reset config level, as get_interface_dict() will alter it + conf.set_level([]) # we already have a dict representation of the config from get_config_dict(), # but with the extended information from get_interface_dict() we also # get the DHCP client default-route-distance default option if not specified. - ifconfig = get_interface_dict(conf, ['interfaces', section], ifname) + _, ifconfig = get_interface_dict(conf, ['interfaces', section], ifname) - tmp = check_dhcp(ifconfig, ifname) + tmp = check_dhcp(ifconfig) dhcp_interfaces.update(tmp) # check per VLAN interfaces for vif, vif_config in ifconfig.get('vif', {}).items(): - tmp = check_dhcp(vif_config, f'{ifname}.{vif}') + tmp = check_dhcp(vif_config) dhcp_interfaces.update(tmp) # check QinQ VLAN interfaces - for vif_s, vif_s_config in ifconfig.get('vif-s', {}).items(): - tmp = check_dhcp(vif_s_config, f'{ifname}.{vif_s}') + for vif_s, vif_s_config in ifconfig.get('vif_s', {}).items(): + tmp = check_dhcp(vif_s_config) dhcp_interfaces.update(tmp) - for vif_c, vif_c_config in vif_s_config.get('vif-c', {}).items(): - tmp = check_dhcp(vif_c_config, f'{ifname}.{vif_s}.{vif_c}') + for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): + tmp = check_dhcp(vif_c_config) dhcp_interfaces.update(tmp) return dhcp_interfaces +def get_pppoe_interfaces(conf, vrf=None): + """ Common helper functions to retrieve all interfaces from current CLI + sessions that have DHCP configured. """ + pppoe_interfaces = {} + for ifname in conf.list_nodes(['interfaces', 'pppoe']): + # always reset config level, as get_interface_dict() will alter it + conf.set_level([]) + # we already have a dict representation of the config from get_config_dict(), + # but with the extended information from get_interface_dict() we also + # get the DHCP client default-route-distance default option if not specified. + ifconfig = get_interface_dict(conf, ['interfaces', 'pppoe'], ifname) + + options = {} + if 'default_route_distance' in ifconfig: + options.update({'default_route_distance' : ifconfig['default_route_distance']}) + if 'no_default_route' in ifconfig: + options.update({'no_default_route' : {}}) + if 'vrf' in ifconfig: + if vrf is ifconfig['vrf']: pppoe_interfaces.update({ifname : options}) + else: pppoe_interfaces.update({ifname : options}) + + return pppoe_interfaces + def get_interface_dict(config, base, ifname=''): """ Common utility function to retrieve and mangle the interfaces configuration @@ -376,7 +386,6 @@ def get_interface_dict(config, base, ifname=''): Return a dictionary with the necessary interface config keys. """ - if not ifname: from vyos import ConfigError # determine tagNode instance @@ -393,9 +402,8 @@ def get_interface_dict(config, base, ifname=''): for vif in ['vif', 'vif_s']: if vif in default_values: del default_values[vif] - # setup config level which is extracted in get_removed_vlans() - config.set_level(base + [ifname]) - dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True, + dict = config.get_config_dict(base + [ifname], key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) # Check if interface has been removed. We must use exists() as @@ -403,8 +411,8 @@ def get_interface_dict(config, base, ifname=''): # node like the following exists. # +macsec macsec1 { # +} - if not config.exists([]): - dict.update({'deleted' : ''}) + if not config.exists(base + [ifname]): + dict.update({'deleted' : {}}) # Add interface instance name into dictionary dict.update({'ifname': ifname}) @@ -431,7 +439,7 @@ def get_interface_dict(config, base, ifname=''): # XXX: T2665: blend in proper DHCPv6-PD default values dict = T2665_set_dhcpv6pd_defaults(dict) - address = leaf_node_changed(config, ['address']) + address = leaf_node_changed(config, base + [ifname, 'address']) if address: dict.update({'address_old' : address}) # Check if we are a member of a bridge device @@ -462,10 +470,10 @@ def get_interface_dict(config, base, ifname=''): tmp = is_member(config, dict['source_interface'], 'bonding') if tmp: dict.update({'source_interface_is_bond_member' : tmp}) - mac = leaf_node_changed(config, ['mac']) + mac = leaf_node_changed(config, base + [ifname, 'mac']) if mac: dict.update({'mac_old' : mac}) - eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64']) + eui64 = leaf_node_changed(config, base + [ifname, 'ipv6', 'address', 'eui64']) if eui64: tmp = dict_search('ipv6.address', dict) if not tmp: @@ -477,6 +485,9 @@ def get_interface_dict(config, base, ifname=''): # identical for all types of VLAN interfaces as they all include the same # XML definitions which hold the defaults. for vif, vif_config in dict.get('vif', {}).items(): + # Add subinterface name to dictionary + dict['vif'][vif].update({'ifname' : f'{ifname}.{vif}'}) + default_vif_values = defaults(base + ['vif']) # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely # remove the default values from the dict. @@ -486,7 +497,7 @@ def get_interface_dict(config, base, ifname=''): # Only add defaults if interface is not about to be deleted - this is # to keep a cleaner config dict. if 'deleted' not in dict: - address = leaf_node_changed(config, ['vif', vif, 'address']) + address = leaf_node_changed(config, base + [ifname, 'vif', vif, 'address']) if address: dict['vif'][vif].update({'address_old' : address}) dict['vif'][vif] = dict_merge(default_vif_values, dict['vif'][vif]) @@ -508,6 +519,9 @@ def get_interface_dict(config, base, ifname=''): if dhcp: dict['vif'][vif].update({'dhcp_options_changed' : ''}) for vif_s, vif_s_config in dict.get('vif_s', {}).items(): + # Add subinterface name to dictionary + dict['vif_s'][vif_s].update({'ifname' : f'{ifname}.{vif_s}'}) + default_vif_s_values = defaults(base + ['vif-s']) # XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c if 'vif_c' in default_vif_s_values: del default_vif_s_values['vif_c'] @@ -520,7 +534,7 @@ def get_interface_dict(config, base, ifname=''): # Only add defaults if interface is not about to be deleted - this is # to keep a cleaner config dict. if 'deleted' not in dict: - address = leaf_node_changed(config, ['vif-s', vif_s, 'address']) + address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'address']) if address: dict['vif_s'][vif_s].update({'address_old' : address}) dict['vif_s'][vif_s] = dict_merge(default_vif_s_values, @@ -544,6 +558,9 @@ def get_interface_dict(config, base, ifname=''): if dhcp: dict['vif_s'][vif_s].update({'dhcp_options_changed' : ''}) for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): + # Add subinterface name to dictionary + dict['vif_s'][vif_s]['vif_c'][vif_c].update({'ifname' : f'{ifname}.{vif_s}.{vif_c}'}) + default_vif_c_values = defaults(base + ['vif-s', 'vif-c']) # XXX: T2665: When there is no DHCPv6-PD configuration given, we can safely @@ -554,7 +571,7 @@ def get_interface_dict(config, base, ifname=''): # Only add defaults if interface is not about to be deleted - this is # to keep a cleaner config dict. if 'deleted' not in dict: - address = leaf_node_changed(config, ['vif-s', vif_s, 'vif-c', vif_c, 'address']) + address = leaf_node_changed(config, base + [ifname, 'vif-s', vif_s, 'vif-c', vif_c, 'address']) if address: dict['vif_s'][vif_s]['vif_c'][vif_c].update( {'address_old' : address}) @@ -581,8 +598,8 @@ def get_interface_dict(config, base, ifname=''): if dhcp: dict['vif_s'][vif_s]['vif_c'][vif_c].update({'dhcp_options_changed' : ''}) # Check vif, vif-s/vif-c VLAN interfaces for removal - dict = get_removed_vlans(config, dict) - return dict + dict = get_removed_vlans(config, base + [ifname], dict) + return ifname, dict def get_vlan_ids(interface): """ |