Blame SOURCES/BZ_2169642-Fix-sriov.patch

703e39
diff -Nur nmstate-1.3.3.old/libnmstate/dns.py nmstate-1.3.3/libnmstate/dns.py
703e39
--- nmstate-1.3.3.old/libnmstate/dns.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/dns.py	2023-02-15 15:48:33.668708842 +0800
703e39
@@ -144,6 +144,7 @@
703e39
         Find interface to store the DNS configurations in the order of:
703e39
             * Any interface with static gateway
703e39
             * Any interface configured as dynamic IP with 'auto-dns:False'
703e39
+        The loopback interface is ignored.
703e39
         Return tuple: (ipv4_iface, ipv6_iface)
703e39
         """
703e39
         ipv4_iface, ipv6_iface = self._find_ifaces_with_static_gateways(
703e39
@@ -168,6 +169,8 @@
703e39
         ipv4_iface = None
703e39
         ipv6_iface = None
703e39
         for iface_name, route_set in route_state.config_iface_routes.items():
703e39
+            if iface_name == "lo":
703e39
+                continue
703e39
             for route in route_set:
703e39
                 if ipv4_iface and ipv6_iface:
703e39
                     return (ipv4_iface, ipv6_iface)
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/base_iface.py nmstate-1.3.3/libnmstate/ifaces/base_iface.py
703e39
--- nmstate-1.3.3.old/libnmstate/ifaces/base_iface.py	2023-02-15 15:48:11.745846015 +0800
703e39
+++ nmstate-1.3.3/libnmstate/ifaces/base_iface.py	2023-02-15 15:48:45.118768209 +0800
703e39
@@ -61,6 +61,7 @@
703e39
                 InterfaceIP.AUTO_ROUTES,
703e39
                 InterfaceIP.AUTO_GATEWAY,
703e39
                 InterfaceIP.AUTO_DNS,
703e39
+                InterfaceIP.AUTO_ROUTE_METRIC,
703e39
             ):
703e39
                 self._info.pop(dhcp_option, None)
703e39
 
703e39
@@ -123,7 +124,6 @@
703e39
 
703e39
 
703e39
 class BaseIface:
703e39
-    CONTROLLER_METADATA = "_controller"
703e39
     CONTROLLER_TYPE_METADATA = "_controller_type"
703e39
     DNS_METADATA = "_dns"
703e39
     ROUTES_METADATA = "_routes"
703e39
@@ -253,7 +253,6 @@
703e39
                     self.ip_state(family).validate(
703e39
                         IPState(family, self._origin_info.get(family, {}))
703e39
                     )
703e39
-                self._validate_port_ip()
703e39
                 ip_state = self.ip_state(family)
703e39
                 ip_state.remove_link_local_address()
703e39
                 self._info[family] = ip_state.to_dict()
703e39
@@ -302,7 +301,7 @@
703e39
             if current_external_ids:
703e39
                 other._info[OvsDB.OVS_DB_SUBTREE].pop(OvsDB.EXTERNAL_IDS)
703e39
 
703e39
-    def _validate_port_ip(self):
703e39
+    def validate_port_ip(self):
703e39
         for family in (Interface.IPV4, Interface.IPV6):
703e39
             ip_state = IPState(family, self._origin_info.get(family, {}))
703e39
             if (
703e39
@@ -356,10 +355,11 @@
703e39
         return False
703e39
 
703e39
     def set_controller(self, controller_iface_name, controller_type):
703e39
-        self._info[BaseIface.CONTROLLER_METADATA] = controller_iface_name
703e39
+        self._info[Interface.CONTROLLER] = controller_iface_name
703e39
         self._info[BaseIface.CONTROLLER_TYPE_METADATA] = controller_type
703e39
         if (
703e39
-            not self.can_have_ip_as_port
703e39
+            controller_iface_name
703e39
+            and not self.can_have_ip_as_port
703e39
             and controller_type != InterfaceType.VRF
703e39
         ):
703e39
             for family in (Interface.IPV4, Interface.IPV6):
703e39
@@ -367,7 +367,7 @@
703e39
 
703e39
     @property
703e39
     def controller(self):
703e39
-        return self._info.get(BaseIface.CONTROLLER_METADATA)
703e39
+        return self._info.get(Interface.CONTROLLER)
703e39
 
703e39
     @property
703e39
     def controller_type(self):
703e39
@@ -378,7 +378,8 @@
703e39
             for port_name in self.port:
703e39
                 port_iface = ifaces.all_kernel_ifaces.get(port_name)
703e39
                 if port_iface:
703e39
-                    port_iface.set_controller(self.name, self.type)
703e39
+                    if port_iface.controller != "":
703e39
+                        port_iface.set_controller(self.name, self.type)
703e39
 
703e39
     def update(self, info):
703e39
         self._info.update(info)
703e39
@@ -444,6 +445,8 @@
703e39
             state[Interface.STATE] = InterfaceState.DOWN
703e39
         _convert_ovs_external_ids_values_to_string(state)
703e39
         state.pop(BaseIface.PERMANENT_MAC_ADDRESS_METADATA, None)
703e39
+        if self.controller == "":
703e39
+            state.pop(Interface.CONTROLLER, None)
703e39
 
703e39
         return state
703e39
 
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/ethtool.py nmstate-1.3.3/libnmstate/ifaces/ethtool.py
703e39
--- nmstate-1.3.3.old/libnmstate/ifaces/ethtool.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/ifaces/ethtool.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -176,6 +176,7 @@
703e39
         "rx-ntuple-filter",
703e39
         "rx-vlan-hw-parse",
703e39
         "tx-vlan-hw-insert",
703e39
+        "highdma",
703e39
     }
703e39
 
703e39
     def __init__(self, feature_info):
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/ifaces.py nmstate-1.3.3/libnmstate/ifaces/ifaces.py
703e39
--- nmstate-1.3.3.old/libnmstate/ifaces/ifaces.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/ifaces/ifaces.py	2023-02-15 15:48:45.118768209 +0800
703e39
@@ -1,22 +1,6 @@
703e39
-#
703e39
-# Copyright (c) 2020-2021 Red Hat, Inc.
703e39
-#
703e39
-# This file is part of nmstate
703e39
-#
703e39
-# This program is free software: you can redistribute it and/or modify
703e39
-# it under the terms of the GNU Lesser General Public License as published by
703e39
-# the Free Software Foundation, either version 2.1 of the License, or
703e39
-# (at your option) any later version.
703e39
-#
703e39
-# This program is distributed in the hope that it will be useful,
703e39
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
703e39
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
703e39
-# GNU Lesser General Public License for more details.
703e39
-#
703e39
-# You should have received a copy of the GNU Lesser General Public License
703e39
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
703e39
-#
703e39
+# SPDX-License-Identifier: LGPL-2.1-or-later
703e39
 
703e39
+from copy import deepcopy
703e39
 import logging
703e39
 
703e39
 from libnmstate.error import NmstateKernelIntegerRoundedError
703e39
@@ -24,6 +8,7 @@
703e39
 from libnmstate.error import NmstateVerificationError
703e39
 from libnmstate.prettystate import format_desired_current_state_diff
703e39
 from libnmstate.schema import BondMode
703e39
+from libnmstate.schema import Ethernet
703e39
 from libnmstate.schema import Interface
703e39
 from libnmstate.schema import InterfaceType
703e39
 from libnmstate.schema import InterfaceState
703e39
@@ -97,18 +82,20 @@
703e39
         self._cur_user_space_ifaces = _UserSpaceIfaces()
703e39
         if cur_iface_infos:
703e39
             for iface_info in cur_iface_infos:
703e39
-                cur_iface = _to_specific_iface_obj(iface_info, save_to_disk)
703e39
+                cur_iface = _to_specific_iface_obj(
703e39
+                    deepcopy(iface_info), save_to_disk
703e39
+                )
703e39
                 if cur_iface.is_user_space_only:
703e39
-                    self._user_space_ifaces.set(cur_iface)
703e39
+                    self._user_space_ifaces.set(deepcopy(cur_iface))
703e39
                     self._cur_user_space_ifaces.set(cur_iface)
703e39
                 else:
703e39
-                    self._kernel_ifaces[cur_iface.name] = cur_iface
703e39
+                    self._kernel_ifaces[cur_iface.name] = deepcopy(cur_iface)
703e39
                     self._cur_kernel_ifaces[cur_iface.name] = cur_iface
703e39
 
703e39
         if des_iface_infos:
703e39
             for iface_info in des_iface_infos:
703e39
                 iface = BaseIface(iface_info, save_to_disk)
703e39
-                if not iface.is_up and self._gen_conf_mode:
703e39
+                if not (iface.is_up or iface.is_down) and self._gen_conf_mode:
703e39
                     continue
703e39
                 if iface.type == InterfaceType.UNKNOWN:
703e39
                     cur_ifaces = self._get_cur_ifaces(iface.name)
703e39
@@ -157,6 +144,7 @@
703e39
             self._validate_infiniband_as_bridge_port()
703e39
             self._validate_infiniband_as_bond_port()
703e39
             self._apply_copy_mac_from()
703e39
+            self._validate_controller_and_port_list_conflict()
703e39
             self.gen_metadata()
703e39
             for iface in self.all_ifaces():
703e39
                 if iface.is_desired and iface.is_up:
703e39
@@ -164,6 +152,67 @@
703e39
 
703e39
             self._pre_edit_validation_and_cleanup()
703e39
 
703e39
+    # Return True when SR-IOV `total-vfs` changed and having interface not
703e39
+    # exists in current.
703e39
+    def has_vf_count_change_and_missing_eth(self):
703e39
+        return self._has_vf_count_change() and self._has_missing_veth()
703e39
+
703e39
+    def _has_vf_count_change(self):
703e39
+        for iface in self.all_kernel_ifaces.values():
703e39
+            cur_iface = self._cur_kernel_ifaces.get(iface.name)
703e39
+            if (
703e39
+                cur_iface
703e39
+                and iface.is_desired
703e39
+                and iface.is_up
703e39
+                and iface.type == InterfaceType.ETHERNET
703e39
+            ):
703e39
+                des_vf_count = (
703e39
+                    iface.original_desire_dict.get(Ethernet.CONFIG_SUBTREE, {})
703e39
+                    .get(Ethernet.SRIOV_SUBTREE, {})
703e39
+                    .get(Ethernet.SRIOV.TOTAL_VFS, 0)
703e39
+                )
703e39
+                cur_vf_count = (
703e39
+                    cur_iface.raw.get(Ethernet.CONFIG_SUBTREE, {})
703e39
+                    .get(Ethernet.SRIOV_SUBTREE, {})
703e39
+                    .get(Ethernet.SRIOV.TOTAL_VFS, 0)
703e39
+                )
703e39
+                if des_vf_count != cur_vf_count:
703e39
+                    return True
703e39
+        return False
703e39
+
703e39
+    def _has_missing_veth(self):
703e39
+        for iface in self.all_kernel_ifaces.values():
703e39
+            cur_iface = self._cur_kernel_ifaces.get(iface.name)
703e39
+            if cur_iface is None and iface.type == InterfaceType.ETHERNET:
703e39
+                return True
703e39
+        return False
703e39
+
703e39
+    # Return list of cloned iface_info(dictionary) which SRIOV PF conf only.
703e39
+    def get_sriov_pf_ifaces(self):
703e39
+        sriov_ifaces = []
703e39
+        for iface in self.all_kernel_ifaces.values():
703e39
+            if (
703e39
+                iface.is_desired
703e39
+                and iface.is_up
703e39
+                and iface.type == InterfaceType.ETHERNET
703e39
+            ):
703e39
+                sriov_conf = iface.original_desire_dict.get(
703e39
+                    Ethernet.CONFIG_SUBTREE, {}
703e39
+                ).get(Ethernet.SRIOV_SUBTREE, {})
703e39
+                if sriov_conf:
703e39
+                    eth_conf = iface.original_desire_dict.get(
703e39
+                        Ethernet.CONFIG_SUBTREE
703e39
+                    )
703e39
+                    sriov_ifaces.append(
703e39
+                        {
703e39
+                            Interface.NAME: iface.name,
703e39
+                            Interface.TYPE: InterfaceType.ETHERNET,
703e39
+                            Interface.STATE: InterfaceState.UP,
703e39
+                            Ethernet.CONFIG_SUBTREE: deepcopy(eth_conf),
703e39
+                        }
703e39
+                    )
703e39
+        return sriov_ifaces
703e39
+
703e39
     @property
703e39
     def _ignored_ifaces(self):
703e39
         return [iface for iface in self.all_ifaces() if iface.is_ignore]
703e39
@@ -275,6 +324,13 @@
703e39
         self._validate_ovs_patch_peers()
703e39
         self._remove_unknown_type_interfaces()
703e39
         self._validate_veth_peers()
703e39
+        self._resolve_controller_type()
703e39
+        self._validate_port_ip()
703e39
+
703e39
+    def _validate_port_ip(self):
703e39
+        for iface in self.all_ifaces():
703e39
+            if iface.is_desired and iface.is_up:
703e39
+                iface.validate_port_ip()
703e39
 
703e39
     def _bring_port_up_if_not_in_desire(self):
703e39
         """
