diff options
author | Christian Breunig <christian@breunig.cc> | 2023-12-30 23:25:20 +0100 |
---|---|---|
committer | Christian Breunig <christian@breunig.cc> | 2024-01-01 09:25:32 +0100 |
commit | c9eaafd9f808aba8d29be73054e11d37577e539a (patch) | |
tree | aeccfda0a305cf6aca41630900e75bd32961a911 /src/conf_mode/interfaces-ethernet.py | |
parent | 2078253176046ea4d07e69caeb7932ea439b5614 (diff) | |
download | vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.tar.gz vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.zip |
T5474: establish common file name pattern for XML conf mode commands
We will use _ as CLI level divider. The XML definition filename and also
the Python helper should match the CLI node.
Example:
set interfaces ethernet -> interfaces_ethernet.xml.in
set interfaces bond -> interfaces_bond.xml.in
set service dhcp-server -> service_dhcp-server-xml.in
(cherry picked from commit 4ef110fd2c501b718344c72d495ad7e16d2bd465)
Diffstat (limited to 'src/conf_mode/interfaces-ethernet.py')
-rwxr-xr-x | src/conf_mode/interfaces-ethernet.py | 400 |
1 files changed, 0 insertions, 400 deletions
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py deleted file mode 100755 index 7374a29f7..000000000 --- a/src/conf_mode/interfaces-ethernet.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2019-2021 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 os -import pprint - -from glob import glob -from sys import exit - -from vyos.base import Warning -from vyos.config import Config -from vyos.configdict import get_interface_dict -from vyos.configdict import is_node_changed -from vyos.configverify import verify_address -from vyos.configverify import verify_dhcpv6 -from vyos.configverify import verify_eapol -from vyos.configverify import verify_interface_exists -from vyos.configverify import verify_mirror_redirect -from vyos.configverify import verify_mtu -from vyos.configverify import verify_mtu_ipv6 -from vyos.configverify import verify_vlan_config -from vyos.configverify import verify_vrf -from vyos.configverify import verify_bond_bridge_member -from vyos.ethtool import Ethtool -from vyos.ifconfig import EthernetIf -from vyos.ifconfig import BondIf -from vyos.pki import find_chain -from vyos.pki import encode_certificate -from vyos.pki import load_certificate -from vyos.pki import wrap_private_key -from vyos.template import render -from vyos.utils.process import call -from vyos.utils.dict import dict_search -from vyos.utils.dict import dict_to_paths_values -from vyos.utils.dict import dict_set -from vyos.utils.dict import dict_delete -from vyos.utils.file import write_file -from vyos import ConfigError -from vyos import airbag -airbag.enable() - -# XXX: wpa_supplicant works on the source interface -cfg_dir = '/run/wpa_supplicant' -wpa_suppl_conf = '/run/wpa_supplicant/{ifname}.conf' - -def update_bond_options(conf: Config, eth_conf: dict) -> list: - """ - Return list of blocked options if interface is a bond member - :param conf: Config object - :type conf: Config - :param eth_conf: Ethernet config dictionary - :type eth_conf: dict - :return: List of blocked options - :rtype: list - """ - blocked_list = [] - bond_name = list(eth_conf['is_bond_member'].keys())[0] - config_without_defaults = conf.get_config_dict( - ['interfaces', 'ethernet', eth_conf['ifname']], - key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True, - with_defaults=False, - with_recursive_defaults=False) - config_with_defaults = conf.get_config_dict( - ['interfaces', 'ethernet', eth_conf['ifname']], - key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True, - with_defaults=True, - with_recursive_defaults=True) - bond_config_with_defaults = conf.get_config_dict( - ['interfaces', 'bonding', bond_name], - key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True, - with_defaults=True, - with_recursive_defaults=True) - eth_dict_paths = dict_to_paths_values(config_without_defaults) - eth_path_base = ['interfaces', 'ethernet', eth_conf['ifname']] - - #if option is configured under ethernet section - for option_path, option_value in eth_dict_paths.items(): - bond_option_value = dict_search(option_path, bond_config_with_defaults) - - #If option is allowed for changing then continue - if option_path in EthernetIf.get_bond_member_allowed_options(): - continue - # if option is inherited from bond then set valued from bond interface - if option_path in BondIf.get_inherit_bond_options(): - # If option equals to bond option then do nothing - if option_value == bond_option_value: - continue - else: - # if ethernet has option and bond interface has - # then copy it from bond - if bond_option_value is not None: - if is_node_changed(conf, eth_path_base + option_path.split('.')): - Warning( - f'Cannot apply "{option_path.replace(".", " ")}" to "{option_value}".' \ - f' Interface "{eth_conf["ifname"]}" is a bond member.' \ - f' Option is inherited from bond "{bond_name}"') - dict_set(option_path, bond_option_value, eth_conf) - continue - # if ethernet has option and bond interface does not have - # then delete it form dict and do not apply it - else: - if is_node_changed(conf, eth_path_base + option_path.split('.')): - Warning( - f'Cannot apply "{option_path.replace(".", " ")}".' \ - f' Interface "{eth_conf["ifname"]}" is a bond member.' \ - f' Option is inherited from bond "{bond_name}"') - dict_delete(option_path, eth_conf) - blocked_list.append(option_path) - - # if inherited option is not configured under ethernet section but configured under bond section - for option_path in BondIf.get_inherit_bond_options(): - bond_option_value = dict_search(option_path, bond_config_with_defaults) - if bond_option_value is not None: - if option_path not in eth_dict_paths: - if is_node_changed(conf, eth_path_base + option_path.split('.')): - Warning( - f'Cannot apply "{option_path.replace(".", " ")}" to "{dict_search(option_path, config_with_defaults)}".' \ - f' Interface "{eth_conf["ifname"]}" is a bond member. ' \ - f'Option is inherited from bond "{bond_name}"') - dict_set(option_path, bond_option_value, eth_conf) - eth_conf['bond_blocked_changes'] = blocked_list - return None - -def get_config(config=None): - """ - Retrive CLI config as dictionary. Dictionary can never be empty, as at least the - interface name will be added or a deleted flag - """ - if config: - conf = config - else: - conf = Config() - - # This must be called prior to get_interface_dict(), as this function will - # alter the config level (config.set_level()) - pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) - - base = ['interfaces', 'ethernet'] - ifname, ethernet = get_interface_dict(conf, base) - if 'is_bond_member' in ethernet: - update_bond_options(conf, ethernet) - - if 'deleted' not in ethernet: - if pki: ethernet['pki'] = pki - - tmp = is_node_changed(conf, base + [ifname, 'speed']) - if tmp: ethernet.update({'speed_duplex_changed': {}}) - - tmp = is_node_changed(conf, base + [ifname, 'duplex']) - if tmp: ethernet.update({'speed_duplex_changed': {}}) - - return ethernet - - - -def verify_speed_duplex(ethernet: dict, ethtool: Ethtool): - """ - Verify speed and duplex - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - :param ethtool: Ethernet object - :type ethtool: Ethtool - """ - if ((ethernet['speed'] == 'auto' and ethernet['duplex'] != 'auto') or - (ethernet['speed'] != 'auto' and ethernet['duplex'] == 'auto')): - raise ConfigError( - 'Speed/Duplex missmatch. Must be both auto or manually configured') - - if ethernet['speed'] != 'auto' and ethernet['duplex'] != 'auto': - # We need to verify if the requested speed and duplex setting is - # supported by the underlaying NIC. - speed = ethernet['speed'] - duplex = ethernet['duplex'] - if not ethtool.check_speed_duplex(speed, duplex): - raise ConfigError( - f'Adapter does not support changing speed ' \ - f'and duplex settings to: {speed}/{duplex}!') - - -def verify_flow_control(ethernet: dict, ethtool: Ethtool): - """ - Verify flow control - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - :param ethtool: Ethernet object - :type ethtool: Ethtool - """ - if 'disable_flow_control' in ethernet: - if not ethtool.check_flow_control(): - raise ConfigError( - 'Adapter does not support changing flow-control settings!') - - -def verify_ring_buffer(ethernet: dict, ethtool: Ethtool): - """ - Verify ring buffer - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - :param ethtool: Ethernet object - :type ethtool: Ethtool - """ - if 'ring_buffer' in ethernet: - max_rx = ethtool.get_ring_buffer_max('rx') - if not max_rx: - raise ConfigError( - 'Driver does not support RX ring-buffer configuration!') - - max_tx = ethtool.get_ring_buffer_max('tx') - if not max_tx: - raise ConfigError( - 'Driver does not support TX ring-buffer configuration!') - - rx = dict_search('ring_buffer.rx', ethernet) - if rx and int(rx) > int(max_rx): - raise ConfigError(f'Driver only supports a maximum RX ring-buffer ' \ - f'size of "{max_rx}" bytes!') - - tx = dict_search('ring_buffer.tx', ethernet) - if tx and int(tx) > int(max_tx): - raise ConfigError(f'Driver only supports a maximum TX ring-buffer ' \ - f'size of "{max_tx}" bytes!') - - -def verify_offload(ethernet: dict, ethtool: Ethtool): - """ - Verify offloading capabilities - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - :param ethtool: Ethernet object - :type ethtool: Ethtool - """ - if dict_search('offload.rps', ethernet) != None: - if not os.path.exists(f'/sys/class/net/{ethernet["ifname"]}/queues/rx-0/rps_cpus'): - raise ConfigError('Interface does not suport RPS!') - driver = ethtool.get_driver_name() - # T3342 - Xen driver requires special treatment - if driver == 'vif': - if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: - raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\ - 'for MTU size larger then 1500 bytes') - - -def verify_allowedbond_changes(ethernet: dict): - """ - Verify changed options if interface is in bonding - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - """ - if 'bond_blocked_changes' in ethernet: - for option in ethernet['bond_blocked_changes']: - raise ConfigError(f'Cannot configure "{option.replace(".", " ")}"' \ - f' on interface "{ethernet["ifname"]}".' \ - f' Interface is a bond member') - - -def verify(ethernet): - if 'deleted' in ethernet: - return None - if 'is_bond_member' in ethernet: - verify_bond_member(ethernet) - else: - verify_ethernet(ethernet) - - -def verify_bond_member(ethernet): - """ - Verification function for ethernet interface which is in bonding - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - """ - ifname = ethernet['ifname'] - verify_interface_exists(ifname) - verify_eapol(ethernet) - verify_mirror_redirect(ethernet) - ethtool = Ethtool(ifname) - verify_speed_duplex(ethernet, ethtool) - verify_flow_control(ethernet, ethtool) - verify_ring_buffer(ethernet, ethtool) - verify_offload(ethernet, ethtool) - verify_allowedbond_changes(ethernet) - -def verify_ethernet(ethernet): - """ - Verification function for simple ethernet interface - :param ethernet: dictionary which is received from get_interface_dict - :type ethernet: dict - """ - ifname = ethernet['ifname'] - verify_interface_exists(ifname) - verify_mtu(ethernet) - verify_mtu_ipv6(ethernet) - verify_dhcpv6(ethernet) - verify_address(ethernet) - verify_vrf(ethernet) - verify_bond_bridge_member(ethernet) - verify_eapol(ethernet) - verify_mirror_redirect(ethernet) - ethtool = Ethtool(ifname) - # No need to check speed and duplex keys as both have default values. - verify_speed_duplex(ethernet, ethtool) - verify_flow_control(ethernet, ethtool) - verify_ring_buffer(ethernet, ethtool) - verify_offload(ethernet, ethtool) - # use common function to verify VLAN configuration - verify_vlan_config(ethernet) - return None - - -def generate(ethernet): - # render real configuration file once - wpa_supplicant_conf = wpa_suppl_conf.format(**ethernet) - - if 'deleted' in ethernet: - # delete configuration on interface removal - if os.path.isfile(wpa_supplicant_conf): - os.unlink(wpa_supplicant_conf) - return None - - if 'eapol' in ethernet: - ifname = ethernet['ifname'] - - render(wpa_supplicant_conf, 'ethernet/wpa_supplicant.conf.j2', ethernet) - - cert_file_path = os.path.join(cfg_dir, f'{ifname}_cert.pem') - cert_key_path = os.path.join(cfg_dir, f'{ifname}_cert.key') - - cert_name = ethernet['eapol']['certificate'] - pki_cert = ethernet['pki']['certificate'][cert_name] - - loaded_pki_cert = load_certificate(pki_cert['certificate']) - loaded_ca_certs = {load_certificate(c['certificate']) - for c in ethernet['pki']['ca'].values()} if 'ca' in ethernet['pki'] else {} - - cert_full_chain = find_chain(loaded_pki_cert, loaded_ca_certs) - - write_file(cert_file_path, - '\n'.join(encode_certificate(c) for c in cert_full_chain)) - write_file(cert_key_path, wrap_private_key(pki_cert['private']['key'])) - - if 'ca_certificate' in ethernet['eapol']: - ca_cert_file_path = os.path.join(cfg_dir, f'{ifname}_ca.pem') - ca_chains = [] - - for ca_cert_name in ethernet['eapol']['ca_certificate']: - pki_ca_cert = ethernet['pki']['ca'][ca_cert_name] - loaded_ca_cert = load_certificate(pki_ca_cert['certificate']) - ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs) - ca_chains.append( - '\n'.join(encode_certificate(c) for c in ca_full_chain)) - - write_file(ca_cert_file_path, '\n'.join(ca_chains)) - - return None - -def apply(ethernet): - ifname = ethernet['ifname'] - # take care about EAPoL supplicant daemon - eapol_action='stop' - - e = EthernetIf(ifname) - if 'deleted' in ethernet: - # delete interface - e.remove() - else: - e.update(ethernet) - if 'eapol' in ethernet: - eapol_action='reload-or-restart' - - call(f'systemctl {eapol_action} wpa_supplicant-wired@{ifname}') - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - - apply(c) - except ConfigError as e: - print(e) - exit(1) |