Blame libnmstate/nm/bond.py

Packit Service 0535c1
#
Packit Service 0535c1
# Copyright (c) 2018-2020 Red Hat, Inc.
Packit Service 0535c1
#
Packit Service 0535c1
# This file is part of nmstate
Packit Service 0535c1
#
Packit Service 0535c1
# This program is free software: you can redistribute it and/or modify
Packit Service 0535c1
# it under the terms of the GNU Lesser General Public License as published by
Packit Service 0535c1
# the Free Software Foundation, either version 2.1 of the License, or
Packit Service 0535c1
# (at your option) any later version.
Packit Service 0535c1
#
Packit Service 0535c1
# This program is distributed in the hope that it will be useful,
Packit Service 0535c1
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 0535c1
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 0535c1
# GNU Lesser General Public License for more details.
Packit Service 0535c1
#
Packit Service 0535c1
# You should have received a copy of the GNU Lesser General Public License
Packit Service 0535c1
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Packit Service 0535c1
#
Packit Service 0535c1
Packit Service 0535c1
import contextlib
Packit Service 0535c1
import os
Packit Service 0535c1
import glob
Packit Service 0535c1
import re
Packit Service 0535c1
Packit Service 0535c1
from libnmstate.error import NmstateValueError
Packit Service 0535c1
from libnmstate.ifaces.bond import BondIface
Packit Service 0535c1
from libnmstate.schema import Bond
Packit Service 0535c1
from .common import NM
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
BOND_TYPE = "bond"
Packit Service 0535c1
Packit Service 0535c1
SYSFS_EMPTY_VALUE = ""
Packit Service 0535c1
Packit Service 0535c1
NM_SUPPORTED_BOND_OPTIONS = NM.SettingBond.get_valid_options(
Packit Service 0535c1
    NM.SettingBond.new()
Packit Service 0535c1
)
Packit Service 0535c1
Packit Service 0535c1
SYSFS_BOND_OPTION_FOLDER_FMT = "/sys/class/net/{ifname}/bonding"
Packit Service 0535c1
Packit Service c7b39a
BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC = "00:00:00:00:00:00"
Packit Service c7b39a
Packit Service 0535c1
Packit Service 0535c1
def create_setting(options, wired_setting):
Packit Service 0535c1
    bond_setting = NM.SettingBond.new()
Packit Service 0535c1
    _fix_bond_option_arp_interval(options)
Packit Service 0535c1
    for option_name, option_value in options.items():
Packit Service 0535c1
        if wired_setting and BondIface.is_mac_restricted_mode(
Packit Service 0535c1
            options.get(Bond.MODE), options
Packit Service 0535c1
        ):
Packit Service 0535c1
            # When in MAC restricted mode, MAC address should be unset.
Packit Service 0535c1
            wired_setting.props.cloned_mac_address = None
Packit Service c7b39a
        if (
Packit Service c7b39a
            option_name == "ad_actor_system"
Packit Service c7b39a
            and option_value == BOND_AD_ACTOR_SYSTEM_USE_BOND_MAC
Packit Service c7b39a
        ):
Packit Service c7b39a
            # The all zero ad_actor_system is the kernel default value
Packit Service c7b39a
            # And it is invalid to set as all zero
Packit Service c7b39a
            continue
Packit Service 0535c1
        if option_value != SYSFS_EMPTY_VALUE:
Packit Service 0535c1
            success = bond_setting.add_option(option_name, str(option_value))
Packit Service 0535c1
            if not success:
Packit Service 0535c1
                raise NmstateValueError(
Packit Service 0535c1
                    "Invalid bond option: '{}'='{}'".format(
Packit Service 0535c1
                        option_name, option_value
Packit Service 0535c1
                    )
Packit Service 0535c1
                )
Packit Service 0535c1
Packit Service 0535c1
    return bond_setting
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def is_bond_type_id(type_id):
Packit Service 0535c1
    return type_id == NM.DeviceType.BOND
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def get_bond_info(nm_device):
Packit Service 0535c1
    slaves = get_slaves(nm_device)
Packit Service 0535c1
    options = _get_options(nm_device)
Packit Service 0535c1
    if slaves or options:
Packit Service 0535c1
        return {"slaves": slaves, "options": options}
Packit Service 0535c1
    else:
Packit Service 0535c1
        return {}
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _get_options(nm_device):
Packit Service 0535c1
    ifname = nm_device.get_iface()
Packit Service 0535c1
    bond_option_names_in_profile = get_bond_option_names_in_profile(nm_device)
Packit Service 0535c1
    if (
Packit Service 0535c1
        "miimon" in bond_option_names_in_profile
Packit Service 0535c1
        or "arp_interval" in bond_option_names_in_profile
Packit Service 0535c1
    ):