703e39
@@ -400,6 +456,72 @@
703e39
                             f"{iface.name} is in {iface.bond_mode} mode."
703e39
                         )
703e39
 
703e39
+    def _validate_controller_and_port_list_conflict(self):
703e39
+        """
703e39
+        Validate Check whether user defined both controller property and port
703e39
+        list of controller interface, examples of invalid desire state:
703e39
+            * eth1 has controller: br1, but br1 has no eth1 in port list
703e39
+            * eth2 has controller: br1, but br2 has eth2 in port list
703e39
+            * eth1 has controller: Some("") (detach), but br1 has eth1 in port
703e39
+              list
703e39
+        """
703e39
+        self._validate_controller_not_in_port_list()
703e39
+        self._validate_controller_in_other_port_list()
703e39
+
703e39
+    def _validate_controller_not_in_port_list(self):
703e39
+        for iface_name, iface in self._kernel_ifaces.items():
703e39
+            if (
703e39
+                not iface.is_up
703e39
+                or not iface.controller
703e39
+                or Interface.CONTROLLER not in iface.original_desire_dict
703e39
+            ):
703e39
+                continue
703e39
+            ctrl_iface = self._user_space_ifaces.get(
703e39
+                iface.controller, InterfaceType.OVS_BRIDGE
703e39
+            )
703e39
+            if not ctrl_iface:
703e39
+                ctrl_iface = self._kernel_ifaces.get(iface.controller)
703e39
+            if ctrl_iface:
703e39
+                if not ctrl_iface.is_desired:
703e39
+                    continue
703e39
+                if ctrl_iface.port and iface_name not in ctrl_iface.port:
703e39
+                    raise NmstateValueError(
703e39
+                        f"Interface {iface_name} desired controller "
703e39
+                        f"is {iface.controller}, but not listed in port "
703e39
+                        "list of controller interface"
703e39
+                    )
703e39
+
703e39
+    def _validate_controller_in_other_port_list(self):
703e39
+        port_to_ctrl = {}
703e39
+        for iface in self.all_ifaces():
703e39
+            if iface.is_controller and iface.is_desired and iface.is_up:
703e39
+                for port in iface.port:
703e39
+                    port_to_ctrl[port] = iface.name
703e39
+
703e39
+        for iface in self._kernel_ifaces.values():
703e39
+            if (
703e39
+                not iface.is_desired
703e39
+                or not iface.is_up
703e39
+                or iface.controller is None
703e39
+                or iface.name not in port_to_ctrl
703e39
+                or Interface.CONTROLLER not in iface.original_desire_dict
703e39
+            ):
703e39
+                continue
703e39
+            ctrl_name = port_to_ctrl.get(iface.name)
703e39
+            if ctrl_name != iface.controller:
703e39
+                if iface.controller:
703e39
+                    raise NmstateValueError(
703e39
+                        f"Interface {iface.name} has controller property set "
703e39
+                        f"to {iface.controller}, but been listed as "
703e39
+                        f"port of controller {ctrl_name} "
703e39
+                    )
703e39
+                else:
703e39
+                    raise NmstateValueError(
703e39
+                        f"Interface {iface.name} desired to detach controller "
703e39
+                        "via controller property set to '', but "
703e39
+                        f"still been listed as port of controller {ctrl_name}"
703e39
+                    )
703e39
+
703e39
     def _handle_controller_port_list_change(self):
