Blame libnmstate/ifaces/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
# This file will hold the common code shared by linux bridge and ovs bridge.
Packit Service 0535c1
Packit Service 0535c1
from operator import itemgetter
Packit Service 0535c1
Packit Service 0535c1
from libnmstate.schema import Bridge
Packit Service 0535c1
from libnmstate.schema import LinuxBridge
Packit Service 0535c1
Packit Service 0535c1
from ..state import merge_dict
Packit Service 0535c1
from .base_iface import BaseIface
Packit Service 0535c1
Packit Service 0535c1
READ_ONLY_OPTIONS = [
Packit Service 0535c1
    LinuxBridge.Options.HELLO_TIMER,
Packit Service 0535c1
    LinuxBridge.Options.GC_TIMER,
Packit Service 0535c1
]
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
class BridgeIface(BaseIface):
Packit Service 0535c1
    BRPORT_OPTIONS_METADATA = "_brport_options"
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
    def sort_slaves(self):
Packit Service 0535c1
        if self.slaves:
Packit Service 0535c1
            self.raw[Bridge.CONFIG_SUBTREE][Bridge.PORT_SUBTREE].sort(
Packit Service 0535c1
                key=itemgetter(Bridge.Port.NAME)
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def _bridge_config(self):
Packit Service 0535c1
        return self.raw.get(Bridge.CONFIG_SUBTREE, {})
Packit Service 0535c1
Packit Service 0535c1
    @property
Packit Service 0535c1
    def port_configs(self):
Packit Service 0535c1
        return self._bridge_config.get(Bridge.PORT_SUBTREE, [])
Packit Service 0535c1
Packit Service 0535c1
    def merge(self, other):
Packit Service 0535c1
        super().merge(other)
Packit Service 0535c1
        self._merge_bridge_ports(other)
Packit Service 0535c1
Packit Service 0535c1
    def _merge_bridge_ports(self, other):
Packit Service 0535c1
        """
Packit Service 0535c1
        Given a bridge desired state, and it's current state, merges
Packit Service 0535c1
        those together.
Packit Service 0535c1
Packit Service 0535c1
        This extension of the interface merging mechanism simplifies the user's
Packit Service 0535c1
        life in scenarios where the user wants to partially update the bridge's
Packit Service 0535c1
        configuration - e.g. update only the bridge's port STP configuration -
Packit Service 0535c1
        since it enables the user to simply specify the updated values rather
Packit Service 0535c1
        than the full current state + the updated value.
Packit Service 0535c1
        """
Packit Service 0535c1
        if self._bridge_config.get(Bridge.PORT_SUBTREE) == []:
Packit Service 0535c1
            # User explictly defined empty list for ports and expecting
Packit Service 0535c1
            # removal of all ports.
Packit Service 0535c1
            return
Packit Service 0535c1
Packit Service 0535c1
        other_indexed_ports = _index_port_configs(other.port_configs)
Packit Service 0535c1
        self_indexed_ports = _index_port_configs(self.port_configs)
Packit Service 0535c1
Packit Service 0535c1
        # When defined, user need to specify the whole list of ports.
Packit Service 0535c1
        for port_iface_name in (
Packit Service 0535c1
            other_indexed_ports.keys() & self_indexed_ports.keys()
Packit Service 0535c1
        ):
Packit Service 0535c1
            merge_dict(
Packit Service 0535c1
                self_indexed_ports[port_iface_name],
Packit Service 0535c1
                other_indexed_ports[port_iface_name],
Packit Service 0535c1
            )
Packit Service 0535c1
        self.raw[Bridge.CONFIG_SUBTREE][Bridge.PORT_SUBTREE] = list(
Packit Service 0535c1
            self_indexed_ports.values()
Packit Service 0535c1
        )
Packit Service 0535c1
Packit Service 0535c1
    def pre_edit_validation_and_cleanup(self):
Packit Service 0535c1
        self.sort_slaves()
Packit Service 0535c1
        super().pre_edit_validation_and_cleanup()
Packit Service 0535c1
Packit Service 0535c1
    def state_for_verify(self):
Packit Service 0535c1
        self._normalize_linux_bridge_port_vlan()
Packit Service 0535c1
        self._remove_read_only_bridge_options()
Packit Service 0535c1
        state = super().state_for_verify()
Packit Service 0535c1
        return state
Packit Service 0535c1
Packit Service 0535c1
    def config_changed_slaves(self, cur_iface):
Packit Service 0535c1
        changed_slaves = []
Packit Service 0535c1
        cur_indexed_ports = _index_port_configs(cur_iface.port_configs)
Packit Service 0535c1
        for port_config in self.port_configs:
Packit Service 0535c1
            port_name = port_config[Bridge.Port.NAME]
Packit Service 0535c1
            cur_port_config = cur_indexed_ports.get(port_name)
Packit Service 0535c1
            if cur_port_config != port_config:
Packit Service 0535c1
                changed_slaves.append(port_name)
Packit Service 0535c1
        return changed_slaves
Packit Service 0535c1
Packit Service 0535c1
    def _normalize_linux_bridge_port_vlan(self):
Packit Service 0535c1
        """
Packit Service 0535c1
        Set LinuxBridge.Port.VLAN_SUBTREE as {} when not defined.
Packit Service 0535c1
        """
Packit Service 0535c1
        for port_config in self.port_configs:
Packit Service 0535c1
            if not port_config.get(Bridge.Port.VLAN_SUBTREE):
Packit Service 0535c1
                port_config[Bridge.Port.VLAN_SUBTREE] = {}
Packit Service 0535c1
Packit Service 0535c1
    def _remove_read_only_bridge_options(self):
Packit Service 0535c1
        for key in READ_ONLY_OPTIONS:
Packit Service 0535c1
            self._bridge_config.get(LinuxBridge.OPTIONS_SUBTREE, {}).pop(
Packit Service 0535c1
                key, None
Packit Service 0535c1
            )
Packit Service 0535c1
Packit Service 0535c1
Packit Service 0535c1
def _index_port_configs(port_configs):
Packit Service 0535c1
    return {port[Bridge.Port.NAME]: port for port in port_configs}