diff options
| -rw-r--r-- | data/config-mode-dependencies/vyos-1x.json | 3 | ||||
| -rw-r--r-- | data/templates/dns-dynamic/ddclient.conf.j2 | 2 | ||||
| -rw-r--r-- | debian/control | 2 | ||||
| -rw-r--r-- | interface-definitions/dns-dynamic.xml.in | 6 | ||||
| -rw-r--r-- | interface-definitions/include/version/dns-dynamic-version.xml.i | 2 | ||||
| -rw-r--r-- | op-mode-definitions/include/bgp/show-bgp-common.xml.i | 16 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_service_dns_dynamic.py | 11 | ||||
| -rwxr-xr-x | src/completion/list_ddclient_protocols.sh | 2 | ||||
| -rwxr-xr-x | src/conf_mode/dns_dynamic.py | 38 | ||||
| -rwxr-xr-x | src/conf_mode/interfaces-bridge.py | 18 | ||||
| -rw-r--r-- | src/migration-scripts/dns-dynamic/1-to-2 | 70 | ||||
| -rwxr-xr-x | src/validators/ddclient-protocol | 2 | 
12 files changed, 140 insertions, 32 deletions
| diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json index f87185d6a..918fb0f17 100644 --- a/data/config-mode-dependencies/vyos-1x.json +++ b/data/config-mode-dependencies/vyos-1x.json @@ -9,6 +9,9 @@    "interfaces_bonding": {             "ethernet": ["interfaces-ethernet"]           }, +  "interfaces_bridge": { +           "vxlan": ["interfaces-vxlan"] +         },    "load_balancing_wan": {                            "conntrack": ["conntrack"],                            "conntrack_sync": ["conntrack_sync"] diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 6e77abdb5..879887a1f 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -21,7 +21,7 @@ if{{ ipv }}={{ address }}, \  {{ host }}  {% endmacro %}  ### Autogenerated by dns_dynamic.py ### -daemon={{ timeout }} +daemon={{ interval }}  syslog=yes  ssl=yes  pid={{ config_file | replace('.conf', '.pid') }} diff --git a/debian/control b/debian/control index 32de13f1b..d2ed3991a 100644 --- a/debian/control +++ b/debian/control @@ -155,7 +155,7 @@ Depends:  # For "set service aws glb"    aws-gwlbtun,  # For "service dns dynamic" -  ddclient (>= 3.9.1), +  ddclient (>= 3.11.1),  # End "service dns dynamic"  # # For "service ids"    fastnetmon [amd64], diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in index 723223f1c..07b1bf1b8 100644 --- a/interface-definitions/dns-dynamic.xml.in +++ b/interface-definitions/dns-dynamic.xml.in @@ -134,9 +134,9 @@                    </tagNode>                  </children>                </tagNode> -              <leafNode name="timeout"> +              <leafNode name="interval">                  <properties> -                  <help>Time in seconds to wait between DNS updates</help> +                  <help>Interval in seconds to wait between Dynamic DNS updates</help>                    <valueHelp>                      <format>u32:60-3600</format>                      <description>Time in seconds</description> @@ -144,7 +144,7 @@                    <constraint>                      <validator name="numeric" argument="--range 60-3600"/>                    </constraint> -                  <constraintErrorMessage>Timeout must be between 60 and 3600 seconds</constraintErrorMessage> +                  <constraintErrorMessage>Interval must be between 60 and 3600 seconds</constraintErrorMessage>                  </properties>                  <defaultValue>300</defaultValue>                </leafNode> diff --git a/interface-definitions/include/version/dns-dynamic-version.xml.i b/interface-definitions/include/version/dns-dynamic-version.xml.i index b25fc6e76..7bdb90a35 100644 --- a/interface-definitions/include/version/dns-dynamic-version.xml.i +++ b/interface-definitions/include/version/dns-dynamic-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/dns-dynamic-version.xml.i --> -<syntaxVersion component='dns-dynamic' version='1'></syntaxVersion> +<syntaxVersion component='dns-dynamic' version='2'></syntaxVersion>  <!-- include end --> diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i index de794a879..d888bc3b0 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -107,6 +107,12 @@              #include <include/vni-tagnode.xml.i>            </children>          </node> +        <leafNode name="es-vrf"> +          <properties> +            <help>Ethernet Segment per VRF</help> +          </properties> +          <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +        </leafNode>          <leafNode name="import-rt">            <properties>              <help>Show import route target</help> @@ -136,11 +142,17 @@              </leafNode>            </children>          </tagNode> +        <leafNode name="next-hops"> +          <properties> +            <help>EVPN Nexthops</help> +          </properties> +          <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +        </leafNode>          <tagNode name="rd">            <properties> -            <help>Show detailed BGP neighbor information</help> +            <help>Display information for a route distinguisher</help>              <completionHelp> -              <list>ASN:NN IPADDRESS:NN</list> +              <list>ASN:NN IPADDRESS:NN all</list>              </completionHelp>            </properties>            <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index acabc0070..9624f823f 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -112,7 +112,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):      # IPv6 only DDNS service configuration      def test_02_dyndns_service_ipv6(self): -        timeout = '60' +        interval = '60'          svc_path = ['address', interface, 'service', 'dynv6']          proto = 'dyndns2'          ip_version = 'ipv6' @@ -120,7 +120,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):          expiry_time_good = '3600'          expiry_time_bad = '360' -        self.cli_set(base_path + ['timeout', timeout]) +        self.cli_set(base_path + ['interval', interval])          self.cli_set(base_path + svc_path + ['ip-version', ip_version])          self.cli_set(base_path + svc_path + ['protocol', proto])          self.cli_set(base_path + svc_path + ['server', server]) @@ -140,7 +140,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):          # Check the generating config parameters          ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') -        self.assertIn(f'daemon={timeout}', ddclient_conf) +        self.assertIn(f'daemon={interval}', ddclient_conf)          self.assertIn(f'usev6=ifv6', ddclient_conf)          self.assertIn(f'ifv6={interface}', ddclient_conf)          self.assertIn(f'protocol={proto}', ddclient_conf) @@ -246,10 +246,11 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):              self.assertIn(f'{name}', ddclient_conf)      def test_06_dyndns_vrf(self): -        vrf_name = f'vyos-test-{"".join(random.choices(string.ascii_letters + string.digits, k=5))}' +        vrf_table = "".join(random.choices(string.digits, k=5)) +        vrf_name = f'vyos-test-{vrf_table}'          svc_path = ['address', interface, 'service', 'cloudflare'] -        self.cli_set(['vrf', 'name', vrf_name, 'table', '12345']) +        self.cli_set(['vrf', 'name', vrf_name, 'table', vrf_table])          self.cli_set(base_path + ['vrf', vrf_name])          self.cli_set(base_path + svc_path + ['protocol', 'cloudflare']) diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh index 3b4eff4d6..c8855b5d1 100755 --- a/src/completion/list_ddclient_protocols.sh +++ b/src/completion/list_ddclient_protocols.sh @@ -14,4 +14,4 @@  # You should have received a copy of the GNU General Public License  # along with this program.  If not, see <http://www.gnu.org/licenses/>. -echo -n $(ddclient -list-protocols | grep  -vE 'nsupdate|cloudns') +echo -n $(ddclient -list-protocols | grep  -vE 'nsupdate|cloudns|porkbun') diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py index 874c4b689..d6ef620fe 100755 --- a/src/conf_mode/dns_dynamic.py +++ b/src/conf_mode/dns_dynamic.py @@ -30,16 +30,21 @@ config_file = r'/run/ddclient/ddclient.conf'  systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf'  # Protocols that require zone -zone_necessary = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn'] +zone_necessary = ['cloudflare', 'digitalocean', 'godaddy', 'hetzner', 'gandi', 'nfsn'] +zone_supported = zone_necessary + ['dnsexit2', 'zoneedit1']  # Protocols that do not require username -username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla'] +username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'digitalocean', 'dnsexit2', +                        'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla', +                        'regfishde']  # Protocols that support TTL -ttl_supported = ['cloudflare', 'gandi', 'hetzner', 'dnsexit', 'godaddy', 'nfsn'] +ttl_supported = ['cloudflare', 'dnsexit2', 'gandi', 'hetzner', 'godaddy', 'nfsn']  # Protocols that support both IPv4 and IPv6 -dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla'] +dualstack_supported = ['cloudflare', 'digitalocean', 'dnsexit2', 'duckdns', +                       'dyndns2', 'easydns', 'freedns', 'hetzner', 'infomaniak', +                       'njalla']  # dyndns2 protocol in ddclient honors dual stack for selective servers  # because of the way it is implemented in ddclient @@ -82,34 +87,37 @@ def verify(dyndns):                                            f'based Dynamic DNS service on "{address}"')          # Dynamic DNS service provider - configuration validation +        if 'web_options' in dyndns['address'][address] and address != 'web': +            raise ConfigError(f'"web-options" is applicable only when using HTTP(S) web request to obtain the IP address') + +        # Dynamic DNS service provider - configuration validation          if 'service' in dyndns['address'][address]:              for service, config in dyndns['address'][address]['service'].items(): -                error_msg = f'is required for Dynamic DNS service "{service}" on "{address}"' +                error_msg_req = f'is required for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"' +                error_msg_uns = f'is not supported for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"'                  for field in ['host_name', 'password', 'protocol']:                      if field not in config: -                        raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}') +                        raise ConfigError(f'"{field.replace("_", "-")}" {error_msg_req}')                  if config['protocol'] in zone_necessary and 'zone' not in config: -                    raise ConfigError(f'"zone" {error_msg}') +                    raise ConfigError(f'"zone" {error_msg_req}') -                if config['protocol'] not in zone_necessary and 'zone' in config: -                    raise ConfigError(f'"{config["protocol"]}" does not support "zone"') +                if config['protocol'] not in zone_supported and 'zone' in config: +                    raise ConfigError(f'"zone" {error_msg_uns}')                  if config['protocol'] not in username_unnecessary and 'username' not in config: -                    raise ConfigError(f'"username" {error_msg}') +                    raise ConfigError(f'"username" {error_msg_req}')                  if config['protocol'] not in ttl_supported and 'ttl' in config: -                    raise ConfigError(f'"{config["protocol"]}" does not support "ttl"') +                    raise ConfigError(f'"ttl" {error_msg_uns}')                  if config['ip_version'] == 'both':                      if config['protocol'] not in dualstack_supported: -                        raise ConfigError(f'"{config["protocol"]}" does not support ' -                                          f'both IPv4 and IPv6 at the same time') +                        raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns}')                      # dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org)                      if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] not in dyndns_dualstack_servers: -                        raise ConfigError(f'"{config["protocol"]}" does not support ' -                                          f'both IPv4 and IPv6 at the same time for "{config["server"]}"') +                        raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns} for "{config["server"]}"')                  if {'wait_time', 'expiry_time'} <= config.keys() and int(config['expiry_time']) < int(config['wait_time']):                          raise ConfigError(f'"expiry-time" must be greater than "wait-time"') diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index c82f01e53..31508a3c5 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -28,7 +28,8 @@ from vyos.configverify import verify_vrf  from vyos.ifconfig import BridgeIf  from vyos.configdict import has_address_configured  from vyos.configdict import has_vrf_configured - +from vyos.configdep import set_dependents +from vyos.configdep import call_dependents  from vyos.utils.dict import dict_search  from vyos import ConfigError @@ -83,6 +84,12 @@ def get_config(config=None):              if 'enable_vlan' in bridge and tmp:                  bridge['member']['interface'][interface].update({'has_vlan' : ''}) +            # When using VXLAN member interfaces that are configured for Single +            # VXLAN Device (SVD) we need to call the VXLAN conf-mode script to re-create +            # VLAN to VNI mappings if required +            if interface.startswith('vxlan'): +                set_dependents('vxlan', conf, interface) +      # delete empty dictionary keys - no need to run code paths if nothing is there to do      if 'member' in bridge:          if 'interface' in bridge['member'] and len(bridge['member']['interface']) == 0: @@ -159,6 +166,13 @@ def apply(bridge):      else:          br.update(bridge) +    for interface in dict_search('member.interface', bridge) or []: +        if interface.startswith('vxlan'): +            try: +                call_dependents() +            except ConfigError: +                raise ConfigError('Error in updating VXLAN interface after changing bridge!') +      return None  if __name__ == '__main__': diff --git a/src/migration-scripts/dns-dynamic/1-to-2 b/src/migration-scripts/dns-dynamic/1-to-2 new file mode 100644 index 000000000..8b599b57a --- /dev/null +++ b/src/migration-scripts/dns-dynamic/1-to-2 @@ -0,0 +1,70 @@ +#!/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/>. + +# T5708: +# - migrate "service dns dynamic timeout ..." +#        to "service dns dynamic interval ..." +# - remove "service dns dynamic address <interface> web-options ..." when <interface> != "web" +# - migrate "service dns dynamic address <interface> service <service> protocol dnsexit" +#        to "service dns dynamic address <interface> service <service> protocol dnsexit2" + +import sys +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: +    print("Must specify file name!") +    sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +config = ConfigTree(config_file) + +base_path = ['service', 'dns', 'dynamic'] +timeout_path = base_path + ['timeout'] +address_path = base_path + ['address'] + +if not config.exists(base_path): +    # Nothing to do +    sys.exit(0) + +# Migrate "service dns dynamic timeout ..." +#      to "service dns dynamic interval ..." +if config.exists(timeout_path): +    config.rename(timeout_path, 'interval') + +# Remove "service dns dynamic address <interface> web-options ..." when <interface> != "web" +for address in config.list_nodes(address_path): +    if config.exists(address_path + [address, 'web-options']) and address != 'web': +        config.delete(address_path + [address, 'web-options']) + +# Migrate "service dns dynamic address <interface> service <service> protocol dnsexit" +#      to "service dns dynamic address <interface> service <service> protocol dnsexit2" +for address in config.list_nodes(address_path): +    for svc_cfg in config.list_nodes(address_path + [address, 'service']): +        if config.exists(address_path + [address, 'service', svc_cfg, 'protocol']): +            protocol = config.return_value(address_path + [address, 'service', svc_cfg, 'protocol']) +            if protocol == 'dnsexit': +                config.set(address_path + [address, 'service', svc_cfg, 'protocol'], 'dnsexit2') + +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)) +    sys.exit(1) diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol index bc6826120..8f455e12e 100755 --- a/src/validators/ddclient-protocol +++ b/src/validators/ddclient-protocol @@ -14,7 +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/>. -ddclient -list-protocols | grep -vE 'nsupdate|cloudns' | grep -qw $1 +ddclient -list-protocols | grep -vE 'nsupdate|cloudns|porkbun' | grep -qw $1  if [ $? -gt 0 ]; then      echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols" | 