703e39
         """
703e39
         * Mark port interface as changed if controller removed.
703e39
@@ -419,6 +541,10 @@
703e39
                 changed_port = (des_port | cur_port) - (des_port & cur_port)
703e39
                 for iface_name in changed_port:
703e39
                     self._kernel_ifaces[iface_name].mark_as_changed()
703e39
+                    if iface_name not in des_port:
703e39
+                        self._kernel_ifaces[iface_name].set_controller(
703e39
+                            None, None
703e39
+                        )
703e39
             if cur_iface:
703e39
                 for port_name in iface.config_changed_port(cur_iface):
703e39
                     if port_name in self._kernel_ifaces:
703e39
@@ -823,6 +949,24 @@
703e39
                     if port_name in ignored_kernel_iface_names:
703e39
                         iface.remove_port(port_name)
703e39
 
703e39
+    def _resolve_controller_type(self):
703e39
+        for iface in self._kernel_ifaces.values():
703e39
+            if (
703e39
+                iface.is_up
703e39
+                and iface.is_desired
703e39
+                and Interface.CONTROLLER in iface.original_desire_dict
703e39
+                and iface.controller
703e39
+                and iface.controller_type is None
703e39
+            ):
703e39
+                ctrl_iface = self._cur_user_space_ifaces.get(
703e39
+                    iface.controller, InterfaceType.OVS_BRIDGE
703e39
+                )
703e39
+                if ctrl_iface is None:
703e39
+                    ctrl_iface = self._cur_kernel_ifaces.get(iface.controller)
703e39
+
703e39
+                if ctrl_iface:
703e39
+                    iface.set_controller(iface.controller, ctrl_iface.type)
703e39
+
703e39
 
703e39
 def _to_specific_iface_obj(info, save_to_disk):
703e39
     iface_type = info.get(Interface.TYPE, InterfaceType.UNKNOWN)
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/ifaces/linux_bridge.py nmstate-1.3.3/libnmstate/ifaces/linux_bridge.py
703e39
--- nmstate-1.3.3.old/libnmstate/ifaces/linux_bridge.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/ifaces/linux_bridge.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -192,9 +192,9 @@
703e39
                 # There is no good way to detect kernel HZ in user space. Hence
703e39
                 # we check whether certain value is rounded.
703e39
                 if cur_value != value:
703e39
-                    if value >= 8 * (10 ** 6):
703e39
+                    if value >= 8 * (10**6):
703e39
                         if abs(value - cur_value) <= int(
703e39
-                            value / 8 * (10 ** 6)
703e39
+                            value / 8 * (10**6)
703e39
                         ):
703e39
                             return key, value, cur_value
703e39
                     else:
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/netapplier.py nmstate-1.3.3/libnmstate/netapplier.py
703e39
--- nmstate-1.3.3.old/libnmstate/netapplier.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/netapplier.py	2023-02-15 15:48:45.118768209 +0800
703e39
@@ -24,7 +24,7 @@
703e39
 
703e39
 from libnmstate import validator
703e39
 from libnmstate.error import NmstateVerificationError
703e39
-from libnmstate.schema import InterfaceType
703e39
+from libnmstate.schema import Interface
703e39
 
703e39
 from .net_state import NetState
703e39
 from .nmstate import create_checkpoints
703e39
@@ -73,20 +73,56 @@
703e39
 
703e39
     desired_state = copy.deepcopy(desired_state)
703e39
     remove_the_reserved_secrets(desired_state)
703e39
+
703e39
     with plugin_context() as plugins:
703e39
         validator.schema_validate(desired_state)
703e39
         current_state = show_with_plugins(
703e39
             plugins, include_status_data=True, include_secrets=True
703e39
         )
703e39
         validator.validate_capabilities(
703e39
-            desired_state, plugins_capabilities(plugins)
703e39
+            copy.deepcopy(desired_state), plugins_capabilities(plugins)
703e39
         )
703e39
         ignored_ifnames = _get_ignored_interface_names(plugins)
703e39
         net_state = NetState(
703e39
             desired_state, ignored_ifnames, current_state, save_to_disk
703e39
         )
703e39
         checkpoints = create_checkpoints(plugins, rollback_timeout)
703e39
-        _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk)
703e39
+        # When we have VF count changes and missing eth, it might be user
703e39
+        # referring future VF in the same desire state, we just apply
703e39
+        # VF changes state only first.
703e39
+        if net_state.ifaces.has_vf_count_change_and_missing_eth():
703e39
+            sriov_ifaces = net_state.ifaces.get_sriov_pf_ifaces()
703e39
+            if sriov_ifaces:
703e39
+                pf_net_state = NetState(
703e39
+                    {Interface.KEY: sriov_ifaces},
703e39
+                    ignored_ifnames,
703e39
+                    current_state,
703e39
+                    save_to_disk,
703e39
+                )
703e39
+                _apply_ifaces_state(
703e39
+                    plugins,
703e39
+                    pf_net_state,
703e39
+                    verify_change,
703e39
+                    save_to_disk,
703e39
+                    has_sriov_pf=True,
703e39
+                )
703e39
+                # Refresh the current state
703e39
+                current_state = show_with_plugins(
703e39
+                    plugins, include_status_data=True, include_secrets=True
703e39
+                )
703e39
+                validator.validate_capabilities(
703e39
+                    desired_state, plugins_capabilities(plugins)
703e39
+                )
703e39
+                ignored_ifnames = _get_ignored_interface_names(plugins)
703e39
+                net_state = NetState(
703e39
+                    copy.deepcopy(desired_state),
703e39
+                    ignored_ifnames,
703e39
+                    current_state,
703e39
+                    save_to_disk,
703e39
+                )
703e39
+        _apply_ifaces_state(
703e39
+            plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False
703e39
+        )
703e39
         if commit:
703e39
             destroy_checkpoints(plugins, checkpoints)
703e39
         else:
703e39
@@ -117,13 +153,17 @@
703e39
         rollback_checkpoints(plugins, checkpoint)
703e39
 
703e39
 
703e39
-def _apply_ifaces_state(plugins, net_state, verify_change, save_to_disk):
703e39
+def _apply_ifaces_state(
703e39
+    plugins, net_state, verify_change, save_to_disk, has_sriov_pf=False
703e39
+):
703e39
     for plugin in plugins:
703e39
-        plugin.apply_changes(net_state, save_to_disk)
703e39
+        # Do not allow plugin to modify the net_state for future verification
703e39
+        tmp_net_state = copy.deepcopy(net_state)
703e39
+        plugin.apply_changes(tmp_net_state, save_to_disk)
703e39
 
703e39
     verified = False
703e39
     if verify_change:
703e39
-        if _net_state_contains_sriov_interface(net_state):
703e39
+        if has_sriov_pf:
703e39
             # If SR-IOV is present, the verification timeout is being increased
703e39
             # to avoid timeouts due to slow drivers like i40e.
703e39
             verify_retry = VERIFY_RETRY_COUNT_SRIOV
703e39
@@ -140,14 +180,6 @@
703e39
             _verify_change(plugins, net_state)
703e39
 
703e39
 
703e39
-def _net_state_contains_sriov_interface(net_state):
703e39
-    for iface in net_state.ifaces.all_kernel_ifaces.values():
703e39
-        if iface.type == InterfaceType.ETHERNET and iface.is_sriov:
703e39
-            return True
703e39
-
703e39
-    return False
703e39
-
703e39
-
703e39
 def _verify_change(plugins, net_state):
703e39
     current_state = remove_metadata_leftover(
703e39
         show_with_plugins(plugins, include_secrets=True)
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/netinfo.py nmstate-1.3.3/libnmstate/netinfo.py
703e39
--- nmstate-1.3.3.old/libnmstate/netinfo.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/netinfo.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -39,6 +39,7 @@
703e39
                 plugins,
703e39
                 include_status_data=include_status_data,
703e39
                 include_secrets=include_secrets,
703e39
+                include_controller_prop=False,
703e39
             )
703e39
         )
703e39
 
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nispor/base_iface.py nmstate-1.3.3/libnmstate/nispor/base_iface.py
703e39
--- nmstate-1.3.3.old/libnmstate/nispor/base_iface.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nispor/base_iface.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -115,6 +115,8 @@
703e39
             if ethtool_info_dict:
703e39
                 iface_info[Ethtool.CONFIG_SUBTREE] = ethtool_info_dict
703e39
 
703e39
+        if self.np_iface.controller:
703e39
+            iface_info[Interface.CONTROLLER] = self.np_iface.controller
703e39
         return iface_info
703e39
 
703e39
 
703e39
@@ -219,6 +221,7 @@
703e39
         "rx-ntuple-filter",
703e39
         "rx-vlan-hw-parse",
703e39
         "tx-vlan-hw-insert",
703e39
+        "highdma",
703e39
     ]
703e39
 
703e39
     def __init__(self, np_ethtool):
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/connection.py nmstate-1.3.3/libnmstate/nm/connection.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/connection.py	2023-02-15 15:48:11.739845988 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/connection.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -94,7 +94,7 @@
703e39
         self._setting = new
703e39
 
703e39
     def set_controller(self, controller, port_type):
703e39
-        if controller is not None:
703e39
+        if controller:
703e39
             self._setting.props.master = controller
703e39
             self._setting.props.slave_type = port_type
703e39
 
703e39
@@ -186,7 +186,7 @@
703e39
         settings.extend(create_ovs_interface_setting(patch_state, dpdk_state))
703e39
     elif iface.type == InterfaceType.INFINIBAND:
703e39
         ib_setting = create_infiniband_setting(
703e39
-            iface_info,
703e39
+            iface,
703e39
             nm_profile,
703e39
             iface.original_desire_dict,
703e39
         )
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/device.py nmstate-1.3.3/libnmstate/nm/device.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/device.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/device.py	2023-02-15 15:48:45.122101559 +0800
703e39
@@ -106,39 +106,41 @@
703e39
         self._iface_name = iface_name
703e39
         self._iface_type = iface_type
703e39
         self._nm_dev = nm_dev
703e39
+        self._action = None
703e39
 
703e39
     def run(self):
703e39
-        action = f"Delete device: {self._iface_type} {self._iface_name}"
703e39
-        user_data = action
703e39
-        self._ctx.register_async(action)
703e39
+        self._action = f"Delete device: {self._iface_type} {self._iface_name}"
703e39
+        retried = False
703e39
+        self._ctx.register_async(self._action)
703e39
         self._nm_dev.delete_async(
703e39
-            self._ctx.cancellable, self._delete_device_callback, user_data
703e39
+            self._ctx.cancellable, self._delete_device_callback, retried
703e39
         )
703e39
 
703e39
-    def _delete_device_callback(self, nm_dev, result, user_data):
703e39
-        action = user_data
703e39
+    def _delete_device_callback(self, nm_dev, result, retried):
703e39
         if self._ctx.is_cancelled():
703e39
             return
703e39
-        error = None
703e39
         try:
703e39
             nm_dev.delete_finish(result)
703e39
+            self._ctx.finish_async(self._action)
703e39
         except Exception as e:
703e39
-            error = e
703e39
-
703e39
-        if not nm_dev.is_real():
703e39
-            logging.debug(
703e39
-                f"Interface is deleted and not real/exist anymore: "
703e39
-                f"iface={self._iface_name} type={self._iface_type}"
703e39
-            )
703e39
-            if error:
703e39
-                logging.debug(f"Ignored error: {error}")
703e39
-            self._ctx.finish_async(action)
703e39
-        else:
703e39
-            self._ctx.fail(
703e39
-                NmstateLibnmError(
703e39
-                    f"{action} failed: error={error or 'unknown'}"
703e39
+            if not nm_dev.is_real():
703e39
+                logging.debug(
703e39
+                    f"Interface is deleted and not real/exist anymore: "
703e39
+                    f"iface={self._iface_name} type={self._iface_type}"
703e39
+                )
703e39
+                logging.debug(f"Ignored error: {e}")
703e39
+                self._ctx.finish_async(self._action)
703e39
+            elif retried:
703e39
+                self._ctx.fail(
703e39
+                    NmstateLibnmError(f"{self._action} failed: error={e}")
703e39
+                )
703e39
+            else:
703e39
+                retried = True
703e39
+                self._nm_dev.delete_async(
703e39
+                    self._ctx.cancellable,
703e39
+                    self._delete_device_callback,
703e39
+                    retried,
703e39
                 )
703e39
-            )
703e39
 
703e39
 
703e39
 def list_devices(client):
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/infiniband.py nmstate-1.3.3/libnmstate/nm/infiniband.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/infiniband.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/infiniband.py	2023-02-14 18:48:57.083997920 +0800
703e39
@@ -71,7 +71,9 @@
703e39
         return None
703e39
 
703e39
 
703e39
-def create_setting(iface_info, base_con_profile, original_iface_info):
703e39
+def create_setting(iface, base_con_profile, original_iface_info):
703e39
+    iface.pre_edit_validation_and_cleanup()
703e39
+    iface_info = iface.to_dict()
703e39
     ib_config = iface_info.get(InfiniBand.CONFIG_SUBTREE)
703e39
     if not ib_config:
703e39
         return None
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/ipv4.py nmstate-1.3.3/libnmstate/nm/ipv4.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/ipv4.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/ipv4.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -27,7 +27,7 @@
703e39
 from ..ifaces import BaseIface
703e39
 from .common import NM
703e39
 
703e39
-INT32_MAX = 2 ** 31 - 1
703e39
+INT32_MAX = 2**31 - 1
703e39
 
703e39
 
703e39
 def create_setting(config, base_con_profile):
703e39
@@ -77,6 +77,9 @@
703e39
             # when the DHCP timeout expired, set it to the maximum value to
703e39
             # make this unlikely.
703e39
             setting_ipv4.props.dhcp_timeout = INT32_MAX
703e39
+            route_metric = config.get(InterfaceIPv4.AUTO_ROUTE_METRIC)
703e39
+            if route_metric is not None:
703e39
+                setting_ipv4.props.route_metric = route_metric
703e39
         elif config.get(InterfaceIPv4.ADDRESS):
703e39
             setting_ipv4.props.method = NM.SETTING_IP4_CONFIG_METHOD_MANUAL
703e39
             _add_addresses(setting_ipv4, config[InterfaceIPv4.ADDRESS])
703e39
@@ -129,6 +132,8 @@
703e39
                 info[InterfaceIPv4.AUTO_GATEWAY] = not props.never_default
703e39
                 info[InterfaceIPv4.AUTO_DNS] = not props.ignore_auto_dns
703e39
                 info[InterfaceIPv4.AUTO_ROUTE_TABLE_ID] = props.route_table
703e39
+                if props.route_metric >= 0:
703e39
+                    info[InterfaceIPv4.AUTO_ROUTE_METRIC] = props.route_metric
703e39
                 if props.dhcp_client_id:
703e39
                     info[InterfaceIPv4.DHCP_CLIENT_ID] = props.dhcp_client_id
703e39
 
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/ipv6.py nmstate-1.3.3/libnmstate/nm/ipv6.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/ipv6.py	2023-02-15 15:48:11.737845979 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/ipv6.py	2023-02-15 15:50:14.713397220 +0800
703e39
@@ -31,7 +31,7 @@
703e39
 from .common import NM
703e39
 
703e39
 IPV6_DEFAULT_ROUTE_METRIC = 1024
703e39
-INT32_MAX = 2 ** 31 - 1
703e39
+INT32_MAX = 2**31 - 1
703e39
 
703e39
 
703e39
 def get_info(active_connection, applied_config):
703e39
@@ -73,6 +73,8 @@
703e39
             info[InterfaceIPv6.AUTO_ROUTE_TABLE_ID] = props.route_table
703e39
             if props.dhcp_duid:
703e39
                 info[InterfaceIPv6.DHCP_DUID] = props.dhcp_duid
703e39
+            if props.route_metric > 0:
703e39
+                info[InterfaceIPv6.AUTO_ROUTE_METRIC] = props.route_metric
703e39
             info[InterfaceIPv6.ADDR_GEN_MODE] = (
703e39
                 InterfaceIPv6.ADDR_GEN_MODE_STABLE_PRIVACY
703e39
                 if props.addr_gen_mode
703e39
@@ -146,6 +148,10 @@
703e39
         if route_table:
703e39
             setting_ip.props.route_table = route_table
703e39
 
703e39
+        route_metric = config.get(InterfaceIPv6.AUTO_ROUTE_METRIC)
703e39
+        if route_metric is not None:
703e39
+            setting_ip.props.route_metric = route_metric
703e39
+
703e39
     elif ip_addresses:
703e39
         _set_static(setting_ip, ip_addresses)
703e39
     else:
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/ovs.py nmstate-1.3.3/libnmstate/nm/ovs.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/ovs.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/ovs.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -22,6 +22,7 @@
703e39
 
703e39
 from libnmstate.ifaces import ovs
703e39
 from libnmstate.ifaces.bridge import BridgeIface
703e39
+from libnmstate.ifaces.ovs import OvsBridgeIface
703e39
 from libnmstate.ifaces.ovs import OvsPortIface
703e39
 from libnmstate.schema import Interface
703e39
 from libnmstate.schema import InterfaceType
703e39
@@ -33,7 +34,6 @@
703e39
 
703e39
 
703e39
 CONTROLLER_TYPE_METADATA = "_controller_type"
703e39
-CONTROLLER_METADATA = "_controller"
703e39
 SETTING_OVS_EXTERNALIDS = "SettingOvsExternalIDs"
703e39
 SETTING_OVS_EXTERNAL_IDS_SETTING_NAME = "ovs-external-ids"
703e39
 
703e39
@@ -338,17 +338,24 @@
703e39
     iface_name = iface.name
703e39
     iface_info = iface.to_dict()
703e39
     port_options = iface_info.get(BridgeIface.BRPORT_OPTIONS_METADATA)
703e39
-    if ovs.is_ovs_lag_port(port_options):
703e39
-        port_name = port_options[OB.Port.NAME]
703e39
+    if port_options:
703e39
+        if ovs.is_ovs_lag_port(port_options):
703e39
+            port_name = port_options[OB.Port.NAME]
703e39
+        else:
703e39
+            port_name = iface_name
703e39
     else:
703e39
+        # User is attaching system port to OVS bridge via `controller` property
703e39
+        # with OVS bridge not mentioned in desired state
703e39
         port_name = iface_name
703e39
+        port_options = {}
703e39
+
703e39
     return OvsPortIface(
703e39
         {
703e39
             Interface.NAME: port_name,
703e39
             Interface.TYPE: InterfaceType.OVS_PORT,
703e39
             Interface.STATE: iface.state,
703e39
             OB.OPTIONS_SUBTREE: port_options,
703e39
-            CONTROLLER_METADATA: iface_info[CONTROLLER_METADATA],
703e39
+            Interface.CONTROLLER: iface_info[Interface.CONTROLLER],
703e39
             CONTROLLER_TYPE_METADATA: iface_info[CONTROLLER_TYPE_METADATA],
703e39
         }
703e39
     )
703e39
@@ -356,3 +363,17 @@
703e39
 
703e39
 def _is_nm_support_ovs_external_ids():
703e39
     return hasattr(NM, SETTING_OVS_EXTERNALIDS)
703e39
+
703e39
+
703e39
+def set_ovs_iface_controller_info(iface_infos):
703e39
+    pending_changes = {}
703e39
+    for iface_info in iface_infos:
703e39
+        if iface_info.get(Interface.TYPE) == InterfaceType.OVS_BRIDGE:
703e39
+            iface = OvsBridgeIface(info=iface_info, save_to_disk=True)
703e39
+            for port in iface.port:
703e39
+                pending_changes[port] = iface.name
703e39
+
703e39
+    for iface_info in iface_infos:
703e39
+        ctrl_name = pending_changes.get(iface_info[Interface.NAME])
703e39
+        if ctrl_name:
703e39
+            iface_info[Interface.CONTROLLER] = ctrl_name
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/plugin.py nmstate-1.3.3/libnmstate/nm/plugin.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/plugin.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/plugin.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -49,6 +49,7 @@
703e39
 from .ovs import get_ovs_bridge_info
703e39
 from .ovs import get_ovsdb_external_ids
703e39
 from .ovs import has_ovs_capability
703e39
+from .ovs import set_ovs_iface_controller_info
703e39
 from .profiles import NmProfiles
703e39
 from .profiles import get_all_applied_configs
703e39
 from .team import get_info as get_team_info
703e39
@@ -192,6 +193,8 @@
703e39
 
703e39
             info.append(iface_info)
703e39
 
703e39
+        set_ovs_iface_controller_info(info)
703e39
+
703e39
         info.sort(key=itemgetter("name"))
703e39
 
703e39
         return info
703e39
@@ -293,7 +296,7 @@
703e39
     def generate_configurations(self, net_state):
703e39
         if not hasattr(NM, "keyfile_write"):
703e39
             raise NmstateNotSupportedError(
703e39
-                f"Current NetworkManager version does not support generating "
703e39
+                "Current NetworkManager version does not support generating "
703e39
                 "configurations, please upgrade to 1.30 or later versoin."
703e39
             )
703e39
         return NmProfiles(None).generate_config_strings(net_state)
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/profile.py nmstate-1.3.3/libnmstate/nm/profile.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/profile.py	2023-02-15 15:48:11.748846029 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/profile.py	2023-02-15 15:48:45.122101559 +0800
703e39
@@ -139,6 +139,11 @@
703e39
         else:
703e39
             return ""
703e39
 
703e39
+    def disable_autoconnect(self):
703e39
+        if self._nm_simple_conn:
703e39
+            nm_conn_setting = self._nm_simple_conn.get_setting_connection()
703e39
+            nm_conn_setting.props.autoconnect = False
703e39
+
703e39
     def update_controller(self, controller):
703e39
         nm_simple_conn_update_controller(self._nm_simple_conn, controller)
703e39
 
703e39
@@ -186,8 +191,10 @@
703e39
         elif self._iface.is_down:
703e39
             if self._nm_ac:
703e39
                 self._add_action(NmProfile.ACTION_DEACTIVATE)
703e39
-            elif self._iface.is_virtual and self._nm_dev:
703e39
+            if self._iface.is_virtual and self._nm_dev:
703e39
                 self._add_action(NmProfile.ACTION_DELETE_DEVICE)
703e39
+            if self._nm_dev and not self._nm_dev.get_managed():
703e39
+                self._add_action(NmProfile.ACTION_DEACTIVATE)
703e39
 
703e39
         if self._iface.raw.get(ROUTE_REMOVED):
703e39
             # This is a workaround for NM bug:
703e39
@@ -276,7 +283,12 @@
703e39
         )
703e39
 
703e39
     def prepare_config(self, save_to_disk, gen_conf_mode=False):
703e39
-        if self._iface.is_absent or self._iface.is_down:
703e39
+        if self._iface.is_absent or (
703e39
+            self._iface.is_down
703e39
+            and not gen_conf_mode
703e39
+            and self._nm_dev
703e39
+            and self._nm_dev.get_managed()
703e39
+        ):
703e39
             return
703e39
 
703e39
         # Don't create new profile if original desire does not ask
703e39
@@ -312,7 +324,9 @@
703e39
         self._gen_actions()
703e39
         if not self.has_pending_change:
703e39
             return
703e39
-        if self._iface.is_absent or self._iface.is_down:
703e39
+        if self._iface.is_absent or (
703e39
+            self._iface.is_down and self._nm_dev and self._nm_dev.get_managed()
703e39
+        ):
703e39
             return
703e39
         # Don't create new profile if original desire does not ask
703e39
         # anything besides state:up and not been marked as changed.
703e39
@@ -411,6 +425,9 @@
703e39
     def _deactivate(self):
703e39
         if self._deactivated:
703e39
             return
703e39
+        self._nm_ac = (
703e39
+            self._nm_dev.get_active_connection() if self._nm_dev else None
703e39
+        )
703e39
         if self._nm_ac:
703e39
             ActiveConnectionDeactivate(
703e39
                 self._ctx, self._iface.name, self._iface.type, self._nm_ac
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nm/profiles.py nmstate-1.3.3/libnmstate/nm/profiles.py
703e39
--- nmstate-1.3.3.old/libnmstate/nm/profiles.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nm/profiles.py	2023-02-15 15:48:45.122101559 +0800
703e39
@@ -26,6 +26,7 @@
703e39
 from libnmstate.schema import Interface
703e39
 from libnmstate.schema import InterfaceState
703e39
 from libnmstate.schema import InterfaceType
703e39
+from libnmstate.schema import OvsDB
703e39
 
703e39
 from .common import NM
703e39
 from .device import is_externally_managed
703e39
@@ -51,9 +52,11 @@
703e39
         _append_nm_ovs_port_iface(net_state)
703e39
         all_profiles = []
703e39
         for iface in net_state.ifaces.all_ifaces():
703e39
-            if iface.is_up:
703e39
+            if iface.is_up or iface.is_down:
703e39
                 profile = NmProfile(self._ctx, iface)
703e39
                 profile.prepare_config(save_to_disk=False, gen_conf_mode=True)
703e39
+                if iface.is_down:
703e39
+                    profile.disable_autoconnect()
703e39
                 all_profiles.append(profile)
703e39
 
703e39
         _use_uuid_as_controller_and_parent(all_profiles)
703e39
@@ -123,16 +126,18 @@
703e39
     subordinate of NM OVS port profile which is port of the OVS bridge
703e39
     profile.
703e39
     We need to create/delete this NM OVS port profile accordingly.
703e39
+    We skip this action if ovs interface is not changed.
703e39
     """
