From 6aee08497522990b19291de42f9d445679eaae27 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Wed, 13 Sep 2023 01:02:12 -0500 Subject: ddclient: T5574: Support per-service cache management for services Add support for per-service cache management for ddclient providers via `wait-time` and `expiry-time` options. This allows for finer-grained control over how often a service is updated and how long the hostname will be cached before being marked expired in ddclient's cache. More specifically, `wait-time` controls how often ddclient will attempt to check for a change in the hostname's IP address, and `expiry-time` controls how often ddclient to a forced update of the hostname's IP address. These options intentionally don't have any default values because they are provider-specific. They get treated similar to the other provider- specific options in that they are only used if defined. --- data/templates/dns-dynamic/ddclient.conf.j2 | 11 ++++----- interface-definitions/dns-dynamic.xml.in | 2 ++ .../dns/dynamic-service-wait-expiry-time.xml.i | 28 ++++++++++++++++++++++ smoketest/scripts/cli/test_service_dns_dynamic.py | 12 ++++++++++ src/conf_mode/dns_dynamic.py | 3 +++ 5 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2 index 5905b19ea..6e77abdb5 100644 --- a/data/templates/dns-dynamic/ddclient.conf.j2 +++ b/data/templates/dns-dynamic/ddclient.conf.j2 @@ -14,10 +14,8 @@ if{{ ipv }}={{ address }}, \ {% endif %} {% endfor %} {# Other service options #} -{% for k,v in kwargs.items() %} -{% if v is vyos_defined %} -{{ k }}={{ v }}{{ ',' if not loop.last }} \ -{% endif %} +{% for k,v in kwargs.items() if v is vyos_defined %} +{{ k | replace('_', '-') }}={{ v }}{{ ',' if not loop.last }} \ {% endfor %} {# Actual hostname for the service #} {{ host }} @@ -49,7 +47,6 @@ use=no {{ render_config(host, address, service_cfg.web_options, protocol='nsupdate', server=config.server, zone=config.zone, password=config.key, ttl=config.ttl) }} - {% endfor %} {% endfor %} {% endif %} @@ -66,8 +63,8 @@ use=no # Web service dynamic DNS configuration for {{ name }}: [{{ config.protocol }}, {{ host }}] {{ render_config(host, address, service_cfg.web_options, ip_suffixes, protocol=config.protocol, server=config.server, zone=config.zone, - login=config.username, password=config.password, ttl=config.ttl) }} - + login=config.username, password=config.password, ttl=config.ttl, + min_interval=config.wait_time, max_interval=config.expiry_time) }} {% endfor %} {% endfor %} {% endif %} diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in index ba7f426c1..723223f1c 100644 --- a/interface-definitions/dns-dynamic.xml.in +++ b/interface-definitions/dns-dynamic.xml.in @@ -61,6 +61,7 @@ #include #include + #include File containing the TSIG secret key shared with remote DNS server @@ -88,6 +89,7 @@ #include #include + #include #include #include #include diff --git a/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i b/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i new file mode 100644 index 000000000..866690cbe --- /dev/null +++ b/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i @@ -0,0 +1,28 @@ + + + + Time in seconds to wait between update attempts + + u32:60-86400 + Time in seconds + + + + + Wait time must be between 60 and 86400 seconds + + + + + Time in seconds for the hostname to be marked expired in cache + + u32:300-2160000 + Time in seconds + + + + + Expiry time must be between 300 and 2160000 seconds + + + diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index 66dcde434..acabc0070 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -116,6 +116,9 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): svc_path = ['address', interface, 'service', 'dynv6'] proto = 'dyndns2' ip_version = 'ipv6' + wait_time = '600' + expiry_time_good = '3600' + expiry_time_bad = '360' self.cli_set(base_path + ['timeout', timeout]) self.cli_set(base_path + svc_path + ['ip-version', ip_version]) @@ -124,6 +127,13 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + svc_path + ['username', username]) self.cli_set(base_path + svc_path + ['password', password]) self.cli_set(base_path + svc_path + ['host-name', hostname]) + self.cli_set(base_path + svc_path + ['wait-time', wait_time]) + + # expiry-time must be greater than wait-time, exception is raised otherwise + self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_bad]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_good]) # commit changes self.cli_commit() @@ -137,6 +147,8 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): self.assertIn(f'server={server}', ddclient_conf) self.assertIn(f'login={username}', ddclient_conf) self.assertIn(f'password={password}', ddclient_conf) + self.assertIn(f'min-interval={wait_time}', ddclient_conf) + self.assertIn(f'max-interval={expiry_time_good}', ddclient_conf) # IPv4+IPv6 dual DDNS service configuration def test_03_dyndns_service_dual_stack(self): diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py index 8a438cf6f..874c4b689 100755 --- a/src/conf_mode/dns_dynamic.py +++ b/src/conf_mode/dns_dynamic.py @@ -111,6 +111,9 @@ def verify(dyndns): raise ConfigError(f'"{config["protocol"]}" does not support ' f'both IPv4 and IPv6 at the same time 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"') + return None def generate(dyndns): -- cgit v1.2.3