nmstate/SOURCES/BZ_2169642-Fix-sriov.patch
2023-04-04 09:49:00 +00:00

1076 lines
42 KiB
Diff

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