703e39
     nm_ovs_port_ifaces = {}
703e39
 
703e39
     for iface in net_state.ifaces.all_kernel_ifaces.values():
703e39
         if iface.controller_type == InterfaceType.OVS_BRIDGE:
703e39
+            has_ovs_change = _has_ovs_changes(iface, net_state)
703e39
             nm_ovs_port_iface = create_iface_for_nm_ovs_port(iface)
703e39
             iface.set_controller(
703e39
                 nm_ovs_port_iface.name, InterfaceType.OVS_PORT
703e39
             )
703e39
-            if iface.is_desired or iface.is_changed:
703e39
+            if (iface.is_desired or iface.is_changed) and has_ovs_change:
703e39
                 nm_ovs_port_iface.mark_as_changed()
703e39
             nm_ovs_port_ifaces[nm_ovs_port_iface.name] = nm_ovs_port_iface
703e39
 
703e39
@@ -390,6 +395,10 @@
703e39
         iface = nm_profile.iface
703e39
         if not iface.is_up:
703e39
             continue
703e39
+        # InfiniBand setting does not support UUID as parent
703e39
+        if iface.type == InterfaceType.INFINIBAND:
703e39
+            continue
703e39
+
703e39
         if (
703e39
             iface.controller
703e39
             and (iface.is_changed or iface.is_desired)
703e39
@@ -434,3 +443,28 @@
703e39
             ):
