# Copyright VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . # # Migrate 'tcp-flags' to 'tcp-flags is-set' and 'tcp-flags is-not-set' multi-value nodes (T8250) # # Rename acl 'macip' node to 'mac' (T8252) # # Rename `vpp settings logging default-log-level` to # `vpp settings logging default-level` (T8255) # # Move 'vpp nat44' and 'vpp settings nat44' to 'vpp nat nat44'. # Drop settings for nat workers (T8254) # # Delete 'ipsec' node and all settings and replace it with single 'ipsec-acceleration' flag (T8262) # # Unify CPU settings into a single 'cpu-cores' node under 'resource-allocation' (T8268) # # Convert `vpp settings interface rx-mode` and `vpp kernel-interfaces rx-mode` # to `vpp settings interfaces-rx-mode` by choose the worst value from all nodes (T8266) # # Get rid of 'dpdk-options' section for 'num-*' parameters: # - `vpp settings interface dpdk-options num-*` # - `vpp settings interface num-*` # and delete `vpp settings interface dpdk-options promisc` (T8274) # # Migrate all resource settings into 'resource-allocation' section (T8261) # # Move bonding interface from vpp section to 'interfaces vpp bonding' (T8283) # # Move vxlan interface from vpp section to 'interfaces vpp vxlan' (T8296) # # Move ipip interface from vpp section to 'interfaces vpp ipip' (T8314) # # Migrate loopback interface from vpp section to 'interfaces vpp loopback' (T8324) # # Migrate gre interface to 'interfaces vpp gre', remove interfaces with mode "point-to-multipoint" (T8325) # # Migrate bridge interface from vpp section to 'interfaces vpp bridge' (T8327) # # Migrate xconnect interface from vpp section to 'interfaces vpp xconnect' (T8328) # # Remove vif option from vxlan interface (T8340) from vyos.configtree import ConfigTree def _migrate_vpp_acl_tcp_flags(config: ConfigTree) -> None: base = ['vpp', 'acl', 'ip', 'tag-name'] if not config.exists(base): # Nothing to do return for tag_name in config.list_nodes(base): base_tag = base + [tag_name, 'rule'] for rule in config.list_nodes(base_tag): base_tcp_flags = base_tag + [rule, 'tcp-flags'] if not config.exists(base_tcp_flags): return flags = config.list_nodes(base_tcp_flags) set_flags = [flag for flag in flags if flag != 'not'] not_set_flags = config.list_nodes(base_tcp_flags + ['not']) config.delete(base_tcp_flags) if set_flags: for flag in set_flags: config.set(base_tcp_flags + ['is-set'], value=flag, replace=False) if not_set_flags: for flag in not_set_flags: config.set(base_tcp_flags + ['is-not-set'], value=flag, replace=False) def _migrate_vpp_macip(config: ConfigTree) -> None: base = ['vpp', 'acl'] if config.exists(base + ['macip']): config.rename(base + ['macip'], 'mac') def _migrate_vpp_unix_settings(config: ConfigTree) -> None: base_path = ['vpp', 'settings'] old_base_path = base_path + ['unix'] old_path = old_base_path + ['poll-sleep-usec'] new_path = base_path + ['poll-sleep-usec'] if config.exists(old_path): poll_sleep_usec = config.return_value(old_path) config.set(new_path, value=poll_sleep_usec) config.delete(old_base_path) def _migrate_vpp_log(config: ConfigTree) -> None: base = ['vpp', 'settings', 'logging'] if config.exists(base + ['default-log-level']): config.rename(base + ['default-log-level'], 'default-level') def _migrate_vpp_nat44(config: ConfigTree) -> None: base_path = ['vpp', 'nat44'] new_base_path = ['vpp', 'nat', 'nat44'] settings_path = ['vpp', 'settings', 'nat44'] if not config.exists(base_path): # Nothing to do return if not config.exists(['vpp', 'nat']): config.set(['vpp', 'nat']) # copy "vpp nat44" to "vpp nat nat44" config.copy(base_path, new_base_path) config.delete(base_path) # move 'vpp settings nat44' to 'vpp nat nat44' if config.exists(settings_path): if config.exists(settings_path + ['session-limit']): session_limit = config.return_value(settings_path + ['session-limit']) config.set(new_base_path + ['session-limit'], value=session_limit) if config.exists(settings_path + ['timeout']): config.copy(settings_path + ['timeout'], new_base_path + ['timeout']) config.delete(settings_path) def _migrate_vpp_ipsec(config: ConfigTree) -> None: base = ['vpp', 'settings'] if config.exists(base + ['ipsec']): config.set(base + ['ipsec-acceleration']) config.delete(base + ['ipsec']) def _migrate_vpp_cpu(config: ConfigTree) -> None: settings_path = ['vpp', 'settings'] cpu_path = settings_path + ['cpu'] if not config.exists(cpu_path): # Nothing to do return # get number of configured workers workers = 0 if config.exists(cpu_path + ['workers']): workers += int(config.return_value(cpu_path + ['workers'])) # add main-core to total workers workers += 1 if config.exists(cpu_path + ['corelist-workers']): def _count_range(item: str) -> int: if '-' in item: start, end = map(int, item.split('-')) return end - start + 1 return 1 tmp = config.return_values(cpu_path + ['corelist-workers']) workers = sum(_count_range(item) for item in tmp) + 1 # + main core # set 'resource-allocation cpu-cores' if workers: config.set(settings_path + ['resource-allocation', 'cpu-cores'], value=str(workers)) config.delete(cpu_path) def _migrate_vpp_interface_rx_mode(config: ConfigTree) -> None: # Per-interface rx-mode commands external_interface_path = ['vpp', 'settings', 'interface'] kernel_interface_path = ['vpp', 'kernel-interfaces'] # New global interface rx-mode command new_rx_mode_path = ['vpp', 'settings', 'interface-rx-mode'] def _iter_by_rx_modes(base_path: list): if config.exists(base_path): for iface_name in config.list_nodes(base_path): rx_mode_path = base_path + [iface_name, 'rx-mode'] if config.exists(rx_mode_path): rx_mode = config.return_value(rx_mode_path) yield rx_mode, rx_mode_path rx_modes = set() def _collect_rx_modes(base_path: list): for rx_mode, _ in _iter_by_rx_modes(base_path): rx_modes.add(rx_mode) _collect_rx_modes(external_interface_path) _collect_rx_modes(kernel_interface_path) if not rx_modes: return if 'interrupt' in rx_modes: rx_mode = 'interrupt' elif 'adaptive' in rx_modes: rx_mode = 'adaptive' else: rx_mode = 'polling' config.set(new_rx_mode_path, value=rx_mode) def _delete_rx_modes(base_path: list): for _, rx_mode_path in _iter_by_rx_modes(base_path): config.delete(rx_mode_path) _delete_rx_modes(external_interface_path) _delete_rx_modes(kernel_interface_path) def _migrate_vpp_dpdk_options(config: ConfigTree) -> None: base_path = ['vpp', 'settings', 'interface'] params = ['num-rx-desc', 'num-rx-queues', 'num-tx-desc', 'num-tx-queues'] if not config.exists(base_path): return for iface_name in config.list_nodes(base_path): iface_path = base_path + [iface_name] dpdk_path = iface_path + ['dpdk-options'] if config.exists(dpdk_path): for param in params: param_path = dpdk_path + [param] new_param_path = iface_path + [param] if config.exists(param_path): # Move `vpp settings interface dpdk-options num-*` # to `vpp settings interface num-*` config.copy(param_path, new_param_path) config.delete(param_path) # Delete `vpp settings interface dpdk-options promisc` promisc_path = dpdk_path + ['promisc'] if config.exists(promisc_path): config.delete(promisc_path) def _migrate_vpp_resources(config: ConfigTree) -> None: base = ['vpp', 'settings'] new_base = ['vpp', 'settings', 'resource-allocation'] if not config.exists(new_base): config.set(new_base) if config.exists(base + ['buffers']): # copy "settings buffers" to "settings resource-allocation buffers" config.copy(base + ['buffers'], new_base + ['buffers']) config.delete(base + ['buffers']) if config.exists(base + ['memory']): # copy "settings memory" to "settings resource-allocation memory" config.copy(base + ['memory'], new_base + ['memory']) config.delete(base + ['memory']) if config.exists(base + ['statseg']): # copy "settings statseg" to "settings resource-allocation memory stats" if not config.exists(new_base + ['memory']): config.set(new_base + ['memory']) config.copy(base + ['statseg'], new_base + ['memory', 'stats']) config.delete(base + ['statseg']) if config.exists(base + ['ipv6']): # copy "settings ipv6" to "settings resource-allocation ipv6" config.copy(base + ['ipv6'], new_base + ['ipv6']) config.delete(base + ['ipv6']) if config.exists(base + ['lcp']): # move "settings lcp ignore-kernel-routes" to "settings resource-allocation ignore-kernel-routes" # and remove "settings lcp netlink" node if config.exists(base + ['lcp', 'ignore-kernel-routes']): config.set(new_base + ['ignore-kernel-routes']) config.delete(base + ['lcp']) if config.exists(base + ['l2learn']): # move "settings l2learn limit" to "settings resource-allocation mac-limit" if config.exists(base + ['l2learn', 'limit']): tmp = config.return_value(base + ['l2learn', 'limit']) config.set(new_base + ['mac-limit'], value=tmp) config.delete(base + ['l2learn']) if config.exists(base + ['physmem']): # move "settings physmem max-size" to "settings resource-allocation memory physmem-max-size" if config.exists(base + ['physmem', 'max-size']): tmp = config.return_value(base + ['physmem', 'max-size']) config.set(new_base + ['memory', 'physmem-max-size'], value=tmp) config.delete(base + ['physmem']) if len(config.list_nodes(new_base)) == 0: config.delete(new_base) def _migrate_interface(config, type, ifname): base = ['vpp', 'interfaces', type] new_base = ['interfaces', 'vpp', type] kernel_interface_base = ['vpp', 'kernel-interfaces'] kernel_interface_path = base + [ifname, 'kernel-interface'] kernel_iface = None if config.exists(kernel_interface_path): kernel_iface = config.return_value(kernel_interface_path) config.delete(kernel_interface_path) new_ifname = f'vpp{ifname}' iface_base = new_base + [new_ifname] config.copy(base + [ifname], iface_base) config.set_tag(new_base) if kernel_iface and config.exists(kernel_interface_base + [kernel_iface]): if config.exists(kernel_interface_base + [kernel_iface, 'vif']): config.copy(kernel_interface_base + [kernel_iface, 'vif'], iface_base + ['vif']) if config.exists(kernel_interface_base + [kernel_iface, 'mtu']): config.copy(kernel_interface_base + [kernel_iface, 'mtu'], iface_base + ['mtu']) if config.exists(kernel_interface_base + [kernel_iface, 'address']): config.copy(kernel_interface_base + [kernel_iface, 'address'], iface_base + ['address']) config.delete(kernel_interface_base + [kernel_iface]) def _migrate_members_bridge(config, ifname): bridge_path = ['vpp', 'interfaces', 'bridge'] if config.exists(bridge_path): for iface in config.list_nodes(bridge_path): tmp = bridge_path + [iface, 'member', 'interface'] if config.exists(tmp): for name in config.list_nodes(tmp): if name == ifname: new_name = f'vpp{name}' config.rename(tmp + [name], new_name) def _migrate_members_xconnect(config, ifname): xconnect_path = ['vpp', 'interfaces', 'xconnect'] if config.exists(xconnect_path): for iface in config.list_nodes(xconnect_path): tmp = xconnect_path + [iface, 'member', 'interface'] if config.exists(tmp): for name in config.return_values(tmp): if name == ifname: new_name = f'vpp{name}' config.delete_value(tmp, name) config.set(tmp, value=new_name, replace=False) def _migrate_vpp_bonding_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'bonding'] new_base = ['interfaces', 'vpp', 'bonding'] kernel_interface_base = ['vpp', 'kernel-interfaces'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): _migrate_interface(config, 'bonding', ifname) for feature in ['nat44', 'cgnat']: for direction in ['inside', 'outside']: tmp_path = ['vpp', 'nat', feature, 'interface', direction] if not config.exists(tmp_path): continue names = config.return_values(tmp_path) for name in names: if name.split('.')[0] == ifname: new_name = f'vpp{name}' config.delete_value(tmp_path, name) config.set(tmp_path, value=new_name, replace=False) ipfix_path = ['vpp', 'ipfix', 'interface'] if config.exists(ipfix_path): for name in config.list_nodes(ipfix_path): if name.split('.')[0] == ifname: new_name = f'vpp{name}' config.rename(ipfix_path + [name], new_name) _migrate_members_bridge(config, ifname) config.delete(base) if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: config.delete(['vpp', 'kernel-interfaces']) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_vxlan_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'vxlan'] new_base = ['interfaces', 'vpp', 'vxlan'] kernel_interface_base = ['vpp', 'kernel-interfaces'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): _migrate_interface(config, 'vxlan', ifname) _migrate_members_bridge(config, ifname) _migrate_members_xconnect(config, ifname) config.delete(base) if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: config.delete(['vpp', 'kernel-interfaces']) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_ipip_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'ipip'] new_base = ['interfaces', 'vpp', 'ipip'] kernel_interface_base = ['vpp', 'kernel-interfaces'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): _migrate_interface(config, 'ipip', ifname) _migrate_members_xconnect(config, ifname) config.delete(base) if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: config.delete(['vpp', 'kernel-interfaces']) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_loopback_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'loopback'] new_base = ['interfaces', 'vpp', 'loopback'] kernel_interface_base = ['vpp', 'kernel-interfaces'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): _migrate_interface(config, 'loopback', ifname) _migrate_members_bridge(config, ifname) config.delete(base) if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: config.delete(['vpp', 'kernel-interfaces']) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_gre_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'gre'] new_base = ['interfaces', 'vpp', 'gre'] kernel_interface_base = ['vpp', 'kernel-interfaces'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): if config.exists(base + [ifname, 'mode']): if config.return_value(base + [ifname, 'mode']) == 'point-to-multipoint': config.delete(base + [ifname]) continue config.delete(base + [ifname, 'mode']) _migrate_interface(config, 'gre', ifname) _migrate_members_bridge(config, ifname) _migrate_members_xconnect(config, ifname) config.delete(base) if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: config.delete(['vpp', 'kernel-interfaces']) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_bridge_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'bridge'] new_base = ['interfaces', 'vpp', 'bridge'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): _migrate_interface(config, 'bridge', ifname) config.delete(base) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_xconnect_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'xconnect'] new_base = ['interfaces', 'vpp', 'xconnect'] if not config.exists(base): return config.set(new_base) for ifname in config.list_nodes(base): tmp = base + [ifname, 'member', 'interface'] bond_found = any(name.startswith('bond') for name in config.return_values(tmp)) if bond_found: config.delete(base + [ifname]) continue _migrate_interface(config, 'xconnect', ifname) config.delete(base) if len(config.list_nodes(['vpp', 'interfaces'])) == 0: config.delete(['vpp', 'interfaces']) def _migrate_vpp_vxlan_remove_vif(config: ConfigTree) -> None: base = ['interfaces', 'vpp', 'vxlan'] if not config.exists(base): return for ifname in config.list_nodes(base): if config.exists(base + [ifname, 'vif']): config.delete(base + [ifname, 'vif']) def _migrate_vpp_ignore_kernel_routes(config: ConfigTree) -> None: base = ['vpp', 'settings'] old_base = base + ['resource-allocation', 'ignore-kernel-routes'] if config.exists(old_base): config.delete(old_base) config.set(base + ['ignore-kernel-routes']) if len(config.list_nodes(base + ['resource-allocation'])) == 0: config.delete(base + ['resource-allocation']) def migrate(config: ConfigTree) -> None: if not config.exists(['vpp']): # Nothing to do return _migrate_vpp_acl_tcp_flags(config) _migrate_vpp_macip(config) _migrate_vpp_unix_settings(config) _migrate_vpp_log(config) _migrate_vpp_nat44(config) _migrate_vpp_ipsec(config) _migrate_vpp_cpu(config) _migrate_vpp_interface_rx_mode(config) _migrate_vpp_dpdk_options(config) _migrate_vpp_resources(config) _migrate_vpp_bonding_interface(config) _migrate_vpp_vxlan_interface(config) _migrate_vpp_ipip_interface(config) _migrate_vpp_loopback_interface(config) _migrate_vpp_gre_interface(config) _migrate_vpp_bridge_interface(config) _migrate_vpp_xconnect_interface(config) _migrate_vpp_vxlan_remove_vif(config) _migrate_vpp_ignore_kernel_routes(config)