diff options
Diffstat (limited to 'src/migration-scripts')
60 files changed, 4285 insertions, 0 deletions
diff --git a/src/migration-scripts/config-management/0-to-1 b/src/migration-scripts/config-management/0-to-1 new file mode 100755 index 000000000..344359110 --- /dev/null +++ b/src/migration-scripts/config-management/0-to-1 @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# Add commit-revisions option if it doesn't exist + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if config.exists(['system', 'config-management', 'commit-revisions']): + # Nothing to do + sys.exit(0) +else: + config.set(['system', 'config-management', 'commit-revisions'], value='200') + + 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/migration-scripts/dhcp-relay/1-to-2 b/src/migration-scripts/dhcp-relay/1-to-2 new file mode 100755 index 000000000..b72da1028 --- /dev/null +++ b/src/migration-scripts/dhcp-relay/1-to-2 @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +# Delete "set service dhcp-relay relay-options port" option +# Delete "set service dhcpv6-relay listen-port" option + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not (config.exists(['service', 'dhcp-relay', 'relay-options', 'port']) or config.exists(['service', 'dhcpv6-relay', 'listen-port'])): + # Nothing to do + sys.exit(0) +else: + # Delete abandoned node + config.delete(['service', 'dhcp-relay', 'relay-options', 'port']) + # Delete abandoned node + config.delete(['service', 'dhcpv6-relay', 'listen-port']) + + 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/migration-scripts/dhcp-server/4-to-5 b/src/migration-scripts/dhcp-server/4-to-5 new file mode 100755 index 000000000..313b5279a --- /dev/null +++ b/src/migration-scripts/dhcp-server/4-to-5 @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 + +# Removes boolean operator from: +# - "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 ip-forwarding enable (true|false)" +# - "set service dhcp-server shared-network-name <xyz> authoritative (true|false)" +# - "set service dhcp-server disabled (true|false)" + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['service', 'dhcp-server']): + # Nothing to do + sys.exit(0) +else: + base = ['service', 'dhcp-server'] + # Make node "set service dhcp-server dynamic-dns-update enable (true|false)" valueless + if config.exists(base + ['dynamic-dns-update']): + bool_val = config.return_value(base + ['dynamic-dns-update', 'enable']) + + # Delete the node with the old syntax + config.delete(base + ['dynamic-dns-update']) + if str(bool_val) == 'true': + # Enable dynamic-dns-update with new syntax + config.set(base + ['dynamic-dns-update'], value=None) + + # Make node "set service dhcp-server disabled (true|false)" valueless + if config.exists(base + ['disabled']): + bool_val = config.return_value(base + ['disabled']) + + # Delete the node with the old syntax + config.delete(base + ['disabled']) + if str(bool_val) == 'true': + # Now disable DHCP server with the new syntax + config.set(base + ['disable'], value=None) + + # Make node "set service dhcp-server hostfile-update (enable|disable) valueless + if config.exists(base + ['hostfile-update']): + bool_val = config.return_value(base + ['hostfile-update']) + + # Delete the node with the old syntax incl. all subnodes + config.delete(base + ['hostfile-update']) + if str(bool_val) == 'enable': + # Enable hostfile update with new syntax + config.set(base + ['hostfile-update'], value=None) + + # Run this for every instance if 'shared-network-name' + for network in config.list_nodes(base + ['shared-network-name']): + base_network = base + ['shared-network-name', network] + # format as tag node to avoid loading problems + config.set_tag(base + ['shared-network-name']) + + # Run this for every specified 'subnet' + for subnet in config.list_nodes(base_network + ['subnet']): + base_subnet = base_network + ['subnet', subnet] + # format as tag node to avoid loading problems + config.set_tag(base_network + ['subnet']) + + # Make node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 ip-forwarding enable" valueless + if config.exists(base_subnet + ['ip-forwarding', 'enable']): + bool_val = config.return_value(base_subnet + ['ip-forwarding', 'enable']) + # Delete the node with the old syntax + config.delete(base_subnet + ['ip-forwarding']) + if str(bool_val) == 'true': + # Recreate node with new syntax + config.set(base_subnet + ['ip-forwarding'], value=None) + + # Rename node "set service dhcp-server shared-network-name <xyz> subnet 172.31.0.0/24 start <172.16.0.4> stop <172.16.0.9> + if config.exists(base_subnet + ['start']): + # This is the new "range" id for DHCP lease ranges + r_id = 0 + for range in config.list_nodes(base_subnet + ['start']): + range_start = range + range_stop = config.return_value(base_subnet + ['start', range_start, 'stop']) + + # Delete the node with the old syntax + config.delete(base_subnet + ['start', range_start]) + + # Create the node for the new syntax + # Note: range is a tag node, counter is its child, not a value + config.set(base_subnet + ['range', r_id]) + config.set(base_subnet + ['range', r_id, 'start'], value=range_start) + config.set(base_subnet + ['range', r_id, 'stop'], value=range_stop) + + # format as tag node to avoid loading problems + config.set_tag(base_subnet + ['range']) + + # increment range id for possible next range definition + r_id += 1 + + # Delete the node with the old syntax + config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'subnet', subnet, 'start']) + + + # Make node "set service dhcp-server shared-network-name <xyz> authoritative" valueless + if config.exists(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']): + authoritative = config.return_value(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']) + + # Delete the node with the old syntax + config.delete(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']) + + # Recreate node with new syntax - if required + if authoritative == "enable": + config.set(['service', 'dhcp-server', 'shared-network-name', network, 'authoritative']) + + 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/migration-scripts/dhcpv6-server/0-to-1 b/src/migration-scripts/dhcpv6-server/0-to-1 new file mode 100755 index 000000000..6f1150da1 --- /dev/null +++ b/src/migration-scripts/dhcpv6-server/0-to-1 @@ -0,0 +1,61 @@ +#!/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/>. + +# combine both sip-server-address and sip-server-name nodes to common sip-server + +from sys import argv, exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['service', 'dhcpv6-server', 'shared-network-name'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + # we need to run this for every configured network + for network in config.list_nodes(base): + for subnet in config.list_nodes(base + [network, 'subnet']): + sip_server = [] + + # Do we have 'sip-server-address' configured? + if config.exists(base + [network, 'subnet', subnet, 'sip-server-address']): + sip_server += config.return_values(base + [network, 'subnet', subnet, 'sip-server-address']) + config.delete(base + [network, 'subnet', subnet, 'sip-server-address']) + + # Do we have 'sip-server-name' configured? + if config.exists(base + [network, 'subnet', subnet, 'sip-server-name']): + sip_server += config.return_values(base + [network, 'subnet', subnet, 'sip-server-name']) + config.delete(base + [network, 'subnet', subnet, 'sip-server-name']) + + # Write new CLI value for sip-server + for server in sip_server: + config.set(base + [network, 'subnet', subnet, 'sip-server'], value=server, replace=False) + + 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) diff --git a/src/migration-scripts/dns-forwarding/0-to-1 b/src/migration-scripts/dns-forwarding/0-to-1 new file mode 100755 index 000000000..6e8720eef --- /dev/null +++ b/src/migration-scripts/dns-forwarding/0-to-1 @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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/>. +# + +# This migration script will check if there is a allow-from directive configured +# for the dns forwarding service - if not, the node will be created with the old +# default values of 0.0.0.0/0 and ::/0 + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['service', 'dns', 'forwarding'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + if not config.exists(base + ['allow-from']): + config.set(base + ['allow-from'], value='0.0.0.0/0', replace=False) + config.set(base + ['allow-from'], value='::/0', replace=False) + + 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/migration-scripts/dns-forwarding/1-to-2 b/src/migration-scripts/dns-forwarding/1-to-2 new file mode 100755 index 000000000..8c4f4b5c7 --- /dev/null +++ b/src/migration-scripts/dns-forwarding/1-to-2 @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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/>. +# + +# This migration script will remove the deprecated 'listen-on' statement +# from the dns forwarding service and will add the corresponding +# listen-address nodes instead. This is required as PowerDNS can only listen +# on interface addresses and not on interface names. + +from ipaddress import ip_interface +from sys import argv, exit +from vyos.ifconfig import Interface +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +base = ['service', 'dns', 'forwarding'] +if not config.exists(base): + # Nothing to do + exit(0) + +if config.exists(base + ['listen-on']): + listen_intf = config.return_values(base + ['listen-on']) + # Delete node with abandoned command + config.delete(base + ['listen-on']) + + # retrieve interface addresses for every configured listen-on interface + listen_addr = [] + for intf in listen_intf: + # we need to evaluate the interface section before manipulating the 'intf' variable + section = Interface.section(intf) + if not section: + raise ValueError(f'Invalid interface name {intf}') + + # we need to treat vif and vif-s interfaces differently, + # both "real interfaces" use dots for vlan identifiers - those + # need to be exchanged with vif and vif-s identifiers + if intf.count('.') == 1: + # this is a regular VLAN interface + intf = intf.split('.')[0] + ' vif ' + intf.split('.')[1] + elif intf.count('.') == 2: + # this is a QinQ VLAN interface + intf = intf.split('.')[0] + ' vif-s ' + intf.split('.')[1] + ' vif-c ' + intf.split('.')[2] + + # retrieve corresponding interface addresses in CIDR format + # those need to be converted in pure IP addresses without network information + path = ['interfaces', section, intf, 'address'] + for addr in config.return_values(path): + listen_addr.append( ip_interface(addr).ip ) + + for addr in listen_addr: + config.set(base + ['listen-address'], value=addr, replace=False) + + 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) + +exit(0) diff --git a/src/migration-scripts/dns-forwarding/2-to-3 b/src/migration-scripts/dns-forwarding/2-to-3 new file mode 100755 index 000000000..01e445b22 --- /dev/null +++ b/src/migration-scripts/dns-forwarding/2-to-3 @@ -0,0 +1,51 @@ +#!/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/>. +# + +# Sets the new options "addnta" and "recursion-desired" for all +# 'dns forwarding domain' as this is usually desired + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['service', 'dns', 'forwarding'] +if not config.exists(base): + # Nothing to do + sys.exit(0) + +if config.exists(base + ['domain']): + for domain in config.list_nodes(base + ['domain']): + domain_base = base + ['domain', domain] + config.set(domain_base + ['addnta']) + config.set(domain_base + ['recursion-desired']) + + 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/migration-scripts/https/0-to-1 b/src/migration-scripts/https/0-to-1 new file mode 100755 index 000000000..23809f5ad --- /dev/null +++ b/src/migration-scripts/https/0-to-1 @@ -0,0 +1,69 @@ +#!/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/>. + +# * Move server block directives under 'virtual-host' tag node, instead of +# relying on 'listen-address' tag node + +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) + +old_base = ['service', 'https', 'listen-address'] +if not config.exists(old_base): + # Nothing to do + sys.exit(0) +else: + new_base = ['service', 'https', 'virtual-host'] + config.set(new_base) + config.set_tag(new_base) + + index = 0 + for addr in config.list_nodes(old_base): + tag_name = f'vhost{index}' + config.set(new_base + [tag_name]) + config.set(new_base + [tag_name, 'listen-address'], value=addr) + + if config.exists(old_base + [addr, 'listen-port']): + port = config.return_value(old_base + [addr, 'listen-port']) + config.set(new_base + [tag_name, 'listen-port'], value=port) + + if config.exists(old_base + [addr, 'server-name']): + names = config.return_values(old_base + [addr, 'server-name']) + for name in names: + config.set(new_base + [tag_name, 'server-name'], value=name, + replace=False) + + index += 1 + + config.delete(old_base) + + 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/migration-scripts/https/1-to-2 b/src/migration-scripts/https/1-to-2 new file mode 100755 index 000000000..b1cf37ea6 --- /dev/null +++ b/src/migration-scripts/https/1-to-2 @@ -0,0 +1,54 @@ +#!/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/>. + +# * Move 'api virtual-host' list to 'api-restrict virtual-host' so it +# is owned by https.py instead of http-api.py + +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) + +old_base = ['service', 'https', 'api', 'virtual-host'] +if not config.exists(old_base): + # Nothing to do + sys.exit(0) +else: + new_base = ['service', 'https', 'api-restrict', 'virtual-host'] + config.set(new_base) + + names = config.return_values(old_base) + for name in names: + config.set(new_base, value=name, replace=False) + + config.delete(old_base) + + 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/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1 new file mode 100755 index 000000000..ee4d6b82c --- /dev/null +++ b/src/migration-scripts/interfaces/0-to-1 @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +# Change syntax of bridge interface +# - move interface based bridge-group to actual bridge (de-nest) +# - make stp and igmp-snooping nodes valueless +# https://phabricator.vyos.net/T1556 + +import sys +from vyos.configtree import ConfigTree + +def migrate_bridge(config, tree, intf): + # check if bridge-group exists + tree_bridge = tree + ['bridge-group'] + if config.exists(tree_bridge): + bridge = config.return_value(tree_bridge + ['bridge']) + # create new bridge member interface + config.set(base + [bridge, 'member', 'interface', intf]) + # format as tag node to avoid loading problems + config.set_tag(base + [bridge, 'member', 'interface']) + + # cost: migrate if configured + tree_cost = tree + ['bridge-group', 'cost'] + if config.exists(tree_cost): + cost = config.return_value(tree_cost) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'cost'], value=cost) + + # priority: migrate if configured + tree_priority = tree + ['bridge-group', 'priority'] + if config.exists(tree_priority): + priority = config.return_value(tree_priority) + # set new node + config.set(base + [bridge, 'member', 'interface', intf, 'priority'], value=priority) + + # Delete the old bridge-group assigned to an interface + config.delete(tree_bridge) + + +if __name__ == '__main__': + if (len(sys.argv) < 1): + 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 = ['interfaces', 'bridge'] + + if not config.exists(base): + # Nothing to do + sys.exit(0) + else: + # + # make stp and igmp-snooping nodes valueless + # + for br in config.list_nodes(base): + # STP: check if enabled + if config.exists(base + [br, 'stp']): + stp_val = config.return_value(base + [br, 'stp']) + # STP: delete node with old syntax + config.delete(base + [br, 'stp']) + # STP: set new node - if enabled + if stp_val == "true": + config.set(base + [br, 'stp'], value=None) + + # igmp-snooping: check if enabled + if config.exists(base + [br, 'igmp-snooping', 'querier']): + igmp_val = config.return_value(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: delete node with old syntax + config.delete(base + [br, 'igmp-snooping', 'querier']) + # igmp-snooping: set new node - if enabled + if igmp_val == "enable": + config.set(base + [br, 'igmp', 'querier'], value=None) + + # + # move interface based bridge-group to actual bridge (de-nest) + # + bridge_types = ['bonding', 'ethernet', 'l2tpv3', 'openvpn', 'vxlan', 'wireless'] + for type in bridge_types: + if not config.exists(['interfaces', type]): + continue + + for interface in config.list_nodes(['interfaces', type]): + # check if bridge-group exists + bridge_group = ['interfaces', type, interface] + if config.exists(bridge_group + ['bridge-group']): + migrate_bridge(config, bridge_group, interface) + + # We also need to migrate VLAN interfaces + vlan_base = ['interfaces', type, interface, 'vif'] + if config.exists(vlan_base): + for vlan in config.list_nodes(vlan_base): + intf = "{}.{}".format(interface, vlan) + migrate_bridge(config, vlan_base + [vlan], intf) + + # And then we have service VLANs (vif-s) interfaces + vlan_base = ['interfaces', type, interface, 'vif-s'] + if config.exists(vlan_base): + for vif_s in config.list_nodes(vlan_base): + intf = "{}.{}".format(interface, vif_s) + migrate_bridge(config, vlan_base + [vif_s], intf) + + # Every service VLAN can have multiple customer VLANs (vif-c) + vlan_c = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c'] + if config.exists(vlan_c): + for vif_c in config.list_nodes(vlan_c): + intf = "{}.{}.{}".format(interface, vif_s, vif_c) + migrate_bridge(config, vlan_c + [vif_c], intf) + + 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/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2 new file mode 100755 index 000000000..050137318 --- /dev/null +++ b/src/migration-scripts/interfaces/1-to-2 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +# Change syntax of bond interface +# - move interface based bond-group to actual bond (de-nest) +# https://phabricator.vyos.net/T1614 + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['interfaces', 'bonding'] + +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + # + # move interface based bond-group to actual bond (de-nest) + # + for intf in config.list_nodes(['interfaces', 'ethernet']): + # check if bond-group exists + if config.exists(['interfaces', 'ethernet', intf, 'bond-group']): + # get configured bond interface + bond = config.return_value(['interfaces', 'ethernet', intf, 'bond-group']) + # delete old interface asigned (nested) bond group + config.delete(['interfaces', 'ethernet', intf, 'bond-group']) + # create new bond member interface + config.set(base + [bond, 'member', 'interface'], value=intf, replace=False) + + # + # some combinations were allowed in the past from a CLI perspective + # but the kernel overwrote them - remove from CLI to not confuse the users. + # In addition new consitency checks are in place so users can't repeat the + # mistake. One of those nice issues is https://phabricator.vyos.net/T532 + for bond in config.list_nodes(base): + if config.exists(base + [bond, 'arp-monitor', 'interval']) and config.exists(base + [bond, 'mode']): + mode = config.return_value(base + [bond, 'mode']) + if mode in ['802.3ad', 'transmit-load-balance', 'adaptive-load-balance']: + intvl = int(config.return_value(base + [bond, 'arp-monitor', 'interval'])) + if intvl > 0: + # this is not allowed and the linux kernel replies with: + # option arp_interval: mode dependency failed, not supported in mode 802.3ad(4) + # option arp_interval: mode dependency failed, not supported in mode balance-alb(6) + # option arp_interval: mode dependency failed, not supported in mode balance-tlb(5) + # + # so we simply disable arp_interval by setting it to 0 and miimon will take care about the link + config.set(base + [bond, 'arp-monitor', 'interval'], value='0') + + 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/migration-scripts/interfaces/10-to-11 b/src/migration-scripts/interfaces/10-to-11 new file mode 100755 index 000000000..6b8e49ed9 --- /dev/null +++ b/src/migration-scripts/interfaces/10-to-11 @@ -0,0 +1,55 @@ +#!/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/>. + +# rename WWAN (wirelessmodem) serial interface from non persistent ttyUSB2 to +# a bus like name, e.g. "usb0b1.3p1.3" + +import os + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + base = ['interfaces', 'wirelessmodem'] + if not config.exists(base): + # Nothing to do + exit(0) + + for wwan in config.list_nodes(base): + if config.exists(base + [wwan, 'device']): + device = config.return_value(base + [wwan, 'device']) + + for root, dirs, files in os.walk('/dev/serial/by-bus'): + for file in files: + device_file = os.path.realpath(os.path.join(root, file)) + if os.path.basename(device_file) == device: + config.set(base + [wwan, 'device'], value=file, replace=True) + + 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) diff --git a/src/migration-scripts/interfaces/11-to-12 b/src/migration-scripts/interfaces/11-to-12 new file mode 100755 index 000000000..0dad24642 --- /dev/null +++ b/src/migration-scripts/interfaces/11-to-12 @@ -0,0 +1,58 @@ +#!/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/>. + +# - rename 'dhcpv6-options prefix-delegation' from single node to a new tag node +# 'dhcpv6-options pd 0' +# - delete 'sla-len' from CLI - value is calculated on demand + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + for type in config.list_nodes(['interfaces']): + for interface in config.list_nodes(['interfaces', type]): + # cache current config tree + base_path = ['interfaces', type, interface, 'dhcpv6-options'] + old_base = base_path + ['prefix-delegation'] + new_base = base_path + ['pd'] + if config.exists(old_base): + config.set(new_base) + config.set_tag(new_base) + config.copy(old_base, new_base + ['0']) + config.delete(old_base) + + for pd in config.list_nodes(new_base): + for tmp in config.list_nodes(new_base + [pd, 'interface']): + sla_config = new_base + [pd, 'interface', tmp, 'sla-len'] + if config.exists(sla_config): + config.delete(sla_config) + + 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) diff --git a/src/migration-scripts/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3 new file mode 100755 index 000000000..a63a54cdf --- /dev/null +++ b/src/migration-scripts/interfaces/2-to-3 @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# Change syntax of openvpn encryption settings +# - move cipher from encryption to encryption cipher +# https://phabricator.vyos.net/T1704 + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['interfaces', 'openvpn'] + +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + # + # move cipher from "encryption" to "encryption cipher" + # + for intf in config.list_nodes(['interfaces', 'openvpn']): + # Check if encryption is set + if config.exists(['interfaces', 'openvpn', intf, 'encryption']): + # Get cipher used + cipher = config.return_value(['interfaces', 'openvpn', intf, 'encryption']) + # Delete old syntax + config.delete(['interfaces', 'openvpn', intf, 'encryption']) + # Add new syntax to config + config.set(['interfaces', 'openvpn', intf, 'encryption', 'cipher'], value=cipher) + 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/migration-scripts/interfaces/3-to-4 b/src/migration-scripts/interfaces/3-to-4 new file mode 100755 index 000000000..e3bd25a68 --- /dev/null +++ b/src/migration-scripts/interfaces/3-to-4 @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +# Change syntax of wireless interfaces +# Migrate boolean nodes to valueless + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['interfaces', 'wireless'] + +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + for wifi in config.list_nodes(base): + # as converting a node to bool is always the same, we can script it + to_bool_nodes = ['capabilities ht 40MHz-incapable', + 'capabilities ht auto-powersave', + 'capabilities ht delayed-block-ack', + 'capabilities ht dsss-cck-40', + 'capabilities ht greenfield', + 'capabilities ht ldpc', + 'capabilities ht lsig-protection', + 'capabilities ht stbc tx', + 'capabilities require-ht', + 'capabilities require-vht', + 'capabilities vht antenna-pattern-fixed', + 'capabilities vht ldpc', + 'capabilities vht stbc tx', + 'capabilities vht tx-powersave', + 'capabilities vht vht-cf', + 'expunge-failing-stations', + 'isolate-stations'] + + for node in to_bool_nodes: + if config.exists(base + [wifi, node]): + tmp = config.return_value(base + [wifi, node]) + # delete old node + config.delete(base + [wifi, node]) + # set new node if it was enabled + if tmp == 'true': + # OLD CLI used camel casing in 40MHz-incapable which is + # not supported in the new backend. Convert all to lower-case + config.set(base + [wifi, node.lower()]) + + # Remove debug node + if config.exists(base + [wifi, 'debug']): + config.delete(base + [wifi, 'debug']) + + # RADIUS servers + if config.exists(base + [wifi, 'security', 'wpa', 'radius-server']): + for server in config.list_nodes(base + [wifi, 'security', 'wpa', 'radius-server']): + base_server = base + [wifi, 'security', 'wpa', 'radius-server', server] + + # Migrate RADIUS shared secret + if config.exists(base_server + ['secret']): + key = config.return_value(base_server + ['secret']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'key'], value=key) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # Migrate RADIUS port + if config.exists(base_server + ['port']): + port = config.return_value(base_server + ['port']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'port'], value=port) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # Migrate RADIUS accounting + if config.exists(base_server + ['accounting']): + port = config.return_value(base_server + ['accounting']) + # write new configuration node + config.set(base + [wifi, 'security', 'wpa', 'radius', 'server', server, 'accounting']) + # format as tag node + config.set_tag(base + [wifi, 'security', 'wpa', 'radius', 'server']) + + # delete old radius-server nodes + config.delete(base + [wifi, 'security', 'wpa', 'radius-server']) + + 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/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5 new file mode 100755 index 000000000..2a42c60ff --- /dev/null +++ b/src/migration-scripts/interfaces/4-to-5 @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +# De-nest PPPoE interfaces +# Migrate boolean nodes to valueless + +import sys +from vyos.configtree import ConfigTree + +def migrate_dialer(config, tree, intf): + for pppoe in config.list_nodes(tree): + # assemble string, 0 -> pppoe0 + new_base = ['interfaces', 'pppoe'] + pppoe_base = new_base + ['pppoe' + pppoe] + config.set(new_base) + # format as tag node to avoid loading problems + config.set_tag(new_base) + + # Copy the entire old node to the new one before migrating individual + # parts + config.copy(tree + [pppoe], pppoe_base) + + # Instead of letting the user choose between auto and none + # where auto is default, it makes more sesne to just offer + # an option to disable the default behavior (declutter CLI) + if config.exists(pppoe_base + ['name-server']): + tmp = config.return_value(pppoe_base + ['name-server']) + if tmp == "none": + config.set(pppoe_base + ['no-peer-dns']) + config.delete(pppoe_base + ['name-server']) + + # Migrate user-id and password nodes under an 'authentication' + # node + if config.exists(pppoe_base + ['user-id']): + user = config.return_value(pppoe_base + ['user-id']) + config.set(pppoe_base + ['authentication', 'user'], value=user) + config.delete(pppoe_base + ['user-id']) + + if config.exists(pppoe_base + ['password']): + pwd = config.return_value(pppoe_base + ['password']) + config.set(pppoe_base + ['authentication', 'password'], value=pwd) + config.delete(pppoe_base + ['password']) + + # remove enable-ipv6 node and rather place it under ipv6 node + if config.exists(pppoe_base + ['enable-ipv6']): + config.set(pppoe_base + ['ipv6', 'enable']) + config.delete(pppoe_base + ['enable-ipv6']) + + # Source interface migration + config.set(pppoe_base + ['source-interface'], value=intf) + + # Remove IPv6 router-advert nodes as this makes no sense on a + # client diale rinterface to send RAs back into the network + # https://phabricator.vyos.net/T2055 + ipv6_ra = pppoe_base + ['ipv6', 'router-advert'] + if config.exists(ipv6_ra): + config.delete(ipv6_ra) + + +if __name__ == '__main__': + if (len(sys.argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = sys.argv[1] + + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + pppoe_links = ['bonding', 'ethernet'] + + for link_type in pppoe_links: + if not config.exists(['interfaces', link_type]): + continue + + for interface in config.list_nodes(['interfaces', link_type]): + # check if PPPoE exists + base_if = ['interfaces', link_type, interface] + pppoe_if = base_if + ['pppoe'] + if config.exists(pppoe_if): + for dialer in config.list_nodes(pppoe_if): + migrate_dialer(config, pppoe_if, interface) + + # Delete old PPPoE interface + config.delete(pppoe_if) + + # bail out early if there are no VLAN interfaces to migrate + if not config.exists(base_if + ['vif']): + continue + + # Migrate PPPoE interfaces attached to a VLAN + for vlan in config.list_nodes(base_if + ['vif']): + vlan_if = base_if + ['vif', vlan] + pppoe_if = vlan_if + ['pppoe'] + if config.exists(pppoe_if): + for dialer in config.list_nodes(pppoe_if): + intf = "{}.{}".format(interface, vlan) + migrate_dialer(config, pppoe_if, intf) + + # Delete old PPPoE interface + config.delete(pppoe_if) + + # Add interface description that this is required for PPPoE + if not config.exists(vlan_if + ['description']): + config.set(vlan_if + ['description'], value='PPPoE link interface') + + 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/migration-scripts/interfaces/5-to-6 b/src/migration-scripts/interfaces/5-to-6 new file mode 100755 index 000000000..1291751d8 --- /dev/null +++ b/src/migration-scripts/interfaces/5-to-6 @@ -0,0 +1,123 @@ +#!/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/>. + +# Migrate IPv6 router advertisments from a nested interface configuration to +# a denested "service router-advert" + +import sys +from vyos.configtree import ConfigTree + +def copy_rtradv(c, old_base, interface): + base = ['service', 'router-advert', 'interface'] + + if c.exists(old_base): + if not c.exists(base): + c.set(base) + c.set_tag(base) + + # take the old node as a whole and copy it to new new path, + # additional migrations will be done afterwards + new_base = base + [interface] + c.copy(old_base, new_base) + c.delete(old_base) + + # cur-hop-limit has been renamed to hop-limit + if c.exists(new_base + ['cur-hop-limit']): + c.rename(new_base + ['cur-hop-limit'], 'hop-limit') + + bool_cleanup = ['managed-flag', 'other-config-flag'] + for bool in bool_cleanup: + if c.exists(new_base + [bool]): + tmp = c.return_value(new_base + [bool]) + c.delete(new_base + [bool]) + if tmp == 'true': + c.set(new_base + [bool]) + + # max/min interval moved to subnode + intervals = ['max-interval', 'min-interval'] + for interval in intervals: + if c.exists(new_base + [interval]): + tmp = c.return_value(new_base + [interval]) + c.delete(new_base + [interval]) + min_max = interval.split('-')[0] + c.set(new_base + ['interval', min_max], value=tmp) + + # cleanup boolean nodes in individual prefix + prefix_base = new_base + ['prefix'] + if c.exists(prefix_base): + for prefix in config.list_nodes(prefix_base): + if c.exists(prefix_base + [prefix, 'autonomous-flag']): + tmp = c.return_value(prefix_base + [prefix, 'autonomous-flag']) + c.delete(prefix_base + [prefix, 'autonomous-flag']) + if tmp == 'false': + c.set(prefix_base + [prefix, 'no-autonomous-flag']) + + if c.exists(prefix_base + [prefix, 'on-link-flag']): + tmp = c.return_value(prefix_base + [prefix, 'on-link-flag']) + c.delete(prefix_base + [prefix, 'on-link-flag']) + if tmp == 'true': + c.set(prefix_base + [prefix, 'on-link-flag']) + + # router advertisement can be individually disabled per interface + # the node has been renamed from send-advert {true | false} to no-send-advert + if c.exists(new_base + ['send-advert']): + tmp = c.return_value(new_base + ['send-advert']) + c.delete(new_base + ['send-advert']) + if tmp == 'false': + c.set(new_base + ['no-send-advert']) + + # link-mtu advertisement was formerly disabled by setting its value to 0 + # ... this makes less sense - if it should not be send, just do not + # configure it + if c.exists(new_base + ['link-mtu']): + tmp = c.return_value(new_base + ['link-mtu']) + if tmp == '0': + c.delete(new_base + ['link-mtu']) + +if __name__ == '__main__': + if (len(sys.argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = sys.argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + # list all individual interface types like dummy, ethernet and so on + for if_type in config.list_nodes(['interfaces']): + base_if_type = ['interfaces', if_type] + + # for every individual interface we need to check if there is an + # ipv6 ra configured ... and also for every VIF (VLAN) interface + for intf in config.list_nodes(base_if_type): + old_base = base_if_type + [intf, 'ipv6', 'router-advert'] + copy_rtradv(config, old_base, intf) + + vif_base = base_if_type + [intf, 'vif'] + if config.exists(vif_base): + for vif in config.list_nodes(vif_base): + old_base = vif_base + [vif, 'ipv6', 'router-advert'] + vlan_name = f'{intf}.{vif}' + copy_rtradv(config, old_base, vlan_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) diff --git a/src/migration-scripts/interfaces/6-to-7 b/src/migration-scripts/interfaces/6-to-7 new file mode 100755 index 000000000..220c7e601 --- /dev/null +++ b/src/migration-scripts/interfaces/6-to-7 @@ -0,0 +1,63 @@ +#!/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/>. + +# Remove network provider name from CLI and rather use provider APN from CLI + +import sys +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(sys.argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = sys.argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + base = ['interfaces', 'wirelessmodem'] + + if not config.exists(base): + # Nothing to do + sys.exit(0) + + # list all individual wwan/wireless modem interfaces + for i in config.list_nodes(base): + iface = base + [i] + + # only three carries have been supported in the past, thus + # this will be fairly simple \o/ - and only one (AT&T) did + # configure an APN + if config.exists(iface + ['network']): + network = config.return_value(iface + ['network']) + if network == "att": + apn = 'isp.cingular' + config.set(iface + ['apn'], value=apn) + + config.delete(iface + ['network']) + + # synchronize DNS configuration with PPPoE interfaces to have a + # uniform CLI experience + if config.exists(iface + ['no-dns']): + config.rename(iface + ['no-dns'], 'no-peer-dns') + + 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/migration-scripts/interfaces/7-to-8 b/src/migration-scripts/interfaces/7-to-8 new file mode 100755 index 000000000..a4051301f --- /dev/null +++ b/src/migration-scripts/interfaces/7-to-8 @@ -0,0 +1,76 @@ +#!/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/>. + +# Split WireGuard endpoint into address / port nodes to make use of common +# validators + +import os + +from sys import exit, argv +from vyos.configtree import ConfigTree +from vyos.util import chown, chmod_750 + +def migrate_default_keys(): + kdir = r'/config/auth/wireguard' + if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'): + location = f'{kdir}/default' + if not os.path.exists(location): + os.makedirs(location) + + chown(location, 'root', 'vyattacfg') + chmod_750(location) + os.rename(f'{kdir}/private.key', f'{location}/private.key') + os.rename(f'{kdir}/public.key', f'{location}/public.key') + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + base = ['interfaces', 'wireguard'] + + migrate_default_keys() + + if not config.exists(base): + # Nothing to do + exit(0) + + # list all individual wireguard interface isntance + for i in config.list_nodes(base): + iface = base + [i] + for peer in config.list_nodes(iface + ['peer']): + base_peer = iface + ['peer', peer] + if config.exists(base_peer + ['endpoint']): + endpoint = config.return_value(base_peer + ['endpoint']) + address = endpoint.split(':')[0] + port = endpoint.split(':')[1] + # delete old node + config.delete(base_peer + ['endpoint']) + # setup new nodes + config.set(base_peer + ['address'], value=address) + config.set(base_peer + ['port'], value=port) + + 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) diff --git a/src/migration-scripts/interfaces/8-to-9 b/src/migration-scripts/interfaces/8-to-9 new file mode 100755 index 000000000..2d1efd418 --- /dev/null +++ b/src/migration-scripts/interfaces/8-to-9 @@ -0,0 +1,52 @@ +#!/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/>. + +# Rename link nodes to source-interface for the following interface types: +# - vxlan +# - pseudo-ethernet + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + for if_type in ['vxlan', 'pseudo-ethernet']: + base = ['interfaces', if_type] + if not config.exists(base): + # Nothing to do + continue + + # list all individual interface isntance + for i in config.list_nodes(base): + iface = base + [i] + if config.exists(iface + ['link']): + config.rename(iface + ['link'], 'source-interface') + + 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) diff --git a/src/migration-scripts/interfaces/9-to-10 b/src/migration-scripts/interfaces/9-to-10 new file mode 100755 index 000000000..4aa2c42b5 --- /dev/null +++ b/src/migration-scripts/interfaces/9-to-10 @@ -0,0 +1,64 @@ +#!/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/>. + +# - rename CLI node 'dhcpv6-options delgate' to 'dhcpv6-options prefix-delegation +# interface' +# - rename CLI node 'interface-id' for prefix-delegation to 'address' as it +# represents the local interface IPv6 address assigned by DHCPv6-PD + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + + for intf_type in config.list_nodes(['interfaces']): + for intf in config.list_nodes(['interfaces', intf_type]): + # cache current config tree + base_path = ['interfaces', intf_type, intf, 'dhcpv6-options', + 'delegate'] + + if config.exists(base_path): + # cache new config tree + new_path = ['interfaces', intf_type, intf, 'dhcpv6-options', + 'prefix-delegation'] + if not config.exists(new_path): + config.set(new_path) + + # copy to new node + config.copy(base_path, new_path + ['interface']) + + # rename interface-id to address + for interface in config.list_nodes(new_path + ['interface']): + config.rename(new_path + ['interface', interface, 'interface-id'], 'address') + + # delete old noe + config.delete(base_path) + + 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) diff --git a/src/migration-scripts/ipoe-server/0-to-1 b/src/migration-scripts/ipoe-server/0-to-1 new file mode 100755 index 000000000..f328ebced --- /dev/null +++ b/src/migration-scripts/ipoe-server/0-to-1 @@ -0,0 +1,133 @@ +#!/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/>. + +# - remove primary/secondary identifier from nameserver +# - Unifi RADIUS configuration by placing it all under "authentication radius" node + +import os +import sys + +from sys import argv, exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['service', 'ipoe-server'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + + # Migrate IPv4 DNS servers + dns_base = base + ['dns-server'] + if config.exists(dns_base): + for server in ['server-1', 'server-2']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate IPv6 DNS servers + dns_base = base + ['dnsv6-server'] + if config.exists(dns_base): + for server in ['server-1', 'server-2', 'server-3']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate radius-settings node to RADIUS and use this as base for the + # later migration of the RADIUS servers - this will save a lot of code + radius_settings = base + ['authentication', 'radius-settings'] + if config.exists(radius_settings): + config.rename(radius_settings, 'radius') + + # Migrate RADIUS dynamic author / change of authorisation server + dae_old = base + ['authentication', 'radius', 'dae-server'] + if config.exists(dae_old): + config.rename(dae_old, 'dynamic-author') + dae_new = base + ['authentication', 'radius', 'dynamic-author'] + + if config.exists(dae_new + ['ip-address']): + config.rename(dae_new + ['ip-address'], 'server') + + if config.exists(dae_new + ['secret']): + config.rename(dae_new + ['secret'], 'key') + + # Migrate RADIUS server + radius_server = base + ['authentication', 'radius-server'] + if config.exists(radius_server): + new_base = base + ['authentication', 'radius', 'server'] + config.set(new_base) + config.set_tag(new_base) + for server in config.list_nodes(radius_server): + old_base = radius_server + [server] + config.copy(old_base, new_base + [server]) + + # migrate key + if config.exists(new_base + [server, 'secret']): + config.rename(new_base + [server, 'secret'], 'key') + + # remove old req-limit node + if config.exists(new_base + [server, 'req-limit']): + config.delete(new_base + [server, 'req-limit']) + + config.delete(radius_server) + + # Migrate IPv6 prefixes + ipv6_base = base + ['client-ipv6-pool'] + if config.exists(ipv6_base + ['prefix']): + prefix_old = config.return_values(ipv6_base + ['prefix']) + # delete old prefix CLI nodes + config.delete(ipv6_base + ['prefix']) + # create ned prefix tag node + config.set(ipv6_base + ['prefix']) + config.set_tag(ipv6_base + ['prefix']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask) + + if config.exists(ipv6_base + ['delegate-prefix']): + prefix_old = config.return_values(ipv6_base + ['delegate-prefix']) + # delete old delegate prefix CLI nodes + config.delete(ipv6_base + ['delegate-prefix']) + # create ned delegation tag node + config.set(ipv6_base + ['delegate']) + config.set_tag(ipv6_base + ['delegate']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask) + + 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) diff --git a/src/migration-scripts/ipsec/4-to-5 b/src/migration-scripts/ipsec/4-to-5 new file mode 100755 index 000000000..b64aa8462 --- /dev/null +++ b/src/migration-scripts/ipsec/4-to-5 @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +# log-modes have changed, keyword all to any + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +ctree = ConfigTree(config_file) + +if not ctree.exists(['vpn', 'ipsec', 'logging','log-modes']): + # Nothing to do + sys.exit(0) +else: + lmodes = ctree.return_values(['vpn', 'ipsec', 'logging','log-modes']) + for mode in lmodes: + if mode == 'all': + ctree.set(['vpn', 'ipsec', 'logging','log-modes'], value='any', replace=True) + + try: + open(file_name,'w').write(ctree.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/l2tp/0-to-1 b/src/migration-scripts/l2tp/0-to-1 new file mode 100755 index 000000000..686ebc655 --- /dev/null +++ b/src/migration-scripts/l2tp/0-to-1 @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +# Unclutter L2TP VPN configuiration - move radius-server top level tag +# nodes to a regular node which now also configures the radius source address +# used when querying a radius server + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +cfg_base = ['vpn', 'l2tp', 'remote-access', 'authentication'] +if not config.exists(cfg_base): + # Nothing to do + sys.exit(0) +else: + # Migrate "vpn l2tp authentication radius-source-address" to new + # "vpn l2tp authentication radius source-address" + if config.exists(cfg_base + ['radius-source-address']): + address = config.return_value(cfg_base + ['radius-source-address']) + # delete old configuration node + config.delete(cfg_base + ['radius-source-address']) + # write new configuration node + config.set(cfg_base + ['radius', 'source-address'], value=address) + + # Migrate "vpn l2tp authentication radius-server" tag node to new + # "vpn l2tp authentication radius server" tag node + if config.exists(cfg_base + ['radius-server']): + for server in config.list_nodes(cfg_base + ['radius-server']): + base_server = cfg_base + ['radius-server', server] + key = config.return_value(base_server + ['key']) + + # delete old configuration node + config.delete(base_server) + # write new configuration node + config.set(cfg_base + ['radius', 'server', server, 'key'], value=key) + + # format as tag node + config.set_tag(cfg_base + ['radius', 'server']) + + # delete top level tag node + if config.exists(cfg_base + ['radius-server']): + config.delete(cfg_base + ['radius-server']) + + 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/migration-scripts/l2tp/1-to-2 b/src/migration-scripts/l2tp/1-to-2 new file mode 100755 index 000000000..c46eba8f8 --- /dev/null +++ b/src/migration-scripts/l2tp/1-to-2 @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +# Delete depricated outside-nexthop address + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +cfg_base = ['vpn', 'l2tp', 'remote-access'] +if not config.exists(cfg_base): + # Nothing to do + sys.exit(0) +else: + if config.exists(cfg_base + ['outside-nexthop']): + config.delete(cfg_base + ['outside-nexthop']) + + 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/migration-scripts/l2tp/2-to-3 b/src/migration-scripts/l2tp/2-to-3 new file mode 100755 index 000000000..3472ee3ed --- /dev/null +++ b/src/migration-scripts/l2tp/2-to-3 @@ -0,0 +1,111 @@ +#!/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/>. + +# - remove primary/secondary identifier from nameserver +# - TODO: remove radius server req-limit + +import os +import sys + +from sys import argv, exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['vpn', 'l2tp', 'remote-access'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + + # Migrate IPv4 DNS servers + dns_base = base + ['dns-servers'] + if config.exists(dns_base): + for server in ['server-1', 'server-2']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate IPv6 DNS servers + dns_base = base + ['dnsv6-servers'] + if config.exists(dns_base): + for server in config.return_values(dns_base): + config.set(base + ['name-server'], value=server, replace=False) + + config.delete(dns_base) + + # Migrate IPv4 WINS servers + wins_base = base + ['wins-servers'] + if config.exists(wins_base): + for server in ['server-1', 'server-2']: + if config.exists(wins_base + [server]): + wins = config.return_value(wins_base + [server]) + config.set(base + ['wins-server'], value=wins, replace=False) + + config.delete(wins_base) + + + # Remove RADIUS server req-limit node + radius_base = base + ['authentication', 'radius'] + if config.exists(radius_base): + for server in config.list_nodes(radius_base + ['server']): + if config.exists(radius_base + ['server', server, 'req-limit']): + config.delete(radius_base + ['server', server, 'req-limit']) + + # Migrate IPv6 prefixes + ipv6_base = base + ['client-ipv6-pool'] + if config.exists(ipv6_base + ['prefix']): + prefix_old = config.return_values(ipv6_base + ['prefix']) + # delete old prefix CLI nodes + config.delete(ipv6_base + ['prefix']) + # create ned prefix tag node + config.set(ipv6_base + ['prefix']) + config.set_tag(ipv6_base + ['prefix']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask) + + if config.exists(ipv6_base + ['delegate-prefix']): + prefix_old = config.return_values(ipv6_base + ['delegate-prefix']) + # delete old delegate prefix CLI nodes + config.delete(ipv6_base + ['delegate-prefix']) + # create ned delegation tag node + config.set(ipv6_base + ['delegate']) + config.set_tag(ipv6_base + ['delegate']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['delegate', prefix, 'delegate-prefix'], value=mask) + + 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) diff --git a/src/migration-scripts/lldp/0-to-1 b/src/migration-scripts/lldp/0-to-1 new file mode 100755 index 000000000..5f66570e7 --- /dev/null +++ b/src/migration-scripts/lldp/0-to-1 @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +# Delete "set service lldp interface <interface> location civic-based" option +# as it was broken most of the time anyways + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['service', 'lldp', 'interface'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + # Delete nodes with abandoned CLI syntax + for interface in config.list_nodes(base): + if config.exists(base + [interface, 'location', 'civic-based']): + config.delete(base + [interface, 'location', 'civic-based']) + + 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/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5 new file mode 100755 index 000000000..dda191719 --- /dev/null +++ b/src/migration-scripts/nat/4-to-5 @@ -0,0 +1,58 @@ +#!/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/>. + +# Drop the enable/disable from the nat "log" node. If log node is specified +# it is "enabled" + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +if not config.exists(['nat']): + # Nothing to do + exit(0) +else: + for direction in ['source', 'destination']: + if not config.exists(['nat', direction]): + continue + + for rule in config.list_nodes(['nat', direction, 'rule']): + base = ['nat', direction, 'rule', rule] + + # Check if the log node exists and if log is enabled, + # migrate it to the new valueless 'log' node + if config.exists(base + ['log']): + tmp = config.return_value(base + ['log']) + config.delete(base + ['log']) + if tmp == 'enable': + config.set(base + ['log']) + + 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) diff --git a/src/migration-scripts/ntp/0-to-1 b/src/migration-scripts/ntp/0-to-1 new file mode 100755 index 000000000..9c66f3109 --- /dev/null +++ b/src/migration-scripts/ntp/0-to-1 @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +# Delete "set system ntp server <n> dynamic" option + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['system', 'ntp']): + # Nothing to do + sys.exit(0) +else: + # Delete abandoned leaf node if found inside tag node for + # "set system ntp server <n> dynamic" + base = ['system', 'ntp', 'server'] + for server in config.list_nodes(base): + if config.exists(base + [server, 'dynamic']): + config.delete(base + [server, 'dynamic']) + + 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/migration-scripts/pppoe-server/0-to-1 b/src/migration-scripts/pppoe-server/0-to-1 new file mode 100755 index 000000000..bb24211b6 --- /dev/null +++ b/src/migration-scripts/pppoe-server/0-to-1 @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# Convert "service pppoe-server authentication radius-server node key" +# to: +# "service pppoe-server authentication radius-server node secret" + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +ctree = ConfigTree(config_file) + + +if not ctree.exists(['service', 'pppoe-server', 'authentication','radius-server']): + # Nothing to do + sys.exit(0) +else: + nodes = ctree.list_nodes(['service', 'pppoe-server', 'authentication','radius-server']) + for node in nodes: + if ctree.exists(['service', 'pppoe-server', 'authentication', 'radius-server', node, 'key']): + val = ctree.return_value(['service', 'pppoe-server', 'authentication', 'radius-server', node, 'key']) + ctree.set(['service', 'pppoe-server', 'authentication', 'radius-server', node, 'secret'], value=val, replace=False) + ctree.delete(['service', 'pppoe-server', 'authentication', 'radius-server', node, 'key']) + try: + open(file_name,'w').write(ctree.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2 new file mode 100755 index 000000000..fa83896d3 --- /dev/null +++ b/src/migration-scripts/pppoe-server/1-to-2 @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# Convert "service pppoe-server interface ethX" +# to: +# "service pppoe-server interface ethX {}" + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +ctree = ConfigTree(config_file) +cbase = ['service', 'pppoe-server','interface'] + +if not ctree.exists(cbase): + sys.exit(0) +else: + nics = ctree.return_values(cbase) + # convert leafNode to a tagNode + ctree.set(cbase) + ctree.set_tag(cbase) + for nic in nics: + ctree.set(cbase + [nic]) + + try: + open(file_name,'w').write(ctree.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) + diff --git a/src/migration-scripts/pppoe-server/2-to-3 b/src/migration-scripts/pppoe-server/2-to-3 new file mode 100755 index 000000000..5f9730a41 --- /dev/null +++ b/src/migration-scripts/pppoe-server/2-to-3 @@ -0,0 +1,141 @@ +#!/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/>. + +# - remove primary/secondary identifier from nameserver + +import os + +from sys import argv, exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['service', 'pppoe-server'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + + # Migrate IPv4 DNS servers + dns_base = base + ['dns-servers'] + if config.exists(dns_base): + for server in ['server-1', 'server-2']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate IPv6 DNS servers + dns_base = base + ['dnsv6-servers'] + if config.exists(dns_base): + for server in ['server-1', 'server-2', 'server-3']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate IPv4 WINS servers + wins_base = base + ['wins-servers'] + if config.exists(wins_base): + for server in ['server-1', 'server-2']: + if config.exists(wins_base + [server]): + wins = config.return_value(wins_base + [server]) + config.set(base + ['wins-server'], value=wins, replace=False) + + config.delete(wins_base) + + # Migrate radius-settings node to RADIUS and use this as base for the + # later migration of the RADIUS servers - this will save a lot of code + radius_settings = base + ['authentication', 'radius-settings'] + if config.exists(radius_settings): + config.rename(radius_settings, 'radius') + + # Migrate RADIUS dynamic author / change of authorisation server + dae_old = base + ['authentication', 'radius', 'dae-server'] + if config.exists(dae_old): + config.rename(dae_old, 'dynamic-author') + dae_new = base + ['authentication', 'radius', 'dynamic-author'] + + if config.exists(dae_new + ['ip-address']): + config.rename(dae_new + ['ip-address'], 'server') + + if config.exists(dae_new + ['secret']): + config.rename(dae_new + ['secret'], 'key') + + # Migrate RADIUS server + radius_server = base + ['authentication', 'radius-server'] + if config.exists(radius_server): + new_base = base + ['authentication', 'radius', 'server'] + config.set(new_base) + config.set_tag(new_base) + for server in config.list_nodes(radius_server): + old_base = radius_server + [server] + config.copy(old_base, new_base + [server]) + + # migrate key + if config.exists(new_base + [server, 'secret']): + config.rename(new_base + [server, 'secret'], 'key') + + # remove old req-limit node + if config.exists(new_base + [server, 'req-limit']): + config.delete(new_base + [server, 'req-limit']) + + config.delete(radius_server) + + # Migrate IPv6 prefixes + ipv6_base = base + ['client-ipv6-pool'] + if config.exists(ipv6_base + ['prefix']): + prefix_old = config.return_values(ipv6_base + ['prefix']) + # delete old prefix CLI nodes + config.delete(ipv6_base + ['prefix']) + # create ned prefix tag node + config.set(ipv6_base + ['prefix']) + config.set_tag(ipv6_base + ['prefix']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask) + + if config.exists(ipv6_base + ['delegate-prefix']): + prefix_old = config.return_values(ipv6_base + ['delegate-prefix']) + # delete old delegate prefix CLI nodes + config.delete(ipv6_base + ['delegate-prefix']) + # create ned delegation tag node + config.set(ipv6_base + ['delegate']) + config.set_tag(ipv6_base + ['delegate']) + + for p in prefix_old: + prefix = p.split(',')[0] + mask = p.split(',')[1] + config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask) + + 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) diff --git a/src/migration-scripts/pppoe-server/3-to-4 b/src/migration-scripts/pppoe-server/3-to-4 new file mode 100755 index 000000000..ed5a01625 --- /dev/null +++ b/src/migration-scripts/pppoe-server/3-to-4 @@ -0,0 +1,54 @@ +#!/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/>. + +# change mppe node to a leaf node with value prefer + +import os + +from sys import argv, exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['service', 'pppoe-server'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + mppe_base = base + ['ppp-options', 'mppe'] + if config.exists(mppe_base): + # drop node first ... + config.delete(mppe_base) + # ... and set new default + config.set(mppe_base, value='prefer') + + print(config.to_string()) + exit(1) + + 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) diff --git a/src/migration-scripts/pptp/0-to-1 b/src/migration-scripts/pptp/0-to-1 new file mode 100755 index 000000000..d0c7a83b5 --- /dev/null +++ b/src/migration-scripts/pptp/0-to-1 @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# Unclutter PPTP VPN configuiration - move radius-server top level tag +# nodes to a regular node which now also configures the radius source address +# used when querying a radius server + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +cfg_base = ['vpn', 'pptp', 'remote-access', 'authentication'] +if not config.exists(cfg_base): + # Nothing to do + sys.exit(0) +else: + # Migrate "vpn pptp authentication radius-source-address" to new + # "vpn pptp authentication radius source-address" + if config.exists(cfg_base + ['radius-source-address']): + address = config.return_value(cfg_base + ['radius-source-address']) + # delete old configuration node + config.delete(cfg_base + ['radius-source-address']) + # write new configuration node + config.set(cfg_base + ['radius', 'source-address'], value=address) + + # Migrate "vpn pptp authentication radius-server" tag node to new + # "vpn pptp authentication radius server" tag node + for server in config.list_nodes(cfg_base + ['radius-server']): + base_server = cfg_base + ['radius-server', server] + key = config.return_value(base_server + ['key']) + + # delete old configuration node + config.delete(base_server) + # write new configuration node + config.set(cfg_base + ['radius', 'server', server, 'key'], value=key) + + # format as tag node + config.set_tag(cfg_base + ['radius', 'server']) + + # delete top level tag node + if config.exists(cfg_base + ['radius-server']): + config.delete(cfg_base + ['radius-server']) + + 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/migration-scripts/pptp/1-to-2 b/src/migration-scripts/pptp/1-to-2 new file mode 100755 index 000000000..a13cc3a4f --- /dev/null +++ b/src/migration-scripts/pptp/1-to-2 @@ -0,0 +1,71 @@ +#!/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/>. + +# - migrate dns-servers node to common name-servers +# - remove radios req-limit node + +from sys import argv, exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['vpn', 'pptp', 'remote-access'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + # Migrate IPv4 DNS servers + dns_base = base + ['dns-servers'] + if config.exists(dns_base): + for server in ['server-1', 'server-2']: + if config.exists(dns_base + [server]): + dns = config.return_value(dns_base + [server]) + config.set(base + ['name-server'], value=dns, replace=False) + + config.delete(dns_base) + + # Migrate IPv4 WINS servers + wins_base = base + ['wins-servers'] + if config.exists(wins_base): + for server in ['server-1', 'server-2']: + if config.exists(wins_base + [server]): + wins = config.return_value(wins_base + [server]) + config.set(base + ['wins-server'], value=wins, replace=False) + + config.delete(wins_base) + + # Remove RADIUS server req-limit node + radius_base = base + ['authentication', 'radius'] + if config.exists(radius_base): + for server in config.list_nodes(radius_base + ['server']): + if config.exists(radius_base + ['server', server, 'req-limit']): + config.delete(radius_base + ['server', server, 'req-limit']) + + 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) diff --git a/src/migration-scripts/quagga/2-to-3 b/src/migration-scripts/quagga/2-to-3 new file mode 100755 index 000000000..4c1cd86a3 --- /dev/null +++ b/src/migration-scripts/quagga/2-to-3 @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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 sys + +from vyos.configtree import ConfigTree + + +if (len(sys.argv) < 1): + 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) + +def migrate_neighbor(config, neighbor_path, neighbor): + if config.exists(neighbor_path): + neighbors = config.list_nodes(neighbor_path) + for neighbor in neighbors: + # Move the valueless options: as-override, next-hop-self, route-reflector-client, route-server-client, + # remove-private-as + for valueless_option in ['as-override', 'nexthop-self', 'route-reflector-client', 'route-server-client', + 'remove-private-as']: + if config.exists(neighbor_path + [neighbor, valueless_option]): + config.set(neighbor_path + [neighbor] + af_path + [valueless_option]) + config.delete(neighbor_path + [neighbor, valueless_option]) + + # Move filter options: distribute-list, filter-list, prefix-list, and route-map + # They share the same syntax inside so we can group them + for filter_type in ['distribute-list', 'filter-list', 'prefix-list', 'route-map']: + if config.exists(neighbor_path + [neighbor, filter_type]): + for filter_dir in ['import', 'export']: + if config.exists(neighbor_path + [neighbor, filter_type, filter_dir]): + filter_name = config.return_value(neighbor_path + [neighbor, filter_type, filter_dir]) + config.set(neighbor_path + [neighbor] + af_path + [filter_type, filter_dir], value=filter_name) + config.delete(neighbor_path + [neighbor, filter_type]) + + # Move simple leaf node options: maximum-prefix, unsuppress-map, weight + for leaf_option in ['maximum-prefix', 'unsuppress-map', 'weight']: + if config.exists(neighbor_path + [neighbor, leaf_option]): + if config.exists(neighbor_path + [neighbor, leaf_option]): + leaf_opt_value = config.return_value(neighbor_path + [neighbor, leaf_option]) + config.set(neighbor_path + [neighbor] + af_path + [leaf_option], value=leaf_opt_value) + config.delete(neighbor_path + [neighbor, leaf_option]) + + # The rest is special cases, for better or worse + + # Move allowas-in + if config.exists(neighbor_path + [neighbor, 'allowas-in']): + if config.exists(neighbor_path + [neighbor, 'allowas-in', 'number']): + allowas_in = config.return_value(neighbor_path + [neighbor, 'allowas-in', 'number']) + config.set(neighbor_path + [neighbor] + af_path + ['allowas-in', 'number'], value=allowas_in) + config.delete(neighbor_path + [neighbor, 'allowas-in']) + + # Move attribute-unchanged options + if config.exists(neighbor_path + [neighbor, 'attribute-unchanged']): + for attr in ['as-path', 'med', 'next-hop']: + if config.exists(neighbor_path + [neighbor, 'attribute-unchanged', attr]): + config.set(neighbor_path + [neighbor] + af_path + ['attribute-unchanged', attr]) + config.delete(neighbor_path + [neighbor, 'attribute-unchanged', attr]) + config.delete(neighbor_path + [neighbor, 'attribute-unchanged']) + + # Move capability options + if config.exists(neighbor_path + [neighbor, 'capability']): + # "capability dynamic" is a peer-global option, we only migrate ORF + if config.exists(neighbor_path + [neighbor, 'capability', 'orf']): + if config.exists(neighbor_path + [neighbor, 'capability', 'orf', 'prefix-list']): + for orf in ['send', 'receive']: + if config.exists(neighbor_path + [neighbor, 'capability', 'orf', 'prefix-list', orf]): + config.set(neighbor_path + [neighbor] + af_path + ['capability', 'orf', 'prefix-list', orf]) + config.delete(neighbor_path + [neighbor, 'capability', 'orf', 'prefix-list', orf]) + config.delete(neighbor_path + [neighbor, 'capability', 'orf', 'prefix-list']) + config.delete(neighbor_path + [neighbor, 'capability', 'orf']) + + # Move default-originate + if config.exists(neighbor_path + [neighbor, 'default-originate']): + if config.exists(neighbor_path + [neighbor, 'default-originate', 'route-map']): + route_map = config.return_value(neighbor_path + [neighbor, 'default-originate', 'route-map']) + config.set(neighbor_path + [neighbor] + af_path + ['default-originate', 'route-map'], value=route_map) + else: + # Empty default-originate node is meaningful so we re-create it + config.set(neighbor_path + [neighbor] + af_path + ['default-originate']) + config.delete(neighbor_path + [neighbor, 'default-originate']) + + # Move soft-reconfiguration + if config.exists(neighbor_path + [neighbor, 'soft-reconfiguration']): + if config.exists(neighbor_path + [neighbor, 'soft-reconfiguration', 'inbound']): + config.set(neighbor_path + [neighbor] + af_path + ['soft-reconfiguration', 'inbound']) + # Empty soft-reconfiguration is meaningless, so we just remove it + config.delete(neighbor_path + [neighbor, 'soft-reconfiguration']) + + # Move disable-send-community + if config.exists(neighbor_path + [neighbor, 'disable-send-community']): + for comm_type in ['standard', 'extended']: + if config.exists(neighbor_path + [neighbor, 'disable-send-community', comm_type]): + config.set(neighbor_path + [neighbor] + af_path + ['disable-send-community', comm_type]) + config.delete(neighbor_path + [neighbor, 'disable-send-community', comm_type]) + config.delete(neighbor_path + [neighbor, 'disable-send-community']) + + +if not config.exists(['protocols', 'bgp']): + # Nothing to do + sys.exit(0) +else: + # Just to avoid writing it so many times + af_path = ['address-family', 'ipv4-unicast'] + + # Check if BGP is actually configured and obtain the ASN + asn_list = config.list_nodes(['protocols', 'bgp']) + if asn_list: + # There's always just one BGP node, if any + asn = asn_list[0] + bgp_path = ['protocols', 'bgp', asn] + else: + # There's actually no BGP, just its empty shell + sys.exit(0) + + ## Move global IPv4-specific BGP options to "address-family ipv4-unicast" + + # Move networks + network_path = ['protocols', 'bgp', asn, 'network'] + if config.exists(network_path): + config.set(bgp_path + af_path + ['network']) + config.set_tag(bgp_path + af_path + ['network']) + + networks = config.list_nodes(network_path) + for network in networks: + config.set(bgp_path + af_path + ['network', network]) + if config.exists(network_path + [network, 'route-map']): + route_map = config.return_value(network_path + [network, 'route-map']) + config.set(bgp_path + af_path + ['network', network, 'route-map'], value=route_map) + config.delete(network_path) + + # Move aggregate-address statements + aggregate_path = ['protocols', 'bgp', asn, 'aggregate-address'] + if config.exists(aggregate_path): + config.set(bgp_path + af_path + ['aggregate-address']) + config.set_tag(bgp_path + af_path + ['aggregate-address']) + + aggregates = config.list_nodes(aggregate_path) + for aggregate in aggregates: + config.set(bgp_path + af_path + ['aggregate-address', aggregate]) + if config.exists(aggregate_path + [aggregate, 'as-set']): + config.set(bgp_path + af_path + ['aggregate-address', aggregate, 'as-set']) + if config.exists(aggregate_path + [aggregate, 'summary-only']): + config.set(bgp_path + af_path + ['aggregate-address', aggregate, 'summary-only']) + config.delete(aggregate_path) + + ## Migrate neighbor options + neighbor_path = ['protocols', 'bgp', asn, 'neighbor'] + if config.exists(neighbor_path): + neighbors = config.list_nodes(neighbor_path) + for neighbor in neighbors: + migrate_neighbor(config, neighbor_path, neighbor) + + peer_group_path = ['protocols', 'bgp', asn, 'peer-group'] + if config.exists(peer_group_path): + peer_groups = config.list_nodes(peer_group_path) + for peer_group in peer_groups: + migrate_neighbor(config, peer_group_path, peer_group) + + ## Migrate redistribute statements + redistribute_path = ['protocols', 'bgp', asn, 'redistribute'] + if config.exists(redistribute_path): + config.set(bgp_path + af_path + ['redistribute']) + + redistributes = config.list_nodes(redistribute_path) + for redistribute in redistributes: + config.set(bgp_path + af_path + ['redistribute', redistribute]) + if config.exists(redistribute_path + [redistribute, 'metric']): + redist_metric = config.return_value(redistribute_path + [redistribute, 'metric']) + config.set(bgp_path + af_path + ['redistribute', redistribute, 'metric'], value=redist_metric) + if config.exists(redistribute_path + [redistribute, 'route-map']): + redist_route_map = config.return_value(redistribute_path + [redistribute, 'route-map']) + config.set(bgp_path + af_path + ['redistribute', redistribute, 'route-map'], value=redist_route_map) + + config.delete(redistribute_path) + + 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/migration-scripts/quagga/3-to-4 b/src/migration-scripts/quagga/3-to-4 new file mode 100755 index 000000000..be3528391 --- /dev/null +++ b/src/migration-scripts/quagga/3-to-4 @@ -0,0 +1,76 @@ +#!/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/>. +# +# + +# Between 1.2.3 and 1.2.4, FRR added per-neighbor enforce-first-as option. +# Unfortunately they also removed the global enforce-first-as option, +# which broke all old configs that used to have it. +# +# To emulate the effect of the original option, we insert it in every neighbor +# if the config used to have the original global option + +import sys + +from vyos.configtree import ConfigTree + + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['protocols', 'bgp']): + # Nothing to do + sys.exit(0) +else: + # Check if BGP is actually configured and obtain the ASN + asn_list = config.list_nodes(['protocols', 'bgp']) + if asn_list: + # There's always just one BGP node, if any + asn = asn_list[0] + else: + # There's actually no BGP, just its empty shell + sys.exit(0) + + # Check if BGP enforce-first-as option is set + enforce_first_as_path = ['protocols', 'bgp', asn, 'parameters', 'enforce-first-as'] + if config.exists(enforce_first_as_path): + # Delete the obsolete option + config.delete(enforce_first_as_path) + + # Now insert it in every peer + peers = config.list_nodes(['protocols', 'bgp', asn, 'neighbor']) + for p in peers: + config.set(['protocols', 'bgp', asn, 'neighbor', p, 'enforce-first-as']) + else: + # Do nothing + sys.exit(0) + + # Save a new configuration file + 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/migration-scripts/quagga/4-to-5 b/src/migration-scripts/quagga/4-to-5 new file mode 100755 index 000000000..f8c87ce8c --- /dev/null +++ b/src/migration-scripts/quagga/4-to-5 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 sys + +from vyos.configtree import ConfigTree + + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['protocols', 'bgp']): + # Nothing to do + sys.exit(0) +else: + # Check if BGP is actually configured and obtain the ASN + asn_list = config.list_nodes(['protocols', 'bgp']) + if asn_list: + # There's always just one BGP node, if any + asn = asn_list[0] + else: + # There's actually no BGP, just its empty shell + sys.exit(0) + + # Check if BGP scan-time parameter exist + scan_time_param = ['protocols', 'bgp', asn, 'parameters', 'scan-time'] + if config.exists(scan_time_param): + # Delete BGP scan-time parameter + config.delete(scan_time_param) + else: + # Do nothing + sys.exit(0) + + # Save a new configuration file + 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/migration-scripts/quagga/5-to-6 b/src/migration-scripts/quagga/5-to-6 new file mode 100755 index 000000000..a71851942 --- /dev/null +++ b/src/migration-scripts/quagga/5-to-6 @@ -0,0 +1,63 @@ +#!/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/>. + +# * Remove parameter 'disable-network-import-check' which, as implemented, +# had no effect on boot. + +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) + +if not config.exists(['protocols', 'bgp']): + # Nothing to do + sys.exit(0) +else: + # Check if BGP is actually configured and obtain the ASN + asn_list = config.list_nodes(['protocols', 'bgp']) + if asn_list: + # There's always just one BGP node, if any + asn = asn_list[0] + else: + # There's actually no BGP, just its empty shell + sys.exit(0) + + # Check if BGP parameter disable-network-import-check exists + param = ['protocols', 'bgp', asn, 'parameters', 'disable-network-import-check'] + if config.exists(param): + # Delete parameter + config.delete(param) + else: + # Do nothing + sys.exit(0) + + 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/migration-scripts/salt/0-to-1 b/src/migration-scripts/salt/0-to-1 new file mode 100755 index 000000000..79053c056 --- /dev/null +++ b/src/migration-scripts/salt/0-to-1 @@ -0,0 +1,58 @@ +#!/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/>. + +# Delete log_file, log_level and user nodes +# rename hash_type to hash +# rename mine_interval to interval + +from sys import argv,exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +base = ['service', 'salt-minion'] +if not config.exists(base): + # Nothing to do + exit(0) +else: + + # delete nodes which are now populated with sane defaults + for node in ['log_file', 'log_level', 'user']: + if config.exists(base + [node]): + config.delete(base + [node]) + + if config.exists(base + ['hash_type']): + config.rename(base + ['hash_type'], 'hash') + + if config.exists(base + ['mine_interval']): + config.rename(base + ['mine_interval'], 'interval') + + 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) diff --git a/src/migration-scripts/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1 new file mode 100755 index 000000000..a836f7011 --- /dev/null +++ b/src/migration-scripts/snmp/0-to-1 @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) +config_base = ['service', 'snmp', 'v3'] + +if not config.exists(config_base): + # Nothing to do + sys.exit(0) +else: + # we no longer support a per trap target engine ID (https://phabricator.vyos.net/T818) + if config.exists(config_base + ['v3', 'trap-target']): + for target in config.list_nodes(config_base + ['v3', 'trap-target']): + config.delete(config_base + ['v3', 'trap-target', target, 'engineid']) + + # we no longer support a per user engine ID (https://phabricator.vyos.net/T818) + if config.exists(config_base + ['v3', 'user']): + for user in config.list_nodes(config_base + ['v3', 'user']): + config.delete(config_base + ['v3', 'user', user, 'engineid']) + + # we drop TSM support as there seem to be no users and this code is untested + # https://phabricator.vyos.net/T1769 + if config.exists(config_base + ['v3', 'tsm']): + config.delete(config_base + ['v3', 'tsm']) + + 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/migration-scripts/snmp/1-to-2 b/src/migration-scripts/snmp/1-to-2 new file mode 100755 index 000000000..466a624e6 --- /dev/null +++ b/src/migration-scripts/snmp/1-to-2 @@ -0,0 +1,89 @@ +#!/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/>. + +from sys import argv, exit +from vyos.configtree import ConfigTree + +def migrate_keys(config, path): + # authentication: rename node 'encrypted-key' -> 'encrypted-password' + config_path_auth = path + ['auth', 'encrypted-key'] + if config.exists(config_path_auth): + config.rename(config_path_auth, 'encrypted-password') + config_path_auth = path + ['auth', 'encrypted-password'] + + # remove leading '0x' from string if present + tmp = config.return_value(config_path_auth) + if tmp.startswith(prefix): + tmp = tmp.replace(prefix, '') + config.set(config_path_auth, value=tmp) + + # privacy: rename node 'encrypted-key' -> 'encrypted-password' + config_path_priv = path + ['privacy', 'encrypted-key'] + if config.exists(config_path_priv): + config.rename(config_path_priv, 'encrypted-password') + config_path_priv = path + ['privacy', 'encrypted-password'] + + # remove leading '0x' from string if present + tmp = config.return_value(config_path_priv) + if tmp.startswith(prefix): + tmp = tmp.replace(prefix, '') + config.set(config_path_priv, value=tmp) + +if __name__ == '__main__': + if (len(argv) < 1): + print("Must specify file name!") + exit(1) + + file_name = argv[1] + + with open(file_name, 'r') as f: + config_file = f.read() + + config = ConfigTree(config_file) + config_base = ['service', 'snmp', 'v3'] + + if not config.exists(config_base): + # Nothing to do + exit(0) + else: + # We no longer support hashed values prefixed with '0x' to unclutter + # CLI and also calculate the hases in advance instead of retrieving + # them after service startup - which was always a bad idea + prefix = '0x' + + config_engineid = config_base + ['engineid'] + if config.exists(config_engineid): + tmp = config.return_value(config_engineid) + if tmp.startswith(prefix): + tmp = tmp.replace(prefix, '') + config.set(config_engineid, value=tmp) + + config_user = config_base + ['user'] + if config.exists(config_user): + for user in config.list_nodes(config_user): + migrate_keys(config, config_user + [user]) + + config_trap = config_base + ['trap-target'] + if config.exists(config_trap): + for trap in config.list_nodes(config_trap): + migrate_keys(config, config_trap + [trap]) + + 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) diff --git a/src/migration-scripts/ssh/0-to-1 b/src/migration-scripts/ssh/0-to-1 new file mode 100755 index 000000000..91b832276 --- /dev/null +++ b/src/migration-scripts/ssh/0-to-1 @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +# Delete "service ssh allow-root" option + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['service', 'ssh', 'allow-root']): + # Nothing to do + sys.exit(0) +else: + # Delete node with abandoned command + config.delete(['service', 'ssh', 'allow-root']) + + 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/migration-scripts/ssh/1-to-2 b/src/migration-scripts/ssh/1-to-2 new file mode 100755 index 000000000..bc8815753 --- /dev/null +++ b/src/migration-scripts/ssh/1-to-2 @@ -0,0 +1,55 @@ +#!/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/>. + +# VyOS 1.2 crux allowed configuring a lower or upper case loglevel. This +# is no longer supported as the input data is validated and will lead to +# an error. If user specifies an upper case logleve, make it lowercase + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['service', 'ssh', 'loglevel'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) +else: + # red in configured loglevel and convert it to lower case + tmp = config.return_value(base).lower() + + # VyOS 1.2 had no proper value validation on the CLI thus the + # user could use any arbitrary values - sanitize them + if tmp not in ['quiet', 'fatal', 'error', 'info', 'verbose']: + tmp = 'info' + + config.set(base, value=tmp) + + 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) diff --git a/src/migration-scripts/sstp/0-to-1 b/src/migration-scripts/sstp/0-to-1 new file mode 100755 index 000000000..0e8dd1c4b --- /dev/null +++ b/src/migration-scripts/sstp/0-to-1 @@ -0,0 +1,130 @@ +#!/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/>. + + +# - migrate from "service sstp-server" to "vpn sstp" +# - remove primary/secondary identifier from nameserver +# - migrate RADIUS configuration to a more uniform syntax accross the system +# - authentication radius-server x.x.x.x to authentication radius server x.x.x.x +# - authentication radius-settings to authentication radius +# - do not migrate radius server req-limit, use default of unlimited +# - migrate SSL certificate path + +import os +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['service', 'sstp-server'] +if not config.exists(old_base): + # Nothing to do + sys.exit(0) +else: + # ensure new base path exists + if not config.exists(['vpn']): + config.set(['vpn']) + + new_base = ['vpn', 'sstp'] + # copy entire tree + config.copy(old_base, new_base) + config.delete(old_base) + + # migrate DNS servers + dns_base = new_base + ['network-settings', 'dns-server'] + if config.exists(dns_base): + if config.exists(dns_base + ['primary-dns']): + dns = config.return_value(dns_base + ['primary-dns']) + config.set(new_base + ['network-settings', 'name-server'], value=dns, replace=False) + + if config.exists(dns_base + ['secondary-dns']): + dns = config.return_value(dns_base + ['secondary-dns']) + config.set(new_base + ['network-settings', 'name-server'], value=dns, replace=False) + + config.delete(dns_base) + + + # migrate radius options - copy subtree + # thus must happen before migration of the individual RADIUS servers + old_options = new_base + ['authentication', 'radius-settings'] + if config.exists(old_options): + new_options = new_base + ['authentication', 'radius'] + config.copy(old_options, new_options) + config.delete(old_options) + + # migrate radius dynamic author / change of authorisation server + dae_old = new_base + ['authentication', 'radius', 'dae-server'] + if config.exists(dae_old): + config.rename(dae_old, 'dynamic-author') + dae_new = new_base + ['authentication', 'radius', 'dynamic-author'] + + if config.exists(dae_new + ['ip-address']): + config.rename(dae_new + ['ip-address'], 'server') + + if config.exists(dae_new + ['secret']): + config.rename(dae_new + ['secret'], 'key') + + + # migrate radius server + radius_server = new_base + ['authentication', 'radius-server'] + if config.exists(radius_server): + for server in config.list_nodes(radius_server): + base = radius_server + [server] + new = new_base + ['authentication', 'radius', 'server', server] + + # convert secret to key + if config.exists(base + ['secret']): + tmp = config.return_value(base + ['secret']) + config.set(new + ['key'], value=tmp) + + if config.exists(base + ['fail-time']): + tmp = config.return_value(base + ['fail-time']) + config.set(new + ['fail-time'], value=tmp) + + config.set_tag(new_base + ['authentication', 'radius', 'server']) + config.delete(radius_server) + + # migrate SSL certificates + old_ssl = new_base + ['sstp-settings', 'ssl-certs'] + new_ssl = new_base + ['ssl'] + config.copy(old_ssl, new_ssl) + config.delete(old_ssl) + + if config.exists(new_ssl + ['ca']): + config.rename(new_ssl + ['ca'], 'ca-cert-file') + + if config.exists(new_ssl + ['server-cert']): + config.rename(new_ssl + ['server-cert'], 'cert-file') + + if config.exists(new_ssl + ['server-key']): + config.rename(new_ssl + ['server-key'], 'key-file') + + + 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/migration-scripts/sstp/1-to-2 b/src/migration-scripts/sstp/1-to-2 new file mode 100755 index 000000000..94cb04831 --- /dev/null +++ b/src/migration-scripts/sstp/1-to-2 @@ -0,0 +1,110 @@ +#!/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/>. + +# - migrate relative path SSL certificate to absolute path, as certs are only +# allowed to stored in /config/user-data/sstp/ this is pretty straight +# forward move. Delete certificates from source directory + +import os +import sys + +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): + 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 = ['vpn', 'sstp', 'ssl'] +if not config.exists(base_path): + # Nothing to do + sys.exit(0) +else: + cert_path_old ='/config/user-data/sstp/' + cert_path_new ='/config/auth/sstp/' + + if not os.path.isdir(cert_path_new): + os.mkdir(cert_path_new) + + # + # migrate ca-cert-file to new path + if config.exists(base_path + ['ca-cert-file']): + tmp = config.return_value(base_path + ['ca-cert-file']) + cert_old = cert_path_old + tmp + cert_new = cert_path_new + tmp + + if os.path.isfile(cert_old): + # adjust file permissions on source file, + # permissions will be copied by copy2() + os.chmod(cert_old, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + copy2(cert_old, cert_path_new) + # delete old certificate file + os.unlink(cert_old) + + config.set(base_path + ['ca-cert-file'], value=cert_new, replace=True) + + # + # migrate cert-file to new path + if config.exists(base_path + ['cert-file']): + tmp = config.return_value(base_path + ['cert-file']) + cert_old = cert_path_old + tmp + cert_new = cert_path_new + tmp + + if os.path.isfile(cert_old): + # adjust file permissions on source file, + # permissions will be copied by copy2() + os.chmod(cert_old, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + copy2(cert_old, cert_path_new) + # delete old certificate file + os.unlink(cert_old) + + config.set(base_path + ['cert-file'], value=cert_new, replace=True) + + # + # migrate key-file to new path + if config.exists(base_path + ['key-file']): + tmp = config.return_value(base_path + ['key-file']) + cert_old = cert_path_old + tmp + cert_new = cert_path_new + tmp + + if os.path.isfile(cert_old): + # adjust file permissions on source file, + # permissions will be copied by copy2() + os.chmod(cert_old, S_IRUSR | S_IWUSR) + copy2(cert_old, cert_path_new) + # delete old certificate file + os.unlink(cert_old) + + config.set(base_path + ['key-file'], value=cert_new, replace=True) + + # + # check if old certificate directory exists but is empty + if os.path.isdir(cert_path_old) and not os.listdir(cert_path_old): + os.rmdir(cert_path_old) + + 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/migration-scripts/system/10-to-11 b/src/migration-scripts/system/10-to-11 new file mode 100755 index 000000000..1a0233c7d --- /dev/null +++ b/src/migration-scripts/system/10-to-11 @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +# Unclutter RADIUS configuration +# +# Move radius-server top level tag nodes to a regular node which allows us +# to specify additional general features for the RADIUS client. + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) +cfg_base = ['system', 'login'] +if not (config.exists(cfg_base + ['radius-server']) or config.exists(cfg_base + ['radius-source-address'])): + # Nothing to do + sys.exit(0) +else: + # + # Migrate "system login radius-source-address" to "system login radius" + # + if config.exists(cfg_base + ['radius-source-address']): + address = config.return_value(cfg_base + ['radius-source-address']) + # delete old configuration node + config.delete(cfg_base + ['radius-source-address']) + # write new configuration node + config.set(cfg_base + ['radius', 'source-address'], value=address) + + # + # Migrate "system login radius-server" tag node to new + # "system login radius server" tag node and also rename the "secret" node to "key" + # + for server in config.list_nodes(cfg_base + ['radius-server']): + base_server = cfg_base + ['radius-server', server] + # "key" node is mandatory + key = config.return_value(base_server + ['secret']) + config.set(cfg_base + ['radius', 'server', server, 'key'], value=key) + + # "port" is optional + if config.exists(base_server + ['port']): + port = config.return_value(base_server + ['port']) + config.set(cfg_base + ['radius', 'server', server, 'port'], value=port) + + # "timeout is optional" + if config.exists(base_server + ['timeout']): + timeout = config.return_value(base_server + ['timeout']) + config.set(cfg_base + ['radius', 'server', server, 'timeout'], value=timeout) + + # format as tag node + config.set_tag(cfg_base + ['radius', 'server']) + + # delete old configuration node + config.delete(base_server) + + # delete top level tag node + if config.exists(cfg_base + ['radius-server']): + config.delete(cfg_base + ['radius-server']) + + 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/migration-scripts/system/11-to-12 b/src/migration-scripts/system/11-to-12 new file mode 100755 index 000000000..0c92a0746 --- /dev/null +++ b/src/migration-scripts/system/11-to-12 @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# converts 'set system syslog host <address>:<port>' +# to 'set system syslog host <address> port <port>' + +import sys +import re + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) +cbase = ['system', 'syslog', 'host'] + +if not config.exists(cbase): + sys.exit(0) + +for host in config.list_nodes(cbase): + if re.search(':[0-9]{1,5}$',host): + h = re.search('^[a-zA-Z\-0-9\.]+', host).group(0) + p = re.sub(':', '', re.search(':[0-9]+$', host).group(0)) + config.set(cbase + [h]) + config.set(cbase + [h, 'port'], value=p) + for fac in config.list_nodes(cbase + [host, 'facility']): + config.set(cbase + [h, 'facility', fac]) + config.set_tag(cbase + [h, 'facility']) + if config.exists(cbase + [host, 'facility', fac, 'protocol']): + proto = config.return_value(cbase + [host, 'facility', fac, 'protocol']) + config.set(cbase + [h, 'facility', fac, 'protocol'], value=proto) + if config.exists(cbase + [host, 'facility', fac, 'level']): + lvl = config.return_value(cbase + [host, 'facility', fac, 'level']) + config.set(cbase + [h, 'facility', fac, 'level'], value=lvl) + config.delete(cbase + [host]) + + try: + open(file_name,'w').write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/system/12-to-13 b/src/migration-scripts/system/12-to-13 new file mode 100755 index 000000000..5b068f4fc --- /dev/null +++ b/src/migration-scripts/system/12-to-13 @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# Fixup non existent time-zones. Some systems have time-zone set to: Los* +# (Los_Angeles), Den* (Denver), New* (New_York) ... but those are no real IANA +# assigned time zones. In the past they have been silently remapped. +# +# Time to clean it up! +# +# Migrate all configured timezones to real IANA assigned timezones! + +import re +import sys + +from vyos.configtree import ConfigTree +from vyos.util import cmd + + +if (len(sys.argv) < 1): + 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) +tz_base = ['system', 'time-zone'] +if not config.exists(tz_base): + # Nothing to do + sys.exit(0) +else: + tz = config.return_value(tz_base) + + # retrieve all valid timezones + try: + tz_datas = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::') + except OSError: + tz_datas = '' + tz_data = tz_datas.split('\n') + + if re.match(r'[Ll][Oo][Ss].+', tz): + tz = 'America/Los_Angeles' + elif re.match(r'[Dd][Ee][Nn].+', tz): + tz = 'America/Denver' + elif re.match(r'[Hh][Oo][Nn][Oo].+', tz): + tz = 'Pacific/Honolulu' + elif re.match(r'[Nn][Ee][Ww].+', tz): + tz = 'America/New_York' + elif re.match(r'[Cc][Hh][Ii][Cc]*.+', tz): + tz = 'America/Chicago' + elif re.match(r'[Aa][Nn][Cc].+', tz): + tz = 'America/Anchorage' + elif re.match(r'[Pp][Hh][Oo].+', tz): + tz = 'America/Phoenix' + elif re.match(r'GMT(.+)?', tz): + tz = 'Etc/' + tz + elif tz not in tz_data: + # assign default UTC timezone + tz = 'UTC' + + # replace timezone data is required + config.set(tz_base, value=tz) + + 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/migration-scripts/system/13-to-14 b/src/migration-scripts/system/13-to-14 new file mode 100755 index 000000000..c055dad1f --- /dev/null +++ b/src/migration-scripts/system/13-to-14 @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Delete 'system ipv6 blacklist' option as the IPv6 module can no longer be +# blacklisted as it is required by e.g. WireGuard and thus will always be +# loaded. + +import os +import sys + +ipv6_blacklist_file = '/etc/modprobe.d/vyatta_blacklist_ipv6.conf' + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) +ip_base = ['system', 'ipv6'] +if not config.exists(ip_base): + # Nothing to do + sys.exit(0) +else: + # delete 'system ipv6 blacklist' node + if config.exists(ip_base + ['blacklist']): + config.delete(ip_base + ['blacklist']) + if os.path.isfile(ipv6_blacklist_file): + os.unlink(ipv6_blacklist_file) + + 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/migration-scripts/system/14-to-15 b/src/migration-scripts/system/14-to-15 new file mode 100755 index 000000000..2491e3d0d --- /dev/null +++ b/src/migration-scripts/system/14-to-15 @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Make 'system options reboot-on-panic' valueless + +import os +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['system', 'options'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + if config.exists(base + ['reboot-on-panic']): + reboot = config.return_value(base + ['reboot-on-panic']) + config.delete(base + ['reboot-on-panic']) + # create new valueless node if action was true + if reboot == "true": + config.set(base + ['reboot-on-panic']) + + 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/migration-scripts/system/15-to-16 b/src/migration-scripts/system/15-to-16 new file mode 100755 index 000000000..e70893d55 --- /dev/null +++ b/src/migration-scripts/system/15-to-16 @@ -0,0 +1,55 @@ +#!/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/>. + +# * remove "system login user <user> group" node, Why should be add a user to a +# 3rd party group when the system is fully managed by CLI? +# * remove "system login user <user> level" node +# This is the only privilege level left and also the default, what is the +# sense in keeping this orphaned node? + +import os +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['system', 'login', 'user'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + for user in config.list_nodes(base): + if config.exists(base + [user, 'group']): + config.delete(base + [user, 'group']) + + if config.exists(base + [user, 'level']): + config.delete(base + [user, 'level']) + + 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/migration-scripts/system/16-to-17 b/src/migration-scripts/system/16-to-17 new file mode 100755 index 000000000..8f762c0e2 --- /dev/null +++ b/src/migration-scripts/system/16-to-17 @@ -0,0 +1,76 @@ +#!/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/>. + +# remove "system console netconsole" +# remove "system console device <device> modem" + +import os +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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 = ['system', 'console'] +if not config.exists(base): + # Nothing to do + sys.exit(0) +else: + # remove "system console netconsole" (T2561) + if config.exists(base + ['netconsole']): + config.delete(base + ['netconsole']) + + if config.exists(base + ['device']): + for device in config.list_nodes(base + ['device']): + dev_path = base + ['device', device] + # remove "system console device <device> modem" (T2570) + if config.exists(dev_path + ['modem']): + config.delete(dev_path + ['modem']) + + # Only continue on USB based serial consoles + if not 'ttyUSB' in device: + continue + + # A serial console has been configured but it does no longer + # exist on the system - cleanup + if not os.path.exists(f'/dev/{device}'): + config.delete(dev_path) + continue + + # migrate from ttyUSB device to new device in /dev/serial/by-bus + for root, dirs, files in os.walk('/dev/serial/by-bus'): + for usb_device in files: + device_file = os.path.realpath(os.path.join(root, usb_device)) + # migrate to new USB device names (T2529) + if os.path.basename(device_file) == device: + config.copy(dev_path, base + ['device', usb_device]) + # Delete old USB node from config + config.delete(dev_path) + + 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/migration-scripts/system/17-to-18 b/src/migration-scripts/system/17-to-18 new file mode 100755 index 000000000..dd2abce00 --- /dev/null +++ b/src/migration-scripts/system/17-to-18 @@ -0,0 +1,105 @@ +#!/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/>. +# + +# migrate disable-dhcp-nameservers (boolean) to name-servers-dhcp <interface> +# if disable-dhcp-nameservers is set, just remove it +# else retrieve all interface names that have configured dhcp(v6) address and +# add them to the new name-servers-dhcp node + +from sys import argv, exit +from vyos.ifconfig import Interface +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +base = ['system'] +if not config.exists(base): + # Nothing to do + exit(0) + +if config.exists(base + ['disable-dhcp-nameservers']): + config.delete(base + ['disable-dhcp-nameservers']) +else: + dhcp_interfaces = [] + + # go through all interfaces searching for 'address dhcp(v6)?' + for sect in Interface.sections(): + sect_base = ['interfaces', sect] + + if not config.exists(sect_base): + continue + + for intf in config.list_nodes(sect_base): + intf_base = sect_base + [intf] + + # try without vlans + if config.exists(intf_base + ['address']): + for addr in config.return_values(intf_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(intf) + + # try vif + if config.exists(intf_base + ['vif']): + for vif in config.list_nodes(intf_base + ['vif']): + vif_base = intf_base + ['vif', vif] + if config.exists(vif_base + ['address']): + for addr in config.return_values(vif_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif}') + + # try vif-s + if config.exists(intf_base + ['vif-s']): + for vif_s in config.list_nodes(intf_base + ['vif-s']): + vif_s_base = intf_base + ['vif-s', vif_s] + if config.exists(vif_s_base + ['address']): + for addr in config.return_values(vif_s_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif_s}') + + # try vif-c + if config.exists(intf_base + ['vif-c', vif_c]): + for vif_c in config.list_nodes(vif_s_base + ['vif-c', vif_c]): + vif_c_base = vif_s_base + ['vif-c', vif_c] + if config.exists(vif_c_base + ['address']): + for addr in config.return_values(vif_c_base + ['address']): + if addr in ['dhcp', 'dhcpv6']: + dhcp_interfaces.append(f'{intf}.{vif_s}.{vif_c}') + + # set new config nodes + for intf in dhcp_interfaces: + config.set(base + ['name-servers-dhcp'], value=intf, replace=False) + + # delete old node + config.delete(base + ['disable-dhcp-nameservers']) + +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) + +exit(0) diff --git a/src/migration-scripts/system/6-to-7 b/src/migration-scripts/system/6-to-7 new file mode 100755 index 000000000..bf07abf3a --- /dev/null +++ b/src/migration-scripts/system/6-to-7 @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Change smp_affinity to smp-affinity + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +update_required = False + +intf_types = config.list_nodes(["interfaces"]) + +for intf_type in intf_types: + intf_type_path = ["interfaces", intf_type] + intfs = config.list_nodes(intf_type_path) + + for intf in intfs: + intf_path = intf_type_path + [intf] + if not config.exists(intf_path + ["smp_affinity"]): + # Nothing to do. + continue + else: + # Rename the node. + old_smp_affinity_path = intf_path + ["smp_affinity"] + config.rename(old_smp_affinity_path, "smp-affinity") + update_required = True + +if update_required: + 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/migration-scripts/system/7-to-8 b/src/migration-scripts/system/7-to-8 new file mode 100755 index 000000000..4cbb21f17 --- /dev/null +++ b/src/migration-scripts/system/7-to-8 @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +# Converts "system gateway-address" option to "protocols static route 0.0.0.0/0 next-hop $gw" + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['system', 'gateway-address']): + # Nothing to do + sys.exit(0) +else: + # Save the address + gw = config.return_value(['system', 'gateway-address']) + + # Create the node for the new syntax + # Note: next-hop is a tag node, gateway address is its child, not a value + config.set(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', gw]) + + # Delete the node with the old syntax + config.delete(['system', 'gateway-address']) + + # Now, the interesting part. Both route and next-hop are supposed to be tag nodes, + # which you can verify with "cli-shell-api isTag $configPath". + # They must be formatted as such to load correctly. + config.set_tag(['protocols', 'static', 'route']) + config.set_tag(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop']) + + 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/migration-scripts/system/8-to-9 b/src/migration-scripts/system/8-to-9 new file mode 100755 index 000000000..db3fefdea --- /dev/null +++ b/src/migration-scripts/system/8-to-9 @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +# Deletes "system package" option as it is deprecated + +import sys + +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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) + +if not config.exists(['system', 'package']): + # Nothing to do + sys.exit(0) +else: + # Delete the node with the old syntax + config.delete(['system', 'package']) + + 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/migration-scripts/system/9-to-10 b/src/migration-scripts/system/9-to-10 new file mode 100755 index 000000000..3c49f0d95 --- /dev/null +++ b/src/migration-scripts/system/9-to-10 @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +# Operator accounts have been deprecated due to a security issue. Those accounts +# will be converted to regular admin accounts. + +import sys +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + 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_level = ['system', 'login', 'user'] + +if not config.exists(base_level): + # Nothing to do, which shouldn't happen anyway + # only if you wipe the config and reboot. + sys.exit(0) +else: + for user in config.list_nodes(base_level): + if config.exists(base_level + [user, 'level']): + if config.return_value(base_level + [user, 'level']) == 'operator': + config.set(base_level + [user, 'level'], value="admin", replace=True) + + try: + open(file_name,'w').write(config.to_string()) + + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) diff --git a/src/migration-scripts/vrrp/1-to-2 b/src/migration-scripts/vrrp/1-to-2 new file mode 100755 index 000000000..b2e61dd38 --- /dev/null +++ b/src/migration-scripts/vrrp/1-to-2 @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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 re +import sys + +from vyos.configtree import ConfigTree + + +if (len(sys.argv) < 1): + 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) + +# Convert the old VRRP syntax to the new syntax + +# The old approach was to put VRRP groups inside interfaces, +# as in "interfaces ethernet eth0 vrrp vrrp-group 10 ...". +# It was supported only under ethernet and bonding and their +# respective vif, vif-s, and vif-c subinterfaces + +def get_vrrp_group(path): + group = {"preempt": True, "rfc_compatibility": False, "disable": False} + + if config.exists(path + ["advertise-interval"]): + group["advertise_interval"] = config.return_value(path + ["advertise-interval"]) + + if config.exists(path + ["description"]): + group["description"] = config.return_value(path + ["description"]) + + if config.exists(path + ["disable"]): + group["disable"] = True + + if config.exists(path + ["hello-source-address"]): + group["hello_source"] = config.return_value(path + ["hello-source-address"]) + + # 1.1.8 didn't have it, but earlier 1.2.0 did, we don't want to break + # configs of early adopters! + if config.exists(path + ["peer-address"]): + group["peer_address"] = config.return_value(path + ["peer-address"]) + + if config.exists(path + ["preempt"]): + preempt = config.return_value(path + ["preempt"]) + if preempt == "false": + group["preempt"] = False + + if config.exists(path + ["rfc3768-compatibility"]): + group["rfc_compatibility"] = True + + if config.exists(path + ["preempt-delay"]): + group["preempt_delay"] = config.return_value(path + ["preempt-delay"]) + + if config.exists(path + ["priority"]): + group["priority"] = config.return_value(path + ["priority"]) + + if config.exists(path + ["sync-group"]): + group["sync_group"] = config.return_value(path + ["sync-group"]) + + if config.exists(path + ["authentication", "type"]): + group["auth_type"] = config.return_value(path + ["authentication", "type"]) + + if config.exists(path + ["authentication", "password"]): + group["auth_password"] = config.return_value(path + ["authentication", "password"]) + + if config.exists(path + ["virtual-address"]): + group["virtual_addresses"] = config.return_values(path + ["virtual-address"]) + + if config.exists(path + ["run-transition-scripts"]): + if config.exists(path + ["run-transition-scripts", "master"]): + group["master_script"] = config.return_value(path + ["run-transition-scripts", "master"]) + if config.exists(path + ["run-transition-scripts", "backup"]): + group["backup_script"] = config.return_value(path + ["run-transition-scripts", "backup"]) + if config.exists(path + ["run-transition-scripts", "fault"]): + group["fault_script"] = config.return_value(path + ["run-transition-scripts", "fault"]) + + # Also not present in 1.1.8, but supported by earlier 1.2.0 + if config.exists(path + ["health-check"]): + if config.exists(path + ["health-check", "interval"]): + group["health_check_interval"] = config.return_value(path + ["health-check", "interval"]) + if config.exists(path + ["health-check", "failure-count"]): + group["health_check_count"] = config.return_value(path + ["health-check", "failure-count"]) + if config.exists(path + ["health-check", "script"]): + group["health_check_script"] = config.return_value(path + ["health-check", "script"]) + + return group + +# Since VRRP is all over the place, there's no way to just check a path and exit early +# if it doesn't exist, we have to walk all interfaces and collect VRRP settings from them. +# Only if no data is collected from any interface we can conclude that VRRP is not configured +# and exit. + +groups = [] +base_paths = [] + +if config.exists(["interfaces", "ethernet"]): + base_paths.append("ethernet") +if config.exists(["interfaces", "bonding"]): + base_paths.append("bonding") + +for bp in base_paths: + parent_path = ["interfaces", bp] + + parent_intfs = config.list_nodes(parent_path) + + for pi in parent_intfs: + # Extract VRRP groups from the parent interface + vg_path =[pi, "vrrp", "vrrp-group"] + if config.exists(parent_path + vg_path): + pgroups = config.list_nodes(parent_path + vg_path) + for pg in pgroups: + g = get_vrrp_group(parent_path + vg_path + [pg]) + g["interface"] = pi + g["vrid"] = pg + groups.append(g) + + # Delete the VRRP subtree + # If left in place, configs will not load correctly + config.delete(parent_path + [pi, "vrrp"]) + + # Extract VRRP groups from 802.1q VLAN interfaces + if config.exists(parent_path + [pi, "vif"]): + vifs = config.list_nodes(parent_path + [pi, "vif"]) + for vif in vifs: + vif_vg_path = [pi, "vif", vif, "vrrp", "vrrp-group"] + if config.exists(parent_path + vif_vg_path): + vifgroups = config.list_nodes(parent_path + vif_vg_path) + for vif_group in vifgroups: + g = get_vrrp_group(parent_path + vif_vg_path + [vif_group]) + g["interface"] = "{0}.{1}".format(pi, vif) + g["vrid"] = vif_group + groups.append(g) + + config.delete(parent_path + [pi, "vif", vif, "vrrp"]) + + # Extract VRRP groups from 802.3ad QinQ service VLAN interfaces + if config.exists(parent_path + [pi, "vif-s"]): + vif_ss = config.list_nodes(parent_path + [pi, "vif-s"]) + for vif_s in vif_ss: + vifs_vg_path = [pi, "vif-s", vif_s, "vrrp", "vrrp-group"] + if config.exists(parent_path + vifs_vg_path): + vifsgroups = config.list_nodes(parent_path + vifs_vg_path) + for vifs_group in vifsgroups: + g = get_vrrp_group(parent_path + vifs_vg_path + [vifs_group]) + g["interface"] = "{0}.{1}".format(pi, vif_s) + g["vrid"] = vifs_group + groups.append(g) + + config.delete(parent_path + [pi, "vif-s", vif_s, "vrrp"]) + + # Extract VRRP groups from QinQ client VLAN interfaces nested in the vif-s + if config.exists(parent_path + [pi, "vif-s", vif_s, "vif-c"]): + vif_cs = config.list_nodes(parent_path + [pi, "vif-s", vif_s, "vif-c"]) + for vif_c in vif_cs: + vifc_vg_path = [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp", "vrrp-group"] + vifcgroups = config.list_nodes(parent_path + vifc_vg_path) + for vifc_group in vifcgroups: + g = get_vrrp_group(parent_path + vifc_vg_path + [vifc_group]) + g["interface"] = "{0}.{1}.{2}".format(pi, vif_s, vif_c) + g["vrid"] = vifc_group + groups.append(g) + + config.delete(parent_path + [pi, "vif-s", vif_s, "vif-c", vif_c, "vrrp"]) + +# If nothing was collected before this point, it means the config has no VRRP setup +if not groups: + sys.exit(0) + +# Otherwise, there is VRRP to convert + +# Now convert the collected groups to the new syntax +base_group_path = ["high-availability", "vrrp", "group"] +sync_path = ["high-availability", "vrrp", "sync-group"] + +for g in groups: + group_name = "{0}-{1}".format(g["interface"], g["vrid"]) + group_path = base_group_path + [group_name] + + config.set(group_path + ["interface"], value=g["interface"]) + config.set(group_path + ["vrid"], value=g["vrid"]) + + if "advertise_interval" in g: + config.set(group_path + ["advertise-interval"], value=g["advertise_interval"]) + + if "priority" in g: + config.set(group_path + ["priority"], value=g["priority"]) + + if not g["preempt"]: + config.set(group_path + ["no-preempt"], value=None) + + if "preempt_delay" in g: + config.set(group_path + ["preempt-delay"], value=g["preempt_delay"]) + + if g["rfc_compatibility"]: + config.set(group_path + ["rfc3768-compatibility"], value=None) + + if g["disable"]: + config.set(group_path + ["disable"], value=None) + + if "hello_source" in g: + config.set(group_path + ["hello-source-address"], value=g["hello_source"]) + + if "peer_address" in g: + config.set(group_path + ["peer-address"], value=g["peer_address"]) + + if "auth_password" in g: + config.set(group_path + ["authentication", "password"], value=g["auth_password"]) + if "auth_type" in g: + config.set(group_path + ["authentication", "type"], value=g["auth_type"]) + + if "master_script" in g: + config.set(group_path + ["transition-script", "master"], value=g["master_script"]) + if "backup_script" in g: + config.set(group_path + ["transition-script", "backup"], value=g["backup_script"]) + if "fault_script" in g: + config.set(group_path + ["transition-script", "fault"], value=g["fault_script"]) + + if "health_check_interval" in g: + config.set(group_path + ["health-check", "interval"], value=g["health_check_interval"]) + if "health_check_count" in g: + config.set(group_path + ["health-check", "failure-count"], value=g["health_check_count"]) + if "health_check_script" in g: + config.set(group_path + ["health-check", "script"], value=g["health_check_script"]) + + # Not that it should ever be absent... + if "virtual_addresses" in g: + # The new CLI disallows addresses without prefix length + # Pre-rewrite configs didn't support IPv6 VRRP, but handle it anyway + for va in g["virtual_addresses"]: + if not re.search(r'/', va): + if re.search(r':', va): + va = "{0}/128".format(va) + else: + va = "{0}/32".format(va) + config.set(group_path + ["virtual-address"], value=va, replace=False) + + # Sync group + if "sync_group" in g: + config.set(sync_path + [g["sync_group"], "member"], value=group_name, replace=False) + +# Set the tag flag +config.set_tag(base_group_path) +if config.exists(sync_path): + config.set_tag(sync_path) + +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/migration-scripts/webproxy/1-to-2 b/src/migration-scripts/webproxy/1-to-2 new file mode 100755 index 000000000..070ff356d --- /dev/null +++ b/src/migration-scripts/webproxy/1-to-2 @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +# migrate old style `webproxy proxy-bypass 1.2.3.4/24` +# to new style `webproxy whitelist destination-address 1.2.3.4/24` + +import sys + +from vyos.configtree import ConfigTree + +if len(sys.argv) < 1: + 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) + +cfg_webproxy_base = ['service', 'webproxy'] +if not config.exists(cfg_webproxy_base + ['proxy-bypass']): + # Nothing to do + sys.exit(0) +else: + bypass_addresses = config.return_values(cfg_webproxy_base + ['proxy-bypass']) + # delete old configuration node + config.delete(cfg_webproxy_base + ['proxy-bypass']) + for bypass_address in bypass_addresses: + # add data to new configuration node + config.set(cfg_webproxy_base + ['whitelist', 'destination-address'], value=bypass_address, replace=False) + + # save updated configuration + 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) |