703e39
                 return True
703e39
     return False
703e39
+
703e39
+
703e39
+def _has_ovs_changes(iface, net_state):
703e39
+    """
703e39
+    Return False only when below all matches:
703e39
+    * Desired interface is up
703e39
+    * Desire state did not mentioned its OVS bridge controller
703e39
+    * Interface has no changed to controller property
703e39
+    * Interface has no ovs-db setting change in desire state
703e39
+    """
703e39
+    ctrl_iface = net_state.ifaces.get_iface(
703e39
+        iface.controller, InterfaceType.OVS_BRIDGE
703e39
+    )
703e39
+    if (
703e39
+        iface.is_desired
703e39
+        and iface.is_up
703e39
+        and ctrl_iface
703e39
+        and not ctrl_iface.is_desired
703e39
+        and not ctrl_iface.is_changed
703e39
+        and Interface.CONTROLLER not in iface.original_desire_dict
703e39
+        and OvsDB.KEY not in iface.original_desire_dict
703e39
+    ):
703e39
+        return False
703e39
+
703e39
+    return True
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/nmstate.py nmstate-1.3.3/libnmstate/nmstate.py
703e39
--- nmstate-1.3.3.old/libnmstate/nmstate.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/nmstate.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -73,6 +73,7 @@
703e39
     include_status_data=None,