Packit Service 0535c1
        bond_option_names_in_profile.add("arp_interval")
Packit Service 0535c1
        bond_option_names_in_profile.add("miimon")
Packit Service 0535c1
Packit Service 0535c1
    # Mode is required
Packit Service 0535c1
    sysfs_folder = SYSFS_BOND_OPTION_FOLDER_FMT.format(ifname=ifname)
Packit Service 0535c1
    mode = _read_sysfs_file(f"{sysfs_folder}/mode")
Packit Service 0535c1
Packit Service 0535c1
    bond_setting = NM.SettingBond.new()
Packit Service 0535c1
    bond_setting.add_option(Bond.MODE, mode)
Packit Service 0535c1
Packit Service 0535c1
    options = {Bond.MODE: mode}
Packit Service 0535c1
    for sysfs_file in glob.iglob(f"{sysfs_folder}/*"):
Packit Service 0535c1
        option = os.path.basename(sysfs_file)
Packit Service 0535c1
        if option in NM_SUPPORTED_BOND_OPTIONS:
Packit Service 0535c1
            value = _read_sysfs_file(sysfs_file)
Packit Service 0535c1
            # When default_value is None, it means this option is invalid
Packit Service 0535c1
            # under this bond mode
Packit Service 0535c1
            default_value = bond_setting.get_option_default(option)
Packit Service 0535c1
            if (
Packit Service 0535c1
                (default_value and value != default_value)
Packit Service 0535c1
                # Always include bond options which are explicitly defined in
Packit Service 0535c1
                # on-disk profile.
Packit Service 0535c1
                or option in bond_option_names_in_profile
Packit Service 0535c1
            ):
Packit Service 0535c1
                if option == "arp_ip_target":
Packit Service 0535c1
                    value = value.replace(" ", ",")
Packit Service 0535c1
                options[option] = value
Packit Service 0535c1
    # Workaround of https://bugzilla.redhat.com/show_bug.cgi?id=1806549
Packit Service 0535c1
    if "miimon" not in options:
Packit Service 0535c1
        options["miimon"] = bond_setting.get_option_default("miimon")
Packit Service 0535c1
    return options
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _read_sysfs_file(file_path):
Packit Service 0535c1
    with open(file_path) as fd:
Packit Service 0535c1
        return _strip_sysfs_name_number_value(fd.read().rstrip("\n"))
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _strip_sysfs_name_number_value(value):
Packit Service 0535c1
    """
Packit Service 0535c1
    In sysfs/kernel, the value of some are shown with both human friendly
Packit Service 0535c1
    string and integer. For example, bond mode in sysfs is shown as
Packit Service 0535c1
    'balance-rr 0'. This function only return the human friendly string.
Packit Service 0535c1
    """
Packit Service 0535c1
    return re.sub(" [0-9]$", "", value)
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def get_slaves(nm_device):
Packit Service 0535c1
    return nm_device.get_slaves()
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def get_bond_option_names_in_profile(nm_device):
Packit Service 0535c1
    ac = nm_device.get_active_connection()
Packit Service 0535c1
    with contextlib.suppress(AttributeError):
Packit Service 0535c1
        bond_setting = ac.get_connection().get_setting_bond()
Packit Service 0535c1
        return {
Packit Service 0535c1
            bond_setting.get_option(i)[1]
Packit Service 0535c1
            for i in range(0, bond_setting.get_num_options())
Packit Service 0535c1
        }
Packit Service 0535c1
    return set()
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _fix_bond_option_arp_interval(bond_options):
Packit Service 0535c1
    """
Packit Service 0535c1
    Due to bug https://bugzilla.redhat.com/show_bug.cgi?id=1806549
Packit Service 0535c1
    NM 1.22.8 treat 'arp_interval 0' as arp_interval enabled(0 actual means
Packit Service 0535c1
    disabled), which then conflict with 'miimon'.
Packit Service 0535c1
    The workaround is remove 'arp_interval 0' when 'miimon' > 0.
Packit Service 0535c1
    """
Packit Service 0535c1
    if "miimon" in bond_options and "arp_interval" in bond_options:
Packit Service 0535c1
        try:
Packit Service 0535c1
            miimon = int(bond_options["miimon"])
Packit Service 0535c1
            arp_interval = int(bond_options["arp_interval"])
Packit Service 0535c1
        except ValueError as e:
Packit Service 0535c1
            raise NmstateValueError(f"Invalid bond option: {e}")
Packit Service 0535c1
        if miimon > 0 and arp_interval == 0:
Packit Service 0535c1
            bond_options.pop("arp_interval")
Packit Service 0535c1
            bond_options.pop("arp_ip_target", None)