Blame libnmstate/ifaces/bridge.py

Packit b9ca78
#
Packit b9ca78
# Copyright (c) 2020 Red Hat, Inc.
Packit b9ca78
#
Packit b9ca78
# This file is part of nmstate
Packit b9ca78
#
Packit b9ca78
# This program is free software: you can redistribute it and/or modify
Packit b9ca78
# it under the terms of the GNU Lesser General Public License as published by
Packit b9ca78
# the Free Software Foundation, either version 2.1 of the License, or
Packit b9ca78
# (at your option) any later version.
Packit b9ca78
#
Packit b9ca78
# This program is distributed in the hope that it will be useful,
Packit b9ca78
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b9ca78
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b9ca78
# GNU Lesser General Public License for more details.
Packit b9ca78
#
Packit b9ca78
# You should have received a copy of the GNU Lesser General Public License
Packit b9ca78
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Packit b9ca78
#
Packit b9ca78
Packit b9ca78
# This file will hold the common code shared by linux bridge and ovs bridge.
Packit b9ca78
Packit b9ca78
from operator import itemgetter
Packit b9ca78
Packit b9ca78
from libnmstate.schema import Bridge
Packit b9ca78
from libnmstate.schema import LinuxBridge
Packit b9ca78
Packit b9ca78
from ..state import merge_dict
Packit b9ca78
from .base_iface import BaseIface
Packit b9ca78
Packit b9ca78
READ_ONLY_OPTIONS = [
Packit b9ca78
    LinuxBridge.Options.HELLO_TIMER,
Packit b9ca78
    LinuxBridge.Options.GC_TIMER,
Packit b9ca78
]
Packit b9ca78
Packit b9ca78
Packit b9ca78
class BridgeIface(BaseIface):
Packit b9ca78
    BRPORT_OPTIONS_METADATA = "_brport_options"
Packit b9ca78
Packit b9ca78
    @property
Packit b9ca78
    def is_master(self):
Packit b9ca78
        return True
Packit b9ca78
Packit b9ca78
    @property
Packit b9ca78
    def is_virtual(self):
Packit b9ca78
        return True
Packit b9ca78
Packit b9ca78
    def sort_slaves(self):
Packit b9ca78
        if self.slaves:
Packit b9ca78
            self.raw[Bridge.CONFIG_SUBTREE][Bridge.PORT_SUBTREE].sort(
Packit b9ca78
                key=itemgetter(Bridge.Port.NAME)
Packit b9ca78
            )
Packit b9ca78
Packit b9ca78
    @property
Packit b9ca78
    def _bridge_config(self):
Packit b9ca78
        return self.raw.get(Bridge.CONFIG_SUBTREE, {})
Packit b9ca78
Packit b9ca78
    @property
Packit b9ca78
    def port_configs(self):
Packit b9ca78
        return self._bridge_config.get(Bridge.PORT_SUBTREE, [])
Packit b9ca78
Packit b9ca78
    def merge(self, other):
Packit b9ca78
        super().merge(other)
Packit b9ca78
        self._merge_bridge_ports(other)
Packit b9ca78
Packit b9ca78
    def _merge_bridge_ports(self, other):
Packit b9ca78
        """
Packit b9ca78
        Given a bridge desired state, and it's current state, merges
Packit b9ca78
        those together.
Packit b9ca78
Packit b9ca78
        This extension of the interface merging mechanism simplifies the user's
Packit b9ca78
        life in scenarios where the user wants to partially update the bridge's
Packit b9ca78
        configuration - e.g. update only the bridge's port STP configuration -
Packit b9ca78
        since it enables the user to simply specify the updated values rather
Packit b9ca78
        than the full current state + the updated value.
Packit b9ca78
        """
Packit b9ca78
        if self._bridge_config.get(Bridge.PORT_SUBTREE) == []:
Packit b9ca78
            # User explictly defined empty list for ports and expecting
Packit b9ca78
            # removal of all ports.
Packit b9ca78
            return
Packit b9ca78
Packit b9ca78
        other_indexed_ports = _index_port_configs(other.port_configs)
Packit b9ca78
        self_indexed_ports = _index_port_configs(self.port_configs)
Packit b9ca78
Packit b9ca78
        # When defined, user need to specify the whole list of ports.
Packit b9ca78
        for port_iface_name in (
Packit b9ca78
            other_indexed_ports.keys() & self_indexed_ports.keys()
Packit b9ca78
        ):
Packit b9ca78
            merge_dict(
Packit b9ca78
                self_indexed_ports[port_iface_name],
Packit b9ca78
                other_indexed_ports[port_iface_name],
Packit b9ca78
            )
Packit b9ca78
        self.raw[Bridge.CONFIG_SUBTREE][Bridge.PORT_SUBTREE] = list(
Packit b9ca78
            self_indexed_ports.values()
Packit b9ca78
        )
Packit b9ca78
Packit b9ca78
    def pre_edit_validation_and_cleanup(self):
Packit b9ca78
        self.sort_slaves()
Packit b9ca78
        super().pre_edit_validation_and_cleanup()
Packit b9ca78
Packit b9ca78
    def state_for_verify(self):
Packit b9ca78
        self._normalize_linux_bridge_port_vlan()
Packit b9ca78
        self._remove_read_only_bridge_options()
Packit b9ca78
        state = super().state_for_verify()
Packit b9ca78
        return state
Packit b9ca78
Packit b9ca78
    def config_changed_slaves(self, cur_iface):
Packit b9ca78
        changed_slaves = []
Packit b9ca78
        cur_indexed_ports = _index_port_configs(cur_iface.port_configs)
Packit b9ca78
        for port_config in self.port_configs:
Packit b9ca78
            port_name = port_config[Bridge.Port.NAME]
Packit b9ca78
            cur_port_config = cur_indexed_ports.get(port_name)
Packit b9ca78
            if cur_port_config != port_config:
Packit b9ca78
                changed_slaves.append(port_name)
Packit b9ca78
        return changed_slaves
Packit b9ca78
Packit b9ca78
    def _normalize_linux_bridge_port_vlan(self):
Packit b9ca78
        """
Packit b9ca78
        Set LinuxBridge.Port.VLAN_SUBTREE as {} when not defined.
Packit b9ca78
        """
Packit b9ca78
        for port_config in self.port_configs:
Packit b9ca78
            if not port_config.get(Bridge.Port.VLAN_SUBTREE):
Packit b9ca78
                port_config[Bridge.Port.VLAN_SUBTREE] = {}
Packit b9ca78
Packit b9ca78
    def _remove_read_only_bridge_options(self):
Packit b9ca78
        for key in READ_ONLY_OPTIONS:
Packit b9ca78
            self._bridge_config.get(LinuxBridge.OPTIONS_SUBTREE, {}).pop(
Packit b9ca78
                key, None
Packit b9ca78
            )
Packit b9ca78
Packit b9ca78
Packit b9ca78
def _index_port_configs(port_configs):
Packit b9ca78
    return {port[Bridge.Port.NAME]: port for port in port_configs}