703e39
     info_type=_INFO_TYPE_RUNNING,
703e39
     include_secrets=False,
703e39
+    include_controller_prop=True,
703e39
 ):
703e39
     for plugin in plugins:
703e39
         plugin.refresh_content()
703e39
@@ -81,7 +82,7 @@
703e39
         report["capabilities"] = plugins_capabilities(plugins)
703e39
 
703e39
     report[Interface.KEY] = _get_interface_info_from_plugins(
703e39
-        plugins, info_type
703e39
+        plugins, info_type, include_controller_prop=include_controller_prop
703e39
     )
703e39
 
703e39
     report[Route.KEY] = _get_routes_from_plugins(plugins, info_type)
703e39
@@ -103,6 +104,7 @@
703e39
 
703e39
     if not include_secrets:
703e39
         hide_the_secrets(report)
703e39
+
703e39
     return report
703e39
 
703e39
 
703e39
@@ -185,7 +187,9 @@
703e39
     return chose_plugin
703e39
 
703e39
 
703e39
-def _get_interface_info_from_plugins(plugins, info_type):
703e39
+def _get_interface_info_from_plugins(
703e39
+    plugins, info_type, include_controller_prop=True
703e39
+):
703e39
     all_ifaces = {}
703e39
     IFACE_PRIORITY_METADATA = "_plugin_priority"
