Blame libnmstate/ifaces/bond.py

Packit Service 0535c1
#
Packit Service 0535c1
# Copyright (c) 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 logging
Packit Service 0535c1
Packit Service 0535c1
from libnmstate.error import NmstateValueError
Packit Service 0535c1
from libnmstate.schema import Bond
Packit Service 0535c1
from libnmstate.schema import BondMode
Packit Service 0535c1
from libnmstate.schema import Interface
Packit Service 0535c1
Packit Service 0535c1
from .base_iface import BaseIface
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
class BondIface(BaseIface):
Packit Service 0535c1
    _MODE_CHANGE_METADATA = "_bond_mode_changed"
Packit Service 0535c1
Packit Service 0535c1
    def sort_slaves(self):
Packit Service 0535c1
        if self.slaves:
Packit Service 0535c1
            self.raw[Bond.CONFIG_SUBTREE][Bond.SLAVES].sort()
Packit Service 0535c1
Packit Service 0535c1
    def __init__(self, info, save_to_disk=True):
Packit Service 0535c1
        super().__init__(info, save_to_disk)
Packit Service 0535c1
        self._normalize_options_values()
Packit Service 0535c1
        self._fix_bond_option_arp_monitor()
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def slaves(self):
Packit Service 0535c1
        return self.raw.get(Bond.CONFIG_SUBTREE, {}).get(Bond.SLAVES, [])
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def is_master(self):
Packit Service 0535c1
        return True
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def is_virtual(self):
Packit Service 0535c1
        return True
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def bond_mode(self):
Packit Service 0535c1
        return self.raw.get(Bond.CONFIG_SUBTREE, {}).get(Bond.MODE)
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def _bond_options(self):
Packit Service 0535c1
        return self.raw.get(Bond.CONFIG_SUBTREE, {}).get(
Packit Service 0535c1
            Bond.OPTIONS_SUBTREE, {}
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def is_bond_mode_changed(self):
Packit Service 0535c1
        return self.raw.get(BondIface._MODE_CHANGE_METADATA) is True
Packit Service 0535c1
Packit Service 0535c1
    def _set_bond_mode_changed_metadata(self, value):
Packit Service 0535c1
        self.raw[BondIface._MODE_CHANGE_METADATA] = value
Packit Service 0535c1
Packit Service 0535c1
    def _generate_bond_mode_change_metadata(self, ifaces):
Packit Service 0535c1
        if self.is_up:
Packit Service 0535c1
            cur_iface = ifaces.current_ifaces.get(self.name)
Packit Service 0535c1
            if cur_iface and self.bond_mode != cur_iface.bond_mode:
Packit Service 0535c1
                self._set_bond_mode_changed_metadata(True)
Packit Service 0535c1
Packit Service 0535c1
    def gen_metadata(self, ifaces):
Packit Service 0535c1
        super().gen_metadata(ifaces)
Packit Service 0535c1
        if not self.is_absent:
Packit Service 0535c1
            self._generate_bond_mode_change_metadata(ifaces)
Packit Service 0535c1
Packit Service 0535c1
    def pre_edit_validation_and_cleanup(self):
Packit Service 0535c1
        super().pre_edit_validation_and_cleanup()
Packit Service 0535c1
        if self.is_up:
Packit Service 0535c1
            self._discard_bond_option_when_mode_change()
Packit Service 0535c1
            self._validate_bond_mode()
Packit Service 0535c1
            self._fix_mac_restriced_mode()
Packit Service 0535c1
            self._validate_miimon_conflict_with_arp_interval()
Packit Service 0535c1
Packit Service 0535c1
    def _discard_bond_option_when_mode_change(self):
Packit Service 0535c1
        if self.is_bond_mode_changed:
Packit Service 0535c1
            logging.warning(
Packit Service 0535c1
                "Discarding all current bond options as interface "
Packit Service 0535c1
                f"{self.name} has bond mode changed"
Packit Service 0535c1
            )
Packit Service 0535c1
            self.raw[Bond.CONFIG_SUBTREE][
Packit Service 0535c1
                Bond.OPTIONS_SUBTREE
Packit Service 0535c1
            ] = self.original_dict.get(Bond.CONFIG_SUBTREE, {}).get(
Packit Service 0535c1
                Bond.OPTIONS_SUBTREE, {}
Packit Service 0535c1
            )
Packit Service 0535c1
            self._normalize_options_values()
Packit Service 0535c1
Packit Service 0535c1
    def _validate_bond_mode(self):
Packit Service 0535c1
        if self.bond_mode is None:
Packit Service 0535c1
            raise NmstateValueError(
Packit Service 0535c1
                f"Bond interface {self.name} does not have bond mode defined"
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    def _fix_mac_restriced_mode(self):
Packit Service 0535c1
        if self.is_in_mac_restricted_mode:
Packit Service 0535c1
            if self.original_dict.get(Interface.MAC):
Packit Service 0535c1
                raise NmstateValueError(
Packit Service 0535c1
                    "MAC address cannot be specified in bond interface along "
Packit Service 0535c1
                    "with fail_over_mac active on active backup mode"
Packit Service 0535c1
                )
Packit Service 0535c1
            else:
Packit Service 0535c1
                self.raw.pop(Interface.MAC, None)
Packit Service 0535c1
Packit Service 0535c1
    def _validate_miimon_conflict_with_arp_interval(self):
Packit Service 0535c1
        bond_options = self._bond_options
Packit Service 0535c1
        if bond_options.get("miimon") and bond_options.get("arp_interval"):
Packit Service 0535c1
            raise NmstateValueError(
Packit Service 0535c1
                "Bond option arp_interval is conflicting with miimon, "
Packit Service 0535c1
                "please disable one of them by setting to 0"
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    @staticmethod
Packit Service 0535c1
    def is_mac_restricted_mode(mode, bond_options):
Packit Service 0535c1
        return (
Packit Service 0535c1
            mode == BondMode.ACTIVE_BACKUP
Packit Service 0535c1
            and bond_options.get("fail_over_mac") == "active"
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def is_in_mac_restricted_mode(self):
Packit Service 0535c1
        """
Packit Service 0535c1
        Return True when Bond option does not allow MAC address defined.
Packit Service 0535c1
        In MAC restricted mode means:
Packit Service 0535c1
            Bond mode is BondMode.ACTIVE_BACKUP
Packit Service 0535c1
            Bond option "fail_over_mac" is active.
Packit Service 0535c1
        """
Packit Service 0535c1
        return BondIface.is_mac_restricted_mode(
Packit Service 0535c1
            self.bond_mode, self._bond_options
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
    def _normalize_options_values(self):
Packit Service 0535c1
        if self._bond_options:
Packit Service 0535c1
            normalized_options = {}
Packit Service 0535c1
            for option_name, option_value in self._bond_options.items():
Packit Service 0535c1
                with contextlib.suppress(ValueError):
Packit Service 0535c1
                    option_value = int(option_value)
Packit Service 0535c1
                option_value = _get_bond_named_option_value_by_id(
Packit Service 0535c1
                    option_name, option_value
Packit Service 0535c1
                )
Packit Service 0535c1
                normalized_options[option_name] = option_value
Packit Service 0535c1
            self._bond_options.update(normalized_options)
Packit Service 0535c1
Packit Service 0535c1
    def _fix_bond_option_arp_monitor(self):
Packit Service 0535c1
        """
Packit Service 0535c1
        Adding 'arp_ip_target=""' when ARP monitor is disabled by
Packit Service 0535c1
        `arp_interval=0`
Packit Service 0535c1
        """
Packit Service 0535c1
        if self._bond_options:
Packit Service 0535c1
            _include_arp_ip_target_explictly_when_disable(
Packit Service 0535c1
                self.raw[Bond.CONFIG_SUBTREE][Bond.OPTIONS_SUBTREE]
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    def state_for_verify(self):
Packit Service 0535c1
        state = super().state_for_verify()
Packit Service 0535c1
        if state.get(Bond.CONFIG_SUBTREE, {}).get(Bond.OPTIONS_SUBTREE):
Packit Service 0535c1
            _include_arp_ip_target_explictly_when_disable(
Packit Service 0535c1
                state[Bond.CONFIG_SUBTREE][Bond.OPTIONS_SUBTREE]
Packit Service 0535c1
            )
Packit Service 0535c1
        return state
Packit Service 0535c1
Packit Service 0535c1
    def remove_slave(self, slave_name):
Packit Service 0535c1
        self.raw[Bond.CONFIG_SUBTREE][Bond.SLAVES] = [
Packit Service 0535c1
            s for s in self.slaves if s != slave_name
Packit Service 0535c1
        ]
Packit Service 0535c1
        self.sort_slaves()
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
class _BondNamedOptions:
Packit Service 0535c1
    AD_SELECT = "ad_select"
Packit Service 0535c1
    ARP_ALL_TARGETS = "arp_all_targets"
Packit Service 0535c1
    ARP_VALIDATE = "arp_validate"
Packit Service 0535c1
    FAIL_OVER_MAC = "fail_over_mac"
Packit Service 0535c1
    LACP_RATE = "lacp_rate"
Packit Service 0535c1
    MODE = "mode"
Packit Service 0535c1
    PRIMARY_RESELECT = "primary_reselect"
Packit Service 0535c1
    XMIT_HASH_POLICY = "xmit_hash_policy"
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
_BOND_OPTIONS_NUMERIC_TO_NAMED_MAP = {
Packit Service 0535c1
    _BondNamedOptions.AD_SELECT: ("stable", "bandwidth", "count"),
Packit Service 0535c1
    _BondNamedOptions.ARP_ALL_TARGETS: ("any", "all"),
Packit Service 0535c1
    _BondNamedOptions.ARP_VALIDATE: (
Packit Service 0535c1
        "none",
Packit Service 0535c1
        "active",
Packit Service 0535c1
        "backup",
Packit Service 0535c1
        "all",
Packit Service 0535c1
        "filter",
Packit Service 0535c1
        "filter_active",
Packit Service 0535c1
        "filter_backup",
Packit Service 0535c1
    ),
Packit Service 0535c1
    _BondNamedOptions.FAIL_OVER_MAC: ("none", "active", "follow"),
Packit Service 0535c1
    _BondNamedOptions.LACP_RATE: ("slow", "fast"),
Packit Service 0535c1
    _BondNamedOptions.MODE: (
Packit Service 0535c1
        "balance-rr",
Packit Service 0535c1
        "active-backup",
Packit Service 0535c1
        "balance-xor",
Packit Service 0535c1
        "broadcast",
Packit Service 0535c1
        "802.3ad",
Packit Service 0535c1
        "balance-tlb",
Packit Service 0535c1
        "balance-alb",
Packit Service 0535c1
    ),
Packit Service 0535c1
    _BondNamedOptions.PRIMARY_RESELECT: ("always", "better", "failure"),
Packit Service 0535c1
    _BondNamedOptions.XMIT_HASH_POLICY: (
Packit Service 0535c1
        "layer2",
Packit Service 0535c1
        "layer3+4",
Packit Service 0535c1
        "layer2+3",
Packit Service 0535c1
        "encap2+3",
Packit Service 0535c1
        "encap3+4",
Packit Service 0535c1
    ),
Packit Service 0535c1
}
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _get_bond_named_option_value_by_id(option_name, option_id_value):
Packit Service 0535c1
    """
Packit Service 0535c1
    Given an option name and its value, return a named option value
Packit Service 0535c1
    if it exists.
Packit Service 0535c1
    Return the same option value as inputted if:
Packit Service 0535c1
    - The option name has no dual named and id values.
Packit Service 0535c1
    - The option value is not numeric.
Packit Service 0535c1
    - The option value has no corresponding named value (not in range).
Packit Service 0535c1
    """
Packit Service 0535c1
    option_value = _BOND_OPTIONS_NUMERIC_TO_NAMED_MAP.get(option_name)
Packit Service 0535c1
    if option_value:
Packit Service 0535c1
        with contextlib.suppress(ValueError, IndexError):
Packit Service 0535c1
            return option_value[int(option_id_value)]
Packit Service 0535c1
    return option_id_value
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _include_arp_ip_target_explictly_when_disable(bond_options):
Packit Service 0535c1
    if (
Packit Service 0535c1
        bond_options.get("arp_interval") == 0
Packit Service 0535c1
        and "arp_ip_target" not in bond_options
Packit Service 0535c1
    ):
Packit Service 0535c1
        bond_options["arp_ip_target"] = ""