diff options
Diffstat (limited to 'src')
231 files changed, 1390 insertions, 1740 deletions
| diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py index ced5d212e..31c552f5a 100755 --- a/src/conf_mode/bcast_relay.py +++ b/src/conf_mode/bcast_relay.py @@ -24,7 +24,7 @@ from vyos.config import Config  from vyos.configverify import verify_interface_exists  from vyos.template import render  from vyos.utils.process import call -from vyos.validate import is_afi_configured +from vyos.utils.network import is_afi_configured  from vyos import ConfigError  from vyos import airbag  airbag.enable() diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index 2a77540f7..9c43640a9 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -20,7 +20,6 @@ import re  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.firewall import find_nftables_rule  from vyos.firewall import remove_nftables_rule  from vyos.utils.process import process_named_running @@ -28,7 +27,6 @@ from vyos.utils.dict import dict_search  from vyos.utils.process import cmd  from vyos.utils.process import run  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -77,16 +75,8 @@ def get_config(config=None):      base = ['system', 'conntrack']      conntrack = conf.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) -    # XXX: T2665: we can not safely rely on the defaults() when there are -    # tagNodes in place, it is better to blend in the defaults manually. -    if 'timeout' in default_values and 'custom' in default_values['timeout']: -        del default_values['timeout']['custom'] -    conntrack = dict_merge(default_values, conntrack) +                                     get_first_key=True, +                                     with_recursive_defaults=True)      return conntrack diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py index a83c2274d..4fb2ce27f 100755 --- a/src/conf_mode/conntrack_sync.py +++ b/src/conf_mode/conntrack_sync.py @@ -18,7 +18,6 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_interface_exists  from vyos.utils.dict import dict_search  from vyos.utils.process import process_named_running @@ -27,8 +26,7 @@ from vyos.utils.process import call  from vyos.utils.process import run  from vyos.template import render  from vyos.template import get_ipv4 -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -50,11 +48,7 @@ def get_config(config=None):          return None      conntrack = conf.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) -    conntrack = dict_merge(default_values, conntrack) +                                     get_first_key=True, with_defaults=True)      conntrack['hash_size'] = read_file('/sys/module/nf_conntrack/parameters/hashsize')      conntrack['table_size'] = read_file('/proc/sys/net/netfilter/nf_conntrack_max') diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 3378aac63..ed7cc809c 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -37,7 +37,7 @@ from vyos.template import inc_ip  from vyos.template import is_ipv4  from vyos.template import is_ipv6  from vyos.template import render -from vyos.xml import defaults +from vyos.xml_ref import default_value  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -66,58 +66,26 @@ def get_config(config=None):      base = ['container']      container = conf.get_config_dict(base, key_mangling=('-', '_'), -                                     get_first_key=True, no_tag_node_value_mangle=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) -    # container base default values can not be merged here - remove and add them later -    if 'name' in default_values: -        del default_values['name'] -    # registry will be handled below -    if 'registry' in default_values: -        del default_values['registry'] -    container = dict_merge(default_values, container) - -    # Merge per-container default values -    if 'name' in container: -        default_values = defaults(base + ['name']) -        if 'port' in default_values: -            del default_values['port'] -        if 'volume' in default_values: -            del default_values['volume'] -        for name in container['name']: -            container['name'][name] = dict_merge(default_values, container['name'][name]) - -            # T5047: Any container related configuration changed? We only -            # wan't to restart the required containers and not all of them ... -            tmp = is_node_changed(conf, base + ['name', name]) -            if tmp: -                if 'container_restart' not in container: -                    container['container_restart'] = [name] -                else: -                    container['container_restart'].append(name) - -            # XXX: T2665: we can not safely rely on the defaults() when there are -            # tagNodes in place, it is better to blend in the defaults manually. -            if 'port' in container['name'][name]: -                for port in container['name'][name]['port']: -                    default_values_port = defaults(base + ['name', 'port']) -                    container['name'][name]['port'][port] = dict_merge( -                        default_values_port, container['name'][name]['port'][port]) -            # XXX: T2665: we can not safely rely on the defaults() when there are -            # tagNodes in place, it is better to blend in the defaults manually. -            if 'volume' in container['name'][name]: -                for volume in container['name'][name]['volume']: -                    default_values_volume = defaults(base + ['name', 'volume']) -                    container['name'][name]['volume'][volume] = dict_merge( -                        default_values_volume, container['name'][name]['volume'][volume]) +                                     no_tag_node_value_mangle=True, +                                     get_first_key=True, +                                     with_recursive_defaults=True) + +    for name in container.get('name', []): +        # T5047: Any container related configuration changed? We only +        # wan't to restart the required containers and not all of them ... +        tmp = is_node_changed(conf, base + ['name', name]) +        if tmp: +            if 'container_restart' not in container: +                container['container_restart'] = [name] +            else: +                container['container_restart'].append(name)      # registry is a tagNode with default values - merge the list from      # default_values['registry'] into the tagNode variables      if 'registry' not in container:          container.update({'registry' : {}}) -        default_values = defaults(base) -        for registry in default_values['registry'].split(): +        default_values = default_value(base + ['registry']) +        for registry in default_values:              tmp = {registry : {}}              container['registry'] = dict_merge(tmp, container['registry']) diff --git a/src/conf_mode/dhcp_relay.py b/src/conf_mode/dhcp_relay.py index fd39bd9fe..37d708847 100755 --- a/src/conf_mode/dhcp_relay.py +++ b/src/conf_mode/dhcp_relay.py @@ -20,12 +20,10 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.base import Warning  from vyos.utils.process import call  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -41,11 +39,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    relay = conf.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) -    relay = dict_merge(default_values, relay) +    relay = conf.get_config_dict(base, key_mangling=('-', '_'), +                                 get_first_key=True, +                                 with_recursive_defaults=True)      return relay diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index c29270367..c4c72aae9 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -23,14 +23,12 @@ from netaddr import IPRange  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.dict import dict_search  from vyos.utils.process import call  from vyos.utils.process import run -from vyos.validate import is_subnet_connected -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_subnet_connected +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -109,19 +107,15 @@ def get_config(config=None):      if not conf.exists(base):          return None -    dhcp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) -    # T2665: defaults include lease time per TAG node which need to be added to -    # individual subnet definitions -    default_values = defaults(base + ['shared-network-name', 'subnet']) +    dhcp = conf.get_config_dict(base, key_mangling=('-', '_'), +                                no_tag_node_value_mangle=True, +                                get_first_key=True, +                                with_recursive_defaults=True)      if 'shared_network_name' in dhcp:          for network, network_config in dhcp['shared_network_name'].items():              if 'subnet' in network_config:                  for subnet, subnet_config in network_config['subnet'].items(): -                    if 'lease' not in subnet_config: -                        dhcp['shared_network_name'][network]['subnet'][subnet] = dict_merge( -                            default_values, dhcp['shared_network_name'][network]['subnet'][subnet]) -                      # If exclude IP addresses are defined we need to slice them out of                      # the defined ranges                      if {'exclude', 'range'} <= set(subnet_config): @@ -302,6 +296,10 @@ def generate(dhcp):      render(config_file, 'dhcp-server/dhcpd.conf.j2', dhcp,             formater=lambda _: _.replace(""", '"')) +    # Clean up configuration test file +    if os.path.exists(tmp_file): +        os.unlink(tmp_file) +      return None  def apply(dhcp): diff --git a/src/conf_mode/dhcpv6_relay.py b/src/conf_mode/dhcpv6_relay.py index 0e7da6f89..6537ca3c2 100755 --- a/src/conf_mode/dhcpv6_relay.py +++ b/src/conf_mode/dhcpv6_relay.py @@ -19,14 +19,11 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.ifconfig import Interface  from vyos.template import render  from vyos.template import is_ipv6  from vyos.utils.process import call -from vyos.utils.dict import dict_search -from vyos.validate import is_ipv6_link_local -from vyos.xml import defaults +from vyos.utils.network import is_ipv6_link_local  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -42,11 +39,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    relay = conf.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) -    relay = dict_merge(default_values, relay) +    relay = conf.get_config_dict(base, key_mangling=('-', '_'), +                                 get_first_key=True, +                                 with_recursive_defaults=True)      return relay diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py index f89ad5b9c..427001609 100755 --- a/src/conf_mode/dhcpv6_server.py +++ b/src/conf_mode/dhcpv6_server.py @@ -25,7 +25,7 @@ from vyos.template import render  from vyos.template import is_ipv6  from vyos.utils.process import call  from vyos.utils.dict import dict_search -from vyos.validate import is_subnet_connected +from vyos.utils.network import is_subnet_connected  from vyos import ConfigError  from vyos import airbag  airbag.enable() diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py index d78eb70bc..ab80defe8 100755 --- a/src/conf_mode/dns_dynamic.py +++ b/src/conf_mode/dns_dynamic.py @@ -19,10 +19,8 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -49,16 +47,10 @@ def get_config(config=None):      if not conf.exists(base_level):          return None -    dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True) - -    if 'address' in dyndns: -        for address in dyndns['address']: -            # Apply service specific defaults (svc_type = ['rfc2136', 'service']) -            for svc_type in dyndns['address'][address]: -                default_values = defaults(base_level + ['address', svc_type]) -                for svc_cfg in dyndns['address'][address][svc_type]: -                    dyndns['address'][address][svc_type][svc_cfg] = dict_merge( -                        default_values, dyndns['address'][address][svc_type][svc_cfg]) +    dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), +                                  no_tag_node_value_mangle=True, +                                  get_first_key=True, +                                  with_recursive_defaults=True)      dyndns['config_file'] = config_file      return dyndns diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index 2d98bffe3..c186f47af 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -21,14 +21,12 @@ from sys import exit  from glob import glob  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.hostsd_client import Client as hostsd_client  from vyos.template import render  from vyos.template import bracketize_ipv6  from vyos.utils.process import call  from vyos.utils.permission import chown  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -52,31 +50,10 @@ def get_config(config=None):      if not conf.exists(base):          return None -    dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) -    # We have gathered the dict representation of the CLI, but there are default -    # options which we need to update into the dictionary retrieved. -    default_values = defaults(base) -    # T2665 due to how defaults under tag nodes work, we must clear these out before we merge -    del default_values['authoritative_domain'] -    del default_values['name_server'] -    del default_values['domain']['name_server'] -    dns = dict_merge(default_values, dns) - -    # T2665: we cleared default values for tag node 'name_server' above. -    # We now need to add them back back in a granular way. -    if 'name_server' in dns: -        default_values = defaults(base + ['name-server']) -        for server in dns['name_server']: -            dns['name_server'][server] = dict_merge(default_values, dns['name_server'][server]) - -    # T2665: we cleared default values for tag node 'domain' above. -    # We now need to add them back back in a granular way. -    if 'domain' in dns: -        default_values = defaults(base + ['domain', 'name-server']) -        for domain in dns['domain'].keys(): -            for server in dns['domain'][domain]['name_server']: -                dns['domain'][domain]['name_server'][server] = dict_merge( -                    default_values, dns['domain'][domain]['name_server'][server]) +    dns = conf.get_config_dict(base, key_mangling=('-', '_'), +                               no_tag_node_value_mangle=True, +                               get_first_key=True, +                               with_recursive_defaults=True)      # some additions to the default dictionary      if 'system' in dns: @@ -109,9 +86,6 @@ def get_config(config=None):                      rdata = recorddata[rtype][subnode]                      if rtype in [ 'a', 'aaaa' ]: -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'address' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one address is required')                              continue @@ -127,9 +101,6 @@ def get_config(config=None):                                  'value': address                              })                      elif rtype in ['cname', 'ptr', 'ns']: -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'target' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: target is required')                              continue @@ -141,18 +112,12 @@ def get_config(config=None):                              'value': '{}.'.format(rdata['target'])                          })                      elif rtype == 'mx': -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        del rdefaults['server'] -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'server' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one server is required')                              continue                          for servername in rdata['server']:                              serverdata = rdata['server'][servername] -                            serverdefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'server']) # T2665 -                            serverdata = dict_merge(serverdefaults, serverdata)                              zone['records'].append({                                  'name': subnode,                                  'type': rtype.upper(), @@ -160,9 +125,6 @@ def get_config(config=None):                                  'value': '{} {}.'.format(serverdata['priority'], servername)                              })                      elif rtype == 'txt': -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'value' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one value is required')                              continue @@ -175,9 +137,6 @@ def get_config(config=None):                                  'value': "\"{}\"".format(value.replace("\"", "\\\""))                              })                      elif rtype == 'spf': -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'value' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: value is required')                              continue @@ -189,19 +148,12 @@ def get_config(config=None):                              'value': '"{}"'.format(rdata['value'].replace("\"", "\\\""))                          })                      elif rtype == 'srv': -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        del rdefaults['entry'] -                        rdata = dict_merge(rdefaults, rdata) -                          if not 'entry' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one entry is required')                              continue                          for entryno in rdata['entry']:                              entrydata = rdata['entry'][entryno] -                            entrydefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'entry']) # T2665 -                            entrydata = dict_merge(entrydefaults, entrydata) -                              if not 'hostname' in entrydata:                                  dns['authoritative_zone_errors'].append(f'{subnode}.{node}: hostname is required for entry {entryno}')                                  continue @@ -217,19 +169,12 @@ def get_config(config=None):                                  'value': '{} {} {} {}.'.format(entrydata['priority'], entrydata['weight'], entrydata['port'], entrydata['hostname'])                              })                      elif rtype == 'naptr': -                        rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 -                        del rdefaults['rule'] -                        rdata = dict_merge(rdefaults, rdata) - -                          if not 'rule' in rdata:                              dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one rule is required')                              continue                          for ruleno in rdata['rule']:                              ruledata = rdata['rule'][ruleno] -                            ruledefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'rule']) # T2665 -                            ruledata = dict_merge(ruledefaults, ruledata)                              flags = ""                              if 'lookup-srv' in ruledata:                                  flags += "S" diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 07166d457..e946704b3 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -23,7 +23,6 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configdict import node_changed  from vyos.configdiff import get_config_diff, Diff  from vyos.configdep import set_dependents, call_dependents @@ -37,7 +36,6 @@ from vyos.utils.dict import dict_search_args  from vyos.utils.dict import dict_search_recursive  from vyos.utils.process import process_named_running  from vyos.utils.process import rc_cmd -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -97,19 +95,22 @@ def geoip_updated(conf, firewall):      updated = False      for key, path in dict_search_recursive(firewall, 'geoip'): -        set_name = f'GEOIP_CC_{path[1]}_{path[3]}' -        if path[0] == 'name': +        set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}' +        if (path[0] == 'ipv4'):              out['name'].append(set_name) -        elif path[0] == 'ipv6_name': +        elif (path[0] == 'ipv6'): +            set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}'              out['ipv6_name'].append(set_name) +                      updated = True      if 'delete' in node_diff:          for key, path in dict_search_recursive(node_diff['delete'], 'geoip'): -            set_name = f'GEOIP_CC_{path[1]}_{path[3]}' -            if path[0] == 'name': +            set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}' +            if (path[0] == 'ipv4'):                  out['deleted_name'].append(set_name) -            elif path[0] == 'ipv6-name': +            elif (path[0] == 'ipv6'): +                set_name = f'GEOIP_CC_{path[1]}_{path[2]}_{path[4]}'                  out['deleted_ipv6_name'].append(set_name)              updated = True @@ -125,54 +126,17 @@ def get_config(config=None):          conf = Config()      base = ['firewall'] -    firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, -                                    no_tag_node_value_mangle=True) - -    # We have gathered the dict representation of the CLI, but there are -    # default options which we need to update into the dictionary retrived. -    # XXX: T2665: we currently have no nice way for defaults under tag -    # nodes, thus we load the defaults "by hand" -    default_values = defaults(base) -    for tmp in ['name', 'ipv6_name']: -        if tmp in default_values: -            del default_values[tmp] - -    if 'zone' in default_values: -        del default_values['zone'] - -    firewall = dict_merge(default_values, firewall) - -    # Merge in defaults for IPv4 ruleset -    if 'name' in firewall: -        default_values = defaults(base + ['name']) -        for name in firewall['name']: -            firewall['name'][name] = dict_merge(default_values, -                                                firewall['name'][name]) - -    # Merge in defaults for IPv6 ruleset -    if 'ipv6_name' in firewall: -        default_values = defaults(base + ['ipv6-name']) -        for ipv6_name in firewall['ipv6_name']: -            firewall['ipv6_name'][ipv6_name] = dict_merge(default_values, -                                                          firewall['ipv6_name'][ipv6_name]) - -    if 'zone' in firewall: -        default_values = defaults(base + ['zone']) -        for zone in firewall['zone']: -            firewall['zone'][zone] = dict_merge(default_values, firewall['zone'][zone]) +    firewall = conf.get_config_dict(base, key_mangling=('-', '_'), +                                    no_tag_node_value_mangle=True, +                                    get_first_key=True, +                                    with_recursive_defaults=True) +      firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))      if firewall['group_resync']:          # Update nat and policy-route as firewall groups were updated          set_dependents('group_resync', conf) -    if 'config_trap' in firewall and firewall['config_trap'] == 'enable': -        diff = get_config_diff(conf) -        firewall['trap_diff'] = diff.get_child_nodes_diff_str(base) -        firewall['trap_targets'] = conf.get_config_dict(['service', 'snmp', 'trap-target'], -                                        key_mangling=('-', '_'), get_first_key=True, -                                        no_tag_node_value_mangle=True) -      firewall['geoip_updated'] = geoip_updated(conf, firewall)      fqdn_config_parse(firewall) @@ -191,11 +155,11 @@ def verify_rule(firewall, rule_conf, ipv6):              raise ConfigError('jump-target defined, but action jump needed and it is not defined')          target = rule_conf['jump_target']          if not ipv6: -            if target not in dict_search_args(firewall, 'name'): +            if target not in dict_search_args(firewall, 'ipv4', 'name'):                  raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')          else: -            if target not in dict_search_args(firewall, 'ipv6_name'): -                raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system') +            if target not in dict_search_args(firewall, 'ipv6', 'name'): +                raise ConfigError(f'Invalid jump-target. Firewall ipv6 name {target} does not exist on the system')      if 'queue_options' in rule_conf:          if 'queue' not in rule_conf['action']: @@ -312,10 +276,6 @@ def verify_nested_group(group_name, group, groups, seen):              verify_nested_group(g, groups[g], groups, seen)  def verify(firewall): -    if 'config_trap' in firewall and firewall['config_trap'] == 'enable': -        if not firewall['trap_targets']: -            raise ConfigError(f'Firewall config-trap enabled but "service snmp trap-target" is not defined') -      if 'group' in firewall:          for group_type in nested_group_types:              if group_type in firewall['group']: @@ -323,95 +283,45 @@ def verify(firewall):                  for group_name, group in groups.items():                      verify_nested_group(group_name, group, groups, []) -    for name in ['name', 'ipv6_name']: -        if name in firewall: -            for name_id, name_conf in firewall[name].items(): -                if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: -                    raise ConfigError('default-action set to jump, but no default-jump-target specified') -                if 'default_jump_target' in name_conf: -                    target = name_conf['default_jump_target'] -                    if 'jump' not in name_conf['default_action']: -                        raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined') -                    if name_conf['default_jump_target'] == name_id: -                        raise ConfigError(f'Loop detected on default-jump-target.') -                    ## Now need to check that default-jump-target exists (other firewall chain/name) -                    if target not in dict_search_args(firewall, name): -                        raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system') - -                if 'rule' in name_conf: -                    for rule_id, rule_conf in name_conf['rule'].items(): -                        verify_rule(firewall, rule_conf, name == 'ipv6_name') - -    if 'interface' in firewall: -        for ifname, if_firewall in firewall['interface'].items(): -            # verify ifname needs to be disabled, dynamic devices come up later -            # verify_interface_exists(ifname) - -            for direction in ['in', 'out', 'local']: -                name = dict_search_args(if_firewall, direction, 'name') -                ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name') - -                if name and dict_search_args(firewall, 'name', name) == None: -                    raise ConfigError(f'Invalid firewall name "{name}" referenced on interface {ifname}') - -                if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None: -                    raise ConfigError(f'Invalid firewall ipv6-name "{ipv6_name}" referenced on interface {ifname}') - -    local_zone = False -    zone_interfaces = [] - -    if 'zone' in firewall: -        for zone, zone_conf in firewall['zone'].items(): -            if 'local_zone' not in zone_conf and 'interface' not in zone_conf: -                raise ConfigError(f'Zone "{zone}" has no interfaces and is not the local zone') - -            if 'local_zone' in zone_conf: -                if local_zone: -                    raise ConfigError('There cannot be multiple local zones') -                if 'interface' in zone_conf: -                    raise ConfigError('Local zone cannot have interfaces assigned') -                if 'intra_zone_filtering' in zone_conf: -                    raise ConfigError('Local zone cannot use intra-zone-filtering') -                local_zone = True - -            if 'interface' in zone_conf: -                found_duplicates = [intf for intf in zone_conf['interface'] if intf in zone_interfaces] - -                if found_duplicates: -                    raise ConfigError(f'Interfaces cannot be assigned to multiple zones') - -                zone_interfaces += zone_conf['interface'] - -            if 'intra_zone_filtering' in zone_conf: -                intra_zone = zone_conf['intra_zone_filtering'] - -                if len(intra_zone) > 1: -                    raise ConfigError('Only one intra-zone-filtering action must be specified') - -                if 'firewall' in intra_zone: -                    v4_name = dict_search_args(intra_zone, 'firewall', 'name') -                    if v4_name and not dict_search_args(firewall, 'name', v4_name): -                        raise ConfigError(f'Firewall name "{v4_name}" does not exist') - -                    v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6_name') -                    if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name): -                        raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist') - -                    if not v4_name and not v6_name: -                        raise ConfigError('No firewall names specified for intra-zone-filtering') - -            if 'from' in zone_conf: -                for from_zone, from_conf in zone_conf['from'].items(): -                    if from_zone not in firewall['zone']: -                        raise ConfigError(f'Zone "{zone}" refers to a non-existent or deleted zone "{from_zone}"') - -                    v4_name = dict_search_args(from_conf, 'firewall', 'name') -                    if v4_name and not dict_search_args(firewall, 'name', v4_name): -                        raise ConfigError(f'Firewall name "{v4_name}" does not exist') - -                    v6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name') -                    if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name): -                        raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist') +    if 'ipv4' in firewall: +        for name in ['name','forward','input','output']: +            if name in firewall['ipv4']: +                for name_id, name_conf in firewall['ipv4'][name].items(): +                    if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: +                        raise ConfigError('default-action set to jump, but no default-jump-target specified') +                    if 'default_jump_target' in name_conf: +                        target = name_conf['default_jump_target'] +                        if 'jump' not in name_conf['default_action']: +                            raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined') +                        if name_conf['default_jump_target'] == name_id: +                            raise ConfigError(f'Loop detected on default-jump-target.') +                        ## Now need to check that default-jump-target exists (other firewall chain/name) +                        if target not in dict_search_args(firewall['ipv4'], 'name'): +                            raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') + +                    if 'rule' in name_conf: +                        for rule_id, rule_conf in name_conf['rule'].items(): +                            verify_rule(firewall, rule_conf, False) + +    if 'ipv6' in firewall: +        for name in ['name','forward','input','output']: +            if name in firewall['ipv6']: +                for name_id, name_conf in firewall['ipv6'][name].items(): +                    if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf: +                        raise ConfigError('default-action set to jump, but no default-jump-target specified') +                    if 'default_jump_target' in name_conf: +                        target = name_conf['default_jump_target'] +                        if 'jump' not in name_conf['default_action']: +                            raise ConfigError('default-jump-target defined, but default-action jump needed and it is not defined') +                        if name_conf['default_jump_target'] == name_id: +                            raise ConfigError(f'Loop detected on default-jump-target.') +                        ## Now need to check that default-jump-target exists (other firewall chain/name) +                        if target not in dict_search_args(firewall['ipv6'], 'name'): +                            raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system') + +                    if 'rule' in name_conf: +                        for rule_id, rule_conf in name_conf['rule'].items(): +                            verify_rule(firewall, rule_conf, True)      return None @@ -419,19 +329,6 @@ def generate(firewall):      if not os.path.exists(nftables_conf):          firewall['first_install'] = True -    if 'zone' in firewall: -        for local_zone, local_zone_conf in firewall['zone'].items(): -            if 'local_zone' not in local_zone_conf: -                continue - -            local_zone_conf['from_local'] = {} - -            for zone, zone_conf in firewall['zone'].items(): -                if zone == local_zone or 'from' not in zone_conf: -                    continue -                if local_zone in zone_conf['from']: -                    local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone] -      render(nftables_conf, 'firewall/nftables.j2', firewall)      return None @@ -440,9 +337,8 @@ def apply_sysfs(firewall):          paths = glob(conf['sysfs'])          value = None -        if name in firewall: -            conf_value = firewall[name] - +        if name in firewall['global_options']: +            conf_value = firewall['global_options'][name]              if conf_value in conf:                  value = conf[conf_value]              elif conf_value == 'enable': @@ -459,9 +355,6 @@ def post_apply_trap(firewall):      if 'first_install' in firewall:          return None -    if 'config_trap' not in firewall or firewall['config_trap'] != 'enable': -        return None -      if not process_named_running('snmpd'):          return None diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index bfe906c87..71acd69fa 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -22,14 +22,13 @@ from ipaddress import ip_address  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge +from vyos.config import config_dict_merge  from vyos.configverify import verify_vrf  from vyos.ifconfig import Section  from vyos.template import render  from vyos.utils.process import call  from vyos.utils.process import cmd -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -128,30 +127,19 @@ def get_config(config=None):      flow_accounting = conf.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) +    # We have gathered the dict representation of the CLI, but there are +    # default values which we need to conditionally update into the +    # dictionary retrieved. +    default_values = conf.get_config_defaults(**flow_accounting.kwargs, +                                              recursive=True) -    # delete individual flow type default - should only be added if user uses -    # this feature +    # delete individual flow type defaults - should only be added if user +    # sets this feature      for flow_type in ['sflow', 'netflow']: -        if flow_type in default_values: +        if flow_type not in flow_accounting and flow_type in default_values:              del default_values[flow_type] -    flow_accounting = dict_merge(default_values, flow_accounting) -    for flow_type in ['sflow', 'netflow']: -        if flow_type in flow_accounting: -            default_values = defaults(base + [flow_type]) -            # we need to merge individual server configurations -            if 'server' in default_values: -                del default_values['server'] -            flow_accounting[flow_type] = dict_merge(default_values, flow_accounting[flow_type]) - -            if 'server' in flow_accounting[flow_type]: -                default_values = defaults(base + [flow_type, 'server']) -                for server in flow_accounting[flow_type]['server']: -                    flow_accounting[flow_type]['server'][server] = dict_merge( -                        default_values,flow_accounting[flow_type]['server'][server]) +    flow_accounting = config_dict_merge(default_values, flow_accounting)      return flow_accounting diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py index 0cbd4c49c..626a3757e 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -14,7 +14,6 @@  # 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 ipaddress import ip_interface @@ -23,14 +22,11 @@ from ipaddress import IPv6Interface  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.ifconfig.vrrp import VRRP  from vyos.template import render  from vyos.template import is_ipv4  from vyos.template import is_ipv6  from vyos.utils.process import call -from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -42,42 +38,12 @@ def get_config(config=None):          conf = Config()      base = ['high-availability'] -    base_vrrp = ['high-availability', 'vrrp']      if not conf.exists(base):          return None      ha = conf.get_config_dict(base, key_mangling=('-', '_'), -                                get_first_key=True, no_tag_node_value_mangle=True) -    # We have gathered the dict representation of the CLI, but there are default -    # options which we need to update into the dictionary retrived. -    if 'vrrp' in ha: -        if dict_search('vrrp.global_parameters.garp', ha) != None: -            default_values = defaults(base_vrrp + ['global-parameters', 'garp']) -            ha['vrrp']['global_parameters']['garp'] = dict_merge( -                default_values, ha['vrrp']['global_parameters']['garp']) - -        if 'group' in ha['vrrp']: -            default_values = defaults(base_vrrp + ['group']) -            default_values_garp = defaults(base_vrrp + ['group', 'garp']) - -            # XXX: T2665: we can not safely rely on the defaults() when there are -            # tagNodes in place, it is better to blend in the defaults manually. -            if 'garp' in default_values: -                del default_values['garp'] -            for group in ha['vrrp']['group']: -                ha['vrrp']['group'][group] = dict_merge(default_values, ha['vrrp']['group'][group]) - -                # XXX: T2665: we can not safely rely on the defaults() when there are -                # tagNodes in place, it is better to blend in the defaults manually. -                if 'garp' in ha['vrrp']['group'][group]: -                    ha['vrrp']['group'][group]['garp'] = dict_merge( -                        default_values_garp, ha['vrrp']['group'][group]['garp']) - -    # Merge per virtual-server default values -    if 'virtual_server' in ha: -        default_values = defaults(base + ['virtual-server']) -        for vs in ha['virtual_server']: -            ha['virtual_server'][vs] = dict_merge(default_values, ha['virtual_server'][vs]) +                              no_tag_node_value_mangle=True, +                              get_first_key=True, with_defaults=True)      ## Get the sync group used for conntrack-sync      conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group'] @@ -112,7 +78,7 @@ def verify(ha):                  from vyos.utils.dict import check_mutually_exclusive_options                  try:                      check_mutually_exclusive_options(group_config["health_check"], health_check_types, required=True) -                except ValueError as e: +                except ValueError:                      Warning(f'Health check configuration for VRRP group "{group}" will remain unused ' \                              f'until it has one of the following options: {health_check_types}')                      # XXX: health check has default options so we need to remove it diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index 7bdf448a3..793a90d88 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -24,12 +24,9 @@ from copy import deepcopy  import vyos.defaults  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configdep import set_dependents, call_dependents  from vyos.template import render -from vyos.utils.process import cmd  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -72,8 +69,9 @@ def get_config(config=None):          return None      api_dict = conf.get_config_dict(base, key_mangling=('-', '_'), -                                          no_tag_node_value_mangle=True, -                                          get_first_key=True) +                                    no_tag_node_value_mangle=True, +                                    get_first_key=True, +                                    with_recursive_defaults=True)      # One needs to 'flatten' the keys dict from the config into the      # http-api.conf format for api_keys: @@ -93,8 +91,8 @@ def get_config(config=None):      if 'api_keys' in api_dict:          keys_added = True -    if 'graphql' in api_dict: -        api_dict = dict_merge(defaults(base), api_dict) +    if api_dict.from_defaults(['graphql']): +        del api_dict['graphql']      http_api.update(api_dict) diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py index 4ec2f1835..40db417dd 100755 --- a/src/conf_mode/igmp_proxy.py +++ b/src/conf_mode/igmp_proxy.py @@ -21,11 +21,9 @@ from netifaces import interfaces  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -39,16 +37,9 @@ def get_config(config=None):          conf = Config()      base = ['protocols', 'igmp-proxy'] -    igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - -    if 'interface' in igmp_proxy: -        # T2665: we must add the tagNode defaults individually until this is -        # moved to the base class -        default_values = defaults(base + ['interface']) -        for interface in igmp_proxy['interface']: -            igmp_proxy['interface'][interface] = dict_merge(default_values, -                igmp_proxy['interface'][interface]) - +    igmp_proxy = conf.get_config_dict(base, key_mangling=('-', '_'), +                                      get_first_key=True, +                                      with_defaults=True)      if conf.exists(['protocols', 'igmp']):          igmp_proxy.update({'igmp_configured': ''}) diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py index c2a569fa9..0bd306ed0 100755 --- a/src/conf_mode/interfaces-bonding.py +++ b/src/conf_mode/interfaces-bonding.py @@ -36,8 +36,8 @@ from vyos.configverify import verify_vrf  from vyos.ifconfig import BondIf  from vyos.ifconfig import Section  from vyos.utils.dict import dict_search -from vyos.validate import has_address_configured -from vyos.validate import has_vrf_configured +from vyos.configdict import has_address_configured +from vyos.configdict import has_vrf_configured  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -195,11 +195,11 @@ def verify(bond):                  raise ConfigError(error_msg + 'it does not exist!')              if 'is_bridge_member' in interface_config: -                tmp = interface_config['is_bridge_member'] +                tmp = next(iter(interface_config['is_bridge_member']))                  raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')              if 'is_bond_member' in interface_config: -                tmp = interface_config['is_bond_member'] +                tmp = next(iter(interface_config['is_bond_member']))                  raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')              if 'is_source_interface' in interface_config: diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 087ead20a..c82f01e53 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -14,10 +14,7 @@  # 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 netifaces import interfaces  from vyos.config import Config  from vyos.configdict import get_interface_dict @@ -25,16 +22,13 @@ from vyos.configdict import node_changed  from vyos.configdict import is_member  from vyos.configdict import is_source_interface  from vyos.configdict import has_vlan_subinterface_configured -from vyos.configdict import dict_merge  from vyos.configverify import verify_dhcpv6  from vyos.configverify import verify_mirror_redirect  from vyos.configverify import verify_vrf  from vyos.ifconfig import BridgeIf -from vyos.validate import has_address_configured -from vyos.validate import has_vrf_configured -from vyos.xml import defaults +from vyos.configdict import has_address_configured +from vyos.configdict import has_vrf_configured -from vyos.utils.process import cmd  from vyos.utils.dict import dict_search  from vyos import ConfigError @@ -61,22 +55,8 @@ def get_config(config=None):          else:              bridge.update({'member' : {'interface_remove' : tmp }}) -    if dict_search('member.interface', bridge) != None: -        # XXX: T2665: we need a copy of the dict keys for iteration, else we will get: -        # RuntimeError: dictionary changed size during iteration +    if dict_search('member.interface', bridge) is not None:          for interface in list(bridge['member']['interface']): -            for key in ['cost', 'priority']: -                if interface == key: -                    del bridge['member']['interface'][key] -                    continue - -        # the default dictionary is not properly paged into the dict (see T2665) -        # thus we will ammend it ourself -        default_member_values = defaults(base + ['member', 'interface']) -        for interface,interface_config in bridge['member']['interface'].items(): -            bridge['member']['interface'][interface] = dict_merge( -                    default_member_values, bridge['member']['interface'][interface]) -              # Check if member interface is already member of another bridge              tmp = is_member(conf, interface, 'bridge')              if tmp and bridge['ifname'] not in tmp: diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 6efeac302..e1db3206e 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -29,7 +29,7 @@ from vyos.configverify import verify_mirror_redirect  from vyos.configverify import verify_bond_bridge_member  from vyos.ifconfig import L2TPv3If  from vyos.utils.kernel import check_kmod -from vyos.validate import is_addr_assigned +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 3bef9b8f6..1d0feb56f 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -61,7 +61,7 @@ from vyos.utils.kernel import unload_kmod  from vyos.utils.process import call  from vyos.utils.permission import chown  from vyos.utils.process import cmd -from vyos.validate import is_addr_assigned +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag @@ -166,17 +166,23 @@ def verify_pki(openvpn):              raise ConfigError(f'Invalid shared-secret on openvpn interface {interface}')      if tls: -        if 'ca_certificate' not in tls: -            raise ConfigError(f'Must specify "tls ca-certificate" on openvpn interface {interface}') +        if (mode in ['server', 'client']) and ('ca_certificate' not in tls): +            raise ConfigError(f'Must specify "tls ca-certificate" on openvpn interface {interface},\ +              it is required in server and client modes') +        else: +            if ('ca_certificate' not in tls) and ('peer_fingerprint' not in tls): +                raise ConfigError('Either "tls ca-certificate" or "tls peer-fingerprint" is required\ +                  on openvpn interface {interface} in site-to-site mode') -        for ca_name in tls['ca_certificate']: -            if ca_name not in pki['ca']: -                raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}') +        if 'ca_certificate' in tls: +            for ca_name in tls['ca_certificate']: +                if ca_name not in pki['ca']: +                    raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}') -        if len(tls['ca_certificate']) > 1: -            sorted_chain = sort_ca_chain(tls['ca_certificate'], pki['ca']) -            if not verify_ca_chain(sorted_chain, pki['ca']): -                raise ConfigError(f'CA certificates are not a valid chain') +            if len(tls['ca_certificate']) > 1: +                sorted_chain = sort_ca_chain(tls['ca_certificate'], pki['ca']) +                if not verify_ca_chain(sorted_chain, pki['ca']): +                    raise ConfigError(f'CA certificates are not a valid chain')          if mode != 'client' and 'auth_key' not in tls:              if 'certificate' not in tls: @@ -189,16 +195,7 @@ def verify_pki(openvpn):              if dict_search_args(pki, 'certificate', tls['certificate'], 'private', 'password_protected') is not None:                  raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}') -            if mode == 'server' and 'dh_params' not in tls and not is_ec_private_key(pki, tls['certificate']): -                raise ConfigError('Must specify "tls dh-params" when not using EC keys in server mode') -          if 'dh_params' in tls: -            if 'dh' not in pki: -                raise ConfigError('There are no DH parameters in PKI configuration') - -            if tls['dh_params'] not in pki['dh']: -                raise ConfigError(f'Invalid dh-params on openvpn interface {interface}') -              pki_dh = pki['dh'][tls['dh_params']]              dh_params = load_dh_parameters(pki_dh['parameters'])              dh_numbers = dh_params.parameter_numbers() @@ -207,6 +204,7 @@ def verify_pki(openvpn):              if dh_bits < 2048:                  raise ConfigError(f'Minimum DH key-size is 2048 bits') +          if 'auth_key' in tls or 'crypt_key' in tls:              if not dict_search_args(pki, 'openvpn', 'shared_secret'):                  raise ConfigError('There are no openvpn shared-secrets in PKI configuration') @@ -495,9 +493,6 @@ def verify(openvpn):                  if openvpn['protocol'] == 'tcp-active':                      raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"') -                if not dict_search('tls.dh_params', openvpn): -                    raise ConfigError('Must specify "tls dh-params" when "tls role" is "passive"') -          if 'certificate' in openvpn['tls'] and is_ec_private_key(openvpn['pki'], openvpn['tls']['certificate']):              if 'dh_params' in openvpn['tls']:                  print('Warning: using dh-params and EC keys simultaneously will ' \ diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index 6a075970e..91aed9cc3 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -55,6 +55,9 @@ def get_config(config=None):          tmp = is_node_changed(conf, base + [ifname, 'encapsulation'])          if tmp: tunnel.update({'encapsulation_changed': {}}) +        tmp = is_node_changed(conf, base + [ifname, 'parameters', 'ip', 'key']) +        if tmp: tunnel.update({'key_changed': {}}) +          # We also need to inspect other configured tunnels as there are Kernel          # restrictions where we need to comply. E.g. GRE tunnel key can't be used          # twice, or with multiple GRE tunnels to the same location we must specify @@ -197,7 +200,8 @@ def apply(tunnel):          remote = dict_search('linkinfo.info_data.remote', tmp)      if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or encap in -        ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any']): +        ['gretap', 'ip6gretap', 'erspan', 'ip6erspan'] or remote in ['any'] or +        'key_changed' in tunnel):          if interface in interfaces():              tmp = Interface(interface)              tmp.remove() diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index b1536148c..a3b0867e0 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -87,8 +87,8 @@ def verify(vxlan):              raise ConfigError('Multicast VXLAN requires an underlaying interface')          verify_source_interface(vxlan) -    if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan): -        raise ConfigError('Group, remote or source-address must be configured') +    if not any(tmp in ['group', 'remote', 'source_address', 'source_interface'] for tmp in vxlan): +        raise ConfigError('Group, remote, source-address or source-interface must be configured')      if 'vni' not in vxlan and 'external' not in vxlan:          raise ConfigError( diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index c0f3f4d6e..122d9589a 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -29,7 +29,7 @@ from vyos.configverify import verify_bond_bridge_member  from vyos.ifconfig import WireGuardIf  from vyos.utils.kernel import check_kmod  from vyos.utils.network import check_port_availability -from vyos.validate import is_wireguard_key_pair +from vyos.utils.network import is_wireguard_key_pair  from vyos import ConfigError  from vyos import airbag  airbag.enable() diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py index 42326bea0..e49ad25ac 100755 --- a/src/conf_mode/interfaces-wireless.py +++ b/src/conf_mode/interfaces-wireless.py @@ -79,27 +79,9 @@ def get_config(config=None):      ifname, wifi = get_interface_dict(conf, base) -    # Cleanup "delete" default values when required user selectable values are -    # not defined at all -    tmp = conf.get_config_dict(base + [ifname], key_mangling=('-', '_'), -                               get_first_key=True) -    if not (dict_search('security.wpa.passphrase', tmp) or -            dict_search('security.wpa.radius', tmp)): -        if 'deleted' not in wifi: -            del wifi['security']['wpa'] -            # if 'security' key is empty, drop it too -            if len(wifi['security']) == 0: -                del wifi['security'] - -    # 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 dict_search('security.wpa.radius.server.port', wifi) != None: -        del wifi['security']['wpa']['radius']['server']['port'] -        if not len(wifi['security']['wpa']['radius']['server']): -            del wifi['security']['wpa']['radius'] -        if not len(wifi['security']['wpa']): -            del wifi['security']['wpa'] -        if not len(wifi['security']): +    if 'deleted' not in wifi: +        # then get_interface_dict provides default keys +        if wifi.from_defaults(['security']): # if not set by user              del wifi['security']      if 'security' in wifi and 'wpa' in wifi['security']: @@ -120,14 +102,6 @@ def get_config(config=None):      tmp = find_other_stations(conf, base, wifi['ifname'])      if tmp: wifi['station_interfaces'] = tmp -    # Add individual RADIUS server default values -    if dict_search('security.wpa.radius.server', wifi): -        default_values = defaults(base + ['security', 'wpa', 'radius', 'server']) - -        for server in dict_search('security.wpa.radius.server', wifi): -            wifi['security']['wpa']['radius']['server'][server] = dict_merge( -                default_values, wifi['security']['wpa']['radius']['server'][server]) -      return wifi  def verify(wifi): diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 0e5fc29d3..c2e87d171 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -20,13 +20,11 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.validate import is_addr_assigned -from vyos.validate import is_loopback_addr +from vyos.utils.network import is_addr_assigned +from vyos.utils.network import is_loopback_addr  from vyos.version import get_version_data  from vyos.utils.process import call  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos.template import render  from vyos import ConfigError  from vyos import airbag @@ -46,7 +44,9 @@ def get_config(config=None):          return {}      lldp = conf.get_config_dict(base, key_mangling=('-', '_'), -                                get_first_key=True, no_tag_node_value_mangle=True) +                                no_tag_node_value_mangle=True, +                                get_first_key=True, +                                with_recursive_defaults=True)      if conf.exists(['service', 'snmp']):          lldp['system_snmp_enabled'] = '' @@ -54,27 +54,12 @@ def get_config(config=None):      version_data = get_version_data()      lldp['version'] = version_data['version'] -    # We have gathered the dict representation of the CLI, but there are default -    # options which we need to update into the dictionary retrived. -    # location coordinates have a default value -    if 'interface' in lldp: -        for interface, interface_config in lldp['interface'].items(): -            default_values = defaults(base + ['interface']) -            if dict_search('location.coordinate_based', interface_config) == None: -                # no location specified - no need to add defaults -                del default_values['location']['coordinate_based']['datum'] -                del default_values['location']['coordinate_based']['altitude'] - -            # cleanup default_values dictionary from inner to outer -            # this might feel overkill here, but it does support easy extension -            # in the future with additional default values -            if len(default_values['location']['coordinate_based']) == 0: -                del default_values['location']['coordinate_based'] -            if len(default_values['location']) == 0: -                del default_values['location'] - -            lldp['interface'][interface] = dict_merge(default_values, -                                                   lldp['interface'][interface]) +    # prune location information if not set by user +    for interface in lldp.get('interface', []): +        if lldp.from_defaults(['interface', interface, 'location']): +            del lldp['interface'][interface]['location'] +        elif lldp.from_defaults(['interface', interface, 'location','coordinate_based']): +            del lldp['interface'][interface]['location']['coordinate_based']      return lldp diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py index 2fb0edf8e..8fe429653 100755 --- a/src/conf_mode/load-balancing-haproxy.py +++ b/src/conf_mode/load-balancing-haproxy.py @@ -20,14 +20,12 @@ from sys import exit  from shutil import rmtree  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.utils.process import call  from vyos.utils.network import check_port_availability  from vyos.utils.network import is_listen_port_bind_service  from vyos.pki import wrap_certificate  from vyos.pki import wrap_private_key  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -54,18 +52,8 @@ def get_config(config=None):          lb['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),                                      get_first_key=True, no_tag_node_value_mangle=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) -    if 'backend' in default_values: -        del default_values['backend']      if lb: -        lb = dict_merge(default_values, lb) - -    if 'backend' in lb: -        for backend in lb['backend']: -            default_balues_backend = defaults(base + ['backend']) -            lb['backend'][backend] = dict_merge(default_balues_backend, lb['backend'][backend]) +        lb = conf.merge_defaults(lb, recursive=True)      return lb diff --git a/src/conf_mode/load-balancing-wan.py b/src/conf_mode/load-balancing-wan.py index 3533a5a04..ad9c80d72 100755 --- a/src/conf_mode/load-balancing-wan.py +++ b/src/conf_mode/load-balancing-wan.py @@ -21,10 +21,8 @@ from shutil import rmtree  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.utils.process import cmd  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -41,48 +39,15 @@ def get_config(config=None):          conf = Config()      base = ['load-balancing', 'wan'] -    lb = conf.get_config_dict(base, +    lb = conf.get_config_dict(base, key_mangling=('-', '_'), +                              no_tag_node_value_mangle=True,                                get_first_key=True, -                              key_mangling=('-', '_'), -                              no_tag_node_value_mangle=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) -    # lb base default values can not be merged here - remove and add them later -    if 'interface_health' in default_values: -        del default_values['interface_health'] -    if 'rule' in default_values: -        del default_values['rule'] -    lb = dict_merge(default_values, lb) - -    if 'interface_health' in lb: -        for iface in lb.get('interface_health'): -            default_values_iface = defaults(base + ['interface-health']) -            if 'test' in default_values_iface: -                del default_values_iface['test'] -            lb['interface_health'][iface] = dict_merge( -                default_values_iface, lb['interface_health'][iface]) -            if 'test' in lb['interface_health'][iface]: -                for node_test in lb['interface_health'][iface]['test']: -                    default_values_test = defaults(base + -                                                   ['interface-health', 'test']) -                    lb['interface_health'][iface]['test'][node_test] = dict_merge( -                            default_values_test, -                            lb['interface_health'][iface]['test'][node_test]) - -    if 'rule' in lb: -        for rule in lb.get('rule'): -            default_values_rule = defaults(base + ['rule']) -            if 'interface' in default_values_rule: -                del default_values_rule['interface'] -            lb['rule'][rule] = dict_merge(default_values_rule, lb['rule'][rule]) -            if not conf.exists(base + ['rule', rule, 'limit']): -                del lb['rule'][rule]['limit'] -            if 'interface' in lb['rule'][rule]: -                for iface in lb['rule'][rule]['interface']: -                    default_values_rule_iface = defaults(base + ['rule', 'interface']) -                    lb['rule'][rule]['interface'][iface] = dict_merge(default_values_rule_iface, lb['rule'][rule]['interface'][iface]) +                              with_recursive_defaults=True) + +    # prune limit key if not set by user +    for rule in lb.get('rule', []): +        if lb.from_defaults(['rule', rule, 'limit']): +            del lb['rule'][rule]['limit']      return lb diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index e19b12937..f9d711b36 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -25,7 +25,6 @@ from netifaces import interfaces  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.template import is_ip_network  from vyos.utils.kernel import check_kmod @@ -33,8 +32,7 @@ from vyos.utils.dict import dict_search  from vyos.utils.dict import dict_search_args  from vyos.utils.process import cmd  from vyos.utils.process import run -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag @@ -126,6 +124,18 @@ def verify_rule(config, err_msg, groups_dict):                  if config['protocol'] not in ['tcp', 'udp', 'tcp_udp']:                      raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port-group') +    if 'load_balance' in config: +        for item in ['source-port', 'destination-port']: +            if item in config['load_balance']['hash'] and config['protocol'] not in ['tcp', 'udp']: +                raise ConfigError('Protocol must be tcp or udp when specifying hash ports') +        count = 0 +        if 'backend' in config['load_balance']: +            for member in config['load_balance']['backend']: +                weight = config['load_balance']['backend'][member]['weight'] +                count = count +  int(weight) +            if count != 100: +                Warning(f'Sum of weight for nat load balance rule is not 100. You may get unexpected behaviour') +  def get_config(config=None):      if config:          conf = config @@ -133,16 +143,9 @@ def get_config(config=None):          conf = Config()      base = ['nat'] -    nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) - -    # T2665: we must add the tagNode defaults individually until this is -    # moved to the base class -    for direction in ['source', 'destination', 'static']: -        if direction in nat: -            default_values = defaults(base + [direction, 'rule']) -            for rule in dict_search(f'{direction}.rule', nat) or []: -                nat[direction]['rule'][rule] = dict_merge(default_values, -                    nat[direction]['rule'][rule]) +    nat = conf.get_config_dict(base, key_mangling=('-', '_'), +                               get_first_key=True, +                               with_recursive_defaults=True)      # read in current nftable (once) for further processing      tmp = cmd('nft -j list table raw') @@ -199,7 +202,7 @@ def verify(nat):                  Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')              if not dict_search('translation.address', config) and not dict_search('translation.port', config): -                if 'exclude' not in config: +                if 'exclude' not in config and 'backend' not in config['load_balance']:                      raise ConfigError(f'{err_msg} translation requires address and/or port')              addr = dict_search('translation.address', config) @@ -211,7 +214,6 @@ def verify(nat):              # common rule verification              verify_rule(config, err_msg, nat['firewall_group']) -      if dict_search('destination.rule', nat):          for rule, config in dict_search('destination.rule', nat).items():              err_msg = f'Destination NAT configuration error in rule {rule}:' @@ -223,7 +225,7 @@ def verify(nat):                  Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')              if not dict_search('translation.address', config) and not dict_search('translation.port', config) and not dict_search('translation.redirect.port', config): -                if 'exclude' not in config: +                if 'exclude' not in config and 'backend' not in config['load_balance']:                      raise ConfigError(f'{err_msg} translation requires address and/or port')              # common rule verification diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 25f625b84..4c12618bc 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -23,13 +23,11 @@ from netifaces import interfaces  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import cmd  from vyos.utils.kernel import check_kmod  from vyos.utils.dict import dict_search  from vyos.template import is_ipv6 -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -60,16 +58,6 @@ def get_config(config=None):      base = ['nat66']      nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) -    # T2665: we must add the tagNode defaults individually until this is -    # moved to the base class -    for direction in ['source', 'destination']: -        if direction in nat: -            default_values = defaults(base + [direction, 'rule']) -            if 'rule' in nat[direction]: -                for rule in nat[direction]['rule']: -                    nat[direction]['rule'][rule] = dict_merge(default_values, -                        nat[direction]['rule'][rule]) -      # read in current nftable (once) for further processing      tmp = cmd('nft -j list table ip6 raw')      nftable_json = json.loads(tmp) diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py index eb8cb3940..34ba2fe69 100755 --- a/src/conf_mode/pki.py +++ b/src/conf_mode/pki.py @@ -18,7 +18,6 @@ from sys import exit  from vyos.config import Config  from vyos.configdep import set_dependents, call_dependents -from vyos.configdict import dict_merge  from vyos.configdict import node_changed  from vyos.pki import is_ca_certificate  from vyos.pki import load_certificate @@ -28,7 +27,6 @@ from vyos.pki import load_crl  from vyos.pki import load_dh_parameters  from vyos.utils.dict import dict_search_args  from vyos.utils.dict import dict_search_recursive -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -113,8 +111,7 @@ def get_config(config=None):      # We only merge on the defaults of there is a configuration at all      if conf.exists(base): -        default_values = defaults(base) -        pki = dict_merge(default_values, pki) +        pki = conf.merge_defaults(pki, recursive=True)      # We need to get the entire system configuration to verify that we are not      # deleting a certificate that is still referenced somewhere! diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py index f5ac56f65..104711b55 100755 --- a/src/conf_mode/protocols_babel.py +++ b/src/conf_mode/protocols_babel.py @@ -19,13 +19,13 @@ import os  from sys import exit  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  from vyos.configverify import verify_access_list  from vyos.configverify import verify_prefix_list  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos.template import render_to_string  from vyos import ConfigError  from vyos import frr @@ -38,7 +38,8 @@ def get_config(config=None):      else:          conf = Config()      base = ['protocols', 'babel'] -    babel = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    babel = conf.get_config_dict(base, key_mangling=('-', '_'), +                                 get_first_key=True)      # FRR has VRF support for different routing daemons. As interfaces belong      # to VRFs - or the global VRF, we need to check for changed interfaces so @@ -54,15 +55,13 @@ def get_config(config=None):          return babel      # 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) +    # values which we need to update into the dictionary retrieved. +    default_values = conf.get_config_defaults(base, key_mangling=('-', '_'), +                                              get_first_key=True, +                                              recursive=True) -    # XXX: T2665: we currently have no nice way for defaults under tag nodes, -    # clean them out and add them manually :( -    del default_values['interface'] - -    # merge in remaining default values -    babel = dict_merge(default_values, babel) +    # merge in default values +    babel = config_dict_merge(default_values, babel)      # We also need some additional information from the config, prefix-lists      # and route-maps for instance. They will be used in verify(). diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 0436abaf9..dab784662 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -17,12 +17,10 @@  import os  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_vrf  from vyos.template import is_ipv6  from vyos.template import render_to_string -from vyos.validate import is_ipv6_link_local -from vyos.xml import defaults +from vyos.utils.network import is_ipv6_link_local  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -41,18 +39,7 @@ def get_config(config=None):      if not conf.exists(base):          return bfd -    # We have gathered the dict representation of the CLI, but there are -    # default options which we need to update into the dictionary retrived. -    # XXX: T2665: we currently have no nice way for defaults under tag -    # nodes, thus we load the defaults "by hand" -    default_values = defaults(base + ['peer']) -    if 'peer' in bfd: -        for peer in bfd['peer']: -            bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer]) - -    if 'profile' in bfd: -        for profile in bfd['profile']: -            bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile]) +    bfd = conf.merge_defaults(bfd, recursive=True)      return bfd diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 7b9f15505..00015023c 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -29,7 +29,7 @@ from vyos.template import is_interface  from vyos.template import render_to_string  from vyos.utils.dict import dict_search  from vyos.utils.network import get_interface_vrf -from vyos.validate import is_addr_assigned +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import frr  from vyos import airbag diff --git a/src/conf_mode/protocols_failover.py b/src/conf_mode/protocols_failover.py index faf56d741..e7e44db84 100755 --- a/src/conf_mode/protocols_failover.py +++ b/src/conf_mode/protocols_failover.py @@ -19,10 +19,8 @@ import json  from pathlib import Path  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -42,15 +40,12 @@ def get_config(config=None):          conf = Config()      base = ['protocols', 'failover'] -    failover = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    failover = conf.get_config_dict(base, key_mangling=('-', '_'), +                                    get_first_key=True)      # Set default values only if we set config -    if failover.get('route'): -        for route, route_config in failover.get('route').items(): -            for next_hop, next_hop_config in route_config.get('next_hop').items(): -                default_values = defaults(base + ['route']) -                failover['route'][route]['next_hop'][next_hop] = dict_merge( -                    default_values['next_hop'], failover['route'][route]['next_hop'][next_hop]) +    if failover.get('route') is not None: +        failover = conf.merge_defaults(failover, recursive=True)      return failover diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index 4c637a99f..e00c58ee4 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -28,7 +28,6 @@ from vyos.ifconfig import Interface  from vyos.utils.dict import dict_search  from vyos.utils.network import get_interface_config  from vyos.template import render_to_string -from vyos.xml import defaults  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -64,19 +63,14 @@ def get_config(config=None):      if interfaces_removed:          isis['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):          isis.update({'deleted' : ''})          return isis -    # 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)      # merge in default values -    isis = dict_merge(default_values, isis) +    isis = conf.merge_defaults(isis, recursive=True)      # We also need some additional information from the config, prefix-lists      # and route-maps for instance. They will be used in verify(). @@ -254,7 +248,7 @@ def apply(isis):          if key not in isis:              continue          for interface in isis[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_isisd_config' in isis:          frr_cfg.add_before(frr.default_add_before, isis['frr_isisd_config']) diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index f2075d25b..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 @@ -29,7 +30,6 @@ from vyos.configverify import verify_access_list  from vyos.template import render_to_string  from vyos.utils.dict import dict_search  from vyos.utils.network import get_interface_config -from vyos.xml import defaults  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,62 +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 ['babel', '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]) +    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 '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]) - -    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(). @@ -287,7 +250,7 @@ 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']) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index fbea51f56..5b1adce30 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -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 @@ -29,7 +30,6 @@ from vyos.template import render_to_string  from vyos.ifconfig import Interface  from vyos.utils.dict import dict_search  from vyos.utils.network import get_interface_config -from vyos.xml import defaults  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -64,17 +64,16 @@ def get_config(config=None):      if interfaces_removed:          ospfv3['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):          ospfv3.update({'deleted' : ''})          return ospfv3      # 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(**ospfv3.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 @@ -86,12 +85,10 @@ def get_config(config=None):      if 'graceful_restart' not in ospfv3:          del default_values['graceful_restart'] -    # XXX: T2665: we currently have no nice way for defaults under tag nodes, -    # clean them out and add them manually :( -    del default_values['interface'] +    default_values.pop('interface', {})      # merge in remaining default values -    ospfv3 = dict_merge(default_values, ospfv3) +    ospfv3 = config_dict_merge(default_values, ospfv3)      # We also need some additional information from the config, prefix-lists      # and route-maps for instance. They will be used in verify(). @@ -170,7 +167,7 @@ def apply(ospfv3):          if key not in ospfv3:              continue          for interface in ospfv3[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 'new_frr_config' in ospfv3:          frr_cfg.add_before(frr.default_add_before, ospfv3['new_frr_config']) diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index 5661dc377..bd47dfd00 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -25,7 +25,6 @@ from vyos.configverify import verify_common_route_maps  from vyos.configverify import verify_access_list  from vyos.configverify import verify_prefix_list  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos.template import render_to_string  from vyos import ConfigError  from vyos import frr @@ -55,9 +54,7 @@ def get_config(config=None):      # 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) -    # merge in remaining default values -    rip = dict_merge(default_values, rip) +    rip = conf.merge_defaults(rip, recursive=True)      # We also need some additional information from the config, prefix-lists      # and route-maps for instance. They will be used in verify(). diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py index e3c904e33..dd1550033 100755 --- a/src/conf_mode/protocols_ripng.py +++ b/src/conf_mode/protocols_ripng.py @@ -24,7 +24,6 @@ from vyos.configverify import verify_common_route_maps  from vyos.configverify import verify_access_list  from vyos.configverify import verify_prefix_list  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos.template import render_to_string  from vyos import ConfigError  from vyos import frr @@ -45,9 +44,7 @@ def get_config(config=None):      # 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) -    # merge in remaining default values -    ripng = dict_merge(default_values, ripng) +    ripng = conf.merge_defaults(ripng, recursive=True)      # We also need some additional information from the config, prefix-lists      # and route-maps for instance. They will be used in verify(). diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py index 035b7db05..05e876f3b 100755 --- a/src/conf_mode/protocols_rpki.py +++ b/src/conf_mode/protocols_rpki.py @@ -19,10 +19,8 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render_to_string  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -43,8 +41,7 @@ def get_config(config=None):      # 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) -    rpki = dict_merge(default_values, rpki) +    rpki = conf.merge_defaults(rpki, recursive=True)      return rpki diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py index 53e9ff50d..5536adda2 100755 --- a/src/conf_mode/qos.py +++ b/src/conf_mode/qos.py @@ -38,7 +38,6 @@ from vyos.qos import TrafficShaper  from vyos.qos import TrafficShaperHFSC  from vyos.utils.process import call  from vyos.utils.dict import dict_search_recursive -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -97,63 +96,32 @@ def get_config(config=None):                  type_node = path.split(" ")[1] # return only interface type node                  set_dependents(type_node, conf, ifname) -    if 'policy' in qos: -        for policy in qos['policy']: -            # when calling defaults() we need to use the real CLI node, thus we -            # need a hyphen -            policy_hyphen = policy.replace('_', '-') - -            if policy in ['random_detect']: -                for rd_name, rd_config in qos['policy'][policy].items(): -                    # There are eight precedence levels - ensure all are present -                    # to be filled later down with the appropriate default values -                    default_precedence = {'precedence' : { '0' : {}, '1' : {}, '2' : {}, '3' : {}, -                                                           '4' : {}, '5' : {}, '6' : {}, '7' : {} }} -                    qos['policy']['random_detect'][rd_name] = dict_merge( -                        default_precedence, qos['policy']['random_detect'][rd_name]) - -            for p_name, p_config in qos['policy'][policy].items(): -                default_values = defaults(base + ['policy', policy_hyphen]) - -                if policy in ['priority_queue']: -                    if 'default' not in p_config: -                        raise ConfigError(f'QoS policy {p_name} misses "default" class!') - -                # XXX: T2665: we can not safely rely on the defaults() when there are -                # tagNodes in place, it is better to blend in the defaults manually. -                if 'class' in default_values: -                    del default_values['class'] -                if 'precedence' in default_values: -                    del default_values['precedence'] - -                qos['policy'][policy][p_name] = dict_merge( -                    default_values, qos['policy'][policy][p_name]) - -                # class is another tag node which requires individual handling -                if 'class' in p_config: -                    default_values = defaults(base + ['policy', policy_hyphen, 'class']) -                    for p_class in p_config['class']: -                        qos['policy'][policy][p_name]['class'][p_class] = dict_merge( -                            default_values, qos['policy'][policy][p_name]['class'][p_class]) - -                if 'precedence' in p_config: -                    default_values = defaults(base + ['policy', policy_hyphen, 'precedence']) -                    # precedence values are a bit more complex as they are calculated -                    # under specific circumstances - thus we need to iterate two times. -                    # first blend in the defaults from XML / CLI -                    for precedence in p_config['precedence']: -                        qos['policy'][policy][p_name]['precedence'][precedence] = dict_merge( -                            default_values, qos['policy'][policy][p_name]['precedence'][precedence]) -                    # second calculate defaults based on actual dictionary -                    for precedence in p_config['precedence']: -                        max_thr = int(qos['policy'][policy][p_name]['precedence'][precedence]['maximum_threshold']) -                        if 'minimum_threshold' not in qos['policy'][policy][p_name]['precedence'][precedence]: -                            qos['policy'][policy][p_name]['precedence'][precedence]['minimum_threshold'] = str( -                                int((9 + int(precedence)) * max_thr) // 18); - -                        if 'queue_limit' not in qos['policy'][policy][p_name]['precedence'][precedence]: -                            qos['policy'][policy][p_name]['precedence'][precedence]['queue_limit'] = \ -                                str(int(4 * max_thr)) +    for policy in qos.get('policy', []): +        if policy in ['random_detect']: +            for rd_name in list(qos['policy'][policy]): +                # There are eight precedence levels - ensure all are present +                # to be filled later down with the appropriate default values +                default_precedence = {'precedence' : { '0' : {}, '1' : {}, '2' : {}, '3' : {}, +                                                       '4' : {}, '5' : {}, '6' : {}, '7' : {} }} +                qos['policy']['random_detect'][rd_name] = dict_merge( +                    default_precedence, qos['policy']['random_detect'][rd_name]) + +    qos = conf.merge_defaults(qos, recursive=True) + +    for policy in qos.get('policy', []): +        for p_name, p_config in qos['policy'][policy].items(): +            if 'precedence' in p_config: +                # precedence settings are a bit more complex as they are +                # calculated under specific circumstances: +                for precedence in p_config['precedence']: +                    max_thr = int(qos['policy'][policy][p_name]['precedence'][precedence]['maximum_threshold']) +                    if 'minimum_threshold' not in qos['policy'][policy][p_name]['precedence'][precedence]: +                        qos['policy'][policy][p_name]['precedence'][precedence]['minimum_threshold'] = str( +                            int((9 + int(precedence)) * max_thr) // 18); + +                    if 'queue_limit' not in qos['policy'][policy][p_name]['precedence'][precedence]: +                        qos['policy'][policy][p_name]['precedence'][precedence]['queue_limit'] = \ +                            str(int(4 * max_thr))      return qos @@ -202,7 +170,9 @@ def verify(qos):                                  queue_lim = int(precedence_config['queue_limit'])                                  if queue_lim < max_tr:                                      raise ConfigError(f'Policy "{policy}" uses queue-limit "{queue_lim}" < max-threshold "{max_tr}"!') - +                if policy_type in ['priority_queue']: +                    if 'default' not in policy_config: +                        raise ConfigError(f'Policy {policy} misses "default" class!')                  if 'default' in policy_config:                      if 'bandwidth' not in policy_config['default'] and policy_type not in ['priority_queue', 'round_robin']:                          raise ConfigError('Bandwidth not defined for default traffic!') diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py index 3ff7880b2..a8fce8e01 100755 --- a/src/conf_mode/salt-minion.py +++ b/src/conf_mode/salt-minion.py @@ -22,12 +22,10 @@ from urllib3 import PoolManager  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_interface_exists  from vyos.template import render  from vyos.utils.process import call  from vyos.utils.permission import chown -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -55,8 +53,7 @@ def get_config(config=None):          salt['id'] = gethostname()      # 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) -    salt = dict_merge(default_values, salt) +    salt = conf.merge_defaults(salt, recursive=True)      if not conf.exists(base):          return None diff --git a/src/conf_mode/service_config_sync.py b/src/conf_mode/service_config_sync.py index 5cde735a1..4b8a7f6ee 100755 --- a/src/conf_mode/service_config_sync.py +++ b/src/conf_mode/service_config_sync.py @@ -19,8 +19,6 @@ import json  from pathlib import Path  from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -42,12 +40,8 @@ def get_config(config=None):      base = ['service', 'config-sync']      if not conf.exists(base):          return None -    config = conf.get_config_dict(base, -                                  get_first_key=True, -                                  no_tag_node_value_mangle=True) - -    default_values = defaults(base) -    config = dict_merge(default_values, config) +    config = conf.get_config_dict(base, get_first_key=True, +                                  with_recursive_defaults=True)      return config diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py index 7eb41ea87..b112add3f 100755 --- a/src/conf_mode/service_console-server.py +++ b/src/conf_mode/service_console-server.py @@ -20,10 +20,8 @@ from sys import exit  from psutil import process_iter  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  config_file = '/run/conserver/conserver.cf' @@ -49,11 +47,7 @@ def get_config(config=None):      # 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 + ['device']) -    if 'device' in proxy: -        for device in proxy['device']: -            tmp = dict_merge(default_values, proxy['device'][device]) -            proxy['device'][device] = tmp +    proxy = conf.merge_defaults(proxy, recursive=True)      return proxy diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py index f6b80552b..276a71fcb 100755 --- a/src/conf_mode/service_ids_fastnetmon.py +++ b/src/conf_mode/service_ids_fastnetmon.py @@ -19,10 +19,8 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -41,11 +39,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    fastnetmon = conf.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) -    fastnetmon = dict_merge(default_values, fastnetmon) +    fastnetmon = conf.get_config_dict(base, key_mangling=('-', '_'), +                                      get_first_key=True, +                                      with_recursive_defaults=True)      return fastnetmon diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py index 0269bedd9..40eb13e23 100755 --- a/src/conf_mode/service_monitoring_telegraf.py +++ b/src/conf_mode/service_monitoring_telegraf.py @@ -22,7 +22,6 @@ from sys import exit  from shutil import rmtree  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configdict import is_node_changed  from vyos.configverify import verify_vrf  from vyos.ifconfig import Section @@ -30,7 +29,6 @@ from vyos.template import render  from vyos.utils.process import call  from vyos.utils.permission import chown  from vyos.utils.process import cmd -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -83,8 +81,7 @@ def get_config(config=None):      # 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) -    monitoring = dict_merge(default_values, monitoring) +    monitoring = conf.merge_defaults(monitoring, recursive=True)      monitoring['custom_scripts_dir'] = custom_scripts_dir      monitoring['hostname'] = get_hostname() diff --git a/src/conf_mode/service_monitoring_zabbix-agent.py b/src/conf_mode/service_monitoring_zabbix-agent.py new file mode 100755 index 000000000..98d8a32ca --- /dev/null +++ b/src/conf_mode/service_monitoring_zabbix-agent.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 +# 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 vyos.config import Config +from vyos.template import render +from vyos.utils.process import call +from vyos import ConfigError +from vyos import airbag +airbag.enable() + + +service_name = 'zabbix-agent2' +service_conf = f'/run/zabbix/{service_name}.conf' +systemd_override = r'/run/systemd/system/zabbix-agent2.service.d/10-override.conf' + + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() + +    base = ['service', 'monitoring', 'zabbix-agent'] + +    if not conf.exists(base): +        return None + +    config = conf.get_config_dict(base, key_mangling=('-', '_'), +                                  get_first_key=True, +                                  no_tag_node_value_mangle=True, +                                  with_recursive_defaults=True) + +    # Cut the / from the end, /tmp/ => /tmp +    if 'directory' in config and config['directory'].endswith('/'): +        config['directory'] = config['directory'][:-1] + +    return config + + +def verify(config): +    # bail out early - looks like removal from running config +    if config is None: +        return + +    if 'server' not in config: +        raise ConfigError('Server is required!') + + +def generate(config): +    # bail out early - looks like removal from running config +    if config is None: +        # Remove old config and return +        config_files = [service_conf, systemd_override] +        for file in config_files: +            if os.path.isfile(file): +                os.unlink(file) + +        return None + +    # Write configuration file +    render(service_conf, 'zabbix-agent/zabbix-agent.conf.j2', config) +    render(systemd_override, 'zabbix-agent/10-override.conf.j2', config) + +    return None + + +def apply(config): +    call('systemctl daemon-reload') +    if config: +        call(f'systemctl restart {service_name}.service') +    else: +        call(f'systemctl stop {service_name}.service') + + +if __name__ == '__main__': +    try: +        c = get_config() +        verify(c) +        generate(c) +        apply(c) +    except ConfigError as e: +        print(e) +        exit(1) diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index fe33c43ea..dbb47de4e 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -19,10 +19,8 @@ import os  from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -35,40 +33,9 @@ def get_config(config=None):      else:          conf = Config()      base = ['service', 'router-advert'] -    rtradv = conf.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_interface_values = defaults(base + ['interface']) -    # we deal with prefix, route defaults later on -    if 'prefix' in default_interface_values: -        del default_interface_values['prefix'] -    if 'route' in default_interface_values: -        del default_interface_values['route'] - -    default_prefix_values = defaults(base + ['interface', 'prefix']) -    default_route_values = defaults(base + ['interface', 'route']) - -    if 'interface' in rtradv: -        for interface in rtradv['interface']: -            rtradv['interface'][interface] = dict_merge( -                default_interface_values, rtradv['interface'][interface]) - -            if 'prefix' in rtradv['interface'][interface]: -                for prefix in rtradv['interface'][interface]['prefix']: -                    rtradv['interface'][interface]['prefix'][prefix] = dict_merge( -                        default_prefix_values, rtradv['interface'][interface]['prefix'][prefix]) - -            if 'route' in rtradv['interface'][interface]: -                for route in rtradv['interface'][interface]['route']: -                    rtradv['interface'][interface]['route'][route] = dict_merge( -                        default_route_values, rtradv['interface'][interface]['route'][route]) - -            if 'name_server' in rtradv['interface'][interface]: -                # always use a list when dealing with nameservers - eases the template generation -                if isinstance(rtradv['interface'][interface]['name_server'], str): -                    rtradv['interface'][interface]['name_server'] = [ -                        rtradv['interface'][interface]['name_server']] +    rtradv = conf.get_config_dict(base, key_mangling=('-', '_'), +                                  get_first_key=True, +                                  with_recursive_defaults=True)      return rtradv diff --git a/src/conf_mode/service_sla.py b/src/conf_mode/service_sla.py index 54b72e029..ba5e645f0 100755 --- a/src/conf_mode/service_sla.py +++ b/src/conf_mode/service_sla.py @@ -19,10 +19,8 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -44,11 +42,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    sla = conf.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) -    sla = dict_merge(default_values, sla) +    sla = conf.get_config_dict(base, key_mangling=('-', '_'), +                               get_first_key=True, +                               with_recursive_defaults=True)      # Ignore default XML values if config doesn't exists      # Delete key from dict diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py index b37d502c2..cf26bf9ce 100755 --- a/src/conf_mode/service_upnp.py +++ b/src/conf_mode/service_upnp.py @@ -23,12 +23,10 @@ from ipaddress import IPv4Network  from ipaddress import IPv6Network  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.utils.process import call  from vyos.template import render  from vyos.template import is_ipv4  from vyos.template import is_ipv6 -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -47,10 +45,7 @@ def get_config(config=None):      if not upnpd:          return None -    if 'rule' in upnpd: -        default_member_values = defaults(base + ['rule']) -        for rule,rule_config in upnpd['rule'].items(): -            upnpd['rule'][rule] = dict_merge(default_member_values, upnpd['rule'][rule]) +    upnpd = conf.merge_defaults(upnpd, recursive=True)      uuidgen = uuid.uuid1()      upnpd.update({'uuid': uuidgen}) diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py index bbdb756bd..12ae4135e 100755 --- a/src/conf_mode/service_webproxy.py +++ b/src/conf_mode/service_webproxy.py @@ -20,14 +20,13 @@ from shutil import rmtree  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge +from vyos.config import config_dict_merge  from vyos.template import render  from vyos.utils.process import call  from vyos.utils.permission import chmod_755  from vyos.utils.dict import dict_search  from vyos.utils.file import write_file -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos.base import Warning  from vyos import ConfigError  from vyos import airbag @@ -125,7 +124,8 @@ def get_config(config=None):                                   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) +    default_values = conf.get_config_defaults(**proxy.kwargs, +                                              recursive=True)      # if no authentication method is supplied, no need to add defaults      if not dict_search('authentication.method', proxy): @@ -138,16 +138,7 @@ def get_config(config=None):          proxy['squidguard_conf'] = squidguard_config_file          proxy['squidguard_db_dir'] = squidguard_db_dir -    # XXX: T2665: blend in proper cache-peer default values later -    default_values.pop('cache_peer') -    proxy = dict_merge(default_values, proxy) - -    # XXX: T2665: blend in proper cache-peer default values -    if 'cache_peer' in proxy: -        default_values = defaults(base + ['cache-peer']) -        for peer in proxy['cache_peer']: -            proxy['cache_peer'][peer] = dict_merge(default_values, -                                                   proxy['cache_peer'][peer]) +    proxy = config_dict_merge(default_values, proxy)      return proxy diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index 0f0d97ac3..7882f8510 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -29,9 +29,8 @@ from vyos.template import render  from vyos.utils.process import call  from vyos.utils.permission import chmod_755  from vyos.utils.dict import dict_search -from vyos.validate import is_addr_assigned +from vyos.utils.network import is_addr_assigned  from vyos.version import get_version_data -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -70,26 +69,9 @@ def get_config(config=None):      # 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) - -    # We can not merge defaults for tagNodes - those need to be blended in -    # per tagNode instance -    if 'listen_address' in default_values: -        del default_values['listen_address'] -    if 'community' in default_values: -        del default_values['community'] -    if 'trap_target' in default_values: -        del default_values['trap_target'] -    if 'v3' in default_values: -        del default_values['v3'] -    snmp = dict_merge(default_values, snmp) +    snmp = conf.merge_defaults(snmp, recursive=True)      if 'listen_address' in snmp: -        default_values = defaults(base + ['listen-address']) -        for address in snmp['listen_address']: -            snmp['listen_address'][address] = dict_merge( -                default_values, snmp['listen_address'][address]) -          # Always listen on localhost if an explicit address has been configured          # This is a safety measure to not end up with invalid listen addresses          # that are not configured on this system. See https://vyos.dev/T850 @@ -101,41 +83,6 @@ def get_config(config=None):              tmp = {'::1': {'port': '161'}}              snmp['listen_address'] = dict_merge(tmp, snmp['listen_address']) -    if 'community' in snmp: -        default_values = defaults(base + ['community']) -        if 'network' in default_values: -            # convert multiple default networks to list -            default_values['network'] = default_values['network'].split() -        for community in snmp['community']: -            snmp['community'][community] = dict_merge( -                default_values, snmp['community'][community]) - -    if 'trap_target' in snmp: -        default_values = defaults(base + ['trap-target']) -        for trap in snmp['trap_target']: -            snmp['trap_target'][trap] = dict_merge( -                default_values, snmp['trap_target'][trap]) - -    if 'v3' in snmp: -        default_values = defaults(base + ['v3']) -        # tagNodes need to be merged in individually later on -        for tmp in ['user', 'group', 'trap_target']: -            del default_values[tmp] -        snmp['v3'] = dict_merge(default_values, snmp['v3']) - -        for user_group in ['user', 'group']: -            if user_group in snmp['v3']: -                default_values = defaults(base + ['v3', user_group]) -                for tmp in snmp['v3'][user_group]: -                    snmp['v3'][user_group][tmp] = dict_merge( -                        default_values, snmp['v3'][user_group][tmp]) - -            if 'trap_target' in snmp['v3']: -                default_values = defaults(base + ['v3', 'trap-target']) -                for trap in snmp['v3']['trap_target']: -                    snmp['v3']['trap_target'][trap] = dict_merge( -                        default_values, snmp['v3']['trap_target'][trap]) -      return snmp  def verify(snmp): diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 3b63fcb7d..ee5e1eca2 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -21,12 +21,10 @@ from syslog import syslog  from syslog import LOG_INFO  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configdict import is_node_changed  from vyos.configverify import verify_vrf  from vyos.utils.process import call  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -57,8 +55,8 @@ def get_config(config=None):      # 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) -    ssh = dict_merge(default_values, ssh) +    ssh = conf.merge_defaults(ssh, recursive=True) +      # pass config file path - used in override template      ssh['config_file'] = config_file diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py index cca996e4f..63dff0e36 100755 --- a/src/conf_mode/system-ip.py +++ b/src/conf_mode/system-ip.py @@ -24,7 +24,6 @@ from vyos.utils.process import call  from vyos.utils.dict import dict_search  from vyos.utils.file import write_file  from vyos.utils.system import sysctl_write -from vyos.xml import defaults  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -37,11 +36,9 @@ def get_config(config=None):          conf = Config()      base = ['system', 'ip'] -    opt = conf.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) -    opt = dict_merge(default_values, opt) +    opt = conf.get_config_dict(base, key_mangling=('-', '_'), +                               get_first_key=True, +                               with_recursive_defaults=True)      # When working with FRR we need to know the corresponding address-family      opt['afi'] = 'ip' diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py index 22210c27a..8a4df11fa 100755 --- a/src/conf_mode/system-ipv6.py +++ b/src/conf_mode/system-ipv6.py @@ -24,7 +24,6 @@ from vyos.template import render_to_string  from vyos.utils.dict import dict_search  from vyos.utils.system import sysctl_write  from vyos.utils.file import write_file -from vyos.xml import defaults  from vyos import ConfigError  from vyos import frr  from vyos import airbag @@ -37,12 +36,9 @@ def get_config(config=None):          conf = Config()      base = ['system', 'ipv6'] -    opt = conf.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) -    opt = dict_merge(default_values, opt) +    opt = conf.get_config_dict(base, key_mangling=('-', '_'), +                               get_first_key=True, +                               with_recursive_defaults=True)      # When working with FRR we need to know the corresponding address-family      opt['afi'] = 'ipv6' diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 82941e0c0..02c97afaa 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -24,7 +24,6 @@ from sys import exit  from time import sleep  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_vrf  from vyos.defaults import directories  from vyos.template import render @@ -35,7 +34,6 @@ from vyos.utils.process import call  from vyos.utils.process import rc_cmd  from vyos.utils.process import run  from vyos.utils.process import DEVNULL -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -93,7 +91,9 @@ def get_config(config=None):          conf = Config()      base = ['system', 'login']      login = conf.get_config_dict(base, key_mangling=('-', '_'), -                                 no_tag_node_value_mangle=True, get_first_key=True) +                                 no_tag_node_value_mangle=True, +                                 get_first_key=True, +                                 with_recursive_defaults=True)      # users no longer existing in the running configuration need to be deleted      local_users = get_local_users() @@ -101,27 +101,9 @@ def get_config(config=None):      if 'user' in login:          cli_users = list(login['user']) -        # XXX: T2665: we can not safely rely on the defaults() when there are -        # tagNodes in place, it is better to blend in the defaults manually. -        default_values = defaults(base + ['user']) -        for user in login['user']: -            login['user'][user] = dict_merge(default_values, login['user'][user]) - -    # Add TACACS global defaults -    if 'tacacs' in login: -        default_values = defaults(base + ['tacacs']) -        if 'server' in default_values: -            del default_values['server'] -        login['tacacs'] = dict_merge(default_values, login['tacacs']) - -    # XXX: T2665: we can not safely rely on the defaults() when there are -    # tagNodes in place, it is better to blend in the defaults manually. -    for backend in ['radius', 'tacacs']: -        default_values = defaults(base + [backend, 'server']) -        for server in dict_search(f'{backend}.server', login) or []: -            login[backend]['server'][server] = dict_merge(default_values, -                login[backend]['server'][server]) - +    # prune TACACS global defaults if not set by user +    if login.from_defaults(['tacacs']): +        del login['tacacs']      # create a list of all users, cli and users      all_users = list(set(local_users + cli_users)) diff --git a/src/conf_mode/system-logs.py b/src/conf_mode/system-logs.py index 12145d641..8ad4875d4 100755 --- a/src/conf_mode/system-logs.py +++ b/src/conf_mode/system-logs.py @@ -19,11 +19,9 @@ from sys import exit  from vyos import ConfigError  from vyos import airbag  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.logger import syslog  from vyos.template import render  from vyos.utils.dict import dict_search -from vyos.xml import defaults  airbag.enable()  # path to logrotate configs @@ -38,11 +36,9 @@ def get_config(config=None):          conf = Config()      base = ['system', 'logs'] -    default_values = defaults(base) -    logs_config = conf.get_config_dict(base, -                                       key_mangling=('-', '_'), -                                       get_first_key=True) -    logs_config = dict_merge(default_values, logs_config) +    logs_config = conf.get_config_dict(base, key_mangling=('-', '_'), +                                       get_first_key=True, +                                       with_recursive_defaults=True)      return logs_config diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py index 1495e9223..d92121b3d 100755 --- a/src/conf_mode/system-option.py +++ b/src/conf_mode/system-option.py @@ -21,14 +21,12 @@ from sys import exit  from time import sleep  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_source_interface  from vyos.template import render  from vyos.utils.process import cmd  from vyos.utils.process import is_systemd_service_running -from vyos.validate import is_addr_assigned -from vyos.validate import is_intf_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned +from vyos.utils.network import is_intf_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -48,12 +46,9 @@ def get_config(config=None):      else:          conf = Config()      base = ['system', 'option'] -    options = conf.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) -    options = dict_merge(default_values, options) +    options = conf.get_config_dict(base, key_mangling=('-', '_'), +                                   get_first_key=True, +                                   with_recursive_defaults=True)      return options diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py index 19c87bcee..07fbb0734 100755 --- a/src/conf_mode/system-syslog.py +++ b/src/conf_mode/system-syslog.py @@ -19,12 +19,10 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configdict import is_node_changed  from vyos.configverify import verify_vrf  from vyos.utils.process import call  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -50,43 +48,9 @@ def get_config(config=None):      tmp = is_node_changed(conf, base + ['vrf'])      if tmp: syslog.update({'restart_required': {}}) -    # 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) -    # XXX: some syslog default values can not be merged here (originating from -    # a tagNode - remove and add them later per individual tagNode instance -    if 'console' in default_values: -        del default_values['console'] -    for entity in ['global', 'user', 'host', 'file']: -        if entity in default_values: -            del default_values[entity] - -    syslog = dict_merge(default_values, syslog) - -    # XXX: add defaults for "console" tree -    if 'console' in syslog and 'facility' in syslog['console']: -        default_values = defaults(base + ['console', 'facility']) -        for facility in syslog['console']['facility']: -            syslog['console']['facility'][facility] = dict_merge(default_values, -                                                                syslog['console']['facility'][facility]) - -    # XXX: add defaults for "host" tree -    for syslog_type in ['host', 'user', 'file']: -        # Bail out early if there is nothing to do -        if syslog_type not in syslog: -            continue - -        default_values_host = defaults(base + [syslog_type]) -        if 'facility' in default_values_host: -            del default_values_host['facility'] - -        for tmp, tmp_config in syslog[syslog_type].items(): -            syslog[syslog_type][tmp] = dict_merge(default_values_host, syslog[syslog_type][tmp]) -            if 'facility' in tmp_config: -                default_values_facility = defaults(base + [syslog_type, 'facility']) -                for facility in tmp_config['facility']: -                    syslog[syslog_type][tmp]['facility'][facility] = dict_merge(default_values_facility, -                        syslog[syslog_type][tmp]['facility'][facility]) +    syslog = conf.merge_defaults(syslog, recursive=True) +    if syslog.from_defaults(['global']): +        del syslog['global']      return syslog diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 87d587959..ebf9a113b 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -19,12 +19,10 @@ import re  from pathlib import Path  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.utils.process import call  from vyos.utils.file import read_file  from vyos.utils.file import write_file  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -45,16 +43,12 @@ def get_config(config=None):      if 'device' not in console:          return console -    # convert CLI values to system values -    default_values = defaults(base + ['device'])      for device, device_config in console['device'].items():          if 'speed' not in device_config and device.startswith('hvc'):              # XEN console has a different default console speed              console['device'][device]['speed'] = 38400 -        else: -            # Merge in XML defaults - the proper way to do it -            console['device'][device] = dict_merge(default_values, -                                                   console['device'][device]) + +    console = conf.merge_defaults(console, recursive=True)      return console diff --git a/src/conf_mode/system_sflow.py b/src/conf_mode/system_sflow.py index 9e3d41100..2df1bbb7a 100755 --- a/src/conf_mode/system_sflow.py +++ b/src/conf_mode/system_sflow.py @@ -19,11 +19,9 @@ import os  from sys import exit  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.template import render  from vyos.utils.process import call -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -42,26 +40,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    sflow = conf.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) - -    sflow = dict_merge(default_values, sflow) - -    # Ignore default XML values if config doesn't exists -    # Delete key from dict -    if 'port' in sflow['server']: -        del sflow['server']['port'] - -    # Set default values per server -    if 'server' in sflow: -        for server in sflow['server']: -            default_values = defaults(base + ['server']) -            sflow['server'][server] = dict_merge(default_values, sflow['server'][server]) +    sflow = conf.get_config_dict(base, key_mangling=('-', '_'), +                                 get_first_key=True, +                                 with_recursive_defaults=True)      return sflow diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py index 2735772dc..3ad346e2e 100755 --- a/src/conf_mode/tftp_server.py +++ b/src/conf_mode/tftp_server.py @@ -24,14 +24,12 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.configverify import verify_vrf  from vyos.template import render  from vyos.template import is_ipv4  from vyos.utils.process import call  from vyos.utils.permission import chmod_755 -from vyos.validate import is_addr_assigned -from vyos.xml import defaults +from vyos.utils.network import is_addr_assigned  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -48,11 +46,9 @@ def get_config(config=None):      if not conf.exists(base):          return None -    tftpd = conf.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) -    tftpd = dict_merge(default_values, tftpd) +    tftpd = conf.get_config_dict(base, key_mangling=('-', '_'), +                                 get_first_key=True, +                                 with_recursive_defaults=True)      return tftpd  def verify(tftpd): diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index b0825d0ee..fa271cbdb 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -27,7 +27,7 @@ from vyos.base import Warning  from vyos.config import Config  from vyos.configdict import leaf_node_changed  from vyos.configverify import verify_interface_exists -from vyos.configdict import dict_merge +from vyos.defaults import directories  from vyos.ifconfig import Interface  from vyos.pki import encode_public_key  from vyos.pki import load_private_key @@ -39,12 +39,11 @@ from vyos.template import ip_from_cidr  from vyos.template import is_ipv4  from vyos.template import is_ipv6  from vyos.template import render -from vyos.validate import is_ipv6_link_local +from vyos.utils.network import is_ipv6_link_local  from vyos.utils.dict import dict_search  from vyos.utils.dict import dict_search_args  from vyos.utils.process import call  from vyos.utils.process import run -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -69,7 +68,6 @@ KEY_PATH  = f'{swanctl_dir}/private/'  CA_PATH   = f'{swanctl_dir}/x509ca/'  CRL_PATH  = f'{swanctl_dir}/x509crl/' -DHCP_BASE = '/var/lib/dhcp/dhclient'  DHCP_HOOK_IFLIST = '/tmp/ipsec_dhcp_waiting'  def get_config(config=None): @@ -84,88 +82,23 @@ def get_config(config=None):      # retrieve common dictionary keys      ipsec = conf.get_config_dict(base, key_mangling=('-', '_'), -                                 get_first_key=True, no_tag_node_value_mangle=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) -    # XXX: T2665: we must safely remove default values for tag nodes, those are -    # added in a more fine grained way later on -    del default_values['esp_group'] -    del default_values['ike_group'] -    del default_values['remote_access'] -    del default_values['site_to_site'] -    ipsec = dict_merge(default_values, ipsec) - -    if 'esp_group' in ipsec: -        default_values = defaults(base + ['esp-group']) -        for group in ipsec['esp_group']: -            ipsec['esp_group'][group] = dict_merge(default_values, -                                                   ipsec['esp_group'][group]) -    if 'ike_group' in ipsec: -        default_values = defaults(base + ['ike-group']) -        # proposal is a tag node which may come with individual defaults per node -        if 'proposal' in default_values: -            del default_values['proposal'] - -        for group in ipsec['ike_group']: -            ipsec['ike_group'][group] = dict_merge(default_values, -                                                   ipsec['ike_group'][group]) - -            if 'proposal' in ipsec['ike_group'][group]: -                default_values = defaults(base + ['ike-group', 'proposal']) -                for proposal in ipsec['ike_group'][group]['proposal']: -                    ipsec['ike_group'][group]['proposal'][proposal] = dict_merge(default_values, -                        ipsec['ike_group'][group]['proposal'][proposal]) - -    # XXX: T2665: we can not safely rely on the defaults() when there are -    # tagNodes in place, it is better to blend in the defaults manually. -    if dict_search('remote_access.connection', ipsec): -        default_values = defaults(base + ['remote-access', 'connection']) -        for rw in ipsec['remote_access']['connection']: -            ipsec['remote_access']['connection'][rw] = dict_merge(default_values, -              ipsec['remote_access']['connection'][rw]) - -    # XXX: T2665: we can not safely rely on the defaults() when there are -    # tagNodes in place, it is better to blend in the defaults manually. -    if dict_search('remote_access.radius.server', ipsec): -        # Fist handle the "base" stuff like RADIUS timeout -        default_values = defaults(base + ['remote-access', 'radius']) -        if 'server' in default_values: -            del default_values['server'] -        ipsec['remote_access']['radius'] = dict_merge(default_values, -                                                      ipsec['remote_access']['radius']) - -        # Take care about individual RADIUS servers implemented as tagNodes - this -        # requires special treatment -        default_values = defaults(base + ['remote-access', 'radius', 'server']) -        for server in ipsec['remote_access']['radius']['server']: -            ipsec['remote_access']['radius']['server'][server] = dict_merge(default_values, -                ipsec['remote_access']['radius']['server'][server]) - -    # XXX: T2665: we can not safely rely on the defaults() when there are -    # tagNodes in place, it is better to blend in the defaults manually. -    if dict_search('site_to_site.peer', ipsec): -        default_values = defaults(base + ['site-to-site', 'peer']) -        for peer in ipsec['site_to_site']['peer']: -            ipsec['site_to_site']['peer'][peer] = dict_merge(default_values, -              ipsec['site_to_site']['peer'][peer]) +                                 no_tag_node_value_mangle=True, +                                 get_first_key=True, +                                 with_recursive_defaults=True)      ipsec['dhcp_no_address'] = {}      ipsec['install_routes'] = 'no' if conf.exists(base + ["options", "disable-route-autoinstall"]) else default_install_routes      ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface'])      ipsec['nhrp_exists'] = conf.exists(['protocols', 'nhrp', 'tunnel'])      ipsec['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), -                                             get_first_key=True, -                                             no_tag_node_value_mangle=True) +                                        no_tag_node_value_mangle=True, +                                        get_first_key=True)      tmp = conf.get_config_dict(l2tp_base, key_mangling=('-', '_'), -                                             get_first_key=True, -                                             no_tag_node_value_mangle=True) +                               no_tag_node_value_mangle=True, +                               get_first_key=True)      if tmp: -        ipsec['l2tp'] = tmp -        l2tp_defaults = defaults(l2tp_base) -        ipsec['l2tp'] = dict_merge(l2tp_defaults, ipsec['l2tp']) +        ipsec['l2tp'] = conf.merge_defaults(tmp, recursive=True)          ipsec['l2tp_outside_address'] = conf.return_value(['vpn', 'l2tp', 'remote-access', 'outside-address'])          ipsec['l2tp_ike_default'] = 'aes256-sha1-modp1024,3des-sha1-modp1024'          ipsec['l2tp_esp_default'] = 'aes256-sha1,3des-sha1' @@ -433,8 +366,9 @@ def verify(ipsec):                  dhcp_interface = peer_conf['dhcp_interface']                  verify_interface_exists(dhcp_interface) +                dhcp_base = directories['isc_dhclient_dir'] -                if not os.path.exists(f'{DHCP_BASE}_{dhcp_interface}.conf'): +                if not os.path.exists(f'{dhcp_base}/dhclient_{dhcp_interface}.conf'):                      raise ConfigError(f"Invalid dhcp-interface on site-to-site peer {peer}")                  address = get_dhcp_address(dhcp_interface) diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index e82862fa3..a039172c4 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -19,7 +19,6 @@ from sys import exit  from vyos.base import Warning  from vyos.config import Config -from vyos.configdict import dict_merge  from vyos.pki import wrap_certificate  from vyos.pki import wrap_private_key  from vyos.template import render @@ -28,7 +27,6 @@ from vyos.utils.network import check_port_availability  from vyos.utils.process import is_systemd_service_running  from vyos.utils.network import is_listen_port_bind_service  from vyos.utils.dict import dict_search -from vyos.xml import defaults  from vyos import ConfigError  from passlib.hash import sha512_crypt  from time import sleep @@ -47,66 +45,6 @@ radius_servers = cfg_dir + '/radius_servers'  def get_hash(password):      return sha512_crypt.hash(password) - - -def _default_dict_cleanup(origin: dict, default_values: dict) -> dict: -    """ -    https://vyos.dev/T2665 -    Clear unnecessary key values in merged config by dict_merge function -    :param origin: config -    :type origin: dict -    :param default_values: default values -    :type default_values: dict -    :return: merged dict -    :rtype: dict -    """ -    if 'mode' in origin["authentication"] and "local" in \ -            origin["authentication"]["mode"]: -        del origin['authentication']['local_users']['username']['otp'] -        if not origin["authentication"]["local_users"]["username"]: -            raise ConfigError( -                'Openconnect authentication mode local requires at least one user') -        default_ocserv_usr_values = \ -        default_values['authentication']['local_users']['username']['otp'] -        for user, params in origin['authentication']['local_users'][ -            'username'].items(): -            # Not every configuration requires OTP settings -            if origin['authentication']['local_users']['username'][user].get( -                    'otp'): -                origin['authentication']['local_users']['username'][user][ -                    'otp'] = dict_merge(default_ocserv_usr_values, -                                        origin['authentication'][ -                                            'local_users']['username'][user][ -                                            'otp']) - -    if 'mode' in origin["authentication"] and "radius" in \ -            origin["authentication"]["mode"]: -        del origin['authentication']['radius']['server']['port'] -        if not origin["authentication"]['radius']['server']: -            raise ConfigError( -                'Openconnect authentication mode radius requires at least one RADIUS server') -        default_values_radius_port = \ -        default_values['authentication']['radius']['server']['port'] -        for server, params in origin['authentication']['radius'][ -            'server'].items(): -            if 'port' not in params: -                params['port'] = default_values_radius_port - -    if 'mode' in origin["accounting"] and "radius" in \ -            origin["accounting"]["mode"]: -        del origin['accounting']['radius']['server']['port'] -        if not origin["accounting"]['radius']['server']: -            raise ConfigError( -                'Openconnect accounting mode radius requires at least one RADIUS server') -        default_values_radius_port = \ -            default_values['accounting']['radius']['server']['port'] -        for server, params in origin['accounting']['radius'][ -            'server'].items(): -            if 'port' not in params: -                params['port'] = default_values_radius_port -    return origin - -  def get_config(config=None):      if config:          conf = config @@ -116,16 +54,14 @@ def get_config(config=None):      if not conf.exists(base):          return None -    ocserv = conf.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) -    ocserv = dict_merge(default_values, ocserv) -    # workaround a "know limitation" - https://vyos.dev/T2665 -    ocserv = _default_dict_cleanup(ocserv, default_values) +    ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), +                                  get_first_key=True, +                                  with_recursive_defaults=True) +      if ocserv:          ocserv['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), -                                get_first_key=True, no_tag_node_value_mangle=True) +                                             no_tag_node_value_mangle=True, +                                             get_first_key=True)      return ocserv @@ -142,6 +78,8 @@ def verify(ocserv):      # Check accounting      if "accounting" in ocserv:          if "mode" in ocserv["accounting"] and "radius" in ocserv["accounting"]["mode"]: +            if not origin["accounting"]['radius']['server']: +                raise ConfigError('Openconnect accounting mode radius requires at least one RADIUS server')              if "authentication" not in ocserv or "mode" not in ocserv["authentication"]:                  raise ConfigError('Accounting depends on OpenConnect authentication configuration')              elif "radius" not in ocserv["authentication"]["mode"]: @@ -150,9 +88,13 @@ def verify(ocserv):      # Check authentication      if "authentication" in ocserv:          if "mode" in ocserv["authentication"]: -            if "local" in ocserv["authentication"]["mode"]: -                if "radius" in ocserv["authentication"]["mode"]: +            if ("local" in ocserv["authentication"]["mode"] and +                "radius" in ocserv["authentication"]["mode"]):                      raise ConfigError('OpenConnect authentication modes are mutually-exclusive, remove either local or radius from your configuration') +            if "radius" in ocserv["authentication"]["mode"]: +                if not ocserv["authentication"]['radius']['server']: +                    raise ConfigError('Openconnect authentication mode radius requires at least one RADIUS server') +            if "local" in ocserv["authentication"]["mode"]:                  if not ocserv["authentication"]["local_users"]:                      raise ConfigError('openconnect mode local required at least one user')                  if not ocserv["authentication"]["local_users"]["username"]: diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py index 80ce1e8e3..82c2f236e 100755 --- a/src/conf_mode/vpp.py +++ b/src/conf_mode/vpp.py @@ -22,7 +22,6 @@ from re import search as re_search, MULTILINE as re_M  from vyos.config import Config  from vyos.configdep import set_dependents, call_dependents -from vyos.configdict import dict_merge  from vyos.configdict import node_changed  from vyos.ifconfig import Section  from vyos.utils.boot import boot_configuration_complete @@ -31,7 +30,6 @@ from vyos.utils.process import rc_cmd  from vyos.utils.system import sysctl_read  from vyos.utils.system import sysctl_apply  from vyos.template import render -from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -94,28 +92,18 @@ def get_config(config=None):      if not conf.exists(base):          return {'removed_ifaces': removed_ifaces} -    config = conf.get_config_dict(base, +    config = conf.get_config_dict(base, key_mangling=('-', '_'), +                                  no_tag_node_value_mangle=True,                                    get_first_key=True, -                                  key_mangling=('-', '_'), -                                  no_tag_node_value_mangle=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) -    if 'interface' in default_values: -        del default_values['interface'] -    config = dict_merge(default_values, config) +                                  with_recursive_defaults=True)      if 'interface' in config:          for iface, iface_config in config['interface'].items(): -            default_values_iface = defaults(base + ['interface']) -            config['interface'][iface] = dict_merge(default_values_iface, config['interface'][iface])              # add an interface to a list of interfaces that need              # to be reinitialized after the commit              set_dependents('ethernet', conf, iface) -        # Get PCI address auto -        for iface, iface_config in config['interface'].items(): +            # Get PCI address auto              if iface_config['pci'] == 'auto':                  config['interface'][iface]['pci'] = _get_pci_address_by_interface(iface) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook index 49bb18372..35721d009 100644 --- a/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyos-dhclient-hook @@ -28,7 +28,8 @@ if [[ $reason =~ ^(REBOOT6|INIT6|EXPIRE6|RELEASE6|STOP6|INFORM6|BOUND6|REBIND6|D  fi  if [ "$RUN" = "yes" ]; then -        LOG=/var/lib/dhcp/dhclient_"$interface"."$proto"lease +        BASE_PATH=$(python3 -c "from vyos.defaults import directories; print(directories['isc_dhclient_dir'])") +        LOG=${BASE_PATH}/dhclient_"$interface"."$proto"lease          echo `date` > $LOG          for i in reason interface new_expiry new_dhcp_lease_time medium \ diff --git a/src/etc/netplug/linkdown.d/dhclient b/src/etc/netplug/linkdown.d/dhclient deleted file mode 100755 index 555ff9134..000000000 --- a/src/etc/netplug/linkdown.d/dhclient +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/perl -# -# Module: dhclient -# -# **** License **** -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 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. -# -# A copy of the GNU General Public License is available as -# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution -# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'. -# You can also obtain it by writing to the Free Software Foundation, -# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, -# MA 02110-1301, USA. -# -# This code was originally developed by Vyatta, Inc. -# Portions created by Vyatta are Copyright (C) 2008 Vyatta, Inc. -# All Rights Reserved. -# -# Author: Mohit Mehta -# Date: November 2008 -# Description: Script to release lease on link down -# -# **** End License **** -# - -use lib "/opt/vyatta/share/perl5/"; -use Vyatta::Config; -use Vyatta::Misc; - -use strict; -use warnings; - -sub stop_dhclient { -    my $intf = shift; -    my $dhcp_daemon = '/sbin/dhclient'; -    my ($intf_config_file, $intf_process_id_file, $intf_leases_file) = Vyatta::Misc::generate_dhclient_intf_files($intf); -    my $release_cmd = "sudo $dhcp_daemon -q -cf $intf_config_file -pf $intf_process_id_file -lf $intf_leases_file -r $intf 2> /dev/null;"; -    $release_cmd .= "sudo rm -f $intf_process_id_file 2> /dev/null"; -    system ($release_cmd); -} - - -# -# main -# - -my $dev=shift; - -# only do this if interface is configured to use dhcp for getting IP address -if (Vyatta::Misc::is_dhcp_enabled($dev, "outside_cli")) { -   # do a dhcp lease release for interface -   stop_dhclient($dev); -} - -exit 0; - -# end of file - diff --git a/src/etc/netplug/linkup.d/dhclient b/src/etc/netplug/linkup.d/dhclient deleted file mode 100755 index 8e50715fd..000000000 --- a/src/etc/netplug/linkup.d/dhclient +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/perl -# -# Module: dhclient -# -# **** License **** -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 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. -# -# A copy of the GNU General Public License is available as -# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution -# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'. -# You can also obtain it by writing to the Free Software Foundation, -# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, -# MA 02110-1301, USA. -# -# This code was originally developed by Vyatta, Inc. -# Portions created by Vyatta are Copyright (C) 2008 Vyatta, Inc. -# All Rights Reserved. -# -# Author: Mohit Mehta -# Date: November 2008 -# Description: Script to renew lease on link up -# -# **** End License **** -# - -use lib "/opt/vyatta/share/perl5/"; -use Vyatta::Config; -use Vyatta::Misc; - -use strict; -use warnings; - -sub run_dhclient { -    my $intf = shift; -    my $dhcp_daemon = '/sbin/dhclient'; -    my ($intf_config_file, $intf_process_id_file, $intf_leases_file) = Vyatta::Misc::generate_dhclient_intf_files($intf); -    my $cmd = "sudo $dhcp_daemon -pf $intf_process_id_file -x $intf 2> /dev/null; sudo rm -f $intf_process_id_file 2> /dev/null;"; -    $cmd .= "sudo $dhcp_daemon -q -nw -cf $intf_config_file -pf $intf_process_id_file  -lf $intf_leases_file $intf 2> /dev/null &"; -    system ($cmd); -} - -# -# main -# - -my $dev=shift; - -# only do this if interface is configured to use dhcp for getting IP address -if (Vyatta::Misc::is_dhcp_enabled($dev, "outside_cli")) { -   # do a dhcp lease renew for interface -   run_dhclient($dev); -} - -exit 0; - -# end of file - diff --git a/src/etc/netplug/linkup.d/vyos-python-helper b/src/etc/netplug/linkup.d/vyos-python-helper new file mode 100755 index 000000000..9c59c58ad --- /dev/null +++ b/src/etc/netplug/linkup.d/vyos-python-helper @@ -0,0 +1,4 @@ +#!/bin/sh +PYTHON3=$(which python3) +# Call the real python script and forward commandline arguments +$PYTHON3 /etc/netplug/vyos-netplug-dhcp-client "${@:1}" diff --git a/src/etc/netplug/netplug b/src/etc/netplug/netplug new file mode 100755 index 000000000..60b65e8c9 --- /dev/null +++ b/src/etc/netplug/netplug @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Copyright 2023 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 +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library.  If not, see <http://www.gnu.org/licenses/>. + +dev="$1" +action="$2" + +case "$action" in +in) +   run-parts --arg $dev --arg in /etc/netplug/linkup.d +    ;; +out) +   run-parts --arg $dev --arg out /etc/netplug/linkdown.d +    ;; + +# probe loads and initialises the driver for the interface and brings the +# interface into the "up" state, so that it can generate netlink(7) events. +# This interferes with "admin down" for an interface. Thus, commented out. An +# "admin up" is treated as a "link up" and thus, "link up" action is executed. +# To execute "link down" action on "admin down", run appropriate script in +# /etc/netplug/linkdown.d +#probe) +#    ;; + +*) +    exit 1 +    ;; +esac diff --git a/src/etc/netplug/netplugd.conf b/src/etc/netplug/netplugd.conf new file mode 100644 index 000000000..ab4d826d6 --- /dev/null +++ b/src/etc/netplug/netplugd.conf @@ -0,0 +1,3 @@ +eth* +br* +bond* diff --git a/src/etc/netplug/vyos-netplug-dhcp-client b/src/etc/netplug/vyos-netplug-dhcp-client new file mode 100755 index 000000000..55d15a163 --- /dev/null +++ b/src/etc/netplug/vyos-netplug-dhcp-client @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright 2023 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 +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library.  If not, see <http://www.gnu.org/licenses/>. + +import sys + +from time import sleep + +from vyos.configquery import ConfigTreeQuery +from vyos.ifconfig import Section +from vyos.utils.boot import boot_configuration_complete +from vyos.utils.commit import commit_in_progress +from vyos.utils.process import call +from vyos import airbag +airbag.enable() + +if len(sys.argv) < 3: +    airbag.noteworthy("Must specify both interface and link status!") +    sys.exit(1) + +if not boot_configuration_complete(): +    airbag.noteworthy("System bootup not yet finished...") +    sys.exit(1) + +while commit_in_progress(): +    sleep(1) + +interface = sys.argv[1] +in_out = sys.argv[2] +config = ConfigTreeQuery() + +interface_path = ['interfaces'] + Section.get_config_path(interface).split() + +for _, interface_config in config.get_config_dict(interface_path).items(): +    # Bail out early if we do not have an IP address configured +    if 'address' not in interface_config: +        continue +    # Bail out early if interface ist administrative down +    if 'disable' in interface_config: +        continue +    systemd_action = 'start' +    if in_out == 'out': +        systemd_action = 'stop' +    # Start/Stop DHCP service +    if 'dhcp' in interface_config['address']: +        call(f'systemctl {systemd_action} dhclient@{interface}.service') +    # Start/Stop DHCPv6 service +    if 'dhcpv6' in interface_config['address']: +        call(f'systemctl {systemd_action} dhcp6c@{interface}.service') diff --git a/src/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py index 2036ca72e..7e2fe2462 100755 --- a/src/helpers/vyos-domain-resolver.py +++ b/src/helpers/vyos-domain-resolver.py @@ -26,7 +26,7 @@ from vyos.utils.commit import commit_in_progress  from vyos.utils.dict import dict_search_args  from vyos.utils.process import cmd  from vyos.utils.process import run -from vyos.xml import defaults +from vyos.xml_ref import get_defaults  base = ['firewall']  timeout = 300 @@ -49,13 +49,7 @@ def get_config(conf):      firewall = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,                                      no_tag_node_value_mangle=True) -    default_values = defaults(base) -    for tmp in ['name', 'ipv6_name']: -        if tmp in default_values: -            del default_values[tmp] - -    if 'zone' in default_values: -        del default_values['zone'] +    default_values = get_defaults(base, get_first_key=True)      firewall = dict_merge(default_values, firewall) diff --git a/src/init/vyos-router b/src/init/vyos-router index 7b752b84b..96f163213 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -101,6 +101,16 @@ load_bootfile ()      )  } +# restore if missing pre-config script +restore_if_missing_preconfig_script () +{ +    if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script ]; then +        cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/ +        chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script +        chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script +    fi +} +  # execute the pre-config script  run_preconfig_script ()  { @@ -109,6 +119,16 @@ run_preconfig_script ()      fi  } +# restore if missing post-config script +restore_if_missing_postconfig_script () +{ +    if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script ]; then +        cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/ +        chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script +        chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script +    fi +} +  # execute the post-config scripts  run_postconfig_scripts ()  { @@ -360,6 +380,8 @@ start ()      log_daemon_msg "Starting VyOS router"      disabled migrate || migrate_bootfile +    restore_if_missing_preconfig_script +      run_preconfig_script      run_postupgrade_script @@ -384,6 +406,8 @@ start ()      telinit q      chmod g-w,o-w / +    restore_if_missing_postconfig_script +      run_postconfig_scripts  } diff --git a/src/migration-scripts/bgp/0-to-1 b/src/migration-scripts/bgp/0-to-1 index 5e9dffe1f..03c45107b 100755 --- a/src/migration-scripts/bgp/0-to-1 +++ b/src/migration-scripts/bgp/0-to-1 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/bgp/1-to-2 b/src/migration-scripts/bgp/1-to-2 index e2d3fcd33..96b939b47 100755 --- a/src/migration-scripts/bgp/1-to-2 +++ b/src/migration-scripts/bgp/1-to-2 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/bgp/2-to-3 b/src/migration-scripts/bgp/2-to-3 index 7ced0a3b0..34d321a96 100755 --- a/src/migration-scripts/bgp/2-to-3 +++ b/src/migration-scripts/bgp/2-to-3 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/bgp/3-to-4 b/src/migration-scripts/bgp/3-to-4 index 0df2fbec4..894cdda2b 100755 --- a/src/migration-scripts/bgp/3-to-4 +++ b/src/migration-scripts/bgp/3-to-4 @@ -22,7 +22,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/config-management/0-to-1 b/src/migration-scripts/config-management/0-to-1 index 344359110..6528fd136 100755 --- a/src/migration-scripts/config-management/0-to-1 +++ b/src/migration-scripts/config-management/0-to-1 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/conntrack-sync/1-to-2 b/src/migration-scripts/conntrack-sync/1-to-2 index ebbd8c35a..a8e1007f3 100755 --- a/src/migration-scripts/conntrack-sync/1-to-2 +++ b/src/migration-scripts/conntrack-sync/1-to-2 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2 index 4fc88a1ed..c4fe667fc 100755 --- a/src/migration-scripts/conntrack/1-to-2 +++ b/src/migration-scripts/conntrack/1-to-2 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3 index 8a8b43279..6fb457b7f 100755 --- a/src/migration-scripts/conntrack/2-to-3 +++ b/src/migration-scripts/conntrack/2-to-3 @@ -8,7 +8,7 @@ import sys  from vyos.configtree import ConfigTree  from vyos.version import get_version -if len(sys.argv) < 1: +if len(sys.argv) < 2:      print('Must specify file name!')      sys.exit(1) diff --git a/src/migration-scripts/container/0-to-1 b/src/migration-scripts/container/0-to-1 index 86f89ee04..6b282e082 100755 --- a/src/migration-scripts/container/0-to-1 +++ b/src/migration-scripts/container/0-to-1 @@ -23,7 +23,7 @@ import sys  from vyos.configtree import ConfigTree  from vyos.utils.process import call -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dhcp-relay/1-to-2 b/src/migration-scripts/dhcp-relay/1-to-2 index b72da1028..508bac6be 100755 --- a/src/migration-scripts/dhcp-relay/1-to-2 +++ b/src/migration-scripts/dhcp-relay/1-to-2 @@ -7,7 +7,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dhcp-server/4-to-5 b/src/migration-scripts/dhcp-server/4-to-5 index 313b5279a..d15e0baf5 100755 --- a/src/migration-scripts/dhcp-server/4-to-5 +++ b/src/migration-scripts/dhcp-server/4-to-5 @@ -9,7 +9,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dhcp-server/5-to-6 b/src/migration-scripts/dhcp-server/5-to-6 index aefe84737..f5c766a09 100755 --- a/src/migration-scripts/dhcp-server/5-to-6 +++ b/src/migration-scripts/dhcp-server/5-to-6 @@ -20,7 +20,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dhcpv6-server/0-to-1 b/src/migration-scripts/dhcpv6-server/0-to-1 index 6f1150da1..deae1ca29 100755 --- a/src/migration-scripts/dhcpv6-server/0-to-1 +++ b/src/migration-scripts/dhcpv6-server/0-to-1 @@ -19,7 +19,7 @@  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/dns-dynamic/0-to-1 b/src/migration-scripts/dns-dynamic/0-to-1 index cf0983b01..d80e8d44a 100755 --- a/src/migration-scripts/dns-dynamic/0-to-1 +++ b/src/migration-scripts/dns-dynamic/0-to-1 @@ -43,7 +43,7 @@ service_protocol_mapping = {      'zoneedit': 'zoneedit1'  } -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dns-forwarding/0-to-1 b/src/migration-scripts/dns-forwarding/0-to-1 index 6e8720eef..7f4343652 100755 --- a/src/migration-scripts/dns-forwarding/0-to-1 +++ b/src/migration-scripts/dns-forwarding/0-to-1 @@ -22,7 +22,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2 index a8c930be7..7df2d47e2 100755 --- a/src/migration-scripts/dns-forwarding/1-to-2 +++ b/src/migration-scripts/dns-forwarding/1-to-2 @@ -25,7 +25,7 @@ from sys import argv, exit  from vyos.ifconfig import Interface  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/dns-forwarding/2-to-3 b/src/migration-scripts/dns-forwarding/2-to-3 index 01e445b22..d7ff9e260 100755 --- a/src/migration-scripts/dns-forwarding/2-to-3 +++ b/src/migration-scripts/dns-forwarding/2-to-3 @@ -21,7 +21,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/dns-forwarding/3-to-4 b/src/migration-scripts/dns-forwarding/3-to-4 index 55165c2c5..3d5316ed4 100755 --- a/src/migration-scripts/dns-forwarding/3-to-4 +++ b/src/migration-scripts/dns-forwarding/3-to-4 @@ -20,7 +20,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11 new file mode 100755 index 000000000..716c5a240 --- /dev/null +++ b/src/migration-scripts/firewall/10-to-11 @@ -0,0 +1,374 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 +# 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/>. + +# T5160: Firewall re-writing + +#  cli changes from:        +#  set firewall name <name> ... +#  set firewall ipv6-name <name> ... +#  To +#  set firewall ipv4 name <name>  +#  set firewall ipv6 name <name>  + +## Also from 'firewall interface' removed. +## in and out: +    # set firewall interface <iface> [in|out] [name | ipv6-name] <name> +    # To +    # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> [inbound-interface | outboubd-interface] interface-name <iface> +    # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> action jump +    # set firewall [ipv4 | ipv6] forward filter rule <5,10,15,...> jump-target <name> +## local: +    # set firewall interface <iface> local [name | ipv6-name] <name> +    # To +    # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> inbound-interface interface-name <iface> +    # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> action jump +    # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> jump-target <name> + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +if len(argv) < 2: +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['firewall'] +config = ConfigTree(config_file) + +if not config.exists(base): +    # Nothing to do +    exit(0) + +### Migration of state policies +if config.exists(base + ['state-policy']): +    for family in ['ipv4', 'ipv6']: +        for hook in ['forward', 'input', 'output']: +            for priority in ['filter']: +                # Add default-action== accept for compatibility reasons: +                config.set(base + [family, hook, priority, 'default-action'], value='accept') +                position = 1 +                for state in config.list_nodes(base + ['state-policy']): +                    action = config.return_value(base + ['state-policy', state, 'action']) +                    config.set(base + [family, hook, priority, 'rule']) +                    config.set_tag(base + [family, hook, priority, 'rule']) +                    config.set(base + [family, hook, priority, 'rule', position, 'state', state], value='enable') +                    config.set(base + [family, hook, priority, 'rule', position, 'action'], value=action) +                    position = position + 1 +    config.delete(base + ['state-policy']) + +## migration of global options: +for option in ['all-ping', 'broadcast-ping', 'config-trap', 'ip-src-route', 'ipv6-receive-redirects', 'ipv6-src-route', 'log-martians', +                'receive-redirects', 'resolver-cache', 'resolver-internal', 'send-redirects', 'source-validation', 'syn-cookies', 'twa-hazards-protection']: +    if config.exists(base + [option]): +        if option != 'config-trap': +            val = config.return_value(base + [option]) +            config.set(base + ['global-options', option], value=val) +        config.delete(base + [option]) + +### Migration of firewall name and ipv6-name +if config.exists(base + ['name']): +    config.set(['firewall', 'ipv4', 'name']) +    config.set_tag(['firewall', 'ipv4', 'name']) + +    for ipv4name in config.list_nodes(base + ['name']): +        config.copy(base + ['name', ipv4name], base + ['ipv4', 'name', ipv4name]) +    config.delete(base + ['name']) + +if config.exists(base + ['ipv6-name']): +    config.set(['firewall', 'ipv6', 'name']) +    config.set_tag(['firewall', 'ipv6', 'name']) + +    for ipv6name in config.list_nodes(base + ['ipv6-name']): +        config.copy(base + ['ipv6-name', ipv6name], base + ['ipv6', 'name', ipv6name]) +    config.delete(base + ['ipv6-name']) + +### Migration of firewall interface +if config.exists(base + ['interface']): +    fwd_ipv4_rule = 5 +    inp_ipv4_rule = 5 +    fwd_ipv6_rule = 5 +    inp_ipv6_rule = 5 +    for iface in config.list_nodes(base + ['interface']): +        for direction in ['in', 'out', 'local']: +            if config.exists(base + ['interface', iface, direction]): +                if config.exists(base + ['interface', iface, direction, 'name']): +                    target = config.return_value(base + ['interface', iface, direction, 'name']) +                    if direction == 'in': +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept') +                        new_base = base + ['ipv4', 'forward', 'filter', 'rule'] +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [fwd_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump') +                        config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target) +                        fwd_ipv4_rule = fwd_ipv4_rule + 5 +                    elif direction == 'out': +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept') +                        new_base = base + ['ipv4', 'forward', 'filter', 'rule'] +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [fwd_ipv4_rule, 'outbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [fwd_ipv4_rule, 'action'], value='jump') +                        config.set(new_base + [fwd_ipv4_rule, 'jump-target'], value=target) +                        fwd_ipv4_rule = fwd_ipv4_rule + 5 +                    else: +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept') +                        new_base = base + ['ipv4', 'input', 'filter', 'rule'] +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [inp_ipv4_rule, 'inbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [inp_ipv4_rule, 'action'], value='jump') +                        config.set(new_base + [inp_ipv4_rule, 'jump-target'], value=target) +                        inp_ipv4_rule = inp_ipv4_rule + 5 + +                if config.exists(base + ['interface', iface, direction, 'ipv6-name']): +                    target = config.return_value(base + ['interface', iface, direction, 'ipv6-name']) +                    if direction == 'in': +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept') +                        new_base = base + ['ipv6', 'forward', 'filter', 'rule'] +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [fwd_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump') +                        config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target) +                        fwd_ipv6_rule = fwd_ipv6_rule + 5 +                    elif direction == 'out': +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept') +                        new_base = base + ['ipv6', 'forward', 'filter', 'rule'] +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [fwd_ipv6_rule, 'outbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [fwd_ipv6_rule, 'action'], value='jump') +                        config.set(new_base + [fwd_ipv6_rule, 'jump-target'], value=target) +                        fwd_ipv6_rule = fwd_ipv6_rule + 5 +                    else: +                        new_base = base + ['ipv6', 'input', 'filter', 'rule'] +                        # Add default-action== accept for compatibility reasons: +                        config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept') +                        config.set(new_base) +                        config.set_tag(new_base) +                        config.set(new_base + [inp_ipv6_rule, 'inbound-interface', 'interface-name'], value=iface) +                        config.set(new_base + [inp_ipv6_rule, 'action'], value='jump') +                        config.set(new_base + [inp_ipv6_rule, 'jump-target'], value=target) +                        inp_ipv6_rule = inp_ipv6_rule + 5 + +    config.delete(base + ['interface']) + + +### Migration of zones: +### User interface groups  +if config.exists(base + ['zone']): +    inp_ipv4_rule = 101 +    inp_ipv6_rule = 101 +    fwd_ipv4_rule = 101 +    fwd_ipv6_rule = 101 +    out_ipv4_rule = 101 +    out_ipv6_rule = 101 +    local_zone = 'False' + +    for zone in config.list_nodes(base + ['zone']): +        if config.exists(base + ['zone', zone, 'local-zone']): +            local_zone = 'True' +            # Add default-action== accept for compatibility reasons: +            config.set(base + ['ipv4', 'input', 'filter', 'default-action'], value='accept') +            config.set(base + ['ipv6', 'input', 'filter', 'default-action'], value='accept') +            config.set(base + ['ipv4', 'output', 'filter', 'default-action'], value='accept') +            config.set(base + ['ipv6', 'output', 'filter', 'default-action'], value='accept') +            for from_zone in config.list_nodes(base + ['zone', zone, 'from']): +                group_name = 'IG_' + from_zone +                if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']): +                    # ipv4 input ruleset +                    target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']) +                    config.set(base + ['ipv4', 'input', 'filter', 'rule']) +                    config.set_tag(base + ['ipv4', 'input', 'filter', 'rule']) +                    config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name) +                    config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value='jump') +                    config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'jump-target'], value=target_ipv4_chain) +                    inp_ipv4_rule = inp_ipv4_rule + 5 +                if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']): +                    # ipv6 input ruleset +                    target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']) +                    config.set(base + ['ipv6', 'input', 'filter', 'rule']) +                    config.set_tag(base + ['ipv6', 'input', 'filter', 'rule']) +                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name) +                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value='jump') +                    config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'jump-target'], value=target_ipv6_chain) +                    inp_ipv6_rule = inp_ipv6_rule + 5 + +            # Migrate: set firewall zone <zone> default-action <action> +            # Options: drop or reject. If not specified, is drop +            if config.exists(base + ['zone', zone, 'default-action']): +                local_def_action = config.return_value(base + ['zone', zone, 'default-action']) +            else: +                local_def_action = 'drop' +            config.set(base + ['ipv4', 'input', 'filter', 'rule']) +            config.set_tag(base + ['ipv4', 'input', 'filter', 'rule']) +            config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'action'], value=local_def_action) +            config.set(base + ['ipv6', 'input', 'filter', 'rule']) +            config.set_tag(base + ['ipv6', 'input', 'filter', 'rule']) +            config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'action'], value=local_def_action) +            if config.exists(base + ['zone', zone, 'enable-default-log']): +                config.set(base + ['ipv4', 'input', 'filter', 'rule', inp_ipv4_rule, 'log'], value='enable') +                config.set(base + ['ipv6', 'input', 'filter', 'rule', inp_ipv6_rule, 'log'], value='enable') + +        else: +        # It's not a local zone +            group_name = 'IG_' + zone +            # Add default-action== accept for compatibility reasons: +            config.set(base + ['ipv4', 'forward', 'filter', 'default-action'], value='accept') +            config.set(base + ['ipv6', 'forward', 'filter', 'default-action'], value='accept') +            # intra-filtering migration. By default accept +            intra_zone_ipv4_action = 'accept' +            intra_zone_ipv6_action = 'accept' +             +            if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'action']): +                intra_zone_ipv4_action = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'action']) +                intra_zone_ipv6_action = intra_zone_ipv4_action +            else: +                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']): +                    intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']) +                    intra_zone_ipv4_action = 'jump' +                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']): +                    intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']) +                    intra_zone_ipv6_action = 'jump' +            config.set(base + ['ipv4', 'forward', 'filter', 'rule']) +            config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule']) +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=intra_zone_ipv4_action) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule']) +            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule']) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=intra_zone_ipv6_action) +            if intra_zone_ipv4_action == 'jump': +                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']): +                    intra_zone_ipv4_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'name']) +                    config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=intra_zone_ipv4_target) +            if intra_zone_ipv6_action == 'jump': +                if config.exists(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']): +                    intra_zone_ipv6_target = config.return_value(base + ['zone', zone, 'intra-zone-filtering', 'firewall', 'ipv6-name']) +                    config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=intra_zone_ipv6_target) +            fwd_ipv4_rule = fwd_ipv4_rule + 5 +            fwd_ipv6_rule = fwd_ipv6_rule + 5 + +            if config.exists(base + ['zone', zone, 'interface']): +                # Create interface group IG_<zone> +                group_name = 'IG_' + zone +                config.set(base + ['group', 'interface-group'], value=group_name) +                config.set_tag(base + ['group', 'interface-group']) +                for iface in config.return_values(base + ['zone', zone, 'interface']): +                    config.set(base + ['group', 'interface-group', group_name, 'interface'], value=iface, replace=False) + +            if config.exists(base + ['zone', zone, 'from']): +                for from_zone in config.list_nodes(base + ['zone', zone, 'from']): +                    from_group = 'IG_' + from_zone +                    if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']): +                        target_ipv4_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'name']) +                        if config.exists(base + ['zone', from_zone, 'local-zone']): +                            # It's from LOCAL zone -> Output filtering  +                            config.set(base + ['ipv4', 'output', 'filter', 'rule']) +                            config.set_tag(base + ['ipv4', 'output', 'filter', 'rule']) +                            config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name) +                            config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value='jump') +                            config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'jump-target'], value=target_ipv4_chain) +                            out_ipv4_rule = out_ipv4_rule + 5 +                        else: +                            # It's not LOCAL zone -> forward filtering +                            config.set(base + ['ipv4', 'forward', 'filter', 'rule']) +                            config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule']) +                            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name) +                            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'inbound-interface', 'interface-group'], value=from_group) +                            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value='jump') +                            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'jump-target'], value=target_ipv4_chain) +                            fwd_ipv4_rule = fwd_ipv4_rule + 5 +                    if config.exists(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']): +                        target_ipv6_chain = config.return_value(base + ['zone', zone, 'from', from_zone, 'firewall', 'ipv6-name']) +                        if config.exists(base + ['zone', from_zone, 'local-zone']): +                            # It's from LOCAL zone -> Output filtering +                            config.set(base + ['ipv6', 'output', 'filter', 'rule']) +                            config.set_tag(base + ['ipv6', 'output', 'filter', 'rule']) +                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name) +                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value='jump') +                            config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'jump-target'], value=target_ipv6_chain) +                            out_ipv6_rule = out_ipv6_rule + 5 +                        else: +                            # It's not LOCAL zone -> forward filtering +                            config.set(base + ['ipv6', 'forward', 'filter', 'rule']) +                            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule']) +                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name) +                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'inbound-interface', 'interface-group'], value=from_group) +                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value='jump') +                            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'jump-target'], value=target_ipv6_chain) +                            fwd_ipv6_rule = fwd_ipv6_rule + 5 + +            ## Now need to migrate: set firewall zone <zone> default-action <action>    # action=drop if not specified. +            if config.exists(base + ['zone', zone, 'default-action']): +                def_action = config.return_value(base + ['zone', zone, 'default-action']) +            else: +                def_action = 'drop' +            config.set(base + ['ipv4', 'forward', 'filter', 'rule']) +            config.set_tag(base + ['ipv4', 'forward', 'filter', 'rule']) +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'outbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'action'], value=def_action) +            description = 'zone_' + zone + ' default-action' +            config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'description'], value=description) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule']) +            config.set_tag(base + ['ipv6', 'forward', 'filter', 'rule']) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'outbound-interface', 'interface-group'], value=group_name) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'action'], value=def_action) +            config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'description'], value=description) + +            if config.exists(base + ['zone', zone, 'enable-default-log']): +                config.set(base + ['ipv4', 'forward', 'filter', 'rule', fwd_ipv4_rule, 'log'], value='enable') +                config.set(base + ['ipv6', 'forward', 'filter', 'rule', fwd_ipv6_rule, 'log'], value='enable') +            fwd_ipv4_rule = fwd_ipv4_rule + 5 +            fwd_ipv6_rule = fwd_ipv6_rule + 5 + +    # Migrate default-action (force to be drop in output chain) if local zone is defined +    if local_zone == 'True': +        # General drop in output change if needed +        config.set(base + ['ipv4', 'output', 'filter', 'rule']) +        config.set_tag(base + ['ipv4', 'output', 'filter', 'rule']) +        config.set(base + ['ipv4', 'output', 'filter', 'rule', out_ipv4_rule, 'action'], value=local_def_action) +        config.set(base + ['ipv6', 'output', 'filter', 'rule']) +        config.set_tag(base + ['ipv6', 'output', 'filter', 'rule']) +        config.set(base + ['ipv6', 'output', 'filter', 'rule', out_ipv6_rule, 'action'], value=local_def_action) + +    config.delete(base + ['zone']) + +###### END migration zones + +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)) +    exit(1)
\ No newline at end of file diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6 index ccb86830a..e1eaea7a1 100755 --- a/src/migration-scripts/firewall/5-to-6 +++ b/src/migration-scripts/firewall/5-to-6 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) @@ -46,12 +46,54 @@ for interface in config.list_nodes(base):      if config.exists(base + [interface, 'adjust-mss']):          section = Section.section(interface)          tmp = config.return_value(base + [interface, 'adjust-mss']) -        config.set(['interfaces', section, interface, 'ip', 'adjust-mss'], value=tmp) + +        vlan = interface.split('.') +        base_interface_path = ['interfaces', section, vlan[0]] + +        if len(vlan) == 1: +            # Normal interface, no VLAN +            config.set(base_interface_path + ['ip', 'adjust-mss'], value=tmp) +        elif len(vlan) == 2: +            # Regular VIF or VIF-S interface - we need to check the config +            vif = vlan[1] +            if config.exists(base_interface_path + ['vif', vif]): +                config.set(base_interface_path + ['vif', vif, 'ip', 'adjust-mss'], value=tmp) +            elif config.exists(base_interface_path + ['vif-s', vif]): +                config.set(base_interface_path + ['vif-s', vif, 'ip', 'adjust-mss'], value=tmp) +        elif len(vlan) == 3: +            # VIF-S interface with VIF-C subinterface +            vif_s = vlan[1] +            vif_c = vlan[2] +            config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ip', 'adjust-mss'], value=tmp) +            config.set_tag(base_interface_path + ['vif-s']) +            config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])      if config.exists(base + [interface, 'adjust-mss6']):          section = Section.section(interface)          tmp = config.return_value(base + [interface, 'adjust-mss6']) -        config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp) + +        vlan = interface.split('.') +        base_interface_path = ['interfaces', section, vlan[0]] + +        if len(vlan) == 1: +            # Normal interface, no VLAN +            config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp) +        elif len(vlan) == 2: +            # Regular VIF or VIF-S interface - we need to check the config +            vif = vlan[1] +            if config.exists(base_interface_path + ['vif', vif]): +                config.set(base_interface_path + ['vif', vif, 'ipv6', 'adjust-mss'], value=tmp) +                config.set_tag(base_interface_path + ['vif']) +            elif config.exists(base_interface_path + ['vif-s', vif]): +                config.set(base_interface_path + ['vif-s', vif, 'ipv6', 'adjust-mss'], value=tmp) +                config.set_tag(base_interface_path + ['vif-s']) +        elif len(vlan) == 3: +            # VIF-S interface with VIF-C subinterface +            vif_s = vlan[1] +            vif_c = vlan[2] +            config.set(base_interface_path + ['vif-s', vif_s, 'vif-c', vif_c, 'ipv6', 'adjust-mss'], value=tmp) +            config.set_tag(base_interface_path + ['vif-s']) +            config.set_tag(base_interface_path + ['vif-s', vif_s, 'vif-c'])  config.delete(['firewall', 'options']) diff --git a/src/migration-scripts/firewall/6-to-7 b/src/migration-scripts/firewall/6-to-7 index 626d6849f..9ad887acc 100755 --- a/src/migration-scripts/firewall/6-to-7 +++ b/src/migration-scripts/firewall/6-to-7 @@ -28,7 +28,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8 index ce527acf5..d06c3150a 100755 --- a/src/migration-scripts/firewall/7-to-8 +++ b/src/migration-scripts/firewall/7-to-8 @@ -25,7 +25,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/firewall/8-to-9 b/src/migration-scripts/firewall/8-to-9 index f7c1bb90d..d7647354a 100755 --- a/src/migration-scripts/firewall/8-to-9 +++ b/src/migration-scripts/firewall/8-to-9 @@ -28,7 +28,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/firewall/9-to-10 b/src/migration-scripts/firewall/9-to-10 index 6f67cc512..a70460718 100755 --- a/src/migration-scripts/firewall/9-to-10 +++ b/src/migration-scripts/firewall/9-to-10 @@ -28,7 +28,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1 index 72cce77b0..0f790fd9c 100755 --- a/src/migration-scripts/flow-accounting/0-to-1 +++ b/src/migration-scripts/flow-accounting/0-to-1 @@ -21,7 +21,7 @@  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/https/2-to-3 b/src/migration-scripts/https/2-to-3 index fa29fdd18..2beba6d2b 100755 --- a/src/migration-scripts/https/2-to-3 +++ b/src/migration-scripts/https/2-to-3 @@ -25,7 +25,7 @@ from vyos.pki import create_private_key  from vyos.pki import encode_certificate  from vyos.pki import encode_private_key -if (len(sys.argv) < 2): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/https/3-to-4 b/src/migration-scripts/https/3-to-4 index 5ee528b31..b3cfca201 100755 --- a/src/migration-scripts/https/3-to-4 +++ b/src/migration-scripts/https/3-to-4 @@ -20,7 +20,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 2): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ids/0-to-1 b/src/migration-scripts/ids/0-to-1 index 9f08f7dc7..8b7850a1a 100755 --- a/src/migration-scripts/ids/0-to-1 +++ b/src/migration-scripts/ids/0-to-1 @@ -19,7 +19,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1 index c7f324661..25f6842eb 100755 --- a/src/migration-scripts/interfaces/0-to-1 +++ b/src/migration-scripts/interfaces/0-to-1 @@ -37,7 +37,7 @@ def migrate_bridge(config, tree, intf):  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          sys.exit(1) diff --git a/src/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2 index c75404d85..c95623c2b 100755 --- a/src/migration-scripts/interfaces/1-to-2 +++ b/src/migration-scripts/interfaces/1-to-2 @@ -7,7 +7,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/interfaces/10-to-11 b/src/migration-scripts/interfaces/10-to-11 index 6b8e49ed9..cafaa3fa4 100755 --- a/src/migration-scripts/interfaces/10-to-11 +++ b/src/migration-scripts/interfaces/10-to-11 @@ -23,7 +23,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12 index 0dad24642..e9eb7f939 100755 --- a/src/migration-scripts/interfaces/11-to-12 +++ b/src/migration-scripts/interfaces/11-to-12 @@ -22,7 +22,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/12-to-13 b/src/migration-scripts/interfaces/12-to-13 index f866ca9a6..ef1d93903 100755 --- a/src/migration-scripts/interfaces/12-to-13 +++ b/src/migration-scripts/interfaces/12-to-13 @@ -24,7 +24,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/13-to-14 b/src/migration-scripts/interfaces/13-to-14 index 6e6439c36..b20d8b4db 100755 --- a/src/migration-scripts/interfaces/13-to-14 +++ b/src/migration-scripts/interfaces/13-to-14 @@ -21,7 +21,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/14-to-15 b/src/migration-scripts/interfaces/14-to-15 index c38db0bf8..e21251f86 100755 --- a/src/migration-scripts/interfaces/14-to-15 +++ b/src/migration-scripts/interfaces/14-to-15 @@ -20,7 +20,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/15-to-16 b/src/migration-scripts/interfaces/15-to-16 index 804c48be0..ae3441b9f 100755 --- a/src/migration-scripts/interfaces/15-to-16 +++ b/src/migration-scripts/interfaces/15-to-16 @@ -20,7 +20,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17 index d123be06f..75f160686 100755 --- a/src/migration-scripts/interfaces/16-to-17 +++ b/src/migration-scripts/interfaces/16-to-17 @@ -21,7 +21,7 @@ import sys  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          sys.exit(1) @@ -35,7 +35,7 @@ if __name__ == '__main__':      if not config.exists(base):          # Nothing to do          sys.exit(0) -     +      for interface in config.list_nodes(base):          mirror_old_base = base + [interface, 'mirror']          if config.exists(mirror_old_base): @@ -43,7 +43,7 @@ if __name__ == '__main__':              if config.exists(mirror_old_base):                  config.delete(mirror_old_base)                  config.set(mirror_old_base + ['ingress'],intf[0]) -     +      try:          with open(file_name, 'w') as f:              f.write(config.to_string()) diff --git a/src/migration-scripts/interfaces/17-to-18 b/src/migration-scripts/interfaces/17-to-18 index b8cb8c119..51486ac37 100755 --- a/src/migration-scripts/interfaces/17-to-18 +++ b/src/migration-scripts/interfaces/17-to-18 @@ -22,7 +22,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19 index a12c4a6cd..c3209f250 100755 --- a/src/migration-scripts/interfaces/18-to-19 +++ b/src/migration-scripts/interfaces/18-to-19 @@ -41,7 +41,7 @@ def replace_nat_interfaces(config, old, new):  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20 index e96663e54..05abae898 100755 --- a/src/migration-scripts/interfaces/19-to-20 +++ b/src/migration-scripts/interfaces/19-to-20 @@ -19,7 +19,7 @@ from sys import exit  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3 index 68d41de39..15c3bc8be 100755 --- a/src/migration-scripts/interfaces/2-to-3 +++ b/src/migration-scripts/interfaces/2-to-3 @@ -7,7 +7,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index cb1c36882..14ad0fe4d 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -23,7 +23,7 @@ from sys import argv  from vyos.ethtool import Ethtool  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/21-to-22 b/src/migration-scripts/interfaces/21-to-22 index 098102102..1838eb1c0 100755 --- a/src/migration-scripts/interfaces/21-to-22 +++ b/src/migration-scripts/interfaces/21-to-22 @@ -17,7 +17,7 @@  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/22-to-23 b/src/migration-scripts/interfaces/22-to-23 index 06e07572f..8b21fce51 100755 --- a/src/migration-scripts/interfaces/22-to-23 +++ b/src/migration-scripts/interfaces/22-to-23 @@ -75,7 +75,7 @@ def migrate_ripng(config, path, interface):              config.delete(path[:-1])  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/23-to-24 b/src/migration-scripts/interfaces/23-to-24 index d1ec2ad3e..8fd79ecc6 100755 --- a/src/migration-scripts/interfaces/23-to-24 +++ b/src/migration-scripts/interfaces/23-to-24 @@ -22,7 +22,7 @@ import sys  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          sys.exit(1) diff --git a/src/migration-scripts/interfaces/24-to-25 b/src/migration-scripts/interfaces/24-to-25 index f3a1dc464..9aa6ea5e3 100755 --- a/src/migration-scripts/interfaces/24-to-25 +++ b/src/migration-scripts/interfaces/24-to-25 @@ -53,7 +53,7 @@ def read_file_for_pki(config_auth_path):      return output -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/interfaces/25-to-26 b/src/migration-scripts/interfaces/25-to-26 index a8936235e..4967a29fa 100755 --- a/src/migration-scripts/interfaces/25-to-26 +++ b/src/migration-scripts/interfaces/25-to-26 @@ -22,7 +22,7 @@ from sys import argv  from vyos.ethtool import Ethtool  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27 index 949cc55b6..a0d043d11 100755 --- a/src/migration-scripts/interfaces/26-to-27 +++ b/src/migration-scripts/interfaces/26-to-27 @@ -22,7 +22,7 @@ from sys import argv  from vyos.ethtool import Ethtool  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28 index e36c95cc9..ad5bfa653 100755 --- a/src/migration-scripts/interfaces/27-to-28 +++ b/src/migration-scripts/interfaces/27-to-28 @@ -22,7 +22,7 @@ from sys import argv  from vyos.ethtool import Ethtool  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/28-to-29 b/src/migration-scripts/interfaces/28-to-29 index 64c649b02..acb6ee1fb 100755 --- a/src/migration-scripts/interfaces/28-to-29 +++ b/src/migration-scripts/interfaces/28-to-29 @@ -21,7 +21,7 @@ from sys import argv  from vyos.ethtool import Ethtool  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/interfaces/29-to-30 b/src/migration-scripts/interfaces/29-to-30 index 54def1be9..97e1b329c 100755 --- a/src/migration-scripts/interfaces/29-to-30 +++ b/src/migration-scripts/interfaces/29-to-30 @@ -17,7 +17,7 @@  # Deletes Wireguard peers if they have the same public key as the router has.  import sys  from vyos.configtree import ConfigTree -from vyos.validate import is_wireguard_key_pair +from vyos.utils.network import is_wireguard_key_pair  if __name__ == '__main__':      if len(sys.argv) < 2: diff --git a/src/migration-scripts/interfaces/3-to-4 b/src/migration-scripts/interfaces/3-to-4 index e3bd25a68..c7fd7d01d 100755 --- a/src/migration-scripts/interfaces/3-to-4 +++ b/src/migration-scripts/interfaces/3-to-4 @@ -6,7 +6,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5 index f645c5aeb..68d81e846 100755 --- a/src/migration-scripts/interfaces/4-to-5 +++ b/src/migration-scripts/interfaces/4-to-5 @@ -57,7 +57,7 @@ def migrate_dialer(config, tree, intf):  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/5-to-6 b/src/migration-scripts/interfaces/5-to-6 index ae79c1d1b..9d9a49c2d 100755 --- a/src/migration-scripts/interfaces/5-to-6 +++ b/src/migration-scripts/interfaces/5-to-6 @@ -98,7 +98,7 @@ def copy_rtradv(c, old_base, interface):                  c.delete(new_base + ['link-mtu'])  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7 index 220c7e601..49b853d90 100755 --- a/src/migration-scripts/interfaces/6-to-7 +++ b/src/migration-scripts/interfaces/6-to-7 @@ -20,7 +20,7 @@ import sys  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(sys.argv) < 1): +    if len(sys.argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8 index 9845098a7..9343a48a8 100755 --- a/src/migration-scripts/interfaces/7-to-8 +++ b/src/migration-scripts/interfaces/7-to-8 @@ -37,7 +37,7 @@ def migrate_default_keys():          os.rename(f'{kdir}/public.key', f'{location}/public.key')  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/8-to-9 b/src/migration-scripts/interfaces/8-to-9 index 2d1efd418..960962be7 100755 --- a/src/migration-scripts/interfaces/8-to-9 +++ b/src/migration-scripts/interfaces/8-to-9 @@ -22,7 +22,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/interfaces/9-to-10 b/src/migration-scripts/interfaces/9-to-10 index 4aa2c42b5..e9b8cb784 100755 --- a/src/migration-scripts/interfaces/9-to-10 +++ b/src/migration-scripts/interfaces/9-to-10 @@ -23,7 +23,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/ipoe-server/0-to-1 b/src/migration-scripts/ipoe-server/0-to-1 index d768758ba..ac9d13abc 100755 --- a/src/migration-scripts/ipoe-server/0-to-1 +++ b/src/migration-scripts/ipoe-server/0-to-1 @@ -26,7 +26,7 @@ import sys  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/10-to-11 b/src/migration-scripts/ipsec/10-to-11 index 0707a5e3c..509216267 100755 --- a/src/migration-scripts/ipsec/10-to-11 +++ b/src/migration-scripts/ipsec/10-to-11 @@ -20,7 +20,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12 index 8bbde5efa..e34882c23 100755 --- a/src/migration-scripts/ipsec/11-to-12 +++ b/src/migration-scripts/ipsec/11-to-12 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/4-to-5 b/src/migration-scripts/ipsec/4-to-5 index 4e959a7bf..772d05787 100755 --- a/src/migration-scripts/ipsec/4-to-5 +++ b/src/migration-scripts/ipsec/4-to-5 @@ -20,7 +20,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ipsec/5-to-6 b/src/migration-scripts/ipsec/5-to-6 index 3a8b3926d..7d7c777c6 100755 --- a/src/migration-scripts/ipsec/5-to-6 +++ b/src/migration-scripts/ipsec/5-to-6 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/6-to-7 b/src/migration-scripts/ipsec/6-to-7 index 649a18cb3..71fbbe8a1 100755 --- a/src/migration-scripts/ipsec/6-to-7 +++ b/src/migration-scripts/ipsec/6-to-7 @@ -29,7 +29,7 @@ from vyos.pki import encode_certificate  from vyos.pki import encode_private_key  from vyos.utils.process import run -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/7-to-8 b/src/migration-scripts/ipsec/7-to-8 index 5d48b2875..e002db0b1 100755 --- a/src/migration-scripts/ipsec/7-to-8 +++ b/src/migration-scripts/ipsec/7-to-8 @@ -31,7 +31,7 @@ from vyos.pki import load_private_key  from vyos.pki import encode_public_key  from vyos.pki import encode_private_key -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/8-to-9 b/src/migration-scripts/ipsec/8-to-9 index eb44b6216..c08411f83 100755 --- a/src/migration-scripts/ipsec/8-to-9 +++ b/src/migration-scripts/ipsec/8-to-9 @@ -19,7 +19,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10 index de366ef3b..a4a71d38e 100755 --- a/src/migration-scripts/ipsec/9-to-10 +++ b/src/migration-scripts/ipsec/9-to-10 @@ -24,7 +24,7 @@ from vyos.template import is_ipv4  from vyos.template import is_ipv6 -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/isis/0-to-1 b/src/migration-scripts/isis/0-to-1 index b75a7f72c..0149c0c1f 100755 --- a/src/migration-scripts/isis/0-to-1 +++ b/src/migration-scripts/isis/0-to-1 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/isis/1-to-2 b/src/migration-scripts/isis/1-to-2 index f914ea995..9c110bf2a 100755 --- a/src/migration-scripts/isis/1-to-2 +++ b/src/migration-scripts/isis/1-to-2 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/isis/2-to-3 b/src/migration-scripts/isis/2-to-3 index 4490feb0a..78e3c1715 100755 --- a/src/migration-scripts/isis/2-to-3 +++ b/src/migration-scripts/isis/2-to-3 @@ -22,7 +22,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/l2tp/0-to-1 b/src/migration-scripts/l2tp/0-to-1 index 686ebc655..15d229822 100755 --- a/src/migration-scripts/l2tp/0-to-1 +++ b/src/migration-scripts/l2tp/0-to-1 @@ -8,7 +8,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/l2tp/1-to-2 b/src/migration-scripts/l2tp/1-to-2 index c46eba8f8..2ffb91c53 100755 --- a/src/migration-scripts/l2tp/1-to-2 +++ b/src/migration-scripts/l2tp/1-to-2 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/l2tp/2-to-3 b/src/migration-scripts/l2tp/2-to-3 index 3472ee3ed..b46b0f22e 100755 --- a/src/migration-scripts/l2tp/2-to-3 +++ b/src/migration-scripts/l2tp/2-to-3 @@ -23,7 +23,7 @@ import sys  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/l2tp/3-to-4 b/src/migration-scripts/l2tp/3-to-4 index ee6079864..8c2b909b7 100755 --- a/src/migration-scripts/l2tp/3-to-4 +++ b/src/migration-scripts/l2tp/3-to-4 @@ -29,7 +29,7 @@ from vyos.pki import encode_certificate  from vyos.pki import encode_private_key  from vyos.utils.process import run -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/lldp/0-to-1 b/src/migration-scripts/lldp/0-to-1 index 5f66570e7..a936cbdfc 100755 --- a/src/migration-scripts/lldp/0-to-1 +++ b/src/migration-scripts/lldp/0-to-1 @@ -7,7 +7,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/monitoring/0-to-1 b/src/migration-scripts/monitoring/0-to-1 index 803cdb49c..384d22f8c 100755 --- a/src/migration-scripts/monitoring/0-to-1 +++ b/src/migration-scripts/monitoring/0-to-1 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5 index b791996e2..ce215d455 100755 --- a/src/migration-scripts/nat/4-to-5 +++ b/src/migration-scripts/nat/4-to-5 @@ -20,7 +20,7 @@  from sys import argv,exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/nat66/0-to-1 b/src/migration-scripts/nat66/0-to-1 index 83b421926..444b2315f 100755 --- a/src/migration-scripts/nat66/0-to-1 +++ b/src/migration-scripts/nat66/0-to-1 @@ -17,7 +17,7 @@  from sys import argv,exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ntp/0-to-1 b/src/migration-scripts/ntp/0-to-1 index 294964580..cbce45b9b 100755 --- a/src/migration-scripts/ntp/0-to-1 +++ b/src/migration-scripts/ntp/0-to-1 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ntp/1-to-2 b/src/migration-scripts/ntp/1-to-2 index d1e510e4c..fd1f15d91 100755 --- a/src/migration-scripts/ntp/1-to-2 +++ b/src/migration-scripts/ntp/1-to-2 @@ -20,7 +20,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ntp/2-to-3 b/src/migration-scripts/ntp/2-to-3 index 7d4e0bd83..a4351845e 100755 --- a/src/migration-scripts/ntp/2-to-3 +++ b/src/migration-scripts/ntp/2-to-3 @@ -24,7 +24,7 @@ from vyos.configtree import ConfigTree  from vyos.template import is_ipv4  from vyos.template import is_ipv6 -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/openconnect/0-to-1 b/src/migration-scripts/openconnect/0-to-1 index b26023a5b..8be15fad1 100755 --- a/src/migration-scripts/openconnect/0-to-1 +++ b/src/migration-scripts/openconnect/0-to-1 @@ -28,7 +28,7 @@ from vyos.pki import encode_certificate  from vyos.pki import encode_private_key  from vyos.utils.process import run -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/openconnect/1-to-2 b/src/migration-scripts/openconnect/1-to-2 index 51cd6bc37..7978aa56e 100755 --- a/src/migration-scripts/openconnect/1-to-2 +++ b/src/migration-scripts/openconnect/1-to-2 @@ -20,7 +20,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ospf/0-to-1 b/src/migration-scripts/ospf/0-to-1 index 678569d9e..8f02acada 100755 --- a/src/migration-scripts/ospf/0-to-1 +++ b/src/migration-scripts/ospf/0-to-1 @@ -37,7 +37,7 @@ def ospf_passive_migration(config, ospf_base):                  config.set(ospf_base + ['interface', interface, 'passive', 'disable'])              config.delete(ospf_base + ['passive-interface-exclude']) -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ospf/1-to-2 b/src/migration-scripts/ospf/1-to-2 index a6beaf04e..ba9499c60 100755 --- a/src/migration-scripts/ospf/1-to-2 +++ b/src/migration-scripts/ospf/1-to-2 @@ -22,7 +22,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/policy/0-to-1 b/src/migration-scripts/policy/0-to-1 index 7134920ad..8508b734a 100755 --- a/src/migration-scripts/policy/0-to-1 +++ b/src/migration-scripts/policy/0-to-1 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/policy/1-to-2 b/src/migration-scripts/policy/1-to-2 index eebbf9d41..c70490ce9 100755 --- a/src/migration-scripts/policy/1-to-2 +++ b/src/migration-scripts/policy/1-to-2 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/policy/2-to-3 b/src/migration-scripts/policy/2-to-3 index 84cb1ff4a..8a62c8e6f 100755 --- a/src/migration-scripts/policy/2-to-3 +++ b/src/migration-scripts/policy/2-to-3 @@ -23,7 +23,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/policy/3-to-4 b/src/migration-scripts/policy/3-to-4 index 49e0b4c38..1ebb248b0 100755 --- a/src/migration-scripts/policy/3-to-4 +++ b/src/migration-scripts/policy/3-to-4 @@ -98,7 +98,7 @@ def extcommunity_migrate(config: ConfigTree, rule: list[str]) -> None:              config.set(rule + ['soo'], value=community, replace=False) -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/policy/4-to-5 b/src/migration-scripts/policy/4-to-5 index 33c9e6ade..f6f889c35 100755 --- a/src/migration-scripts/policy/4-to-5 +++ b/src/migration-scripts/policy/4-to-5 @@ -24,7 +24,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/0-to-1 b/src/migration-scripts/pppoe-server/0-to-1 index 063c7eb56..4d36f8545 100755 --- a/src/migration-scripts/pppoe-server/0-to-1 +++ b/src/migration-scripts/pppoe-server/0-to-1 @@ -20,7 +20,7 @@  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2 index 902efb86b..c73899ca1 100755 --- a/src/migration-scripts/pppoe-server/1-to-2 +++ b/src/migration-scripts/pppoe-server/1-to-2 @@ -21,7 +21,7 @@ import os  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/2-to-3 b/src/migration-scripts/pppoe-server/2-to-3 index 7cae3b5bc..a7be060df 100755 --- a/src/migration-scripts/pppoe-server/2-to-3 +++ b/src/migration-scripts/pppoe-server/2-to-3 @@ -19,7 +19,7 @@  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/3-to-4 b/src/migration-scripts/pppoe-server/3-to-4 index 5f9730a41..c07bbb1df 100755 --- a/src/migration-scripts/pppoe-server/3-to-4 +++ b/src/migration-scripts/pppoe-server/3-to-4 @@ -21,7 +21,7 @@ import os  from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/4-to-5 b/src/migration-scripts/pppoe-server/4-to-5 index 05e9c17d6..5850db673 100755 --- a/src/migration-scripts/pppoe-server/4-to-5 +++ b/src/migration-scripts/pppoe-server/4-to-5 @@ -20,7 +20,7 @@ from vyos.configtree import ConfigTree  from sys import argv  from sys import exit -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pppoe-server/5-to-6 b/src/migration-scripts/pppoe-server/5-to-6 index e4888f4db..e079ae684 100755 --- a/src/migration-scripts/pppoe-server/5-to-6 +++ b/src/migration-scripts/pppoe-server/5-to-6 @@ -20,7 +20,7 @@ from vyos.configtree import ConfigTree  from sys import argv  from sys import exit -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/pptp/0-to-1 b/src/migration-scripts/pptp/0-to-1 index d0c7a83b5..1b7697c11 100755 --- a/src/migration-scripts/pptp/0-to-1 +++ b/src/migration-scripts/pptp/0-to-1 @@ -8,7 +8,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/pptp/1-to-2 b/src/migration-scripts/pptp/1-to-2 index a13cc3a4f..99624dceb 100755 --- a/src/migration-scripts/pptp/1-to-2 +++ b/src/migration-scripts/pptp/1-to-2 @@ -21,7 +21,7 @@ from sys import argv, exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2 index a689bacc5..cca32d06e 100755 --- a/src/migration-scripts/qos/1-to-2 +++ b/src/migration-scripts/qos/1-to-2 @@ -28,7 +28,7 @@ def bandwidth_percent_to_val(interface, percent) -> int:      speed = int(speed) *1000000 # convert to MBit/s      return speed * int(percent) // 100 # integer division -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/quagga/10-to-11 b/src/migration-scripts/quagga/10-to-11 index 04fc16f79..0ed4f5df6 100755 --- a/src/migration-scripts/quagga/10-to-11 +++ b/src/migration-scripts/quagga/10-to-11 @@ -22,7 +22,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/quagga/2-to-3 b/src/migration-scripts/quagga/2-to-3 index 4c1cd86a3..96b56da70 100755 --- a/src/migration-scripts/quagga/2-to-3 +++ b/src/migration-scripts/quagga/2-to-3 @@ -21,7 +21,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/quagga/3-to-4 b/src/migration-scripts/quagga/3-to-4 index be3528391..1e8c8e2f2 100755 --- a/src/migration-scripts/quagga/3-to-4 +++ b/src/migration-scripts/quagga/3-to-4 @@ -28,7 +28,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/quagga/4-to-5 b/src/migration-scripts/quagga/4-to-5 index f8c87ce8c..fcb496a9c 100755 --- a/src/migration-scripts/quagga/4-to-5 +++ b/src/migration-scripts/quagga/4-to-5 @@ -21,7 +21,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/quagga/5-to-6 b/src/migration-scripts/quagga/5-to-6 index a71851942..f075fc2e7 100755 --- a/src/migration-scripts/quagga/5-to-6 +++ b/src/migration-scripts/quagga/5-to-6 @@ -22,7 +22,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 2): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7 index 25cf5eebd..ed295a95c 100755 --- a/src/migration-scripts/quagga/6-to-7 +++ b/src/migration-scripts/quagga/6-to-7 @@ -23,7 +23,7 @@ from vyos.configtree import ConfigTree  from vyos.template import is_ipv4  from vyos.template import is_ipv6 -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/quagga/7-to-8 b/src/migration-scripts/quagga/7-to-8 index 15c44924f..8f11bf390 100755 --- a/src/migration-scripts/quagga/7-to-8 +++ b/src/migration-scripts/quagga/7-to-8 @@ -22,7 +22,7 @@ from sys import argv  from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/quagga/8-to-9 b/src/migration-scripts/quagga/8-to-9 index 38507bd3d..0f683d5a1 100755 --- a/src/migration-scripts/quagga/8-to-9 +++ b/src/migration-scripts/quagga/8-to-9 @@ -84,7 +84,7 @@ def migrate_route(config, base, path, route_route6):                          config.rename(vrf_path, 'vrf') -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/quagga/9-to-10 b/src/migration-scripts/quagga/9-to-10 index 249738822..3731762f7 100755 --- a/src/migration-scripts/quagga/9-to-10 +++ b/src/migration-scripts/quagga/9-to-10 @@ -21,7 +21,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/rip/0-to-1 b/src/migration-scripts/rip/0-to-1 index 60d510001..08a866374 100755 --- a/src/migration-scripts/rip/0-to-1 +++ b/src/migration-scripts/rip/0-to-1 @@ -22,7 +22,7 @@ from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/rpki/0-to-1 b/src/migration-scripts/rpki/0-to-1 index 5b4893205..a7b5d07d5 100755 --- a/src/migration-scripts/rpki/0-to-1 +++ b/src/migration-scripts/rpki/0-to-1 @@ -18,7 +18,7 @@ from sys import exit  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/salt/0-to-1 b/src/migration-scripts/salt/0-to-1 index 79053c056..481d9de8f 100755 --- a/src/migration-scripts/salt/0-to-1 +++ b/src/migration-scripts/salt/0-to-1 @@ -22,7 +22,7 @@ from sys import argv,exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1 index 096ba779d..b1e61b958 100755 --- a/src/migration-scripts/snmp/0-to-1 +++ b/src/migration-scripts/snmp/0-to-1 @@ -17,7 +17,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/snmp/1-to-2 b/src/migration-scripts/snmp/1-to-2 index 466a624e6..e02cd1aa1 100755 --- a/src/migration-scripts/snmp/1-to-2 +++ b/src/migration-scripts/snmp/1-to-2 @@ -43,7 +43,7 @@ def migrate_keys(config, path):              config.set(config_path_priv, value=tmp)  if __name__ == '__main__': -    if (len(argv) < 1): +    if len(argv) < 2:          print("Must specify file name!")          exit(1) diff --git a/src/migration-scripts/snmp/2-to-3 b/src/migration-scripts/snmp/2-to-3 index 5f8d9c88d..30911aa27 100755 --- a/src/migration-scripts/snmp/2-to-3 +++ b/src/migration-scripts/snmp/2-to-3 @@ -28,7 +28,7 @@ from sys import exit  from vyos.configtree import ConfigTree  from vyos.ifconfig import Section -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/ssh/0-to-1 b/src/migration-scripts/ssh/0-to-1 index 91b832276..2595599ac 100755 --- a/src/migration-scripts/ssh/0-to-1 +++ b/src/migration-scripts/ssh/0-to-1 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/ssh/1-to-2 b/src/migration-scripts/ssh/1-to-2 index 31c40df16..79d65d7d4 100755 --- a/src/migration-scripts/ssh/1-to-2 +++ b/src/migration-scripts/ssh/1-to-2 @@ -21,7 +21,7 @@  from sys import argv,exit  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/sstp/0-to-1 b/src/migration-scripts/sstp/0-to-1 index dc65bdeab..e2fe1ea8f 100755 --- a/src/migration-scripts/sstp/0-to-1 +++ b/src/migration-scripts/sstp/0-to-1 @@ -28,7 +28,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/sstp/1-to-2 b/src/migration-scripts/sstp/1-to-2 index 94cb04831..f7ecbb6d4 100755 --- a/src/migration-scripts/sstp/1-to-2 +++ b/src/migration-scripts/sstp/1-to-2 @@ -25,7 +25,7 @@ from shutil import copy2  from stat import S_IRUSR, S_IWUSR, S_IRGRP, S_IROTH  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/sstp/2-to-3 b/src/migration-scripts/sstp/2-to-3 index 963b2ba4b..245db7ad6 100755 --- a/src/migration-scripts/sstp/2-to-3 +++ b/src/migration-scripts/sstp/2-to-3 @@ -21,7 +21,7 @@ from vyos.configtree import ConfigTree  from sys import argv  from sys import exit -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/sstp/3-to-4 b/src/migration-scripts/sstp/3-to-4 index ea814fdc5..00ca7a52d 100755 --- a/src/migration-scripts/sstp/3-to-4 +++ b/src/migration-scripts/sstp/3-to-4 @@ -28,7 +28,7 @@ from vyos.pki import encode_certificate  from vyos.pki import encode_private_key  from vyos.utils.process import run -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/10-to-11 b/src/migration-scripts/system/10-to-11 index 3c49f0d95..5d662af40 100755 --- a/src/migration-scripts/system/10-to-11 +++ b/src/migration-scripts/system/10-to-11 @@ -6,7 +6,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/11-to-12 b/src/migration-scripts/system/11-to-12 index 9cddaa1a7..880ab56dc 100755 --- a/src/migration-scripts/system/11-to-12 +++ b/src/migration-scripts/system/11-to-12 @@ -8,7 +8,7 @@  import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/12-to-13 b/src/migration-scripts/system/12-to-13 index 36311a19d..e6c4e3802 100755 --- a/src/migration-scripts/system/12-to-13 +++ b/src/migration-scripts/system/12-to-13 @@ -8,7 +8,7 @@ import re  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:    print("Must specify file name!")    sys.exit(1) diff --git a/src/migration-scripts/system/13-to-14 b/src/migration-scripts/system/13-to-14 index 1751f1865..1fa781869 100755 --- a/src/migration-scripts/system/13-to-14 +++ b/src/migration-scripts/system/13-to-14 @@ -15,7 +15,7 @@ from vyos.configtree import ConfigTree  from vyos.utils.process import cmd -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/14-to-15 b/src/migration-scripts/system/14-to-15 index c055dad1f..feaac37de 100755 --- a/src/migration-scripts/system/14-to-15 +++ b/src/migration-scripts/system/14-to-15 @@ -11,7 +11,7 @@ ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf'  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16 index 2491e3d0d..aa1c34032 100755 --- a/src/migration-scripts/system/15-to-16 +++ b/src/migration-scripts/system/15-to-16 @@ -7,7 +7,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 index e70893d55..37e02611d 100755 --- a/src/migration-scripts/system/16-to-17 +++ b/src/migration-scripts/system/16-to-17 @@ -25,7 +25,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/17-to-18 b/src/migration-scripts/system/17-to-18 index 8f762c0e2..f6adebb06 100755 --- a/src/migration-scripts/system/17-to-18 +++ b/src/migration-scripts/system/17-to-18 @@ -22,7 +22,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/18-to-19 b/src/migration-scripts/system/18-to-19 index 38479d222..fad1d17a4 100755 --- a/src/migration-scripts/system/18-to-19 +++ b/src/migration-scripts/system/18-to-19 @@ -24,7 +24,7 @@ from sys import argv, exit  from vyos.ifconfig import Interface  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/19-to-20 b/src/migration-scripts/system/19-to-20 index eb20fd8db..c04e6a5a6 100755 --- a/src/migration-scripts/system/19-to-20 +++ b/src/migration-scripts/system/19-to-20 @@ -21,7 +21,7 @@ import os  from sys import exit, argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/20-to-21 b/src/migration-scripts/system/20-to-21 index 1728995de..4bcf4edab 100755 --- a/src/migration-scripts/system/20-to-21 +++ b/src/migration-scripts/system/20-to-21 @@ -21,7 +21,7 @@ import os  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/21-to-22 b/src/migration-scripts/system/21-to-22 index ad41be646..810b634ab 100755 --- a/src/migration-scripts/system/21-to-22 +++ b/src/migration-scripts/system/21-to-22 @@ -19,7 +19,7 @@ import os  from sys import exit, argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/22-to-23 b/src/migration-scripts/system/22-to-23 index 7f832e48a..8ed198383 100755 --- a/src/migration-scripts/system/22-to-23 +++ b/src/migration-scripts/system/22-to-23 @@ -19,7 +19,7 @@ import os  from sys import exit, argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/23-to-24 b/src/migration-scripts/system/23-to-24 index 97fe82462..fd68dbf22 100755 --- a/src/migration-scripts/system/23-to-24 +++ b/src/migration-scripts/system/23-to-24 @@ -22,7 +22,7 @@ from sys import exit, argv  from vyos.configtree import ConfigTree  from vyos.template import is_ipv4 -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/24-to-25 b/src/migration-scripts/system/24-to-25 index c2f70689d..1c81a76e7 100755 --- a/src/migration-scripts/system/24-to-25 +++ b/src/migration-scripts/system/24-to-25 @@ -19,7 +19,7 @@  from sys import exit, argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/25-to-26 b/src/migration-scripts/system/25-to-26 index 615274430..7bdf3be98 100755 --- a/src/migration-scripts/system/25-to-26 +++ b/src/migration-scripts/system/25-to-26 @@ -21,7 +21,7 @@  from sys import exit, argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/system/6-to-7 b/src/migration-scripts/system/6-to-7 index bf07abf3a..d24521134 100755 --- a/src/migration-scripts/system/6-to-7 +++ b/src/migration-scripts/system/6-to-7 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/7-to-8 b/src/migration-scripts/system/7-to-8 index 4cbb21f17..5d084d2bf 100755 --- a/src/migration-scripts/system/7-to-8 +++ b/src/migration-scripts/system/7-to-8 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/system/8-to-9 b/src/migration-scripts/system/8-to-9 index db3fefdea..e3bb2bca8 100755 --- a/src/migration-scripts/system/8-to-9 +++ b/src/migration-scripts/system/8-to-9 @@ -6,7 +6,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/vrf/0-to-1 b/src/migration-scripts/vrf/0-to-1 index 5df751113..8187138d9 100755 --- a/src/migration-scripts/vrf/0-to-1 +++ b/src/migration-scripts/vrf/0-to-1 @@ -20,7 +20,7 @@ from sys import argv  from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/vrf/1-to-2 b/src/migration-scripts/vrf/1-to-2 index 9bc704e02..52d4c2c7b 100755 --- a/src/migration-scripts/vrf/1-to-2 +++ b/src/migration-scripts/vrf/1-to-2 @@ -20,7 +20,7 @@ from sys import argv  from sys import exit  from vyos.configtree import ConfigTree -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/vrf/2-to-3 b/src/migration-scripts/vrf/2-to-3 index 8e0f97141..d45b185ee 100755 --- a/src/migration-scripts/vrf/2-to-3 +++ b/src/migration-scripts/vrf/2-to-3 @@ -69,7 +69,7 @@ def _search_tables(config_commands, table_num):      return table_items -if (len(argv) < 2): +if len(argv) < 2:      print("Must specify file name!")      exit(1) diff --git a/src/migration-scripts/vrrp/1-to-2 b/src/migration-scripts/vrrp/1-to-2 index b2e61dd38..dba5af81c 100755 --- a/src/migration-scripts/vrrp/1-to-2 +++ b/src/migration-scripts/vrrp/1-to-2 @@ -21,7 +21,7 @@ import sys  from vyos.configtree import ConfigTree -if (len(sys.argv) < 1): +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/migration-scripts/vrrp/2-to-3 b/src/migration-scripts/vrrp/2-to-3 index 1151ae18c..ed583b489 100755 --- a/src/migration-scripts/vrrp/2-to-3 +++ b/src/migration-scripts/vrrp/2-to-3 @@ -19,7 +19,7 @@  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print('Must specify file name!')      exit(1) diff --git a/src/migration-scripts/vrrp/3-to-4 b/src/migration-scripts/vrrp/3-to-4 index b0a6975c2..e5d93578c 100755 --- a/src/migration-scripts/vrrp/3-to-4 +++ b/src/migration-scripts/vrrp/3-to-4 @@ -17,7 +17,7 @@  from sys import argv  from vyos.configtree import ConfigTree -if (len(argv) < 1): +if len(argv) < 2:      print('Must specify file name!')      exit(1) diff --git a/src/migration-scripts/webproxy/1-to-2 b/src/migration-scripts/webproxy/1-to-2 index 070ff356d..03f357878 100755 --- a/src/migration-scripts/webproxy/1-to-2 +++ b/src/migration-scripts/webproxy/1-to-2 @@ -7,7 +7,7 @@ import sys  from vyos.configtree import ConfigTree -if len(sys.argv) < 1: +if len(sys.argv) < 2:      print("Must specify file name!")      sys.exit(1) diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 20ef698bd..f558c18b7 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -295,8 +295,10 @@ def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],  def _get_raw_client_leases(family='inet', interface=None):      from time import mktime      from datetime import datetime +    from vyos.defaults import directories +    from vyos.utils.network import get_interface_vrf -    lease_dir = '/var/lib/dhcp' +    lease_dir = directories['isc_dhclient_dir']      lease_files = []      lease_data = [] @@ -324,6 +326,10 @@ def _get_raw_client_leases(family='inet', interface=None):                  k, v = line.split('=')                  tmp.update({k : v.replace("'", "")}) +        if 'interface' in tmp: +            vrf = get_interface_vrf(tmp['interface']) +            if vrf: tmp.update({'vrf' : vrf}) +          lease_data.append(tmp)      return lease_data @@ -352,6 +358,8 @@ def _get_formatted_client_leases(lease_data, family):              data_entries.append(["DHCP Server", lease['new_dhcp_server_identifier']])          if 'new_dhcp_lease_time' in lease:              data_entries.append(["DHCP Server", lease['new_dhcp_lease_time']]) +        if 'vrf' in lease: +            data_entries.append(["VRF", lease['vrf']])          if 'last_update' in lease:              tmp = strftime(time_string, localtime(int(lease['last_update'])))              data_entries.append(["Last Update", tmp]) diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index 8260bbb77..852a7248a 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -24,62 +24,27 @@ from vyos.config import Config  from vyos.utils.process import cmd  from vyos.utils.dict import dict_search_args -def get_firewall_interfaces(firewall, name=None, ipv6=False): -    directions = ['in', 'out', 'local'] - -    if 'interface' in firewall: -        for ifname, if_conf in firewall['interface'].items(): -            for direction in directions: -                if direction not in if_conf: -                    continue - -                fw_conf = if_conf[direction] -                name_str = f'({ifname},{direction})' - -                if 'name' in fw_conf: -                    fw_name = fw_conf['name'] - -                    if not name: -                        firewall['name'][fw_name]['interface'].append(name_str) -                    elif not ipv6 and name == fw_name: -                        firewall['interface'].append(name_str) - -                if 'ipv6_name' in fw_conf: -                    fw_name = fw_conf['ipv6_name'] - -                    if not name: -                        firewall['ipv6_name'][fw_name]['interface'].append(name_str) -                    elif ipv6 and name == fw_name: -                        firewall['interface'].append(name_str) - -    return firewall - -def get_config_firewall(conf, name=None, ipv6=False, interfaces=True): +def get_config_firewall(conf, hook=None, priority=None, ipv6=False, interfaces=True):      config_path = ['firewall'] -    if name: -        config_path += ['ipv6-name' if ipv6 else 'name', name] +    if hook: +        config_path += ['ipv6' if ipv6 else 'ipv4', hook] +        if priority: +            config_path += [priority]      firewall = conf.get_config_dict(config_path, key_mangling=('-', '_'),                                  get_first_key=True, no_tag_node_value_mangle=True) -    if firewall and interfaces: -        if name: -            firewall['interface'] = {} -        else: -            if 'name' in firewall: -                for fw_name, name_conf in firewall['name'].items(): -                    name_conf['interface'] = [] -            if 'ipv6_name' in firewall: -                for fw_name, name_conf in firewall['ipv6_name'].items(): -                    name_conf['interface'] = [] - -        get_firewall_interfaces(firewall, name, ipv6)      return firewall -def get_nftables_details(name, ipv6=False): +def get_nftables_details(hook, priority, ipv6=False):      suffix = '6' if ipv6 else ''      name_prefix = 'NAME6_' if ipv6 else 'NAME_' -    command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{name}' +    if hook == 'name' or hook == 'ipv6-name': +        command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{priority}' +    else: +        up_hook = hook.upper() +        command = f'sudo nft list chain ip{suffix} vyos_filter VYOS_{up_hook}_{priority}' +      try:          results = cmd(command)      except: @@ -87,7 +52,7 @@ def get_nftables_details(name, ipv6=False):      out = {}      for line in results.split('\n'): -        comment_search = re.search(rf'{name}[\- ](\d+|default-action)', line) +        comment_search = re.search(rf'{priority}[\- ](\d+|default-action)', line)          if not comment_search:              continue @@ -102,18 +67,15 @@ def get_nftables_details(name, ipv6=False):          out[rule_id] = rule      return out -def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None): +def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_id=None):      ip_str = 'IPv6' if ipv6 else 'IPv4' -    print(f'\n---------------------------------\n{ip_str} Firewall "{name}"\n') - -    if name_conf['interface']: -        print('Active on: {0}\n'.format(" ".join(name_conf['interface']))) +    print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {priority}"\n') -    details = get_nftables_details(name, ipv6) +    details = get_nftables_details(hook, priority, ipv6)      rows = [] -    if 'rule' in name_conf: -        for rule_id, rule_conf in name_conf['rule'].items(): +    if 'rule' in firewall_conf: +        for rule_id, rule_conf in firewall_conf['rule'].items():              if single_rule_id and rule_id != single_rule_id:                  continue @@ -128,8 +90,8 @@ def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None):                  row.append(rule_details['conditions'])              rows.append(row) -    if 'default_action' in name_conf and not single_rule_id: -        row = ['default', name_conf['default_action'], 'all'] +    if 'default_action' in firewall_conf and not single_rule_id: +        row = ['default', firewall_conf['default_action'], 'all']          if 'default-action' in details:              rule_details = details['default-action']              row.append(rule_details.get('packets', 0)) @@ -140,18 +102,15 @@ def output_firewall_name(name, name_conf, ipv6=False, single_rule_id=None):          header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']          print(tabulate.tabulate(rows, header) + '\n') -def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=None): +def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_rule_id=None):      ip_str = 'IPv6' if ipv6 else 'IPv4' -    print(f'\n---------------------------------\n{ip_str} Firewall "{name}"\n') +    print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {prior}"\n') -    if name_conf['interface']: -        print('Active on: {0}\n'.format(" ".join(name_conf['interface']))) - -    details = get_nftables_details(name, ipv6) +    details = get_nftables_details(prior, ipv6)      rows = [] -    if 'rule' in name_conf: -        for rule_id, rule_conf in name_conf['rule'].items(): +    if 'rule' in prior_conf: +        for rule_id, rule_conf in prior_conf['rule'].items():              if single_rule_id and rule_id != single_rule_id:                  continue @@ -174,7 +133,7 @@ def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=              row.append(dest_addr)              rows.append(row) -    if 'default_action' in name_conf and not single_rule_id: +    if 'default_action' in prior_conf and not single_rule_id:          row = ['default']          if 'default-action' in details:              rule_details = details['default-action'] @@ -183,7 +142,7 @@ def output_firewall_name_statistics(name, name_conf, ipv6=False, single_rule_id=          else:              row.append('0')              row.append('0') -        row.append(name_conf['default_action']) +        row.append(prior_conf['default_action'])          row.append('0.0.0.0/0') # Source          row.append('0.0.0.0/0') # Dest          rows.append(row) @@ -201,29 +160,47 @@ def show_firewall():      if not firewall:          return -    if 'name' in firewall: -        for name, name_conf in firewall['name'].items(): -            output_firewall_name(name, name_conf, ipv6=False) +    if 'ipv4' in firewall: +        for hook, hook_conf in firewall['ipv4'].items(): +            for prior, prior_conf in firewall['ipv4'][hook].items(): +                output_firewall_name(hook, prior, prior_conf, ipv6=False) + +    if 'ipv6' in firewall: +        for hook, hook_conf in firewall['ipv6'].items(): +            for prior, prior_conf in firewall['ipv6'][hook].items(): +                output_firewall_name(hook, prior, prior_conf, ipv6=True) -    if 'ipv6_name' in firewall: -        for name, name_conf in firewall['ipv6_name'].items(): -            output_firewall_name(name, name_conf, ipv6=True) +def show_firewall_family(family): +    print(f'Rulesets {family} Information') -def show_firewall_name(name, ipv6=False): +    conf = Config() +    firewall = get_config_firewall(conf) + +    if not firewall: +        return + +    for hook, hook_conf in firewall[family].items(): +        for prior, prior_conf in firewall[family][hook].items(): +            if family == 'ipv6': +                output_firewall_name(hook, prior, prior_conf, ipv6=True) +            else: +                output_firewall_name(hook, prior, prior_conf, ipv6=False) + +def show_firewall_name(hook, priority, ipv6=False):      print('Ruleset Information')      conf = Config() -    firewall = get_config_firewall(conf, name, ipv6) +    firewall = get_config_firewall(conf, hook, priority, ipv6)      if firewall: -        output_firewall_name(name, firewall, ipv6) +        output_firewall_name(hook, priority, firewall, ipv6) -def show_firewall_rule(name, rule_id, ipv6=False): +def show_firewall_rule(hook, priority, rule_id, ipv6=False):      print('Rule Information')      conf = Config() -    firewall = get_config_firewall(conf, name, ipv6) +    firewall = get_config_firewall(conf, hook, priority, ipv6)      if firewall: -        output_firewall_name(name, firewall, ipv6, rule_id) +        output_firewall_name(hook, priority, firewall, ipv6, rule_id)  def show_firewall_group(name=None):      conf = Config() @@ -234,19 +211,32 @@ def show_firewall_group(name=None):      def find_references(group_type, group_name):          out = [] -        for name_type in ['name', 'ipv6_name']: -            if name_type not in firewall: -                continue -            for name, name_conf in firewall[name_type].items(): -                if 'rule' not in name_conf: +        family = [] +        if group_type in ['address_group', 'network_group']: +            family = ['ipv4'] +        elif group_type == 'ipv6_address_group': +            family = ['ipv6'] +            group_type = 'address_group' +        elif group_type == 'ipv6_network_group': +            family = ['ipv6'] +            group_type = 'network_group' +        else: +            family = ['ipv4', 'ipv6'] + +        for item in family: +            for name_type in ['name', 'ipv6_name', 'forward', 'input', 'output']: +                if name_type not in firewall[item]:                      continue -                for rule_id, rule_conf in name_conf['rule'].items(): -                    source_group = dict_search_args(rule_conf, 'source', 'group', group_type) -                    dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type) -                    if source_group and group_name == source_group: -                        out.append(f'{name}-{rule_id}') -                    elif dest_group and group_name == dest_group: -                        out.append(f'{name}-{rule_id}') +                for name, name_conf in firewall[item][name_type].items(): +                    if 'rule' not in name_conf: +                        continue +                    for rule_id, rule_conf in name_conf['rule'].items(): +                        source_group = dict_search_args(rule_conf, 'source', 'group', group_type) +                        dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type) +                        if source_group and group_name == source_group: +                            out.append(f'{name}-{rule_id}') +                        elif dest_group and group_name == dest_group: +                            out.append(f'{name}-{rule_id}')          return out      header = ['Name', 'Type', 'References', 'Members'] @@ -284,28 +274,28 @@ def show_summary():      if not firewall:          return -    header = ['Ruleset Name', 'Description', 'References'] +    header = ['Ruleset Hook', 'Ruleset Priority', 'Description', 'References']      v4_out = []      v6_out = [] -    if 'name' in firewall: -        for name, name_conf in firewall['name'].items(): -            description = name_conf.get('description', '') -            interfaces = ", ".join(name_conf['interface']) -            v4_out.append([name, description, interfaces]) +    if 'ipv4' in firewall: +        for hook, hook_conf in firewall['ipv4'].items(): +            for prior, prior_conf in firewall['ipv4'][hook].items(): +                description = prior_conf.get('description', '') +                v4_out.append([hook, prior, description]) -    if 'ipv6_name' in firewall: -        for name, name_conf in firewall['ipv6_name'].items(): -            description = name_conf.get('description', '') -            interfaces = ", ".join(name_conf['interface']) -            v6_out.append([name, description, interfaces or 'N/A']) +    if 'ipv6' in firewall: +        for hook, hook_conf in firewall['ipv6'].items(): +            for prior, prior_conf in firewall['ipv6'][hook].items(): +                description = prior_conf.get('description', '') +                v6_out.append([hook, prior, description])      if v6_out: -        print('\nIPv6 name:\n') +        print('\nIPv6 Ruleset:\n')          print(tabulate.tabulate(v6_out, header) + '\n')      if v4_out: -        print('\nIPv4 name:\n') +        print('\nIPv4 Ruleset:\n')          print(tabulate.tabulate(v4_out, header) + '\n')      show_firewall_group() @@ -319,18 +309,23 @@ def show_statistics():      if not firewall:          return -    if 'name' in firewall: -        for name, name_conf in firewall['name'].items(): -            output_firewall_name_statistics(name, name_conf, ipv6=False) +    if 'ipv4' in firewall: +        for hook, hook_conf in firewall['ipv4'].items(): +            for prior, prior_conf in firewall['ipv4'][hook].items(): +                output_firewall_name_statistics(hook,prior, prior_conf, ipv6=False) -    if 'ipv6_name' in firewall: -        for name, name_conf in firewall['ipv6_name'].items(): -            output_firewall_name_statistics(name, name_conf, ipv6=True) +    if 'ipv6' in firewall: +        for hook, hook_conf in firewall['ipv6'].items(): +            for prior, prior_conf in firewall['ipv6'][hook].items(): +                output_firewall_name_statistics(hook,prior, prior_conf, ipv6=True)  if __name__ == '__main__':      parser = argparse.ArgumentParser()      parser.add_argument('--action', help='Action', required=False)      parser.add_argument('--name', help='Firewall name', required=False, action='store', nargs='?', default='') +    parser.add_argument('--family', help='IP family', required=False, action='store', nargs='?', default='') +    parser.add_argument('--hook', help='Firewall hook', required=False, action='store', nargs='?', default='') +    parser.add_argument('--priority', help='Firewall priority', required=False, action='store', nargs='?', default='')      parser.add_argument('--rule', help='Firewall Rule ID', required=False)      parser.add_argument('--ipv6', help='IPv6 toggle', action='store_true') @@ -338,11 +333,13 @@ if __name__ == '__main__':      if args.action == 'show':          if not args.rule: -            show_firewall_name(args.name, args.ipv6) +            show_firewall_name(args.hook, args.priority, args.ipv6)          else: -            show_firewall_rule(args.name, args.rule, args.ipv6) +            show_firewall_rule(args.hook, args.priority, args.rule, args.ipv6)      elif args.action == 'show_all':          show_firewall() +    elif args.action == 'show_family': +        show_firewall_family(args.family)      elif args.action == 'show_group':          show_firewall_group(args.name)      elif args.action == 'show_statistics': diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 4c31291ad..35c7ce0e2 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -25,9 +25,8 @@ from cryptography import x509  from cryptography.x509.oid import ExtendedKeyUsageOID  from vyos.config import Config -from vyos.configquery import ConfigTreeQuery -from vyos.configdict import dict_merge  from vyos.pki import encode_certificate, encode_public_key, encode_private_key, encode_dh_parameters +from vyos.pki import get_certificate_fingerprint  from vyos.pki import create_certificate, create_certificate_request, create_certificate_revocation_list  from vyos.pki import create_private_key  from vyos.pki import create_dh_parameters @@ -38,21 +37,19 @@ from vyos.utils.io import ask_input  from vyos.utils.io import ask_yes_no  from vyos.utils.misc import install_into_config  from vyos.utils.process import cmd -from vyos.xml import defaults  CERT_REQ_END = '-----END CERTIFICATE REQUEST-----'  auth_dir = '/config/auth'  # Helper Functions -conf = ConfigTreeQuery() +conf = Config()  def get_default_values():      # Fetch default x509 values      base = ['pki', 'x509', 'default']      x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'), +                                     no_tag_node_value_mangle=True,                                       get_first_key=True, -                                     no_tag_node_value_mangle=True) -    default_values = defaults(base) -    x509_defaults = dict_merge(default_values, x509_defaults) +                                     with_recursive_defaults=True)      return x509_defaults @@ -916,6 +913,12 @@ def show_certificate(name=None, pem=False):      print("Certificates:")      print(tabulate.tabulate(data, headers)) +def show_certificate_fingerprint(name, hash): +    cert = get_config_certificate(name=name) +    cert = load_certificate(cert['certificate']) + +    print(get_certificate_fingerprint(cert, hash)) +  def show_crl(name=None, pem=False):      headers = ['CA Name', 'Updated', 'Revokes']      data = [] @@ -961,6 +964,7 @@ if __name__ == '__main__':      parser.add_argument('--sign', help='Sign certificate with specified CA', required=False)      parser.add_argument('--self-sign', help='Self-sign the certificate', action='store_true')      parser.add_argument('--pem', help='Output using PEM encoding', action='store_true') +    parser.add_argument('--fingerprint', help='Show fingerprint and exit', action='store')      # SSH      parser.add_argument('--ssh', help='SSH Key', required=False) @@ -1057,7 +1061,10 @@ if __name__ == '__main__':                      if not conf.exists(['pki', 'certificate', cert_name]):                          print(f'Certificate "{cert_name}" does not exist!')                          exit(1) -                show_certificate(None if args.certificate == 'all' else args.certificate, args.pem) +                if args.fingerprint is None: +                    show_certificate(None if args.certificate == 'all' else args.certificate, args.pem) +                else: +                    show_certificate_fingerprint(args.certificate, args.fingerprint)              elif args.crl:                  show_crl(None if args.crl == 'all' else args.crl, args.pem)              else: diff --git a/src/op_mode/show_openconnect_otp.py b/src/op_mode/show_openconnect_otp.py index 415a5f72c..3771fb385 100755 --- a/src/op_mode/show_openconnect_otp.py +++ b/src/op_mode/show_openconnect_otp.py @@ -17,12 +17,11 @@  import argparse  import os +from base64 import b32encode  from vyos.config import Config -from vyos.xml import defaults -from vyos.configdict import dict_merge +from vyos.utils.dict import dict_search_args  from vyos.utils.process import popen -from base64 import b32encode  otp_file = '/run/ocserv/users.oath' @@ -33,7 +32,7 @@ def check_uname_otp(username):      config = Config()      base_key = ['vpn', 'openconnect', 'authentication', 'local-users', 'username', username, 'otp', 'key']      if not config.exists(base_key): -        return None +        return False      return True  def get_otp_ocserv(username): @@ -41,21 +40,21 @@ def get_otp_ocserv(username):      base = ['vpn', 'openconnect']      if not config.exists(base):          return None -    ocserv = 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) -    ocserv = dict_merge(default_values, ocserv) -    # workaround a "know limitation" - https://vyos.dev/T2665 -    del ocserv['authentication']['local_users']['username']['otp'] -    if not ocserv["authentication"]["local_users"]["username"]: + +    ocserv = config.get_config_dict(base, key_mangling=('-', '_'), +                                    get_first_key=True, +                                    with_recursive_defaults=True) + +    user_path = ['authentication', 'local_users', 'username'] +    users = dict_search_args(ocserv, *user_path) + +    if users is None:          return None -    default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp'] -    for user, params in ocserv['authentication']['local_users']['username'].items(): -        # Not every configuration requires OTP settings -        if ocserv['authentication']['local_users']['username'][user].get('otp'): -            ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp']) -    result = ocserv['authentication']['local_users']['username'][username] + +    # function is called conditionally, if check_uname_otp true, so username +    # exists +    result = users[username] +      return result  def display_otp_ocserv(username, params, info): @@ -101,8 +100,7 @@ if __name__ == '__main__':      parser.add_argument('--info', action="store", type=str, default='full', help='Wich information to display')      args = parser.parse_args() -    check_otp = check_uname_otp(args.user) -    if check_otp: +    if check_uname_otp(args.user):          user_otp_params = get_otp_ocserv(args.user)          display_otp_ocserv(args.user, user_otp_params, args.info)      else: diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py index d27221e54..53144fd52 100644 --- a/src/op_mode/show_techsupport_report.py +++ b/src/op_mode/show_techsupport_report.py @@ -91,7 +91,8 @@ def show_package_repository_config() -> None:  def show_user_startup_scripts() -> None:      """Prints the user startup scripts.""" -    execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts') +    execute_command('cat /config/scripts/vyos-preconfig-bootup.script', 'User Startup Scripts (Preconfig)') +    execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts (Postconfig)')  def show_frr_config() -> None: diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service index 23cd4cfc3..099f7ed52 100644 --- a/src/systemd/dhclient@.service +++ b/src/systemd/dhclient@.service @@ -1,18 +1,17 @@  [Unit]  Description=DHCP client on %i  Documentation=man:dhclient(8) -ConditionPathExists=/var/lib/dhcp/dhclient_%i.conf -ConditionPathExists=/var/lib/dhcp/dhclient_%i.options +StartLimitIntervalSec=0  After=vyos-router.service  [Service] -WorkingDirectory=/var/lib/dhcp  Type=exec -EnvironmentFile=-/var/lib/dhcp/dhclient_%i.options -PIDFile=/var/lib/dhcp/dhclient_%i.pid -ExecStart=/sbin/dhclient -4 $DHCLIENT_OPTS -ExecStop=/sbin/dhclient -4 $DHCLIENT_OPTS -r +ExecStart=/sbin/dhclient -4 -d $DHCLIENT_OPTS +ExecStop=/sbin/dhclient -4 -r $DHCLIENT_OPTS  Restart=always +RestartPreventExitStatus= +RestartSec=10 +RuntimeDirectoryPreserve=yes  TimeoutStopSec=20  SendSIGKILL=true  FinalKillSignal=SIGABRT diff --git a/src/systemd/dhcp6c@.service b/src/systemd/dhcp6c@.service index 495cb7e26..f634bd944 100644 --- a/src/systemd/dhcp6c@.service +++ b/src/systemd/dhcp6c@.service @@ -1,19 +1,19 @@  [Unit]  Description=WIDE DHCPv6 client on %i  Documentation=man:dhcp6c(8) man:dhcp6c.conf(5) -ConditionPathExists=/run/dhcp6c/dhcp6c.%i.conf -ConditionPathExists=/run/dhcp6c/dhcp6c.%i.options -After=vyos-router.service  StartLimitIntervalSec=0 +After=vyos-router.service  [Service] +Type=forking  WorkingDirectory=/run/dhcp6c  EnvironmentFile=-/run/dhcp6c/dhcp6c.%i.options -Type=forking  PIDFile=/run/dhcp6c/dhcp6c.%i.pid  ExecStart=/usr/sbin/dhcp6c $DHCP6C_OPTS -Restart=on-failure -RestartSec=20 +Restart=always +RestartPreventExitStatus= +RestartSec=10 +RuntimeDirectoryPreserve=yes  [Install]  WantedBy=multi-user.target diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py index cb843ff09..ba50d06cc 100644 --- a/src/tests/test_initial_setup.py +++ b/src/tests/test_initial_setup.py @@ -21,14 +21,16 @@ import vyos.configtree  import vyos.initialsetup as vis  from unittest import TestCase -from vyos import xml +from vyos.xml_ref import definition +from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference  class TestInitialSetup(TestCase):      def setUp(self):          with open('tests/data/config.boot.default', 'r') as f:              config_string = f.read()              self.config = vyos.configtree.ConfigTree(config_string) -            self.xml = xml.load_configuration() +            self.xml = definition.Xml() +            self.xml.define(reference)      def test_set_user_password(self):          vis.set_user_password(self.config, 'vyos', 'vyosvyos') diff --git a/src/tests/test_util.py b/src/tests/test_utils.py index 27ee648d4..9ae329ced 100644 --- a/src/tests/test_util.py +++ b/src/tests/test_utils.py @@ -15,8 +15,7 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  from unittest import TestCase - -class TestVyOSUtil(TestCase): +class TestVyOSUtils(TestCase):      def test_key_mangling(self):          from vyos.utils.dict import mangle_dict_keys          data = {"foo-bar": {"baz-quux": None}} diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py new file mode 100644 index 000000000..5a6dc2586 --- /dev/null +++ b/src/tests/test_utils_network.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020-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 +# 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 vyos.utils.network +from unittest import TestCase + +class TestVyOSUtilsNetwork(TestCase): +    def setUp(self): +        pass + +    def test_is_addr_assigned(self): +        self.assertTrue(vyos.utils.network.is_addr_assigned('127.0.0.1')) +        self.assertTrue(vyos.utils.network.is_addr_assigned('::1')) +        self.assertFalse(vyos.utils.network.is_addr_assigned('127.251.255.123')) + +    def test_is_ipv6_link_local(self): +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('169.254.0.1')) +        self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::')) +        self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1')) +        self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1%eth0')) +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::')) +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::%eth0')) +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('VyOS')) +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1')) +        self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1%lo')) + +    def test_is_ipv6_link_local(self): +        self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.0.1')) +        self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.1.1')) +        self.assertTrue(vyos.utils.network.is_loopback_addr('127.1.1.1')) +        self.assertTrue(vyos.utils.network.is_loopback_addr('::1')) + +        self.assertFalse(vyos.utils.network.is_loopback_addr('::2')) +        self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1')) + + + diff --git a/src/tests/test_validate.py b/src/tests/test_validate.py deleted file mode 100644 index 68a257d25..000000000 --- a/src/tests/test_validate.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 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 vyos.validate -from unittest import TestCase - -class TestVyOSValidate(TestCase): -    def setUp(self): -        pass - -    def test_is_addr_assigned(self): -        self.assertTrue(vyos.validate.is_addr_assigned('127.0.0.1')) -        self.assertTrue(vyos.validate.is_addr_assigned('::1')) -        self.assertFalse(vyos.validate.is_addr_assigned('127.251.255.123')) - -    def test_is_ipv6_link_local(self): -        self.assertFalse(vyos.validate.is_ipv6_link_local('169.254.0.1')) -        self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::')) -        self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::affe:1')) -        self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::affe:1%eth0')) -        self.assertFalse(vyos.validate.is_ipv6_link_local('2001:db8::')) -        self.assertFalse(vyos.validate.is_ipv6_link_local('2001:db8::%eth0')) -        self.assertFalse(vyos.validate.is_ipv6_link_local('VyOS')) -        self.assertFalse(vyos.validate.is_ipv6_link_local('::1')) -        self.assertFalse(vyos.validate.is_ipv6_link_local('::1%lo')) - -    def test_is_ipv6_link_local(self): -        self.assertTrue(vyos.validate.is_loopback_addr('127.0.0.1')) -        self.assertTrue(vyos.validate.is_loopback_addr('127.0.1.1')) -        self.assertTrue(vyos.validate.is_loopback_addr('127.1.1.1')) -        self.assertTrue(vyos.validate.is_loopback_addr('::1')) - -        self.assertFalse(vyos.validate.is_loopback_addr('::2')) -        self.assertFalse(vyos.validate.is_loopback_addr('192.0.2.1')) - - - diff --git a/src/validators/ipv6-link-local b/src/validators/ipv6-link-local index 05e693b77..6ac3ea710 100755 --- a/src/validators/ipv6-link-local +++ b/src/validators/ipv6-link-local @@ -1,7 +1,7 @@  #!/usr/bin/python3  import sys -from vyos.validate import is_ipv6_link_local +from vyos.utils.network import is_ipv6_link_local  if __name__ == '__main__':      if len(sys.argv)>1: | 
