From 266758ec0c16740821622b9990a57bed9edcfac0 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 10 2021 12:02:42 +0000 Subject: import nmstate-1.0.2-14.el8_4 --- diff --git a/SOURCES/BZ_1979220_static_ip_on_ovs_same_name.patch b/SOURCES/BZ_1979220_static_ip_on_ovs_same_name.patch new file mode 100644 index 0000000..5321f17 --- /dev/null +++ b/SOURCES/BZ_1979220_static_ip_on_ovs_same_name.patch @@ -0,0 +1,245 @@ +From 9f505f21b90cf122539fbe9bc3bf78ef170f5c12 Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Tue, 6 Jul 2021 11:53:55 +0800 +Subject: [PATCH] nm ovs: Fix OVS bridge and interface using the same name + +Nmstate will fail on verification when setting ip to OVS interface +sharing the same name with OVS bridge. + +This is caused by NM plugin does not index with interface type when +retrieving interface applied configure which lead to OVS bridge or OVS +interface applied configure overlapping each other. + +To fix this problem, we use `NetworkManagerPlugin._kernel_nic_applied_configs` +and `NetworkManagerPlugin._userspace_nic_applied_configs()` to +differentiate them. The kernel data is still indexed by interface name, +the user space data is indexed by interface name and type. + +Integration test case included as tier 1 as OpenShift need it. + +Signed-off-by: Gris Ge +--- + libnmstate/nm/device.py | 8 +++++ + libnmstate/nm/plugin.py | 61 ++++++++++++++++++++++++++------------- + libnmstate/nm/profiles.py | 38 ++++++++++++++++++++---- + 3 files changed, 81 insertions(+), 26 deletions(-) + +diff --git a/libnmstate/nm/device.py b/libnmstate/nm/device.py +index 3c58bae..4c97733 100644 +--- a/libnmstate/nm/device.py ++++ b/libnmstate/nm/device.py +@@ -198,3 +198,11 @@ def get_nm_dev(ctx, iface_name, iface_type): + ): + return nm_dev + return None ++ ++ ++def is_kernel_iface(nm_dev): ++ iface_type = get_iface_type(nm_dev) ++ return iface_type != InterfaceType.UNKNOWN and iface_type not in ( ++ InterfaceType.OVS_BRIDGE, ++ InterfaceType.OVS_PORT, ++ ) +diff --git a/libnmstate/nm/plugin.py b/libnmstate/nm/plugin.py +index da933b3..9c774e5 100644 +--- a/libnmstate/nm/plugin.py ++++ b/libnmstate/nm/plugin.py +@@ -36,6 +36,7 @@ from .common import NM + from .context import NmContext + from .device import get_device_common_info + from .device import get_iface_type ++from .device import is_kernel_iface + from .device import list_devices + from .dns import get_running as get_dns_running + from .dns import get_running_config as get_dns_running_config +@@ -63,7 +64,8 @@ class NetworkManagerPlugin(NmstatePlugin): + def __init__(self): + self._ctx = None + self._checkpoint = None +- self.__applied_configs = None ++ self.__kernel_nic_applied_configs = None ++ self.__userspace_nic_applied_configs = None + + @property + def priority(self): +@@ -79,10 +81,28 @@ class NetworkManagerPlugin(NmstatePlugin): + self._ctx = None + + @property +- def _applied_configs(self): +- if self.__applied_configs is None: +- self.__applied_configs = get_all_applied_configs(self.context) +- return self.__applied_configs ++ def _kernel_nic_applied_configs(self): ++ if ( ++ self.__kernel_nic_applied_configs is None ++ or self.__userspace_nic_applied_configs is None ++ ): ++ ( ++ self.__kernel_nic_applied_configs, ++ self.__userspace_nic_applied_configs, ++ ) = get_all_applied_configs(self.context) ++ return self.__kernel_nic_applied_configs ++ ++ @property ++ def _userspace_nic_applied_configs(self): ++ if ( ++ self.__kernel_nic_applied_configs is None ++ or self.__userspace_nic_applied_configs is None ++ ): ++ ( ++ self.__kernel_nic_applied_configs, ++ self.__userspace_nic_applied_configs, ++ ) = get_all_applied_configs(self.context) ++ return self.__userspace_nic_applied_configs + + @property + def checkpoint(self): +@@ -120,8 +140,6 @@ class NetworkManagerPlugin(NmstatePlugin): + def get_interfaces(self): + info = [] + +- applied_configs = self._applied_configs +- + devices_info = [ + (dev, get_device_common_info(dev)) + for dev in list_devices(self.client) +@@ -131,6 +149,16 @@ class NetworkManagerPlugin(NmstatePlugin): + if not dev.get_managed(): + # Skip unmanaged interface + continue ++ if is_kernel_iface(dev): ++ applied_config = self._kernel_nic_applied_configs.get( ++ dev.get_iface() ++ ) ++ else: ++ iface_type = get_iface_type(dev) ++ applied_config = self._userspace_nic_applied_configs.get( ++ f"{dev.get_iface()}{iface_type}" ++ ) ++ + nm_ac = dev.get_active_connection() + if ( + nm_ac +@@ -140,7 +168,6 @@ class NetworkManagerPlugin(NmstatePlugin): + continue + + iface_info = Nm2Api.get_common_device_info(devinfo) +- applied_config = applied_configs.get(iface_info[Interface.NAME]) + + act_con = dev.get_active_connection() + iface_info[Interface.IPV4] = get_ipv4_info(act_con, applied_config) +@@ -193,11 +220,14 @@ class NetworkManagerPlugin(NmstatePlugin): + def get_dns_client_config(self): + return { + DNS.RUNNING: get_dns_running(self.client), +- DNS.CONFIG: get_dns_running_config(self._applied_configs), ++ DNS.CONFIG: get_dns_running_config( ++ self._kernel_nic_applied_configs ++ ), + } + + def refresh_content(self): +- self.__applied_configs = None ++ self.__kernel_nic_applied_configs = None ++ self.__userspace_nic_applied_configs = None + + def apply_changes(self, net_state, save_to_disk): + NmProfiles(self.context).apply_config(net_state, save_to_disk) +@@ -278,7 +308,7 @@ class NetworkManagerPlugin(NmstatePlugin): + nm_dev + and nm_dev.get_iface() + and not nm_dev.get_managed() +- and _is_kernel_iface(nm_dev) ++ and is_kernel_iface(nm_dev) + ): + ignored_ifaces.add(nm_dev.get_iface()) + return list(ignored_ifaces) +@@ -298,12 +328,3 @@ def _remove_ovs_bridge_unsupported_entries(iface_info): + + def _nm_utils_decode_version(): + return f"{NM.MAJOR_VERSION}.{NM.MINOR_VERSION}.{NM.MICRO_VERSION}" +- +- +-def _is_kernel_iface(nm_dev): +- iface_type = get_iface_type(nm_dev) +- return iface_type != InterfaceType.UNKNOWN and iface_type not in ( +- InterfaceType.OVS_BRIDGE, +- InterfaceType.OVS_INTERFACE, +- InterfaceType.OVS_PORT, +- ) +diff --git a/libnmstate/nm/profiles.py b/libnmstate/nm/profiles.py +index 905a6c8..90e0e70 100644 +--- a/libnmstate/nm/profiles.py ++++ b/libnmstate/nm/profiles.py +@@ -27,7 +27,9 @@ from libnmstate.schema import InterfaceType + from .common import NM + from .device import is_externally_managed + from .device import list_devices ++from .device import get_iface_type + from .device import get_nm_dev ++from .device import is_kernel_iface + from .dns import get_dns_config_iface_names + from .ipv4 import acs_and_ip_profiles as acs_and_ip4_profiles + from .ipv6 import acs_and_ip_profiles as acs_and_ip6_profiles +@@ -129,7 +131,13 @@ def _append_nm_ovs_port_iface(net_state): + + + def get_all_applied_configs(context): +- applied_configs = {} ++ """ ++ Return two dictionaries. ++ First one for kernel interface with interface name as key. ++ Second one for user space interface with interface name and type as key. ++ """ ++ kernel_nic_applied_configs = {} ++ userspace_nic_applid_configs = {} + for nm_dev in list_devices(context.client): + if ( + nm_dev.get_state() +@@ -150,19 +158,37 @@ def get_all_applied_configs(context): + flags=0, + cancellable=context.cancellable, + callback=_get_applied_config_callback, +- user_data=(iface_name, action, applied_configs, context), ++ user_data=( ++ iface_name, ++ action, ++ kernel_nic_applied_configs, ++ userspace_nic_applid_configs, ++ context, ++ ), + ) + context.wait_all_finish() +- return applied_configs ++ return kernel_nic_applied_configs, userspace_nic_applid_configs + + + def _get_applied_config_callback(nm_dev, result, user_data): +- iface_name, action, applied_configs, context = user_data ++ ( ++ iface_name, ++ action, ++ kernel_nic_applied_configs, ++ userspace_nic_applid_configs, ++ context, ++ ) = user_data + context.finish_async(action) + try: ++ iface_name = nm_dev.get_iface() + remote_conn, _ = nm_dev.get_applied_connection_finish(result) +- # TODO: We should use both interface name and type as key below. +- applied_configs[nm_dev.get_iface()] = remote_conn ++ if is_kernel_iface(nm_dev): ++ kernel_nic_applied_configs[iface_name] = remote_conn ++ else: ++ iface_type = get_iface_type(nm_dev) ++ userspace_nic_applid_configs[ ++ f"{iface_name}{iface_type}" ++ ] = remote_conn + except Exception as e: + logging.warning( + "Failed to retrieve applied config for device " +-- +2.32.0 + diff --git a/SOURCES/BZ_1979515-SR-IOV-Fix-BCM57416-failure-when-creating-VFs.patch b/SOURCES/BZ_1979515-SR-IOV-Fix-BCM57416-failure-when-creating-VFs.patch new file mode 100644 index 0000000..b4d9111 --- /dev/null +++ b/SOURCES/BZ_1979515-SR-IOV-Fix-BCM57416-failure-when-creating-VFs.patch @@ -0,0 +1,125 @@ +From 61e775a19652ff1b17581e53367f05921304edfb Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Thu, 10 Jun 2021 14:55:33 +0200 +Subject: [PATCH 1/2] SR-IOV: Fix BCM57416 failure when creating VFs + +Currently there is not a way to see the relation between a SR-IOV PF and +its VFs. Broadcom BCM57416 follows a different name pattern for PF and +VF, therefore it needs to be parsed if present. + +PF name: ens2f0np0 +VF name: ens2f0v0 + +The different name pattern is due to: + 1. The `n` is for `multi-port PCI device` support. + 2. The `p*` is `phys_port_name` provided by the BCM driver `bnxt_en`. + +Ref: https://bugzilla.redhat.com/1959679 + +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/ifaces/ethernet.py | 38 ++++++++++++++++++++++++++++++++--- + libnmstate/nm/profiles.py | 8 ++++++++ + 2 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py +index 55772ce..a47a718 100644 +--- a/libnmstate/ifaces/ethernet.py ++++ b/libnmstate/ifaces/ethernet.py +@@ -25,6 +25,11 @@ from libnmstate.schema import InterfaceState + from .base_iface import BaseIface + + ++BNXT_DRIVER_PHYS_PORT_PREFIX = "p" ++MULTIPORT_PCI_DEVICE_PREFIX = "n" ++IS_GENERATED_VF_METADATA = "_is_generated_vf" ++ ++ + class EthernetIface(BaseIface): + def __init__(self, info, save_to_disk=True): + super().__init__(info, save_to_disk) +@@ -80,21 +85,48 @@ class EthernetIface(BaseIface): + ) + + def create_sriov_vf_ifaces(self): +- return [ ++ # Currently there is not a way to see the relation between a SR-IOV PF ++ # and its VFs. Broadcom BCM57416 follows a different name pattern for ++ # PF and VF, therefore it needs to be parsed if present. ++ # ++ # PF name: ens2f0np0 ++ # VF name: ens2f0v0 ++ # ++ # The different name pattern is due to: ++ # 1. The `n` is for `multi-port PCI device` support. ++ # 2. The `p*` is `phys_port_name` provided by the BCM driver ++ # `bnxt_en`. ++ # ++ # If the NIC is following the standard pattern "pfname+v+vfid", this ++ # split will not touch it and the vf_pattern will be the PF name. ++ # Ref: https://bugzilla.redhat.com/1959679 ++ vf_pattern = self.name ++ multiport_pattern = ( ++ MULTIPORT_PCI_DEVICE_PREFIX + BNXT_DRIVER_PHYS_PORT_PREFIX ++ ) ++ if len(self.name.split(multiport_pattern)) == 2: ++ vf_pattern = self.name.split(multiport_pattern)[0] ++ ++ vf_ifaces = [ + EthernetIface( + { + # According to manpage of systemd.net-naming-scheme(7), + # SRIOV VF interface will have v{slot} in device name. + # Currently, nmstate has no intention to support + # user-defined udev rule on SRIOV interface naming policy. +- Interface.NAME: f"{self.name}v{i}", ++ Interface.NAME: f"{vf_pattern}v{i}", + Interface.TYPE: InterfaceType.ETHERNET, +- # VF will be in DOWN state initialy. ++ # VF will be in DOWN state initialy + Interface.STATE: InterfaceState.DOWN, + } + ) + for i in range(0, self.sriov_total_vfs) + ] ++ # The generated vf metadata cannot be part of the original dict. ++ for vf in vf_ifaces: ++ vf._info[IS_GENERATED_VF_METADATA] = True ++ ++ return vf_ifaces + + def remove_vfs_entry_when_total_vfs_decreased(self): + vfs_count = len( +diff --git a/libnmstate/nm/profiles.py b/libnmstate/nm/profiles.py +index 90e0e70..ec3ef70 100644 +--- a/libnmstate/nm/profiles.py ++++ b/libnmstate/nm/profiles.py +@@ -40,6 +40,9 @@ from .veth import create_iface_for_nm_veth_peer + from .veth import is_nm_veth_supported + + ++IS_GENERATED_VF_METADATA = "_is_generated_vf" ++ ++ + class NmProfiles: + def __init__(self, context): + self._ctx = context +@@ -63,6 +66,7 @@ class NmProfiles: + all_profiles = [ + NmProfile(self._ctx, iface) + for iface in net_state.ifaces.all_ifaces() ++ if not _is_only_for_verify(iface) + ] + + for profile in all_profiles: +@@ -409,3 +413,7 @@ def _nm_ovs_port_has_child(nm_profile, ovs_bridge_iface, net_state): + ): + return True + return False ++ ++ ++def _is_only_for_verify(iface): ++ return iface.to_dict().get(IS_GENERATED_VF_METADATA) +-- +2.31.1 + diff --git a/SOURCES/BZ_1979943-ovs-Regenerate-iface-metadata-after-RouteRule-metada.patch b/SOURCES/BZ_1979943-ovs-Regenerate-iface-metadata-after-RouteRule-metada.patch new file mode 100644 index 0000000..e484490 --- /dev/null +++ b/SOURCES/BZ_1979943-ovs-Regenerate-iface-metadata-after-RouteRule-metada.patch @@ -0,0 +1,103 @@ +From 55d5092c04a974985c4db8609c73d6b92a674cae Mon Sep 17 00:00:00 2001 +From: Gris Ge +Date: Thu, 8 Jul 2021 15:49:48 +0800 +Subject: [PATCH 2/2] ovs: Regenerate iface metadata after RouteRule metadata + generation + +When desire state only contain route rule for OVS interface route table +ID, NetworkManager will complains: + + A connection with a 'ovs-interface' setting must have a master. + +The root cause is because interface metadata is not generated if desire +state has no interface at all. + +The fix is regenerate interface metadata on changed/desired interface +after route rule metadata generation. + +Integration test case added and marked as tier1 due to RHV requirement. + +Signed-off-by: Gris Ge +--- + libnmstate/ifaces/base_iface.py | 5 ++-- + libnmstate/ifaces/ifaces.py | 4 ++-- + libnmstate/ifaces/linux_bridge.py | 8 +++++-- + libnmstate/net_state.py | 3 +++ + tests/integration/ovs_test.py | 40 +++++++++++++++++++++++++++++++ + 5 files changed, 54 insertions(+), 6 deletions(-) + +diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py +index e3f2a1c..a76491e 100644 +--- a/libnmstate/ifaces/base_iface.py ++++ b/libnmstate/ifaces/base_iface.py +@@ -350,8 +350,9 @@ class BaseIface: + def gen_metadata(self, ifaces): + if self.is_controller and not self.is_absent: + for port_name in self.port: +- port_iface = ifaces.all_kernel_ifaces[port_name] +- port_iface.set_controller(self.name, self.type) ++ port_iface = ifaces.all_kernel_ifaces.get(port_name) ++ if port_iface: ++ port_iface.set_controller(self.name, self.type) + + def update(self, info): + self._info.update(info) +diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py +index efa24aa..c240abd 100644 +--- a/libnmstate/ifaces/ifaces.py ++++ b/libnmstate/ifaces/ifaces.py +@@ -158,7 +158,7 @@ class Ifaces: + self._validate_infiniband_as_bridge_port() + self._validate_infiniband_as_bond_port() + self._apply_copy_mac_from() +- self._gen_metadata() ++ self.gen_metadata() + for iface in self.all_ifaces(): + iface.pre_edit_validation_and_cleanup() + +@@ -569,7 +569,7 @@ class Ifaces: + else: + self._kernel_ifaces[iface.name] = iface + +- def _gen_metadata(self): ++ def gen_metadata(self): + for iface in self.all_ifaces(): + # Generate metadata for all interface in case any of them + # been marked as changed by DNS/Route/RouteRule. +diff --git a/libnmstate/ifaces/linux_bridge.py b/libnmstate/ifaces/linux_bridge.py +index bc7768f..09d68cb 100644 +--- a/libnmstate/ifaces/linux_bridge.py ++++ b/libnmstate/ifaces/linux_bridge.py +@@ -125,9 +125,13 @@ class LinuxBridgeIface(BridgeIface): + super().gen_metadata(ifaces) + if not self.is_absent: + for port_config in self.port_configs: +- ifaces.all_kernel_ifaces[ ++ port_iface = ifaces.all_kernel_ifaces.get( + port_config[LinuxBridge.Port.NAME] +- ].update({BridgeIface.BRPORT_OPTIONS_METADATA: port_config}) ++ ) ++ if port_iface: ++ port_iface.update( ++ {BridgeIface.BRPORT_OPTIONS_METADATA: port_config} ++ ) + + def remove_port(self, port_name): + if self._bridge_config: +diff --git a/libnmstate/net_state.py b/libnmstate/net_state.py +index 713b7dc..780981c 100644 +--- a/libnmstate/net_state.py ++++ b/libnmstate/net_state.py +@@ -72,6 +72,9 @@ class NetState: + self._ifaces.gen_dns_metadata(self._dns, self._route) + self._ifaces.gen_route_metadata(self._route) + self._ifaces.gen_route_rule_metadata(self._route_rule, self._route) ++ # DND/Route/RouteRule might introduced new changed interface ++ # Regnerate interface metadata ++ self._ifaces.gen_metadata() + + def _mark_ignored_kernel_ifaces(self, ignored_ifnames): + for iface_name in ignored_ifnames: +-- +2.31.1 + diff --git a/SOURCES/BZ_1986285-nispor-fix-show-of-empty-next_hop_address-and-destin.patch b/SOURCES/BZ_1986285-nispor-fix-show-of-empty-next_hop_address-and-destin.patch new file mode 100644 index 0000000..d653412 --- /dev/null +++ b/SOURCES/BZ_1986285-nispor-fix-show-of-empty-next_hop_address-and-destin.patch @@ -0,0 +1,85 @@ +From ab55d90e31a121f1cb18d79582a8f4d2a138df91 Mon Sep 17 00:00:00 2001 +From: Fernando Fernandez Mancera +Date: Mon, 26 Jul 2021 16:13:15 +0200 +Subject: [PATCH] nispor: fix show of empty next_hop_address and destination + +The correct way of representing an empty next_hop_address is using +"0.0.0.0" for IPv4 and "::" for IPv6. This configuration is valid +because an user should be able to specify the following routes: + +``` +--- +routes: + config: + - destination: 0.0.0.0/0 + next-hop-address: 0.0.0.0 + next-hop-interface: dummy + - destination: ::/0 + next-hop-address: "::" + next-hop-interface: dummy + +``` + +That means each of the hosts within the range of the route are +considered to be directly connected through that interface. + +For example, using iproute2 the user should introduce the following +command: + +`ip route 0.0.0.0 0.0.0.0 dummy` + +Integration test case added. + +Ref: https://bugzilla.redhat.com/1985879 + +Signed-off-by: Fernando Fernandez Mancera +--- + libnmstate/nispor/route.py | 13 +++++++++---- + tests/integration/route_test.py | 30 +++++++++++++++++++++++++++++- + 2 files changed, 38 insertions(+), 5 deletions(-) + +diff --git a/libnmstate/nispor/route.py b/libnmstate/nispor/route.py +index 510ddc3..9852ba5 100644 +--- a/libnmstate/nispor/route.py ++++ b/libnmstate/nispor/route.py +@@ -23,6 +23,9 @@ from libnmstate.schema import Route + IPV4_DEFAULT_GATEWAY_DESTINATION = "0.0.0.0/0" + IPV6_DEFAULT_GATEWAY_DESTINATION = "::/0" + ++IPV4_EMPTY_NEXT_HOP_ADDRESS = "0.0.0.0" ++IPV6_EMPTY_NEXT_HOP_ADDRESS = "::" ++ + LOCAL_ROUTE_TABLE = 255 + + +@@ -50,21 +53,23 @@ def nispor_route_state_to_nmstate_static(np_routes): + def _nispor_route_to_nmstate(np_rt): + if np_rt.dst: + destination = np_rt.dst +- elif np_rt.gateway: ++ else: + destination = ( + IPV6_DEFAULT_GATEWAY_DESTINATION + if np_rt.address_family == "ipv6" + else IPV4_DEFAULT_GATEWAY_DESTINATION + ) +- else: +- destination = "" + + if np_rt.via: + next_hop = np_rt.via + elif np_rt.gateway: + next_hop = np_rt.gateway + else: +- next_hop = "" ++ next_hop = ( ++ IPV6_EMPTY_NEXT_HOP_ADDRESS ++ if np_rt.address_family == "ipv6" ++ else IPV4_EMPTY_NEXT_HOP_ADDRESS ++ ) + + return { + Route.TABLE_ID: np_rt.table, +-- +2.31.1 + diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec index 65ff75a..1e574ec 100644 --- a/SPECS/nmstate.spec +++ b/SPECS/nmstate.spec @@ -4,7 +4,7 @@ Name: nmstate Version: 1.0.2 -Release: 11%{?dist} +Release: 14%{?dist} Summary: Declarative network manager API License: LGPLv2+ URL: https://github.com/%{srcname}/%{srcname} @@ -21,6 +21,10 @@ Patch7: BZ_1964440-nm-ipv4-Deactivate-profile-when-route-removed.patch Patch8: BZ_1966379_fix_bond_opt_tlb_dynamic_lb.patch Patch9: BZ_1966457_Fix_bond_fail_over_mac.patch Patch10: BZ_1966457_only_validate_desired_bond.patch +Patch11: BZ_1979220_static_ip_on_ovs_same_name.patch +Patch12: BZ_1979515-SR-IOV-Fix-BCM57416-failure-when-creating-VFs.patch +Patch13: BZ_1979943-ovs-Regenerate-iface-metadata-after-RouteRule-metada.patch +Patch14: BZ_1986285-nispor-fix-show-of-empty-next_hop_address-and-destin.patch BuildArch: noarch BuildRequires: python3-devel BuildRequires: python3-setuptools @@ -93,6 +97,16 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0} %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb* %changelog +* Wed Jul 28 2021 Fernando Fernandez Mancera - 1.0.2-14 +- Fix showing routes without next-hop-address or destination. RHBZ#1986285 + +* Thu Jul 08 2021 Fernando Fernandez Mancera - 1.0.2-13 +- Fix SR-IOV BCM57416 failure when creating VFs. RHBZ#1979515 +- Fix applying route rule changes over ovs interface. RHBZ#1979943 + +* Tue Jul 06 2021 Gris Ge - 1.0.2-12 +- Fix setting IP on ovs interface sharing the same name with bridge. RHBZ#1979220 + * Thu Jun 03 2021 Gris Ge - 1.0.2-11 - Only validate desired bone interface. RHBZ#1966457