703e39
     IFACE_PLUGIN_SRC_METADATA = "_plugin_source"
703e39
@@ -287,6 +291,8 @@
703e39
     for iface in all_ifaces.values():
703e39
         iface.pop(IFACE_PRIORITY_METADATA)
703e39
         iface.pop(IFACE_PLUGIN_SRC_METADATA)
703e39
+        if not include_controller_prop:
703e39
+            iface.pop(Interface.CONTROLLER, None)
703e39
 
703e39
     return sorted(all_ifaces.values(), key=itemgetter(Interface.NAME))
703e39
 
703e39
@@ -404,6 +410,7 @@
703e39
         plugins,
703e39
         info_type=_INFO_TYPE_RUNNING_CONFIG,
703e39
         include_secrets=include_secrets,
703e39
+        include_controller_prop=False,
703e39
     )
703e39
 
703e39
 
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/plugin.py nmstate-1.3.3/libnmstate/plugin.py
703e39
--- nmstate-1.3.3.old/libnmstate/plugin.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/plugin.py	2023-02-14 18:35:05.960135769 +0800
703e39
@@ -32,6 +32,7 @@
703e39
     PLUGIN_CAPABILITY_ROUTE = "route"
703e39
     PLUGIN_CAPABILITY_ROUTE_RULE = "route_rule"
