Blame libnmstate/ifaces/linux_bridge.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
from libnmstate.error import NmstateValueError
Packit Service 0535c1
from libnmstate.schema import LinuxBridge
Packit Service 0535c1
Packit Service 0535c1
from .bridge import BridgeIface
Packit Service 0535c1
Packit Service 0535c1
# The aging_time, forward_delay, hello_time, max_age options are multipled by
Packit Service 0535c1
# 100(USER_HZ) when apply to kernel(via NM), so they are not impacted by this
Packit Service 0535c1
# integer round up/down issue.
Packit Service 0535c1
INTEGER_ROUNDED_OPTIONS = [
Packit Service 0535c1
    LinuxBridge.Options.MULTICAST_LAST_MEMBER_INTERVAL,
Packit Service 0535c1
    LinuxBridge.Options.MULTICAST_MEMBERSHIP_INTERVAL,
Packit Service 0535c1
    LinuxBridge.Options.MULTICAST_QUERIER_INTERVAL,
Packit Service 0535c1
    LinuxBridge.Options.MULTICAST_QUERY_RESPONSE_INTERVAL,
Packit Service 0535c1
    LinuxBridge.Options.MULTICAST_STARTUP_QUERY_INTERVAL,
Packit Service 0535c1
]
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
class LinuxBridgeIface(BridgeIface):
Packit Service 0535c1
    @property
Packit Service 0535c1
    def _options(self):
