diff options
author | John Estabrook <jestabro@vyos.io> | 2024-06-19 20:16:05 -0500 |
---|---|---|
committer | John Estabrook <jestabro@vyos.io> | 2024-09-11 11:53:48 -0500 |
commit | b5db9395ed576ef97b1692ca66c00900c532d6a1 (patch) | |
tree | 5e06276d2b9b3a90f6ae756bbe6be20225a89cc4 /src/migration-scripts/dns-dynamic | |
parent | ccff9ffdd8e3e7336b94c70575cc7ab4b44047cc (diff) | |
download | vyos-1x-b5db9395ed576ef97b1692ca66c00900c532d6a1.tar.gz vyos-1x-b5db9395ed576ef97b1692ca66c00900c532d6a1.zip |
migration: T6007: convert all migration scripts to load as module
(cherry picked from commit 26740a8d583f64dc0a27b59dd4ae303056972c0b)
Diffstat (limited to 'src/migration-scripts/dns-dynamic')
-rw-r--r--[-rwxr-xr-x] | src/migration-scripts/dns-dynamic/0-to-1 | 161 | ||||
-rw-r--r--[-rwxr-xr-x] | src/migration-scripts/dns-dynamic/1-to-2 | 85 | ||||
-rw-r--r--[-rwxr-xr-x] | src/migration-scripts/dns-dynamic/2-to-3 | 134 | ||||
-rw-r--r--[-rwxr-xr-x] | src/migration-scripts/dns-dynamic/3-to-4 | 87 |
4 files changed, 195 insertions, 272 deletions
diff --git a/src/migration-scripts/dns-dynamic/0-to-1 b/src/migration-scripts/dns-dynamic/0-to-1 index b7674a9c8..6a91b36af 100755..100644 --- a/src/migration-scripts/dns-dynamic/0-to-1 +++ b/src/migration-scripts/dns-dynamic/0-to-1 @@ -1,18 +1,17 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # -# 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 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 program is distributed in the hope that it will be useful, +# 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 General Public License for more details. +# 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 General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# 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/>. # T5144: # - migrate "service dns dynamic interface ..." @@ -27,7 +26,6 @@ # - apply service protocol mapping upfront, they are not 'auto-detected' anymore # - migrate web-options url to stricter format -import sys import re from vyos.configtree import ConfigTree @@ -45,84 +43,67 @@ service_protocol_mapping = { 'zoneedit': 'zoneedit1' } -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) - old_base_path = ['service', 'dns', 'dynamic', 'interface'] new_base_path = ['service', 'dns', 'dynamic', 'address'] -if not config.exists(old_base_path): - # Nothing to do - sys.exit(0) - -# Migrate "service dns dynamic interface" -# to "service dns dynamic address" -config.rename(old_base_path, new_base_path[-1]) - -for address in config.list_nodes(new_base_path): - # Migrate "service dns dynamic interface <interface> rfc2136 <config> record" - # to "service dns dynamic address <address> rfc2136 <config> host-name" - if config.exists(new_base_path + [address, 'rfc2136']): - for rfc_cfg in config.list_nodes(new_base_path + [address, 'rfc2136']): - if config.exists(new_base_path + [address, 'rfc2136', rfc_cfg, 'record']): - config.rename(new_base_path + [address, 'rfc2136', rfc_cfg, 'record'], 'host-name') - - # Migrate "service dns dynamic interface <interface> service <config> login" - # to "service dns dynamic address <address> service <config> username" - if config.exists(new_base_path + [address, 'service']): - for svc_cfg in config.list_nodes(new_base_path + [address, 'service']): - if config.exists(new_base_path + [address, 'service', svc_cfg, 'login']): - config.rename(new_base_path + [address, 'service', svc_cfg, 'login'], 'username') - # Apply global 'ipv6-enable' to per <config> 'ip-version: ipv6' - if config.exists(new_base_path + [address, 'ipv6-enable']): - config.set(new_base_path + [address, 'service', svc_cfg, 'ip-version'], 'ipv6') - config.delete(new_base_path + [address, 'ipv6-enable']) - # Apply service protocol mapping upfront, they are not 'auto-detected' anymore - if svc_cfg in service_protocol_mapping: - config.set(new_base_path + [address, 'service', svc_cfg, 'protocol'], - service_protocol_mapping.get(svc_cfg)) - - # If use-web is set, then: - # Move "service dns dynamic address <address> <service|rfc2136> <service> ..." - # to "service dns dynamic address web <service|rfc2136> <service>-<address> ..." - # Move "service dns dynamic address web use-web ..." - # to "service dns dynamic address web web-options ..." - # Note: The config is named <service>-<address> to avoid name conflict with old entries - if config.exists(new_base_path + [address, 'use-web']): - for svc_type in ['rfc2136', 'service']: - if config.exists(new_base_path + [address, svc_type]): - config.set(new_base_path + ['web', svc_type]) - config.set_tag(new_base_path + ['web', svc_type]) - for svc_cfg in config.list_nodes(new_base_path + [address, svc_type]): - config.copy(new_base_path + [address, svc_type, svc_cfg], - new_base_path + ['web', svc_type, f'{svc_cfg}-{address}']) - - # Multiple web-options were not supported, so copy only the first one - # Also, migrate web-options url to stricter format and transition - # checkip.dyndns.org to https://domains.google.com/checkip for better - # TLS support (see: https://github.com/ddclient/ddclient/issues/597) - if not config.exists(new_base_path + ['web', 'web-options']): - config.copy(new_base_path + [address, 'use-web'], new_base_path + ['web', 'web-options']) - if config.exists(new_base_path + ['web', 'web-options', 'url']): - url = config.return_value(new_base_path + ['web', 'web-options', 'url']) - if re.search("^(https?://)?checkip\.dyndns\.org", url): - config.set(new_base_path + ['web', 'web-options', 'url'], 'https://domains.google.com/checkip') - if not url.startswith(('http://', 'https://')): - config.set(new_base_path + ['web', 'web-options', 'url'], f'https://{url}') - - config.delete(new_base_path + [address]) - -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) +def migrate(config: ConfigTree) -> None: + if not config.exists(old_base_path): + # Nothing to do + return + + # Migrate "service dns dynamic interface" + # to "service dns dynamic address" + config.rename(old_base_path, new_base_path[-1]) + + for address in config.list_nodes(new_base_path): + # Migrate "service dns dynamic interface <interface> rfc2136 <config> record" + # to "service dns dynamic address <address> rfc2136 <config> host-name" + if config.exists(new_base_path + [address, 'rfc2136']): + for rfc_cfg in config.list_nodes(new_base_path + [address, 'rfc2136']): + if config.exists(new_base_path + [address, 'rfc2136', rfc_cfg, 'record']): + config.rename(new_base_path + [address, 'rfc2136', rfc_cfg, 'record'], 'host-name') + + # Migrate "service dns dynamic interface <interface> service <config> login" + # to "service dns dynamic address <address> service <config> username" + if config.exists(new_base_path + [address, 'service']): + for svc_cfg in config.list_nodes(new_base_path + [address, 'service']): + if config.exists(new_base_path + [address, 'service', svc_cfg, 'login']): + config.rename(new_base_path + [address, 'service', svc_cfg, 'login'], 'username') + # Apply global 'ipv6-enable' to per <config> 'ip-version: ipv6' + if config.exists(new_base_path + [address, 'ipv6-enable']): + config.set(new_base_path + [address, 'service', svc_cfg, 'ip-version'], 'ipv6') + config.delete(new_base_path + [address, 'ipv6-enable']) + # Apply service protocol mapping upfront, they are not 'auto-detected' anymore + if svc_cfg in service_protocol_mapping: + config.set(new_base_path + [address, 'service', svc_cfg, 'protocol'], + service_protocol_mapping.get(svc_cfg)) + + # If use-web is set, then: + # Move "service dns dynamic address <address> <service|rfc2136> <service> ..." + # to "service dns dynamic address web <service|rfc2136> <service>-<address> ..." + # Move "service dns dynamic address web use-web ..." + # to "service dns dynamic address web web-options ..." + # Note: The config is named <service>-<address> to avoid name conflict with old entries + if config.exists(new_base_path + [address, 'use-web']): + for svc_type in ['rfc2136', 'service']: + if config.exists(new_base_path + [address, svc_type]): + config.set(new_base_path + ['web', svc_type]) + config.set_tag(new_base_path + ['web', svc_type]) + for svc_cfg in config.list_nodes(new_base_path + [address, svc_type]): + config.copy(new_base_path + [address, svc_type, svc_cfg], + new_base_path + ['web', svc_type, f'{svc_cfg}-{address}']) + + # Multiple web-options were not supported, so copy only the first one + # Also, migrate web-options url to stricter format and transition + # checkip.dyndns.org to https://domains.google.com/checkip for better + # TLS support (see: https://github.com/ddclient/ddclient/issues/597) + if not config.exists(new_base_path + ['web', 'web-options']): + config.copy(new_base_path + [address, 'use-web'], new_base_path + ['web', 'web-options']) + if config.exists(new_base_path + ['web', 'web-options', 'url']): + url = config.return_value(new_base_path + ['web', 'web-options', 'url']) + if re.search("^(https?://)?checkip\.dyndns\.org", url): + config.set(new_base_path + ['web', 'web-options', 'url'], 'https://domains.google.com/checkip') + if not url.startswith(('http://', 'https://')): + config.set(new_base_path + ['web', 'web-options', 'url'], f'https://{url}') + + config.delete(new_base_path + [address]) diff --git a/src/migration-scripts/dns-dynamic/1-to-2 b/src/migration-scripts/dns-dynamic/1-to-2 index 8b599b57a..5dca9e32f 100755..100644 --- a/src/migration-scripts/dns-dynamic/1-to-2 +++ b/src/migration-scripts/dns-dynamic/1-to-2 @@ -1,18 +1,17 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # -# 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 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 program is distributed in the hope that it will be useful, +# 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 General Public License for more details. +# 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 General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# 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/>. # T5708: # - migrate "service dns dynamic timeout ..." @@ -21,50 +20,32 @@ # - 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) +def migrate(config: ConfigTree) -> None: + if not config.exists(base_path): + # Nothing to do + return + + # 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') diff --git a/src/migration-scripts/dns-dynamic/2-to-3 b/src/migration-scripts/dns-dynamic/2-to-3 index 4e0aa37d5..9aafc41a4 100755..100644 --- a/src/migration-scripts/dns-dynamic/2-to-3 +++ b/src/migration-scripts/dns-dynamic/2-to-3 @@ -1,18 +1,17 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # -# 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 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 program is distributed in the hope that it will be useful, +# 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 General Public License for more details. +# 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 General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# 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/>. # T5791: # - migrate "service dns dynamic address web web-options ..." @@ -23,7 +22,6 @@ # to "service dns dynamic name <service> address <interface> ..." # - normalize the all service names to conform with name constraints -import sys import re from unicodedata import normalize from vyos.configtree import ConfigTree @@ -41,79 +39,61 @@ def normalize_name(name): return name - -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'] address_path = base_path + ['address'] name_path = base_path + ['name'] -if not config.exists(address_path): - # Nothing to do - sys.exit(0) +def migrate(config: ConfigTree) -> None: + if not config.exists(address_path): + # Nothing to do + return -# config.copy does not recursively create a path, so initialize the name path as tagged node -if not config.exists(name_path): - config.set(name_path) - config.set_tag(name_path) + # config.copy does not recursively create a path, so initialize the name path as tagged node + if not config.exists(name_path): + config.set(name_path) + config.set_tag(name_path) -for address in config.list_nodes(address_path): + for address in config.list_nodes(address_path): - address_path_tag = address_path + [address] + address_path_tag = address_path + [address] + + # Move web-option as a configuration in each service instead of top level web-option + if config.exists(address_path_tag + ['web-options']) and address == 'web': + for svc_type in ['service', 'rfc2136']: + if config.exists(address_path_tag + [svc_type]): + for svc_cfg in config.list_nodes(address_path_tag + [svc_type]): + config.copy(address_path_tag + ['web-options'], + address_path_tag + [svc_type, svc_cfg, 'web-options']) + config.delete(address_path_tag + ['web-options']) - # Move web-option as a configuration in each service instead of top level web-option - if config.exists(address_path_tag + ['web-options']) and address == 'web': for svc_type in ['service', 'rfc2136']: if config.exists(address_path_tag + [svc_type]): + # Set protocol to 'nsupdate' for RFC2136 configuration + if svc_type == 'rfc2136': + 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 <interface> service <service>' + # to 'name (service|rfc2136)-<service>-<address>' + # Note: The new service is named (service|rfc2136)-<service>-<address> + # to avoid name conflict with old entries for svc_cfg in config.list_nodes(address_path_tag + [svc_type]): - config.copy(address_path_tag + ['web-options'], - address_path_tag + [svc_type, svc_cfg, 'web-options']) - config.delete(address_path_tag + ['web-options']) - - for svc_type in ['service', 'rfc2136']: - if config.exists(address_path_tag + [svc_type]): - # Set protocol to 'nsupdate' for RFC2136 configuration - if svc_type == 'rfc2136': - 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 <interface> service <service>' - # to 'name (service|rfc2136)-<service>-<address>' - # Note: The new service is named (service|rfc2136)-<service>-<address> - # 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 + ['-'.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()) -except OSError as e: - print("Failed to save the modified config: {}".format(e)) - sys.exit(1) + config.set(address_path_tag + [svc_type, svc_cfg, 'address'], address) + 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) diff --git a/src/migration-scripts/dns-dynamic/3-to-4 b/src/migration-scripts/dns-dynamic/3-to-4 index b888a3b6b..c8e1ffeee 100755..100644 --- a/src/migration-scripts/dns-dynamic/3-to-4 +++ b/src/migration-scripts/dns-dynamic/3-to-4 @@ -1,18 +1,17 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2024 VyOS maintainers and contributors +# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io> # -# 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 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 program is distributed in the hope that it will be useful, +# 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 General Public License for more details. +# 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 General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# 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/>. # T5966: # - migrate "service dns dynamic name <service> address <interface>" @@ -22,55 +21,37 @@ # to "service dns dynamic name <service> address web ..." # when <interface> == 'web' -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', 'name'] -if not config.exists(base_path): - # Nothing to do - sys.exit(0) - -for service in config.list_nodes(base_path): +def migrate(config: ConfigTree) -> None: + if not config.exists(base_path): + # Nothing to do + return - service_path = base_path + [service] + for service in config.list_nodes(base_path): - if config.exists(service_path + ['address']): - address = config.return_value(service_path + ['address']) - # 'address' is not a leaf node anymore, delete it first - config.delete(service_path + ['address']) + service_path = base_path + [service] - # When address is an interface (not 'web'), move it to 'address interface' - if address != 'web': - config.set(service_path + ['address', 'interface'], address) + if config.exists(service_path + ['address']): + address = config.return_value(service_path + ['address']) + # 'address' is not a leaf node anymore, delete it first + config.delete(service_path + ['address']) - else: # address == 'web' - # Relocate optional 'web-options' directly under 'address web' - if config.exists(service_path + ['web-options']): - # config.copy does not recursively create a path, so initialize it - config.set(service_path + ['address']) - config.copy(service_path + ['web-options'], - service_path + ['address', 'web']) - config.delete(service_path + ['web-options']) + # When address is an interface (not 'web'), move it to 'address interface' + if address != 'web': + config.set(service_path + ['address', 'interface'], address) - # ensure that valueless 'address web' still exists even if there are no 'web-options' - if not config.exists(service_path + ['address', 'web']): - config.set(service_path + ['address', 'web']) + else: # address == 'web' + # Relocate optional 'web-options' directly under 'address web' + if config.exists(service_path + ['web-options']): + # config.copy does not recursively create a path, so initialize it + config.set(service_path + ['address']) + config.copy(service_path + ['web-options'], + service_path + ['address', 'web']) + config.delete(service_path + ['web-options']) -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) + # ensure that valueless 'address web' still exists even if there are no 'web-options' + if not config.exists(service_path + ['address', 'web']): + config.set(service_path + ['address', 'web']) |