From 975ce1c71352e319abef59e9e63de2c3fca2f281 Mon Sep 17 00:00:00 2001 From: Indrajit Raychaudhuri Date: Mon, 11 Dec 2023 18:02:02 -0400 Subject: ddclient: T5791: Fix migration to normalize config name and avoid config Since `service dns dynamic address
service ...` changed to `service dns dynamic name address
...`, the resulting service and address config flip can result in conflicting `service` name. Additionally, since dynamic DNS service name now have name constraint, we need to normalize the service name to conform with the constraint. We now migrate the service name to (service|rfc2136)--
to avoid the conflict and optionally append an index if there is still a name conflict after normalization. --- src/migration-scripts/dns-dynamic/2-to-3 | 45 +++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/migration-scripts/dns-dynamic/2-to-3 b/src/migration-scripts/dns-dynamic/2-to-3 index 187c2a895..4e0aa37d5 100755 --- a/src/migration-scripts/dns-dynamic/2-to-3 +++ b/src/migration-scripts/dns-dynamic/2-to-3 @@ -21,10 +21,27 @@ # to "service dns dynamic name address protocol 'nsupdate'" # - migrate "service dns dynamic address service ..." # to "service dns dynamic name address ..." +# - normalize the all service names to conform with name constraints import sys +import re +from unicodedata import normalize from vyos.configtree import ConfigTree +def normalize_name(name): + """Normalize service names to conform with name constraints. + + This is necessary as part of migration because there were no constraints in + the old name format. + """ + # Normalize unicode characters to ASCII (NFKD) + # Replace all separators with hypens, strip leading and trailing hyphens + name = normalize('NFKD', name).encode('ascii', 'ignore').decode() + name = re.sub(r'(\s|_|\W)+', '-', name).strip('-') + + return name + + if len(sys.argv) < 2: print("Must specify file name!") sys.exit(1) @@ -64,22 +81,36 @@ for address in config.list_nodes(address_path): for svc_type in ['service', 'rfc2136']: if config.exists(address_path_tag + [svc_type]): - # Move RFC2136 as service configuration, rename to avoid name conflict and set protocol to 'nsupdate' + # Set protocol to 'nsupdate' for RFC2136 configuration if svc_type == 'rfc2136': - for rfc_cfg_old in config.list_nodes(address_path_tag + ['rfc2136']): - rfc_cfg_new = f'{rfc_cfg_old}-rfc2136' - config.rename(address_path_tag + ['rfc2136', rfc_cfg_old], rfc_cfg_new) - config.set(address_path_tag + ['rfc2136', rfc_cfg_new, 'protocol'], 'nsupdate') + for rfc_cfg in config.list_nodes(address_path_tag + ['rfc2136']): + config.set(address_path_tag + ['rfc2136', rfc_cfg, 'protocol'], 'nsupdate') # Add address as config value in each service before moving the service path - # And then copy the services from 'address service ' to 'name ' + # And then copy the services from 'address service ' + # to 'name (service|rfc2136)--
' + # Note: The new service is named (service|rfc2136)--
+ # to avoid name conflict with old entries for svc_cfg in config.list_nodes(address_path_tag + [svc_type]): config.set(address_path_tag + [svc_type, svc_cfg, 'address'], address) - config.copy(address_path_tag + [svc_type, svc_cfg], name_path + [svc_cfg]) + config.copy(address_path_tag + [svc_type, svc_cfg], + name_path + ['-'.join([svc_type, svc_cfg, address])]) # Finally cleanup the old address path config.delete(address_path) +# Normalize the all service names to conform with name constraints +index = 1 +for name in config.list_nodes(name_path): + new_name = normalize_name(name) + if new_name != name: + # Append index if there is still a name conflicts after normalization + # For example, "foo-?(" and "foo-!)" both normalize to "foo-" + if config.exists(name_path + [new_name]): + new_name = f'{new_name}-{index}' + index += 1 + config.rename(name_path + [name], new_name) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3