Packit Service 0535c1
        return self.raw.get(LinuxBridge.CONFIG_SUBTREE, {}).get(
Packit Service 0535c1
            LinuxBridge.OPTIONS_SUBTREE, {}
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
    def pre_edit_validation_and_cleanup(self):
Packit Service 0535c1
        self._validate()
Packit Service 0535c1
        self._fix_vlan_filtering_mode()
Packit Service 0535c1
        super().pre_edit_validation_and_cleanup()
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def slaves(self):
Packit Service 0535c1
        return [p[LinuxBridge.Port.NAME] for p in self.port_configs]
Packit Service 0535c1
Packit Service 0535c1
    def _validate(self):
Packit Service 0535c1
        self._validate_vlan_filtering_trunk_tags()
Packit Service 0535c1
        self._validate_vlan_filtering_tag()
Packit Service 0535c1
        self._validate_vlan_filtering_enable_native()
Packit Service 0535c1
Packit Service 0535c1
    def _validate_vlan_filtering_trunk_tags(self):
Packit Service 0535c1
        for port_config in self.original_dict.get(
Packit Service 0535c1
            LinuxBridge.CONFIG_SUBTREE, {}
Packit Service 0535c1
        ).get(LinuxBridge.PORT_SUBTREE, []):
Packit Service 0535c1
            port_vlan_state = port_config.get(
Packit Service 0535c1
                LinuxBridge.Port.VLAN_SUBTREE, {}
Packit Service 0535c1
            )
Packit Service 0535c1
            vlan_mode = port_vlan_state.get(LinuxBridge.Port.Vlan.MODE)
Packit Service 0535c1
            trunk_tags = port_vlan_state.get(
Packit Service 0535c1
                LinuxBridge.Port.Vlan.TRUNK_TAGS, []
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
            if vlan_mode == LinuxBridge.Port.Vlan.Mode.ACCESS:
Packit Service 0535c1
                if trunk_tags:
Packit Service 0535c1
                    raise NmstateValueError(
Packit Service 0535c1
                        "Access port cannot have trunk tags"
Packit Service 0535c1
                    )
Packit Service 0535c1
            elif port_vlan_state:
Packit Service 0535c1
                if not trunk_tags:
Packit Service 0535c1
                    raise NmstateValueError(
Packit Service 0535c1
                        "A trunk port needs to specify trunk tags"
Packit Service 0535c1
                    )
Packit Service 0535c1
            for trunk_tag in trunk_tags:
Packit Service 0535c1
                _assert_vlan_filtering_trunk_tag(trunk_tag)
Packit Service 0535c1
Packit Service 0535c1
    def _validate_vlan_filtering_tag(self):
Packit Service 0535c1
        """
Packit Service 0535c1
        The "tag" is valid in access mode or tunk mode with
Packit Service 0535c1
        "enable-native:True".
Packit Service 0535c1
        """
Packit Service 0535c1
        for port_config in self.original_dict.get(
Packit Service 0535c1
            LinuxBridge.CONFIG_SUBTREE, {}
Packit Service 0535c1
        ).get(LinuxBridge.PORT_SUBTREE, []):
Packit Service 0535c1
            vlan_config = _get_port_vlan_config(port_config)
Packit Service 0535c1
            if (
Packit Service 0535c1
                vlan_config.get(LinuxBridge.Port.Vlan.TAG)
Packit Service 0535c1
                and _vlan_is_trunk_mode(vlan_config)
Packit Service 0535c1
                and not _vlan_is_enable_native(vlan_config)
Packit Service 0535c1
            ):
Packit Service 0535c1
                raise NmstateValueError(
Packit Service 0535c1
                    "Tag cannot be use in trunk mode without enable-native"
Packit Service 0535c1
                )
Packit Service 0535c1
Packit Service 0535c1
    def _validate_vlan_filtering_enable_native(self):
Packit Service 0535c1
        for port_config in self.original_dict.get(
Packit Service 0535c1
            LinuxBridge.CONFIG_SUBTREE, {}
Packit Service 0535c1
        ).get(LinuxBridge.PORT_SUBTREE, []):
Packit Service 0535c1
            vlan_config = _get_port_vlan_config(port_config)
Packit Service 0535c1
            if _vlan_is_access_mode(vlan_config) and _vlan_is_enable_native(
Packit Service 0535c1
                vlan_config
Packit Service 0535c1
            ):
Packit Service 0535c1
                raise NmstateValueError(
Packit Service 0535c1
                    "enable-native cannot be set in access mode"
Packit Service 0535c1
                )
Packit Service 0535c1
Packit Service 0535c1
    def _fix_vlan_filtering_mode(self):
Packit Service 0535c1
        for port_config in self.port_configs:
Packit Service 0535c1
            _vlan_config_clean_up(_get_port_vlan_config(port_config))
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
            for port_config in self.port_configs:
Packit Service 0535c1
                ifaces[port_config[LinuxBridge.Port.NAME]].update(
Packit Service 0535c1
                    {BridgeIface.BRPORT_OPTIONS_METADATA: port_config}
Packit Service 0535c1
                )
Packit Service 0535c1
Packit Service 0535c1
    def remove_slave(self, slave_name):
Packit Service 0535c1
        if self._bridge_config:
Packit Service 0535c1
            self.raw[LinuxBridge.CONFIG_SUBTREE][LinuxBridge.PORT_SUBTREE] = [
Packit Service 0535c1
                port_config
Packit Service 0535c1
                for port_config in self.port_configs
Packit Service 0535c1
                if port_config[LinuxBridge.Port.NAME] != slave_name
Packit Service 0535c1
            ]
Packit Service 0535c1
        self.sort_slaves()
Packit Service 0535c1
Packit Service 0535c1
    @staticmethod
Packit Service 0535c1
    def is_integer_rounded(iface_state, current_iface_state):
Packit Service 0535c1
        for key, value in iface_state._options.items():
Packit Service 0535c1
            if key in INTEGER_ROUNDED_OPTIONS:
Packit Service 0535c1
                try:
Packit Service 0535c1
                    value = int(value)
Packit Service 0535c1
                    cur_value = int(current_iface_state._options.get(key))
Packit Service 0535c1
                except (TypeError, ValueError):
Packit Service 0535c1
                    continue
Packit Service 0535c1
                # With 250 HZ and 100 USER_HZ, every 8,000,000 will have 1
Packit Service 0535c1
                # deviation, caused by:
Packit Service 0535c1
                # * kernel set the value using clock_t_to_jiffies():
Packit Service 0535c1
                #       jiffies = int(clock * 100 / 250)
Packit Service 0535c1
                # * kernel showing the value using jiffies_to_clock_t():
Packit Service 0535c1
                #       clock =  int(int(jiffies * ( (10 ** 9 + 250/2) / 250)
Packit Service 0535c1
                #                    / 10 ** 9 * 100)
Packit Service 0535c1
                #
Packit Service 0535c1
                # The number 8,000,000 is found by exhaustion.
Packit Service 0535c1
                # There is no good way to detect kernel HZ in user space. Hence
Packit Service 0535c1
                # we check whether certain value is rounded.
Packit Service 0535c1
                if cur_value != value:
Packit Service 0535c1
                    if value >= 8 * (10 ** 6):
Packit Service 0535c1
                        if abs(value - cur_value) <= int(
Packit Service 0535c1
                            value / 8 * (10 ** 6)
Packit Service 0535c1
                        ):
Packit Service 0535c1
                            return key, value, cur_value
Packit Service 0535c1
                    else:
Packit Service 0535c1
                        if abs(value - cur_value) == 1:
Packit Service 0535c1
                            return key, value, cur_value
Packit Service 0535c1
Packit Service 0535c1
        return None, None, None
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _assert_vlan_filtering_trunk_tag(trunk_tag_state):
Packit Service 0535c1
    vlan_id = trunk_tag_state.get(LinuxBridge.Port.Vlan.TrunkTags.ID)
Packit Service 0535c1
    vlan_id_range = trunk_tag_state.get(
Packit Service 0535c1
        LinuxBridge.Port.Vlan.TrunkTags.ID_RANGE
Packit Service 0535c1
    )
Packit Service 0535c1
Packit Service 0535c1
    if vlan_id and vlan_id_range:
Packit Service 0535c1
        raise NmstateValueError(
Packit Service 0535c1
            "Trunk port cannot be configured by both id and range: {}".format(
Packit Service 0535c1
                trunk_tag_state
Packit Service 0535c1
            )
Packit Service 0535c1
        )
Packit Service 0535c1
    elif vlan_id_range:
Packit Service 0535c1
        if not (
Packit Service 0535c1
            {
Packit Service 0535c1
                LinuxBridge.Port.Vlan.TrunkTags.MIN_RANGE,
Packit Service 0535c1
                LinuxBridge.Port.Vlan.TrunkTags.MAX_RANGE,
Packit Service 0535c1
            }
Packit Service 0535c1
            <= set(vlan_id_range)
Packit Service 0535c1
        ):
Packit Service 0535c1
            raise NmstateValueError(
Packit Service 0535c1
                "Trunk port range requires min / max keys: {}".format(
Packit Service 0535c1
                    vlan_id_range
Packit Service 0535c1
                )
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _get_port_vlan_config(port_config):
Packit Service 0535c1
    return port_config.get(LinuxBridge.Port.VLAN_SUBTREE, {})
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
# TODO: Group them into class _LinuxBridgePort
Packit Service 0535c1
def _vlan_is_access_mode(vlan_config):
Packit Service 0535c1
    return (
Packit Service 0535c1
        vlan_config.get(LinuxBridge.Port.Vlan.MODE)
Packit Service 0535c1
        == LinuxBridge.Port.Vlan.Mode.ACCESS
Packit Service 0535c1
    )
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_is_trunk_mode(vlan_config):
Packit Service 0535c1
    return (
Packit Service 0535c1
        vlan_config.get(LinuxBridge.Port.Vlan.MODE)
Packit Service 0535c1
        == LinuxBridge.Port.Vlan.Mode.TRUNK
Packit Service 0535c1
    )
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_is_enable_native(vlan_config):
Packit Service 0535c1
    return vlan_config.get(LinuxBridge.Port.Vlan.ENABLE_NATIVE) is True
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_config_clean_up(vlan_config):
Packit Service 0535c1
    _vlan_remove_enable_native_if_access_mode(vlan_config)
Packit Service 0535c1
    _vlan_remove_tag_if_trunk_mode_without_enable_native(vlan_config)
Packit Service 0535c1
    _vlan_remove_trunk_tag_if_access_mode(vlan_config)
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_remove_enable_native_if_access_mode(vlan_config):
Packit Service 0535c1
    if _vlan_is_access_mode(vlan_config):
Packit Service 0535c1
        vlan_config.pop(LinuxBridge.Port.Vlan.ENABLE_NATIVE, None)
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_remove_tag_if_trunk_mode_without_enable_native(vlan_config):
Packit Service 0535c1
    if _vlan_is_trunk_mode(vlan_config) and not _vlan_is_enable_native(
Packit Service 0535c1
        vlan_config
Packit Service 0535c1
    ):
Packit Service 0535c1
        vlan_config.pop(LinuxBridge.Port.Vlan.TAG, None)
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _vlan_remove_trunk_tag_if_access_mode(vlan_config):
Packit Service 0535c1
    if _vlan_is_access_mode(vlan_config):
Packit Service 0535c1
        vlan_config.pop(LinuxBridge.Port.Vlan.TRUNK_TAGS, None)