703e39
     PLUGIN_CAPABILITY_DNS = "dns"
703e39
+    PLUGIN_CAPABILITY_OVSDB_GLOBAL = "ovsdb_global"
703e39
 
703e39
     DEFAULT_PRIORITY = 10
703e39
 
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/plugins/nmstate_plugin_ovsdb.py nmstate-1.3.3/libnmstate/plugins/nmstate_plugin_ovsdb.py
703e39
--- nmstate-1.3.3.old/libnmstate/plugins/nmstate_plugin_ovsdb.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/plugins/nmstate_plugin_ovsdb.py	2023-02-14 18:35:05.960135769 +0800
703e39
@@ -156,8 +156,16 @@
703e39
         return NmstatePlugin.DEFAULT_PRIORITY + 1
703e39
 
703e39
     @property
703e39
+    def capabilities(self):
703e39
+        return [
703e39
+            NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL,
703e39
+        ]
703e39
+
703e39
+    @property
703e39
     def plugin_capabilities(self):
703e39
-        return NmstatePlugin.PLUGIN_CAPABILITY_IFACE
703e39
+        return [
703e39
+            NmstatePlugin.PLUGIN_CAPABILITY_IFACE,
703e39
+        ]
703e39
 
703e39
     def get_interfaces(self):
703e39
         ifaces = []
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/schema.py nmstate-1.3.3/libnmstate/schema.py
703e39
--- nmstate-1.3.3.old/libnmstate/schema.py	2023-02-15 15:48:11.742846002 +0800
703e39
+++ nmstate-1.3.3/libnmstate/schema.py	2023-02-15 15:48:33.672042194 +0800
703e39
@@ -48,6 +48,7 @@
703e39
     MTU = "mtu"
703e39
     COPY_MAC_FROM = "copy-mac-from"
703e39
     ACCEPT_ALL_MAC_ADDRESSES = "accept-all-mac-addresses"
703e39
+    CONTROLLER = "controller"
703e39
 
703e39
 
703e39
 class Route:
703e39
@@ -148,6 +149,7 @@
703e39
     AUTO_GATEWAY = "auto-gateway"
703e39
     AUTO_ROUTES = "auto-routes"
703e39
     AUTO_ROUTE_TABLE_ID = "auto-route-table-id"
703e39
+    AUTO_ROUTE_METRIC = "auto-route-metric"
703e39
 
703e39
 
703e39
 class InterfaceIPv4(InterfaceIP):
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/validator.py nmstate-1.3.3/libnmstate/validator.py
703e39
--- nmstate-1.3.3.old/libnmstate/validator.py	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/validator.py	2023-02-14 18:35:05.960135769 +0800
703e39
@@ -23,6 +23,7 @@
703e39
 import jsonschema as js
703e39
 
703e39
 from libnmstate.schema import Interface
703e39
+from libnmstate.schema import OvsDB
703e39
 from libnmstate.schema import InterfaceType
703e39
 from libnmstate.error import NmstateDependencyError
703e39
 
703e39
@@ -43,6 +44,7 @@
703e39
 
703e39
 def validate_capabilities(state, capabilities):
703e39
     validate_interface_capabilities(state.get(Interface.KEY, []), capabilities)
703e39
+    validate_ovsdb_global_cap(state.get(OvsDB.KEY, {}), capabilities)
703e39
 
703e39
 
703e39
 def validate_interface_capabilities(ifaces_state, capabilities):
703e39
@@ -78,3 +80,15 @@
703e39
             "Interfaces count exceeds the limit %s in desired state",
703e39
             MAX_SUPPORTED_INTERFACES,
703e39
         )
703e39
+
703e39
+
703e39
+def validate_ovsdb_global_cap(ovsdb_global_conf, capabilities):
703e39
+    if (
703e39
+        ovsdb_global_conf
703e39
+        and NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL not in capabilities
703e39
+    ):
703e39
+        raise NmstateDependencyError(
703e39
+            "Missing plugin for ovs-db global configuration, "
703e39
+            "please try to install 'nmstate-plugin-ovsdb' or other plugin "
703e39
+            "provides NmstatePlugin.PLUGIN_CAPABILITY_OVSDB_GLOBAL"
703e39
+        )
703e39
diff -Nur nmstate-1.3.3.old/libnmstate/VERSION nmstate-1.3.3/libnmstate/VERSION
703e39
--- nmstate-1.3.3.old/libnmstate/VERSION	2022-08-11 23:22:22.000000000 +0800
703e39
+++ nmstate-1.3.3/libnmstate/VERSION	2023-02-15 15:48:33.668708842 +0800
703e39
@@ -1 +1 @@
703e39
-1.3.3
703e39
+1.3.4