diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/configdict.py | 104 | ||||
-rw-r--r-- | python/vyos/configverify.py | 57 | ||||
-rw-r--r-- | python/vyos/xml/definition.py | 2 |
3 files changed, 151 insertions, 12 deletions
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 58ecd3f17..99072a1b9 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -18,12 +18,8 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion. """ import os -from copy import deepcopy - from vyos.util import vyos_dict_search from vyos.xml import defaults -from vyos.xml import is_tag -from vyos.xml import is_leaf from vyos import ConfigError def retrieve_config(path_hash, base_path, config): @@ -198,6 +194,9 @@ def is_member(conf, interface, intftype=None): interface name -> Interface is a member of this interface False -> interface type cannot have members """ + from vyos.xml import is_tag + from vyos.xml import is_leaf + ret_val = None intftypes = ['bonding', 'bridge'] if intftype not in intftypes + [None]: @@ -265,11 +264,12 @@ def is_source_interface(conf, interface, intftype=None): def get_interface_dict(config, base, ifname=''): """ - Common utility function to retrieve and mandgle the interfaces available - in CLI configuration. All interfaces have a common base ground where the - value retrival is identical - so it can and should be reused + Common utility function to retrieve and mangle the interfaces configuration + from the CLI input nodes. All interfaces have a common base where value + retrival is identical. This function must be used whenever possible when + working on the interfaces node! - Will return a dictionary with the necessary interface configuration + Return a dictionary with the necessary interface config keys. """ if not ifname: # determine tagNode instance @@ -290,8 +290,12 @@ def get_interface_dict(config, base, ifname=''): config.set_level(base + [ifname]) dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - # Check if interface has been removed - if dict == {}: + # Check if interface has been removed. We must use exists() as get_config_dict() + # will always return {} - even when an empty interface node like + # +macsec macsec1 { + # +} + # exists. + if not config.exists([]): dict.update({'deleted' : ''}) # Add interface instance name into dictionary @@ -356,6 +360,10 @@ def get_interface_dict(config, base, ifname=''): # XXX: T2665: blend in proper DHCPv6-PD default values dict['vif'][vif] = T2665_set_dhcpv6pd_defaults(dict['vif'][vif]) + # Check if we are a member of a bridge device + bridge = is_member(config, f'{ifname}.{vif}', 'bridge') + if bridge: dict['vif'][vif].update({'is_bridge_member' : bridge}) + for vif_s, vif_s_config in dict.get('vif_s', {}).items(): default_vif_s_values = defaults(base + ['vif-s']) # XXX: T2665: we only wan't the vif-s defaults - do not care about vif-c @@ -371,6 +379,10 @@ def get_interface_dict(config, base, ifname=''): dict['vif_s'][vif_s] = T2665_set_dhcpv6pd_defaults( dict['vif_s'][vif_s]) + # Check if we are a member of a bridge device + bridge = is_member(config, f'{ifname}.{vif_s}', 'bridge') + if bridge: dict['vif_s'][vif_s].update({'is_bridge_member' : bridge}) + for vif_c, vif_c_config in vif_s_config.get('vif_c', {}).items(): default_vif_c_values = defaults(base + ['vif-s', 'vif-c']) @@ -385,6 +397,78 @@ def get_interface_dict(config, base, ifname=''): dict['vif_s'][vif_s]['vif_c'][vif_c] = T2665_set_dhcpv6pd_defaults( dict['vif_s'][vif_s]['vif_c'][vif_c]) + # Check if we are a member of a bridge device + bridge = is_member(config, f'{ifname}.{vif_s}.{vif_c}', 'bridge') + if bridge: dict['vif_s'][vif_s]['vif_c'][vif_c].update( + {'is_bridge_member' : bridge}) + # Check vif, vif-s/vif-c VLAN interfaces for removal dict = get_removed_vlans(config, dict) return dict + + +def get_accel_dict(config, base, chap_secrets): + """ + Common utility function to retrieve and mangle the Accel-PPP configuration + from different CLI input nodes. All Accel-PPP services have a common base + where value retrival is identical. This function must be used whenever + possible when working with Accel-PPP services! + + Return a dictionary with the necessary interface config keys. + """ + from vyos.util import get_half_cpus + from vyos.validate import is_ipv4 + + dict = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + + # defaults include RADIUS server specifics per TAG node which need to be + # added to individual RADIUS servers instead - so we can simply delete them + if vyos_dict_search('authentication.radius.server', default_values): + del default_values['authentication']['radius']['server'] + + # defaults include static-ip address per TAG node which need to be added to + # individual local users instead - so we can simply delete them + if vyos_dict_search('authentication.local_users.username', default_values): + del default_values['authentication']['local_users']['username'] + + dict = dict_merge(default_values, dict) + + # set CPUs cores to process requests + dict.update({'thread_count' : get_half_cpus()}) + # we need to store the path to the secrets file + dict.update({'chap_secrets_file' : chap_secrets}) + + # We can only have two IPv4 and three IPv6 nameservers - also they are + # configured in a different way in the configuration, this is why we split + # the configuration + if 'name_server' in dict: + ns_v4 = [] + ns_v6 = [] + for ns in dict['name_server']: + if is_ipv4(ns): ns_v4.append(ns) + else: ns_v6.append(ns) + + dict.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6}) + del dict['name_server'] + + # Add individual RADIUS server default values + if vyos_dict_search('authentication.radius.server', dict): + default_values = defaults(base + ['authentication', 'radius', 'server']) + + for server in vyos_dict_search('authentication.radius.server', dict): + dict['authentication']['radius']['server'][server] = dict_merge( + default_values, dict['authentication']['radius']['server'][server]) + + # Add individual local-user default values + if vyos_dict_search('authentication.local_users.username', dict): + default_values = defaults(base + ['authentication', 'local_users', 'username']) + + for username in vyos_dict_search('authentication.local_users.username', dict): + dict['authentication']['local_users']['username'][username] = dict_merge( + default_values, dict['authentication']['local_users']['username'][username]) + + return dict diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 944fc4294..f970ca6de 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -22,6 +22,7 @@ # makes use of it! from vyos import ConfigError +from vyos.util import vyos_dict_search def verify_mtu(config): """ @@ -51,7 +52,6 @@ def verify_mtu_ipv6(config): configured on the interface. IPv6 requires a 1280 bytes MTU. """ from vyos.validate import is_ipv6 - from vyos.util import vyos_dict_search # IPv6 minimum required link mtu min_mtu = 1280 @@ -204,3 +204,58 @@ def verify_vlan_config(config): verify_dhcpv6(vlan) verify_address(vlan) verify_vrf(vlan) + +def verify_accel_ppp_base_service(config): + """ + Common helper function which must be used by all Accel-PPP services based + on get_config_dict() + """ + # vertify auth settings + if vyos_dict_search('authentication.mode', config) == 'local': + if not vyos_dict_search('authentication.local_users', config): + raise ConfigError('PPPoE local auth mode requires local users to be configured!') + + for user in vyos_dict_search('authentication.local_users.username', config): + user_config = config['authentication']['local_users']['username'][user] + + if 'password' not in user_config: + raise ConfigError(f'Password required for local user "{user}"') + + if 'rate_limit' in user_config: + # if up/download is set, check that both have a value + if not {'upload', 'download'} <= set(user_config['rate_limit']): + raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \ + 'direction but both upload and download must be given!') + + elif vyos_dict_search('authentication.mode', config) == 'radius': + if not vyos_dict_search('authentication.radius.server', config): + raise ConfigError('RADIUS authentication requires at least one server') + + for server in vyos_dict_search('authentication.radius.server', config): + radius_config = config['authentication']['radius']['server'][server] + if 'key' not in radius_config: + raise ConfigError(f'Missing RADIUS secret key for server "{server}"') + + if 'gateway_address' not in config: + raise ConfigError('PPPoE server requires gateway-address to be configured!') + + if 'name_server_ipv4' in config: + if len(config['name_server_ipv4']) > 2: + raise ConfigError('Not more then two IPv4 DNS name-servers ' \ + 'can be configured') + + if 'name_server_ipv6' in config: + if len(config['name_server_ipv6']) > 3: + raise ConfigError('Not more then three IPv6 DNS name-servers ' \ + 'can be configured') + + if 'client_ipv6_pool' in config: + ipv6_pool = config['client_ipv6_pool'] + if 'delegate' in ipv6_pool: + if 'prefix' not in ipv6_pool: + raise ConfigError('IPv6 "delegate" also requires "prefix" to be defined!') + + for delegate in ipv6_pool['delegate']: + if 'delegation_prefix' not in ipv6_pool['delegate'][delegate]: + raise ConfigError('delegation-prefix length required!') + diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py index a25fc50c5..7831af4d2 100644 --- a/python/vyos/xml/definition.py +++ b/python/vyos/xml/definition.py @@ -297,7 +297,7 @@ class XML(dict): continue value = conf[k] if self.is_multi(fpath) and not isinstance(value, list): - value = [value] + value = value.split(' ') r[under] = value return r |