diff --git a/.gitignore b/.gitignore
index 54812f7..b8d7bdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-SOURCES/nmstate-1.1.0.tar.gz
+SOURCES/nmstate-1.2.1.tar.gz
+SOURCES/nmstate-rust-vendor-1.2.1.tar.xz
 SOURCES/nmstate.gpg
diff --git a/.nmstate.metadata b/.nmstate.metadata
index f6d19a1..fc5808f 100644
--- a/.nmstate.metadata
+++ b/.nmstate.metadata
@@ -1,2 +1,3 @@
-0b7795853d1f7735cb05817389f188884d1f6f09 SOURCES/nmstate-1.1.0.tar.gz
+d47ab133f1d83391a6b617e1674fe2dec92bf536 SOURCES/nmstate-1.2.1.tar.gz
+ee514e17040057d2e83efaf16c2f9779cfe42e8b SOURCES/nmstate-rust-vendor-1.2.1.tar.xz
 b5f872551d434e2c62b30d70471efaeede83ab44 SOURCES/nmstate.gpg
diff --git a/SOURCES/0001-nmstatectl-fix-long-arguments-support.patch b/SOURCES/0001-nmstatectl-fix-long-arguments-support.patch
deleted file mode 100644
index 4334857..0000000
--- a/SOURCES/0001-nmstatectl-fix-long-arguments-support.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 99c7f643bab33a26c317e1b72ca3b8490cb1ea60 Mon Sep 17 00:00:00 2001
-From: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Date: Fri, 16 Jul 2021 08:57:27 +0200
-Subject: [PATCH 1/4] nmstatectl: fix long arguments support
-
-The support for long arguments is broken. This patch is fixing it and
-solving the following errors:
-
-```
-[root@d0b4a6a0f7a5 nmstate-workspace]# nmstatectl show --running-config
-usage: nmstatectl [-h] [--version]
-                  {commit,edit,rollback,set,apply,show,version,gc} ...
-nmstatectl: error: unrecognized arguments: --running-config
-[root@d0b4a6a0f7a5 nmstate-workspace]# nmstatectl show --show-secrets
-usage: nmstatectl [-h] [--version]
-                  {commit,edit,rollback,set,apply,show,version,gc} ...
-nmstatectl: error: unrecognized arguments: --show-secrets
-```
-
-Integration test case added.
-
-Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- nmstatectl/nmstatectl.py | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/nmstatectl/nmstatectl.py b/nmstatectl/nmstatectl.py
-index a9f4cb6..6f83069 100644
---- a/nmstatectl/nmstatectl.py
-+++ b/nmstatectl/nmstatectl.py
-@@ -223,14 +223,16 @@ def setup_subcommand_show(subparsers):
-         dest="yaml",
-     )
-     parser_show.add_argument(
--        "-r, --running-config",
-+        "-r",
-+        "--running-config",
-         help="Show running configurations",
-         default=False,
-         action="store_true",
-         dest="running_config",
-     )
-     parser_show.add_argument(
--        "-s, --show-secrets",
-+        "-s",
-+        "--show-secrets",
-         help="Show secrets also",
-         default=False,
-         action="store_true",
--- 
-2.32.0
-
diff --git a/SOURCES/0002-nm-ethtool-Preserve-existing-ethtool-settings-when-u.patch b/SOURCES/0002-nm-ethtool-Preserve-existing-ethtool-settings-when-u.patch
deleted file mode 100644
index 5f5ebec..0000000
--- a/SOURCES/0002-nm-ethtool-Preserve-existing-ethtool-settings-when-u.patch
+++ /dev/null
@@ -1,105 +0,0 @@
-From b1cb57d1dc4bba6592ba5cfc5c810a2ad19ac941 Mon Sep 17 00:00:00 2001
-From: Gris Ge <fge@redhat.com>
-Date: Thu, 22 Jul 2021 18:40:50 +0800
-Subject: [PATCH 2/4] nm ethtool: Preserve existing ethtool settings when
- undesired
-
-When user does not define ethtool settings in desire state,
-we should preserve existing ethtool setting.
-
-Integration test case included.
-
-Signed-off-by: Gris Ge <fge@redhat.com>
-Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
----
- libnmstate/nm/connection.py | 18 +++---------------
- libnmstate/nm/ethtool.py    | 26 +++++++++++++++++++++++++-
- 2 files changed, 28 insertions(+), 16 deletions(-)
-
-diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
-index 5d60f6d..5a79c6f 100644
---- a/libnmstate/nm/connection.py
-+++ b/libnmstate/nm/connection.py
-@@ -22,8 +22,6 @@
- import uuid
- 
- from libnmstate.error import NmstatePluginError
--from libnmstate.ifaces import IfaceEthtool
--from libnmstate.schema import Ethtool
- from libnmstate.schema import Interface
- from libnmstate.schema import InterfaceType
- from libnmstate.schema import LinuxBridge as LB
-@@ -240,19 +238,9 @@ def create_new_nm_simple_conn(iface, nm_profile):
-     if iface.ieee_802_1x_conf:
-         settings.append(create_802_1x_setting(iface.ieee_802_1x_conf))
- 
--    if Ethtool.CONFIG_SUBTREE in iface.original_desire_dict:
--        iface_ethtool = IfaceEthtool(
--            iface.original_desire_dict[Ethtool.CONFIG_SUBTREE]
--        )
--        iface_ethtool.canonicalize(
--            iface.original_desire_dict[Ethtool.CONFIG_SUBTREE]
--        )
--        setting = create_ethtool_setting(
--            iface_ethtool,
--            nm_profile,
--        )
--        if setting:
--            settings.append(setting)
-+    ethtool_setting = create_ethtool_setting(iface, nm_profile)
-+    if ethtool_setting:
-+        settings.append(ethtool_setting)
- 
-     nm_simple_conn = NM.SimpleConnection.new()
-     for setting in settings:
-diff --git a/libnmstate/nm/ethtool.py b/libnmstate/nm/ethtool.py
-index 466f4f9..3cad1bf 100644
---- a/libnmstate/nm/ethtool.py
-+++ b/libnmstate/nm/ethtool.py
-@@ -22,6 +22,7 @@ import logging
- from .common import NM
- from .common import GLib
- 
-+from libnmstate.ifaces import IfaceEthtool
- from libnmstate.schema import Ethtool
- 
- 
-@@ -59,7 +60,7 @@ _NM_COALESCE_OPT_NAME_MAP = {
- }
- 
- 
--def create_ethtool_setting(iface_ethtool, base_con_profile):
-+def _create_ethtool_setting(iface_ethtool, base_con_profile):
-     nm_setting = None
- 
-     if base_con_profile:
-@@ -159,3 +160,26 @@ def nm_set_pause(nm_setting, autoneg, rx, tx):
-         tx_value,
-     )
-     # pylint: enable=no-member
-+
-+
-+def create_ethtool_setting(iface, base_con_profile):
-+    if Ethtool.CONFIG_SUBTREE in iface.original_desire_dict:
-+        iface_ethtool = IfaceEthtool(
-+            iface.original_desire_dict[Ethtool.CONFIG_SUBTREE]
-+        )
-+        iface_ethtool.canonicalize(
-+            iface.original_desire_dict[Ethtool.CONFIG_SUBTREE]
-+        )
-+        return _create_ethtool_setting(
-+            iface_ethtool,
-+            base_con_profile,
-+        )
-+    else:
-+        # Preserve existing setting but not create new
-+        if base_con_profile:
-+            ethtool_setting = base_con_profile.get_setting_by_name(
-+                NM.SETTING_ETHTOOL_SETTING_NAME
-+            )
-+            if ethtool_setting:
-+                return ethtool_setting.duplicate()
-+        return None
--- 
-2.32.0
-
diff --git a/SOURCES/0003-ovs-fix-state-ignore-for-ovs-port-when-removing-them.patch b/SOURCES/0003-ovs-fix-state-ignore-for-ovs-port-when-removing-them.patch
deleted file mode 100644
index ba000ef..0000000
--- a/SOURCES/0003-ovs-fix-state-ignore-for-ovs-port-when-removing-them.patch
+++ /dev/null
@@ -1,87 +0,0 @@
-From f4d190653c55d399b32afc956b2b4a1ff8d20101 Mon Sep 17 00:00:00 2001
-From: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Date: Mon, 26 Jul 2021 09:58:23 +0200
-Subject: [PATCH 3/4] ovs: fix state=ignore for ovs port when removing them
-
-When removing an ovs port while the interface is marked as ignored, the
-interface should not being removed from the ovs bridge as the user
-specidied it should be ignored.
-
-Example:
-
-```
-interfaces:
-- name: dummy0
-  type: dummy
-  state: ignore
-- name: ovsbr0
-  type: ovs-bridge
-  state: up
-  bridge:
-    port:
-    - name: ovs0
-```
-
-Integration test case added.
-
-Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nm/profiles.py | 22 ++++++++++++++++++++--
- 1 file changed, 20 insertions(+), 2 deletions(-)
-
-diff --git a/libnmstate/nm/profiles.py b/libnmstate/nm/profiles.py
-index beda5c7..3b0b6be 100644
---- a/libnmstate/nm/profiles.py
-+++ b/libnmstate/nm/profiles.py
-@@ -23,6 +23,8 @@
- import logging
- from operator import attrgetter
- 
-+from libnmstate.schema import Interface
-+from libnmstate.schema import InterfaceState
- from libnmstate.schema import InterfaceType
- 
- from .common import NM
-@@ -359,7 +361,7 @@ def _delete_orphan_nm_ovs_port_profiles(
-                 continue
-             # When OVS port has no child, delete it
-             ovs_bridge_iface = ovs_bridge_profile.iface
--            if not _nm_ovs_port_has_child(
-+            if not _nm_ovs_port_has_child_or_is_ignored(
-                 nm_profile, ovs_bridge_iface, net_state
-             ):
-                 ProfileDelete(
-@@ -404,7 +406,9 @@ def _use_uuid_as_controller_and_parent(nm_profiles):
-                 nm_profile.update_parent(uuid)
- 
- 
--def _nm_ovs_port_has_child(nm_profile, ovs_bridge_iface, net_state):
-+def _nm_ovs_port_has_child_or_is_ignored(
-+    nm_profile, ovs_bridge_iface, net_state
-+):
-     ovs_port_uuid = nm_profile.get_uuid()
-     ovs_port_name = nm_profile.get_interface_name()
-     for ovs_iface_name in ovs_bridge_iface.port:
-@@ -415,4 +419,18 @@ def _nm_ovs_port_has_child(nm_profile, ovs_bridge_iface, net_state):
-             and ovs_iface.controller_type == InterfaceType.OVS_PORT
-         ):
-             return True
-+    # Gather the ovs bridge interface from the current state in order to check
-+    # if any port is ignored in the original desired state.
-+    current_ovs_bridge = net_state.ifaces.get_cur_iface(
-+        ovs_bridge_iface.name, InterfaceType.OVS_BRIDGE
-+    )
-+    if current_ovs_bridge:
-+        for port_name in current_ovs_bridge.port:
-+            port_iface = net_state.ifaces.all_kernel_ifaces.get(port_name)
-+            if (
-+                port_iface
-+                and port_iface.original_desire_dict.get(Interface.STATE)
-+                == InterfaceState.IGNORE
-+            ):
-+                return True
-     return False
--- 
-2.32.0
-
diff --git a/SOURCES/0004-nispor-fix-show-of-empty-next_hop_address-and-destin.patch b/SOURCES/0004-nispor-fix-show-of-empty-next_hop_address-and-destin.patch
deleted file mode 100644
index 40be31b..0000000
--- a/SOURCES/0004-nispor-fix-show-of-empty-next_hop_address-and-destin.patch
+++ /dev/null
@@ -1,86 +0,0 @@
-From 369ed3210ecedfa1deda88a6eb7cacc19a47f89d Mon Sep 17 00:00:00 2001
-From: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Date: Mon, 26 Jul 2021 16:13:15 +0200
-Subject: [PATCH 4/4] 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 <ffmancera@riseup.net>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nispor/route.py | 13 +++++++++----
- 1 file changed, 9 insertions(+), 4 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.32.0
-
diff --git a/SOURCES/BZ_2034139-do-not-rename-connection.patch b/SOURCES/BZ_2034139-do-not-rename-connection.patch
deleted file mode 100644
index 7036933..0000000
--- a/SOURCES/BZ_2034139-do-not-rename-connection.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From daad0a535b370d0a3f847526185c185aca2d9935 Mon Sep 17 00:00:00 2001
-From: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Date: Thu, 2 Sep 2021 11:33:14 +0200
-Subject: [PATCH] nm.connection: do not rename existing connections
-
-When an user is specifying a custom name for a connection and then use
-Nmstate to modify the interface, it will be renamed. That makes
-impossible to use other automation tools because it will require to use
-the uuid instead.
-
-Integration test case added.
-
-Ref: https://bugzilla.redhat.com/1998222
-
-Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nm/connection.py | 4 ----
- 1 file changed, 4 deletions(-)
-
-diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
-index 5a79c6f..39eb2b5 100644
---- a/libnmstate/nm/connection.py
-+++ b/libnmstate/nm/connection.py
-@@ -98,9 +98,6 @@ class _ConnectionSetting:
-             self._setting.props.master = controller
-             self._setting.props.slave_type = port_type
- 
--    def set_profile_name(self, con_name):
--        self._setting.props.id = con_name
--
-     @property
-     def setting(self):
-         return self._setting
-@@ -116,7 +113,6 @@ def create_new_nm_simple_conn(iface, nm_profile):
-     con_setting = _ConnectionSetting()
-     if nm_profile and not is_multiconnect_profile(nm_profile):
-         con_setting.import_by_profile(nm_profile, iface.is_controller)
--        con_setting.set_profile_name(iface.name)
-     else:
-         con_setting.create(
-             iface.name, iface.name, nm_iface_type, iface.is_controller
--- 
-2.27.0
-
diff --git a/SOURCES/BZ_2034139-ovs-remove-ovs-port-prefix.patch b/SOURCES/BZ_2034139-ovs-remove-ovs-port-prefix.patch
deleted file mode 100644
index 4225ba0..0000000
--- a/SOURCES/BZ_2034139-ovs-remove-ovs-port-prefix.patch
+++ /dev/null
@@ -1,110 +0,0 @@
-From cd41c2c0f6bef7b952648b358246fffa8ce3cf62 Mon Sep 17 00:00:00 2001
-From: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Date: Thu, 2 Sep 2021 17:39:59 +0200
-Subject: [PATCH 1/2] nm.connection: add postfixes to OVS bridges and
- interfaces connections
-
-When configuring an OVS bridge and interface with the same using
-Nmstate, it will generate two connection with the same name. If the user
-is willing to use nmcli to manage this profiles, it is not user friendly
-because they will need to use the connection UUID instead.
-
-In order to solve this, Nmstate is adding a postfix on the connection
-name. "-if" for OVS interface and "-br" for OVS bridge.
-
-Please, note that this issue does not affect kernel interfaces because
-it is not possible to have duplicated interfaces names in kernel.
-
-Integration test case added.
-
-Ref: https://bugzilla.redhat.com/1998218
-
-Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nm/connection.py | 14 +++++++++++++-
- 1 file changed, 13 insertions(+), 1 deletion(-)
-
-diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
-index 39eb2b5..afe3788 100644
---- a/libnmstate/nm/connection.py
-+++ b/libnmstate/nm/connection.py
-@@ -114,8 +114,20 @@ def create_new_nm_simple_conn(iface, nm_profile):
-     if nm_profile and not is_multiconnect_profile(nm_profile):
-         con_setting.import_by_profile(nm_profile, iface.is_controller)
-     else:
-+        # OVS bridge and interfaces could sharing the same interface name, to
-+        # distinguish them at NM connection level, instead of using interface
-+        # name as connection name, we append a postfix.
-+        con_name = iface.name
-+        if iface.type == InterfaceType.OVS_BRIDGE:
-+            con_name = con_name + "-br"
-+        elif iface.type == InterfaceType.OVS_INTERFACE:
-+            con_name = con_name + "-if"
-+
-         con_setting.create(
--            iface.name, iface.name, nm_iface_type, iface.is_controller
-+            con_name,
-+            iface.name,
-+            nm_iface_type,
-+            iface.is_controller,
-         )
- 
-     apply_lldp_setting(con_setting, iface_info)
--- 
-2.27.0
-
-
-From cfdd5afb1ef136d613d5adb3f6f5f6d156962586 Mon Sep 17 00:00:00 2001
-From: Radim Hrazdil <rhrazdil@redhat.com>
-Date: Wed, 10 Nov 2021 16:57:08 +0100
-Subject: [PATCH 2/2] ovs: remove ovs-port prefix
-
-Prepending the prefix causes ovnkube-node pods to crash.
-This change adds -port postfix to ovs-interface connection instead.
-
-Signed-off-by: Radim Hrazdil <rhrazdil@redhat.com>
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nm/connection.py | 2 ++
- libnmstate/nm/ovs.py        | 4 +---
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
-index afe3788..fb4ceba 100644
---- a/libnmstate/nm/connection.py
-+++ b/libnmstate/nm/connection.py
-@@ -122,6 +122,8 @@ def create_new_nm_simple_conn(iface, nm_profile):
-             con_name = con_name + "-br"
-         elif iface.type == InterfaceType.OVS_INTERFACE:
-             con_name = con_name + "-if"
-+        elif iface.type == InterfaceType.OVS_PORT:
-+            con_name = con_name + "-port"
- 
-         con_setting.create(
-             con_name,
-diff --git a/libnmstate/nm/ovs.py b/libnmstate/nm/ovs.py
-index 424df36..9f2cba0 100644
---- a/libnmstate/nm/ovs.py
-+++ b/libnmstate/nm/ovs.py
-@@ -32,8 +32,6 @@ from libnmstate.schema import OvsDB
- from .common import NM
- 
- 
--PORT_PROFILE_PREFIX = "ovs-port-"
--
- CONTROLLER_TYPE_METADATA = "_controller_type"
- CONTROLLER_METADATA = "_controller"
- SETTING_OVS_EXTERNALIDS = "SettingOvsExternalIDs"
-@@ -311,7 +309,7 @@ def create_iface_for_nm_ovs_port(iface):
-     if ovs.is_ovs_lag_port(port_options):
-         port_name = port_options[OB.Port.NAME]
-     else:
--        port_name = PORT_PROFILE_PREFIX + iface_name
-+        port_name = iface_name
-     return OvsPortIface(
-         {
-             Interface.NAME: port_name,
--- 
-2.27.0
-
diff --git a/SOURCES/BZ_2039285-sriov-New-way-to-wait-VF-been-created.patch b/SOURCES/BZ_2039285-sriov-New-way-to-wait-VF-been-created.patch
deleted file mode 100644
index 5d4b1bd..0000000
--- a/SOURCES/BZ_2039285-sriov-New-way-to-wait-VF-been-created.patch
+++ /dev/null
@@ -1,326 +0,0 @@
-From b8e3c6928c2a22a828b85a6780de3cea1107aa85 Mon Sep 17 00:00:00 2001
-From: Gris Ge <fge@redhat.com>
-Date: Fri, 24 Dec 2021 16:34:30 +0800
-Subject: [PATCH] sriov: New way to wait VF been created
-
-By checking `/sys/class/net/<pf_name>/device/virtfn<sriov_id>/net/`
-we could tell the interface name of VF.
-
-The old VF name guessing method is removed. The `verify_sriov_vf()` is
-created to collect all VF interfaces name, then comparing it with
-`total_vfs` count and check whether they exists in current state.
-
-The test indicate kernel/udev does not require extra time to remove VF
-interface when decreasing SR-IOV VF count.
-
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/ifaces/ethernet.py | 100 +++++++++++++++-------------------
- libnmstate/ifaces/ifaces.py   |  44 +--------------
- libnmstate/nispor/ethernet.py |  17 ++++++
- libnmstate/nm/profiles.py     |   1 -
- libnmstate/nm/sriov.py        |   5 +-
- 5 files changed, 67 insertions(+), 100 deletions(-)
-
-diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
-index 84fc665..720deab 100644
---- a/libnmstate/ifaces/ethernet.py
-+++ b/libnmstate/ifaces/ethernet.py
-@@ -17,10 +17,9 @@
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- 
-+from libnmstate.error import NmstateVerificationError
- from libnmstate.schema import Ethernet
--from libnmstate.schema import Interface
- from libnmstate.schema import InterfaceType
--from libnmstate.schema import InterfaceState
- 
- from .base_iface import BaseIface
- 
-@@ -30,7 +29,7 @@ MULTIPORT_PCI_DEVICE_PREFIX = "n"
- 
- 
- class EthernetIface(BaseIface):
--    IS_GENERATED_VF_METADATA = "_is_generated_vf"
-+    VF_IFACE_NAME_METADATA = "_vf_iface_name"
- 
-     def __init__(self, info, save_to_disk=True):
-         super().__init__(info, save_to_disk)
-@@ -56,10 +55,6 @@ class EthernetIface(BaseIface):
-         state = super().state_for_verify()
-         _capitalize_sriov_vf_mac(state)
-         EthernetIface._canonicalize(state)
--        if self.is_generated_vf:
--            # The VF state is unpredictable when PF is changing total_vfs count
--            # Just don't verify generated VF state.
--            state.pop(Interface.STATE, None)
-         return state
- 
-     @property
-@@ -102,54 +97,6 @@ class EthernetIface(BaseIface):
-     def duplex(self):
-         return self.raw.get(Ethernet.CONFIG_SUBTREE, {}).get(Ethernet.DUPLEX)
- 
--    def create_sriov_vf_ifaces(self):
--        # 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"{vf_pattern}v{i}",
--                    Interface.TYPE: InterfaceType.ETHERNET,
--                    # 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[EthernetIface.IS_GENERATED_VF_METADATA] = True
--
--        return vf_ifaces
--
--    @property
--    def is_generated_vf(self):
--        return self._info.get(EthernetIface.IS_GENERATED_VF_METADATA) is True
--
-     def remove_vfs_entry_when_total_vfs_decreased(self):
-         vfs_count = len(
-             self._info[Ethernet.CONFIG_SUBTREE]
-@@ -173,6 +120,16 @@ class EthernetIface(BaseIface):
-     def check_total_vfs_matches_vf_list(self, total_vfs):
-         return total_vfs == len(self.sriov_vfs)
- 
-+    def to_dict(self):
-+        info = super().to_dict()
-+        for vf_info in (
-+            info.get(Ethernet.CONFIG_SUBTREE, {})
-+            .get(Ethernet.SRIOV_SUBTREE, {})
-+            .get(Ethernet.SRIOV.VFS_SUBTREE, [])
-+        ):
-+            vf_info.pop(EthernetIface.VF_IFACE_NAME_METADATA, None)
-+        return info
-+
- 
- def _capitalize_sriov_vf_mac(state):
-     vfs = (
-@@ -184,3 +141,36 @@ def _capitalize_sriov_vf_mac(state):
-         vf_mac = vf.get(Ethernet.SRIOV.VFS.MAC_ADDRESS)
-         if vf_mac:
-             vf[Ethernet.SRIOV.VFS.MAC_ADDRESS] = vf_mac.upper()
-+
-+
-+def verify_sriov_vf(iface, cur_ifaces):
-+    """
-+    Checking whether VF interface is been created
-+    """
-+    if not (
-+        iface.is_up
-+        and (iface.is_desired or iface.is_changed)
-+        and iface.type == InterfaceType.ETHERNET
-+        and iface.sriov_total_vfs > 0
-+    ):
-+        return
-+    cur_iface = cur_ifaces.get_iface(iface.name, iface.type)
-+    if not cur_iface:
-+        # Other verification will raise proper exception when current interface
-+        # is missing
-+        return
-+    cur_vf_names = []
-+    for sriov_vf in cur_iface.sriov_vfs:
-+        if sriov_vf.get(EthernetIface.VF_IFACE_NAME_METADATA):
-+            cur_vf_names.append(sriov_vf[EthernetIface.VF_IFACE_NAME_METADATA])
-+
-+    if len(cur_vf_names) != iface.sriov_total_vfs:
-+        raise NmstateVerificationError(
-+            f"Found VF ports count does not match desired "
-+            f"{iface.sriov_total_vfs}, current is: {','.join(cur_vf_names)}"
-+        )
-+    for cur_vf_name in cur_vf_names:
-+        if not cur_ifaces.get_iface(cur_vf_name, InterfaceType.ETHERNET):
-+            raise NmstateVerificationError(
-+                f"VF interface {cur_vf_name} of PF {iface.name} not found"
-+            )
-diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
-index be295e3..4bd6792 100644
---- a/libnmstate/ifaces/ifaces.py
-+++ b/libnmstate/ifaces/ifaces.py
-@@ -33,6 +33,7 @@ from .base_iface import BaseIface
- from .bond import BondIface
- from .dummy import DummyIface
- from .ethernet import EthernetIface
-+from .ethernet import verify_sriov_vf
- from .infiniband import InfiniBandIface
- from .linux_bridge import LinuxBridgeIface
- from .macvlan import MacVlanIface
-@@ -151,7 +152,6 @@ class Ifaces:
-                     self._kernel_ifaces[iface.name] = iface
- 
-             self._create_virtual_port()
--            self._create_sriov_vfs_when_changed()
-             self._mark_vf_interface_as_absent_when_sriov_vf_decrease()
-             self._validate_unknown_port()
-             self._validate_unknown_parent()
-@@ -241,35 +241,6 @@ class Ifaces:
-         for iface in new_ifaces:
-             self._kernel_ifaces[iface.name] = iface
- 
--    def _create_sriov_vfs_when_changed(self):
--        """
--        When plugin set the TOTAL_VFS of PF, it might take 1 seconds or
--        more to have the VFs to be ready.
--        Nmstate should use verification retry to make sure VFs are full ready.
--        To do that, we include VFs into desire state.
--        """
--        new_ifaces = []
--        for iface in self.all_ifaces():
--            if (
--                iface.is_up
--                and (iface.is_desired or iface.is_changed)
--                and iface.type == InterfaceType.ETHERNET
--                and iface.sriov_total_vfs > 0
--            ):
--                for new_iface in iface.create_sriov_vf_ifaces():
--                    cur_iface = self._kernel_ifaces.get(new_iface.name)
--                    if cur_iface and cur_iface.is_desired:
--                        raise NmstateNotSupportedError(
--                            "Does not support changing SR-IOV PF interface "
--                            "along with VF interface in the single desire "
--                            f"state: PF {iface.name}, VF {cur_iface.name}"
--                        )
--                    else:
--                        new_iface.mark_as_desired()
--                        new_ifaces.append(new_iface)
--        for new_iface in new_ifaces:
--            self._kernel_ifaces[new_iface.name] = new_iface
--
-     def _mark_vf_interface_as_absent_when_sriov_vf_decrease(self):
-         """
-         When SRIOV TOTAL_VFS decreased, we should mark certain VF interfaces
-@@ -647,6 +618,7 @@ class Ifaces:
-         cur_ifaces._remove_ignore_interfaces(self._ignored_ifaces)
-         self._remove_ignore_interfaces(self._ignored_ifaces)
-         for iface in self.all_ifaces():
-+            verify_sriov_vf(iface, cur_ifaces)
-             if iface.is_desired:
-                 if (
-                     iface.is_virtual
-@@ -700,18 +672,6 @@ class Ifaces:
-                                 cur_iface.state_for_verify(),
-                             )
-                         )
--                    elif (
--                        iface.type == InterfaceType.ETHERNET and iface.is_sriov
--                    ):
--                        if not cur_iface.check_total_vfs_matches_vf_list(
--                            iface.sriov_total_vfs
--                        ):
--                            raise NmstateVerificationError(
--                                "The NIC exceeded the waiting time for "
--                                "verification and it is failing because "
--                                "the `total_vfs` does not match the VF "
--                                "list length."
--                            )
- 
-     def gen_dns_metadata(self, dns_state, route_state):
-         iface_metadata = dns_state.gen_metadata(self, route_state)
-diff --git a/libnmstate/nispor/ethernet.py b/libnmstate/nispor/ethernet.py
-index d6969b9..e4b82ff 100644
---- a/libnmstate/nispor/ethernet.py
-+++ b/libnmstate/nispor/ethernet.py
-@@ -17,10 +17,13 @@
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- 
-+import os
-+
- from libnmstate.schema import Ethernet
- from libnmstate.schema import InterfaceType
- 
- from .base_iface import NisporPluginBaseIface
-+from libnmstate.ifaces.ethernet import EthernetIface
- 
- 
- class NisporPluginEthernetIface(NisporPluginBaseIface):
-@@ -35,6 +38,9 @@ class NisporPluginEthernetIface(NisporPluginBaseIface):
-             for vf in self.np_iface.sr_iov.vfs:
-                 vf_infos.append(
-                     {
-+                        EthernetIface.VF_IFACE_NAME_METADATA: _get_vf_name(
-+                            self.np_iface.name, vf.vf_id
-+                        ),
-                         Ethernet.SRIOV.VFS.ID: vf.vf_id,
-                         Ethernet.SRIOV.VFS.MAC_ADDRESS: vf.mac.upper(),
-                         Ethernet.SRIOV.VFS.SPOOF_CHECK: vf.spoof_check,
-@@ -76,3 +82,14 @@ def np_ethtool_link_mode_to_nmstate(np_link_mode):
-         info[Ethernet.SPEED] = np_link_mode.speed
- 
-     return info
-+
-+
-+def _get_vf_name(pf_name, vf_id):
-+    try:
-+        vf_names = os.listdir(
-+            f"/sys/class/net/{pf_name}/device/virtfn{vf_id}/net/"
-+        )
-+        if len(vf_names) >= 1:
-+            return vf_names[0]
-+    except Exception:
-+        return ""
-diff --git a/libnmstate/nm/profiles.py b/libnmstate/nm/profiles.py
-index 3b0b6be..9e3020b 100644
---- a/libnmstate/nm/profiles.py
-+++ b/libnmstate/nm/profiles.py
-@@ -71,7 +71,6 @@ class NmProfiles:
-             for iface in sorted(
-                 list(net_state.ifaces.all_ifaces()), key=attrgetter("name")
-             )
--            if not getattr(iface, "is_generated_vf", None)
-         ]
- 
-         for profile in all_profiles:
-diff --git a/libnmstate/nm/sriov.py b/libnmstate/nm/sriov.py
-index a7e387c..e0bf60b 100644
---- a/libnmstate/nm/sriov.py
-+++ b/libnmstate/nm/sriov.py
-@@ -102,8 +102,9 @@ def _create_sriov_vfs_from_config(vfs_config, sriov_setting, vf_ids_to_add):
- 
- 
- def _set_nm_attribute(vf_object, key, value):
--    nm_attr, nm_variant = SRIOV_NMSTATE_TO_NM_MAP[key]
--    vf_object.set_attribute(nm_attr, nm_variant(value))
-+    if key in SRIOV_NMSTATE_TO_NM_MAP:
-+        nm_attr, nm_variant = SRIOV_NMSTATE_TO_NM_MAP[key]
-+        vf_object.set_attribute(nm_attr, nm_variant(value))
- 
- 
- def _remove_sriov_vfs_in_setting(vfs_config, sriov_setting, vf_ids_to_remove):
--- 
-2.27.0
-
diff --git a/SOURCES/BZ_2054054-support-multipath-route.patch b/SOURCES/BZ_2054054-support-multipath-route.patch
deleted file mode 100644
index 48850d1..0000000
--- a/SOURCES/BZ_2054054-support-multipath-route.patch
+++ /dev/null
@@ -1,110 +0,0 @@
-From af6b1458f852dc164206dc2fc45467ade6a7f7ae Mon Sep 17 00:00:00 2001
-From: Gris Ge <fge@redhat.com>
-Date: Wed, 9 Feb 2022 23:33:40 +0800
-Subject: [PATCH] python route: Add support of multipath route
-
-Supporting the rout created by below command:
-
-    sudo ip -6 route add 2001:db8:e::/64 proto static \
-        nexthop via 2001:db8:f::254 dev eth1 weight 1 onlink \
-        nexthop via 2001:db8:f::253 dev eth1 weight 256 onlink
-
-Integration test case included.
-
-Signed-off-by: Gris Ge <fge@redhat.com>
----
- libnmstate/nispor/route.py | 53 ++++++++++++++++++++++++++------------
- 1 file changed, 36 insertions(+), 17 deletions(-)
-
-diff --git a/libnmstate/nispor/route.py b/libnmstate/nispor/route.py
-index 9852ba51..87543de2 100644
---- a/libnmstate/nispor/route.py
-+++ b/libnmstate/nispor/route.py
-@@ -1,5 +1,5 @@
- #
--# Copyright (c) 2020 Red Hat, Inc.
-+# Copyright (c) 2020-2022 Red Hat, Inc.
- #
- # This file is part of nmstate
- #
-@@ -17,6 +17,8 @@
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- 
-+from copy import deepcopy
-+
- from libnmstate.schema import Route
- 
- 
-@@ -30,24 +32,24 @@ LOCAL_ROUTE_TABLE = 255
- 
- 
- def nispor_route_state_to_nmstate(np_routes):
--    return [
--        _nispor_route_to_nmstate(rt)
--        for rt in np_routes
--        if rt.scope in ["universe", "link"]
--        and rt.oif != "lo"
--        and rt.table != LOCAL_ROUTE_TABLE
--    ]
-+    ret = []
-+    for np_route in np_routes:
-+        if np_route.oif != "lo" and np_route.table != LOCAL_ROUTE_TABLE:
-+            ret.extend(_nispor_route_to_nmstate(np_route))
-+    return ret
- 
- 
- def nispor_route_state_to_nmstate_static(np_routes):
--    return [
--        _nispor_route_to_nmstate(rt)
--        for rt in np_routes
--        if rt.scope in ["universe", "link"]
--        and rt.protocol not in ["kernel", "ra", "dhcp"]
--        and rt.oif != "lo"
--        and rt.table != LOCAL_ROUTE_TABLE
--    ]
-+    ret = []
-+    for np_route in np_routes:
-+        if (
-+            np_route.oif != "lo"
-+            and np_route.table != LOCAL_ROUTE_TABLE
-+            and np_route.scope in ["universe", "link"]
-+            and np_route.protocol in ["static", "boot"]
-+        ):
-+            ret.extend(_nispor_route_to_nmstate(np_route))
-+    return ret
- 
- 
- def _nispor_route_to_nmstate(np_rt):
-@@ -71,10 +73,27 @@ def _nispor_route_to_nmstate(np_rt):
-             else IPV4_EMPTY_NEXT_HOP_ADDRESS
-         )
- 
--    return {
-+    nm_route = {
-         Route.TABLE_ID: np_rt.table,
-         Route.DESTINATION: destination,
-         Route.NEXT_HOP_INTERFACE: np_rt.oif if np_rt.oif else "",
-         Route.NEXT_HOP_ADDRESS: next_hop,
-         Route.METRIC: np_rt.metric if np_rt.metric else 0,
-     }
-+    np_mp_rts = get_multipath_routes(np_rt)
-+    if np_mp_rts:
-+        ret = []
-+        for np_mp_rt in np_mp_rts:
-+            nm_route_clone = deepcopy(nm_route)
-+            nm_route_clone[Route.NEXT_HOP_INTERFACE] = np_mp_rt["iface"]
-+            nm_route_clone[Route.NEXT_HOP_ADDRESS] = np_mp_rt["via"]
-+            ret.append(nm_route_clone)
-+        return ret
-+    else:
-+        return [nm_route]
-+
-+
-+# Instead of bumping nispor dependency version, we just use nispor private
-+# property temporarily
-+def get_multipath_routes(np_route):
-+    return np_route._info.get("multipath")
--- 
-2.27.0
-
diff --git a/SOURCES/nmstate-1.1.0.tar.gz.asc b/SOURCES/nmstate-1.1.0.tar.gz.asc
deleted file mode 100644
index 3c2d190..0000000
--- a/SOURCES/nmstate-1.1.0.tar.gz.asc
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQIzBAABCAAdFiEE8f1XsqXpyNthgIbGbM3lj+QeKP8FAmDvw+4ACgkQbM3lj+Qe
-KP+WjA/+Nky4rMOTNG16iwV8wc0hvWJdHL6XzDnHR2rrUHPGLMg4ia2B5MhYGKpl
-/1eQk2UnA2rFTLC2P+TlKJbTFTUytxDvoCbR7ODCmneSJ65txG3XFDEd0soGayn4
-w5UchowGTqGEMu/P1ORihYtYC6b8Q1gHFUomqcvryOtdE6b6lzQAMyU/VrG3vEwG
-dSsFWJh6PyMi2WTS5+CAHUYPbs3wZbNxTU74PyHch1Hcl3zwXa3bheqzHZojYh28
-GvvaPXBAHD6xwnCOWTMw3hBgLnjTZKsc62aFqgJ1Zz1VqN+Xlo8mlTZYDGhzwNU3
-m0UfRz2tSeqpbTFty3ObzTfDNYiXe4Y3J6ktD3pjt7Pf/uKY8NNbOKlZ4WhWrqPn
-VGB67ci/pcMQjw/vCPVjOQwpjVMm/EaZ6GQw8TAxbsb9tB5w2NoTncMkNNiPNB4/
-5gquK2zZL8hsPqcE5yY/n+2/zgxhO7E7KuE20dbt1BCW+wmS4e77a7cx3EFgLc7f
-oTGGuh3T+zdI/kxt5FAUBNnFiPWN9zJjQ8e080j7UIyL1Rhpvp+xG70ujwHvfL1I
-qczeFT77eI2aMNU9iX/vbkVdgEKlxD6YDw626PxJR5WQz99zHiKwfDPUf9rJW72q
-tAbGZ3DjfMk/VrerOMFDEGPA1V9Fs9kxGye1DIPAVw4IOwAbqE8=
-=9EnL
------END PGP SIGNATURE-----
diff --git a/SOURCES/nmstate-1.2.1.tar.gz.asc b/SOURCES/nmstate-1.2.1.tar.gz.asc
new file mode 100644
index 0000000..9fdc04f
--- /dev/null
+++ b/SOURCES/nmstate-1.2.1.tar.gz.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCAAdFiEE8f1XsqXpyNthgIbGbM3lj+QeKP8FAmIKEdcACgkQbM3lj+Qe
+KP/qUw//UUp2FkgRKtVwmv0uCv3o7DlQAmj5nHmoEgQpRvjzVQZsDIlJzTv2h7/X
+O44rQmJPbtqgyQE34mc3uioXtttiIhiwJtkgK4mYe1uQRD1txy9DgTPZMoRJWbi5
+vallg5jfPmUBENcMpLrcBZgJo6IRGQBZBpC0rNPfhufNjvVNSAyvARyzTXFaxaDx
+ajRYoFHnW38lOhx3Dyz5jtkPsDtq3oLsVI1G/xpeLOGc/SpCDcJJac70ODlJKOrC
+Zve9xnOdIrjfnI1QLdZ0gH8rAyreOFqRiPm+jyGuyxw9OIcdWxT37pr7s8I2/ct8
+C+qsMOyqY63O01uus9JidbEIbivdaP5l0xFS51tIt/XEt0PCJ64rA0q/RdBGd5v1
+V5h+K1ptQvdAmYI11LtLJDIvLYTc6KeFcjNRfT0s2GO/KFkYrVe1dq+yFj2sCB2u
+DD6UnZUzE2bUzN5lumqBQEmMSi2H6cTVLQYbif8iJbCgPM71JAio5gYmOC5VBp8P
+yaMKBFBYPwQAIZDN71shDVRT9BttRMAnBFOdPLqm03ht9BgkYihbbAZlNUipVrN0
+SIhsPZNc53psWH6ehDqYF8gJHucxZBipPd/qslzxhUJpn5Y4gITHJkrnFunz+4Cs
+qv01LQrvn7RMz2P13YN5GzLY7WDRcjNO6aj0adgHyO9PI8wSMsc=
+=Arcd
+-----END PGP SIGNATURE-----
diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec
index b40dd97..a9d79d1 100644
--- a/SPECS/nmstate.spec
+++ b/SPECS/nmstate.spec
@@ -3,26 +3,20 @@
 %define libname libnmstate
 
 Name:           nmstate
-Version:        1.1.0
-Release:        6%{?dist}
+Version:        1.2.1
+Release:        1%{?dist}
 Summary:        Declarative network manager API
 License:        LGPLv2+
 URL:            https://github.com/%{srcname}/%{srcname}
 Source0:        %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz
 Source1:        %{url}/releases/download/v%{version}/%{srcname}-%{version}.tar.gz.asc
 Source2:        https://www.nmstate.io/nmstate.gpg
-Patch1:         0001-nmstatectl-fix-long-arguments-support.patch
-Patch2:         0002-nm-ethtool-Preserve-existing-ethtool-settings-when-u.patch
-Patch3:         0003-ovs-fix-state-ignore-for-ovs-port-when-removing-them.patch
-Patch4:         0004-nispor-fix-show-of-empty-next_hop_address-and-destin.patch
-Patch5:         BZ_2034139-do-not-rename-connection.patch
-Patch6:         BZ_2034139-ovs-remove-ovs-port-prefix.patch
-Patch7:         BZ_2039285-sriov-New-way-to-wait-VF-been-created.patch
-Patch8:         BZ_2054054-support-multipath-route.patch
-BuildArch:      noarch
+Source3:        nmstate-rust-vendor-%{version}.tar.xz
 BuildRequires:  python3-devel
 BuildRequires:  python3-setuptools
 BuildRequires:  gnupg2
+BuildRequires:  rust-toolset
+BuildRequires:  pkg-config
 Requires:       python3-setuptools
 Requires:       python3-%{libname} = %{?epoch:%{epoch}:}%{version}-%{release}
 
@@ -35,6 +29,7 @@ provider support on the southbound.
 
 %package -n python3-%{libname}
 Summary:        nmstate Python 3 API library
+BuildArch:      noarch
 Requires:       NetworkManager-libnm >= 1:1.26.0
 # Use Recommends for NetworkManager because only access to NM DBus is required,
 # but NM could be running on a different host
@@ -50,11 +45,28 @@ Requires:       python3dist(varlink)
 
 %package -n nmstate-plugin-ovsdb
 Summary:        nmstate plugin for OVS database manipulation
+BuildArch:      noarch
 Requires:       python3-%{libname} = %{?epoch:%{epoch}:}%{version}-%{release}
 # The python-openvswitch rpm pacakge is not in the same repo with nmstate,
 # hence state it as Recommends, no requires.
 Recommends:     python3dist(ovs)
 
+
+%package libs
+Summary:        C binding of nmstate
+License:        ASL 2.0
+
+%package devel
+Summary:        C binding development files of nmstate
+License:        ASL 2.0
+Requires:       nmstate-libs%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description libs
+This package contains the C binding of nmstate.
+
+%description devel
+This package contains the C binding development files of nmstate.
+
 %description -n python3-%{libname}
 This package contains the Python 3 library for nmstate.
 
@@ -66,11 +78,32 @@ gpg2 --import --import-options import-export,import-minimal %{SOURCE2} > ./gpgke
 gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
 %autosetup -p1
 
+pushd rust
+# Source3 is vendored dependencies
+%cargo_prep -V 3
+
+# The cargo_prep will create `.cargo/config` which take precedence over
+# `.cargo/config.toml` shipped by upstream which fix the SONAME of cdylib.
+# To workaround that, merge upstream rustflags into cargo_prep created one.
+_FLAGS=`sed -ne 's/rustflags = "\(.\+\)"/\1/p' .cargo/config.toml`
+sed -i -e "s/rustflags = \[\(.\+\), \]$/rustflags = [\1, \"$_FLAGS\"]/" \
+    .cargo/config
+rm .cargo/config.toml
+
+popd
+
 %build
 %py3_build
 
+pushd rust
+make
+popd
+
 %install
 %py3_install
+pushd rust
+env SKIP_PYTHON_INSTALL=1 PREFIX=%{_prefix} LIBDIR=%{_libdir} %make_install
+popd
 
 %files
 %doc README.md
@@ -79,6 +112,7 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
 %{_mandir}/man8/nmstate-autoconf.8*
 %{python3_sitelib}/nmstatectl
 %{_bindir}/nmstatectl
+%{_bindir}/nmstatectl-rust
 %{_bindir}/nmstate-autoconf
 
 %files -n python3-%{libname}
@@ -92,15 +126,45 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
 %{python3_sitelib}/%{libname}/plugins/nmstate_plugin_ovsdb*
 %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb*
 
+%files libs
+%license rust/LICENSE
+%{_libdir}/libnmstate.so.*
+
+%files devel
+%license LICENSE
+%{_libdir}/libnmstate.so
+%{_includedir}/nmstate.h
+%{_libdir}/pkgconfig/nmstate.pc
+
+%post libs
+/sbin/ldconfig
+
+%postun libs
+/sbin/ldconfig
+
 %changelog
-* Thu Feb 10 2022 Gris Ge <fge@redhat.com> - 1.1.0-6
-- Workaround CNV issue by supporting multipath route. RHBZ#2054054
+* Mon Feb 14 2022 Gris Ge <fge@redhat.com> - 1.2.1-1
+- Upgrade to 1.2.1. RHBZ#1996618
+
+* Thu Jan 27 2022 Gris Ge <ferferna@redhat.com> - 1.2.1-0.2.alpha2
+- Upgrade to 1.2.1 alpha2. RHBZ#1996618
+
+* Thu Jan 13 2022 Gris Ge <fge@redhat.com> - 1.2.1-0.1.alpha1
+- Upgrade to 1.2.1 alpha1. RHBZ#1996618
+
+* Thu Dec 16 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 1.2.0-1
+- Upgrade to 1.2.0. RHBZ#1996618
+
+* Thu Dec 09 2021 Gris Ge <fge@redhat.com> - 1.2.0-0.1.alpha2
+- Upgrade to 1.2.0 alpha2. RHBZ#1996618
 
-* Wed Jan 12 2022 Gris Ge <fge@redhat.com> - 1.1.0-5
-- Remove OVS interface postfix. RHBZ#2034139
+* Tue Oct 12 2021 Gris Ge <fge@redhat.com> - 1.2.0-0.1.alpha1
+- Upgrade to 1.2.0 alpha1.
 
-* Wed Jan 12 2022 Gris Ge <fge@redhat.com> - 1.1.0-4
-- Fix SR-IOV on ixgbe. RHBZ#2039285
+* Wed Sep 15 2021 Ana Cabral <acabral@redhat.com> - 1.1.1-0.1.alpha1
+- Upgrade to 1.1.1 alpha1.
+- Canonicalize ipv6 addresses for dns nameservers. RHBZ#1911241
+- Throw better error when peer is missing for veth interfaces. RHBZ#1973973
 
 * Tue Jul 27 2021 Gris Ge <fge@redhat.com> - 1.1.0-3
 - Fix state=ignore for OVS interface. RHBZ#1944054