diff --git a/SOURCES/BZ_1908724-sriov-use-verification-retry-to-wait-VF-been-created.patch b/SOURCES/BZ_1908724-sriov-use-verification-retry-to-wait-VF-been-created.patch
new file mode 100644
index 0000000..8427fbd
--- /dev/null
+++ b/SOURCES/BZ_1908724-sriov-use-verification-retry-to-wait-VF-been-created.patch
@@ -0,0 +1,185 @@
+From 1d0656c4197f0119d156b0df7b13bffeb5c46861 Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Mon, 4 Jan 2021 11:37:18 +0800
+Subject: [PATCH] sriov: Use verification retry to wait VF been created
+
+When reactivating i40e interface with SR-IOV enabled, the kernel
+takes some time(1 seconds or more in my test) to get the VF interface
+ready in kernel. So at the time of libnmstate returns with success, the
+VF interface might not be ready for use yet.
+
+To fix that, we include VF interfaces in desire state when PV is
+changed/desired. The verification retry will wait the VF to be ready for
+use.
+
+Unit test case and integration test case included.
+
+Also fixed SRIOV integration test cases which are now all passing on i40e
+NIC.
+
+To test on real SRIOV NIC:
+
+    cd tests/integration/
+    sudo env TEST_REAL_NIC=ens1f1 pytest-3 sriov_test.py -vvv
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/ifaces/ethernet.py   |  28 ++++
+ libnmstate/ifaces/ifaces.py     |  23 +++
+ libnmstate/nm/sriov.py          |   2 +-
+ libnmstate/nm/wired.py          |  19 ++-
+ tests/integration/sriov_test.py | 242 ++++++++++++++++++++++----------
+ tests/lib/nm/wired_test.py      |   5 +
+ 6 files changed, 231 insertions(+), 88 deletions(-)
+
+diff --git a/libnmstate/ifaces/ethernet.py b/libnmstate/ifaces/ethernet.py
+index b346c36..644fe6d 100644
+--- a/libnmstate/ifaces/ethernet.py
++++ b/libnmstate/ifaces/ethernet.py
+@@ -18,6 +18,9 @@
+ #
+ 
+ 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
+ 
+@@ -46,6 +49,31 @@ class EthernetIface(BaseIface):
+         _capitalize_sriov_vf_mac(state)
+         return state
+ 
++    @property
++    def sriov_total_vfs(self):
++        return (
++            self.raw.get(Ethernet.CONFIG_SUBTREE, {})
++            .get(Ethernet.SRIOV_SUBTREE, {})
++            .get(Ethernet.SRIOV.TOTAL_VFS, 0)
++        )
++
++    def create_sriov_vf_ifaces(self):
++        return [
++            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.TYPE: InterfaceType.ETHERNET,
++                    # VF will be in DOWN state initialy.
++                    Interface.STATE: InterfaceState.DOWN,
++                }
++            )
++            for i in range(0, self.sriov_total_vfs)
++        ]
++
+ 
+ def _capitalize_sriov_vf_mac(state):
+     vfs = (
+diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
+index 703e672..7723f43 100644
+--- a/libnmstate/ifaces/ifaces.py
++++ b/libnmstate/ifaces/ifaces.py
+@@ -97,6 +97,7 @@ class Ifaces:
+                 self._ifaces[iface.name] = iface
+ 
+             self._create_virtual_slaves()
++            self._create_sriov_vfs_when_changed()
+             self._validate_unknown_slaves()
+             self._validate_unknown_parent()
+             self._gen_metadata()
+@@ -124,6 +125,28 @@ class Ifaces:
+         for iface in new_ifaces:
+             self._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._ifaces.values():
++            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():
++                    if new_iface.name not in self._ifaces:
++                        new_iface.mark_as_desired()
++                        new_ifaces.append(new_iface)
++        for new_iface in new_ifaces:
++            self._ifaces[new_iface.name] = new_iface
++
+     def _pre_edit_validation_and_cleanup(self):
+         self._validate_over_booked_slaves()
+         self._validate_vlan_mtu()
+diff --git a/libnmstate/nm/sriov.py b/libnmstate/nm/sriov.py
+index f544732..25b150c 100644
+--- a/libnmstate/nm/sriov.py
++++ b/libnmstate/nm/sriov.py
+@@ -68,7 +68,7 @@ def create_setting(context, iface_state, base_con_profile):
+     sriov_config = iface_state.get(Ethernet.CONFIG_SUBTREE, {}).get(
+         Ethernet.SRIOV_SUBTREE
+     )
+-    if sriov_config:
++    if sriov_config and sriov_config.get(Ethernet.SRIOV.TOTAL_VFS):
+         if not _has_sriov_capability(context, ifname):
+             raise NmstateNotSupportedError(
+                 f"Interface '{ifname}' does not support SR-IOV"
+diff --git a/libnmstate/nm/wired.py b/libnmstate/nm/wired.py
+index 64662ac..5fea2a5 100644
+--- a/libnmstate/nm/wired.py
++++ b/libnmstate/nm/wired.py
+@@ -162,15 +162,18 @@ def _get_mac_address_from_sysfs(ifname):
+ 
+ 
+ def _get_ethernet_info(device, iface):
++
+     ethernet = {}
++    sriov_info = sriov.get_info(device)
++    if sriov_info:
++        ethernet.update(sriov_info)
++
+     try:
+         speed = int(device.get_speed())
+         if speed > 0:
+             ethernet[Ethernet.SPEED] = speed
+-        else:
+-            return None
+     except AttributeError:
+-        return None
++        pass
+ 
+     ethtool_results = minimal_ethtool(iface)
+     auto_setting = ethtool_results[Ethernet.AUTO_NEGOTIATION]
+@@ -178,17 +181,11 @@ def _get_ethernet_info(device, iface):
+         ethernet[Ethernet.AUTO_NEGOTIATION] = True
+     elif auto_setting is False:
+         ethernet[Ethernet.AUTO_NEGOTIATION] = False
+-    else:
+-        return None
+ 
+     duplex_setting = ethtool_results[Ethernet.DUPLEX]
+     if duplex_setting in [Ethernet.HALF_DUPLEX, Ethernet.FULL_DUPLEX]:
+         ethernet[Ethernet.DUPLEX] = duplex_setting
+-    else:
+-        return None
+-
+-    sriov_info = sriov.get_info(device)
+-    if sriov_info:
+-        ethernet.update(sriov_info)
+ 
++    if not ethernet:
++        return None
+     return ethernet
+ 
+ 
+-- 
+2.25.4
+
diff --git a/SOURCES/BZ_1910193-support-multiple-gateways.patch b/SOURCES/BZ_1910193-support-multiple-gateways.patch
new file mode 100644
index 0000000..b9aa129
--- /dev/null
+++ b/SOURCES/BZ_1910193-support-multiple-gateways.patch
@@ -0,0 +1,92 @@
+From 47bd6db50e33aaa3d3d5e3b70d5f3039122b3a5c Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Sat, 5 Sep 2020 00:36:41 +0800
+Subject: [PATCH] nm route: Add support of multiple gateways
+
+Since NetworkManager 1.22.0, the `NM.SettingIPConfig.props.routes`
+support assigning multiple gateway.
+
+Integration test case updated for this.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/nm/route.py | 30 +++---------------------------
+ 1 file changed, 3 insertions(+), 27 deletions(-)
+
+diff --git a/libnmstate/nm/route.py b/libnmstate/nm/route.py
+index 53bcf7c..548b218 100644
+--- a/libnmstate/nm/route.py
++++ b/libnmstate/nm/route.py
+@@ -21,7 +21,6 @@ from operator import itemgetter
+ import socket
+ 
+ from libnmstate import iplib
+-from libnmstate.error import NmstateNotImplementedError
+ from libnmstate.error import NmstateValueError
+ from libnmstate.nm import active_connection as nm_ac
+ from libnmstate.schema import Interface
+@@ -31,7 +30,6 @@ from libnmstate.schema import RouteRule
+ from .common import GLib
+ from .common import NM
+ 
+-NM_ROUTE_TABLE_ATTRIBUTE = "table"
+ IPV4_DEFAULT_GATEWAY_DESTINATION = "0.0.0.0/0"
+ IPV6_DEFAULT_GATEWAY_DESTINATION = "::/0"
+ 
+@@ -116,7 +114,7 @@ def get_config(acs_and_ip_profiles):
+ 
+ 
+ def _get_per_route_table_id(nm_route, default_table_id):
+-    table = nm_route.get_attribute(NM_ROUTE_TABLE_ATTRIBUTE)
++    table = nm_route.get_attribute(NM.IP_ROUTE_ATTRIBUTE_TABLE)
+     return int(table.get_uint32()) if table else default_table_id
+ 
+ 
+@@ -152,19 +150,7 @@ def _get_default_route_config(gateway, metric, default_table_id, iface_name):
+ 
+ def add_routes(setting_ip, routes):
+     for route in routes:
+-        if route[Route.DESTINATION] in (
+-            IPV4_DEFAULT_GATEWAY_DESTINATION,
+-            IPV6_DEFAULT_GATEWAY_DESTINATION,
+-        ):
+-            if setting_ip.get_gateway():
+-                raise NmstateNotImplementedError(
+-                    "Only a single default gateway is supported due to a "
+-                    "limitation of NetworkManager: "
+-                    "https://bugzilla.redhat.com/1707396"
+-                )
+-            _add_route_gateway(setting_ip, route)
+-        else:
+-            _add_specfic_route(setting_ip, route)
++        _add_specfic_route(setting_ip, route)
+ 
+ 
+ def _add_specfic_route(setting_ip, route):
+@@ -181,22 +167,12 @@ def _add_specfic_route(setting_ip, route):
+     )
+     table_id = route.get(Route.TABLE_ID, Route.USE_DEFAULT_ROUTE_TABLE)
+     ip_route.set_attribute(
+-        NM_ROUTE_TABLE_ATTRIBUTE, GLib.Variant.new_uint32(table_id)
++        NM.IP_ROUTE_ATTRIBUTE_TABLE, GLib.Variant.new_uint32(table_id)
+     )
+     # Duplicate route entry will be ignored by libnm.
+     setting_ip.add_route(ip_route)
+ 
+ 
+-def _add_route_gateway(setting_ip, route):
+-    setting_ip.props.gateway = route[Route.NEXT_HOP_ADDRESS]
+-    setting_ip.props.route_table = route.get(
+-        Route.TABLE_ID, Route.USE_DEFAULT_ROUTE_TABLE
+-    )
+-    setting_ip.props.route_metric = route.get(
+-        Route.METRIC, Route.USE_DEFAULT_METRIC
+-    )
+-
+-
+ def get_static_gateway_iface(family, iface_routes):
+     """
+     Return one interface with gateway for given IP family.
+-- 
+2.27.0
+
diff --git a/SOURCES/BZ_1916073_better-handling-for-timeout.patch b/SOURCES/BZ_1916073_better-handling-for-timeout.patch
new file mode 100644
index 0000000..b47a1d8
--- /dev/null
+++ b/SOURCES/BZ_1916073_better-handling-for-timeout.patch
@@ -0,0 +1,419 @@
+From 803ad90f11eb57221e7805e5cba8c309bafe1de8 Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Wed, 13 Jan 2021 23:51:31 +0800
+Subject: [PATCH 1/2] nm: Better handling for timeout
+
+When creating 1000 VLAN along with 1000 bridge using each VLAN,
+NetworkManager might trigger two timeout:
+
+ * The callback raises `Gio.IOErrorEnum.TIMED_OUT` error.
+ * NetworkManager never call callback, nmstate idle check trigger the
+   timeout.
+
+To solve above issue:
+
+ * Increase the nmstate idle check timeout to 5 minutes.
+
+ * For actions like add profile and activate profile, we add a
+   fallback checker which check whether requested action is already
+   finished using `GLib.timeout_source_new()`
+
+ * When `Gio.IOErrorEnum.TIMED_OUT` happens, ignore the failure and wait
+   fallback checker.
+
+ * The fallback checker is only started 15 seconds after action started,
+   so this does not impact small desire state.
+
+Test results on RHEL 8.3 i7-8665U 2G RAM:
+
+    10m29.212s to create 1000 VLAN and 1000 bridge over each VLAN.
+
+Changed the integration test case to test 500 VLANs + 500 bridges.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/nm/connection.py | 67 ++++++++++++++++++++++++++++++++++++-
+ libnmstate/nm/context.py    |  3 +-
+ 2 files changed, 67 insertions(+), 3 deletions(-)
+
+diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
+index 1f6c734..374a379 100644
+--- a/libnmstate/nm/connection.py
++++ b/libnmstate/nm/connection.py
+@@ -1,5 +1,5 @@
+ #
+-# Copyright (c) 2018-2020 Red Hat, Inc.
++# Copyright (c) 2018-2021 Red Hat, Inc.
+ #
+ # This file is part of nmstate
+ #
+@@ -24,11 +24,14 @@ from libnmstate.error import NmstateLibnmError
+ from libnmstate.error import NmstateInternalError
+ from libnmstate.error import NmstateValueError
+ 
++from .common import GLib
++from .common import Gio
+ from .common import NM
+ from . import ipv4
+ from . import ipv6
+ 
+ ACTIVATION_TIMEOUT_FOR_BRIDGE = 35  # Bridge STP requires 30 seconds.
++FALLBACK_CHECKER_INTERNAL = 15
+ 
+ 
+ class ConnectionProfile:
+@@ -40,6 +43,7 @@ class ConnectionProfile:
+         self._nm_ac = None
+         self._ac_handlers = set()
+         self._dev_handlers = set()
++        self._fallback_checker = None
+ 
+     def create(self, settings):
+         self.profile = NM.SimpleConnection.new()
+@@ -102,6 +106,26 @@ class ConnectionProfile:
+             self._add_connection2_callback,
+             user_data,
+         )
++        self._fallback_checker = GLib.timeout_source_new(
++            FALLBACK_CHECKER_INTERNAL * 1000
++        )
++        self._fallback_checker.set_callback(
++            self._profile_add_fallback_checker_callback, action
++        )
++        self._fallback_checker.attach(self._ctx.context)
++
++    def _profile_add_fallback_checker_callback(self, action):
++        for nm_profile in self._ctx.client.get_connections():
++            if nm_profile.get_uuid() == self.profile.get_uuid():
++                self._fallback_checker_cleanup()
++                self._ctx.finish_async(action)
++                return GLib.SOURCE_REMOVE
++        return GLib.SOURCE_CONTINUE
++
++    def _fallback_checker_cleanup(self):
++        if self._fallback_checker:
++            self._fallback_checker.destroy()
++            self._fallback_checker = None
+ 
+     def delete(self):
+         if not self.profile:
+@@ -152,6 +176,26 @@ class ConnectionProfile:
+             self._active_connection_callback,
+             user_data,
+         )
++        self._fallback_checker = GLib.timeout_source_new(
++            FALLBACK_CHECKER_INTERNAL * 1000
++        )
++        self._fallback_checker.set_callback(
++            self._activation_fallback_checker_callback, action
++        )
++        self._fallback_checker.attach(self._ctx.context)
++
++    def _activation_fallback_checker_callback(self, action):
++        if self.devname:
++            self._nm_dev = self._ctx.get_nm_dev(self.devname)
++            if self._nm_dev:
++                self._activation_progress_check(action)
++            return GLib.SOURCE_CONTINUE
++        else:
++            logging.warn(
++                "Failed to get interface name from profile, "
++                "can not perform flalback check on activation"
++            )
++            return GLib.SOURCE_REMOVE
+ 
+     @property
+     def profile(self):
+@@ -213,6 +257,18 @@ class ConnectionProfile:
+ 
+         try:
+             nm_act_con = src_object.activate_connection_finish(result)
++        except GLib.Error as e:
++            if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
++                logging.debug(
++                    f"{action} timeout on activation, "
++                    "using fallback method to wait activation"
++                )
++                return
++            else:
++                self._ctx.fail(
++                    NmstateLibnmError(f"{action} failed: error={e}")
++                )
++                return
+         except Exception as e:
+             self._ctx.fail(NmstateLibnmError(f"{action} failed: error={e}"))
+             return
+@@ -366,6 +422,7 @@ class ConnectionProfile:
+     def _activation_clean_up(self):
+         self._remove_ac_handlers()
+         self._remove_dev_handlers()
++        self._fallback_checker_cleanup()
+ 
+     def _is_activating(self):
+         if not self._nm_ac or not self._nm_dev:
+@@ -396,6 +453,13 @@ class ConnectionProfile:
+         action = user_data
+         try:
+             profile = src_object.add_connection2_finish(result)[0]
++        except GLib.Error as e:
++            if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
++                logging.debug(
++                    f"{action} timeout, using fallback method to "
++                    "wait profile creation"
++                )
++                return
+         except Exception as e:
+             self._ctx.fail(
+                 NmstateLibnmError(f"{action} failed with error: {e}")
+@@ -410,6 +474,7 @@ class ConnectionProfile:
+                 )
+             )
+         else:
++            self._fallback_checker_cleanup()
+             self._ctx.finish_async(action)
+ 
+     def _update2_callback(self, src_object, result, user_data):
+diff --git a/libnmstate/nm/context.py b/libnmstate/nm/context.py
+index 373ffe8..bc5c41c 100644
+--- a/libnmstate/nm/context.py
++++ b/libnmstate/nm/context.py
+@@ -31,8 +31,7 @@ from .common import Gio
+ # last finish async action.
+ IDLE_CHECK_INTERNAL = 5
+ 
+-# libnm dbus connection has reply timeout 25 seconds.
+-IDLE_TIMEOUT = 25
++IDLE_TIMEOUT = 60 * 5  # 5 minutes
+ 
+ # NetworkManage is using dbus in libnm while the dbus has limitation on
+ # maximum number of pending replies per connection.(RHEL/CentOS 8 is 1024)
+-- 
+2.27.0
+
+
+From ac82d18f96aa2313583efa1477be441291e2957c Mon Sep 17 00:00:00 2001
+From: Fernando Fernandez Mancera <ffmancera@riseup.net>
+Date: Sun, 17 Jan 2021 11:18:10 +0800
+Subject: [PATCH 2/2] nm: Use fallback checker on profile deactivation and
+ delete
+
+When NM is under heave loads, NM might raise timeout error when
+try to deactivate or delete a profile, to solve that this patch
+introduce the same method in 2407f98
+to have a fallback checker on whether profile is deactivated/deleted
+every 15 seconds.
+
+No test case required as current `test_lot_of_vlans_with_bridges` test
+case has `state: absent` which is good enough for testing this patch.
+
+Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/nm/active_connection.py | 61 ++++++++++++++++++++++++------
+ libnmstate/nm/connection.py        | 30 +++++++++++++++
+ 2 files changed, 80 insertions(+), 11 deletions(-)
+
+diff --git a/libnmstate/nm/active_connection.py b/libnmstate/nm/active_connection.py
+index 062c78a..b235e8b 100644
+--- a/libnmstate/nm/active_connection.py
++++ b/libnmstate/nm/active_connection.py
+@@ -21,12 +21,14 @@ import logging
+ 
+ from libnmstate.error import NmstateLibnmError
+ 
++from .common import Gio
+ from .common import GLib
+ from .common import GObject
+ from .common import NM
+ 
+ 
+ NM_AC_STATE_CHANGED_SIGNAL = "state-changed"
++FALLBACK_CHECKER_INTERNAL = 15
+ 
+ 
+ class ActivationError(Exception):
+@@ -37,6 +39,8 @@ class ActiveConnection:
+     def __init__(self, context=None, nm_ac_con=None):
+         self._ctx = context
+         self._act_con = nm_ac_con
++        self._signal_handler = None
++        self._fallback_checker = None
+ 
+         nmdevs = None
+         if nm_ac_con:
+@@ -75,19 +79,35 @@ class ActiveConnection:
+ 
+         action = f"Deactivate profile: {self.devname}"
+         self._ctx.register_async(action)
+-        handler_id = act_connection.connect(
++        self._signal_handler = act_connection.connect(
+             NM_AC_STATE_CHANGED_SIGNAL,
+             self._wait_state_changed_callback,
+             action,
+         )
+         if act_connection.props.state != NM.ActiveConnectionState.DEACTIVATING:
+-            user_data = (handler_id, action)
++            user_data = action
+             self._ctx.client.deactivate_connection_async(
+                 act_connection,
+                 self._ctx.cancellable,
+                 self._deactivate_connection_callback,
+                 user_data,
+             )
++        self._fallback_checker = GLib.timeout_source_new(
++            FALLBACK_CHECKER_INTERNAL * 1000
++        )
++        self._fallback_checker.set_callback(
++            self._deactivation_fallback_checker_callback, action
++        )
++        self._fallback_checker.attach(self._ctx.context)
++
++    def _clean_up(self):
++        if self._signal_handler:
++            if self._act_con:
++                self._act_con.handler_disconnect(self._signal_handler)
++            self._signal_handler = None
++        if self._fallback_checker:
++            self._fallback_checker.destroy()
++            self._fallback_checker = None
+ 
+     def _wait_state_changed_callback(self, act_con, state, reason, action):
+         if self._ctx.is_cancelled():
+@@ -96,13 +116,13 @@ class ActiveConnection:
+             logging.debug(
+                 "Connection deactivation succeeded on %s", self.devname,
+             )
++            self._clean_up()
+             self._ctx.finish_async(action)
+ 
+     def _deactivate_connection_callback(self, src_object, result, user_data):
+-        handler_id, action = user_data
++        action = user_data
+         if self._ctx.is_cancelled():
+-            if self._act_con:
+-                self._act_con.handler_disconnect(handler_id)
++            self._clean_up()
+             return
+ 
+         try:
+@@ -116,16 +136,20 @@ class ActiveConnection:
+                     "Connection is not active on {}, no need to "
+                     "deactivate".format(self.devname)
+                 )
++            elif e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
++                logging.debug(
++                    f"{action} timeout, using fallback method to wait profile "
++                    "deactivation"
++                )
++                return
+             else:
+-                if self._act_con:
+-                    self._act_con.handler_disconnect(handler_id)
++                self._clean_up()
+                 self._ctx.fail(
+                     NmstateLibnmError(f"{action} failed: error={e}")
+                 )
+                 return
+         except Exception as e:
+-            if self._act_con:
+-                self._act_con.handler_disconnect(handler_id)
++            self._clean_up()
+             self._ctx.fail(
+                 NmstateLibnmError(
+                     f"BUG: Unexpected error when activating {self.devname} "
+@@ -135,8 +159,7 @@ class ActiveConnection:
+             return
+ 
+         if not success:
+-            if self._act_con:
+-                self._act_con.handler_disconnect(handler_id)
++            self._clean_up()
+             self._ctx.fail(
+                 NmstateLibnmError(
+                     f"{action} failed: error='None returned from "
+@@ -144,6 +167,22 @@ class ActiveConnection:
+                 )
+             )
+ 
++    def _deactivation_fallback_checker_callback(self, action):
++        if self.devname:
++            self._nmdev = self._ctx.get_nm_dev(self.devname)
++            if self._nmdev:
++                self._act_con = self._nmdev.get_active_connection()
++                if (
++                    self._act_con
++                    and self._act_con.props.state
++                    != NM.ActiveConnectionState.DEACTIVATED
++                ):
++                    return GLib.SOURCE_CONTINUE
++
++        self._clean_up()
++        self._ctx.finish_async(action)
++        return GLib.SOURCE_REMOVE
++
+     @property
+     def nm_active_connection(self):
+         return self._act_con
+diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
+index 374a379..af7296f 100644
+--- a/libnmstate/nm/connection.py
++++ b/libnmstate/nm/connection.py
+@@ -144,6 +144,13 @@ class ConnectionProfile:
+                 self._delete_connection_callback,
+                 user_data,
+             )
++            self._fallback_checker = GLib.timeout_source_new(
++                FALLBACK_CHECKER_INTERNAL * 1000
++            )
++            self._fallback_checker.set_callback(
++                self._delete_fallback_checker_callback, action
++            )
++            self._fallback_checker.attach(self._ctx.context)
+ 
+     def activate(self):
+         if self.con_id:
+@@ -504,11 +511,24 @@ class ConnectionProfile:
+         action = user_data
+         try:
+             success = src_object.delete_finish(result)
++        except GLib.Error as e:
++            if e.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT):
++                logging.debug(
++                    f"{action} timeout, using fallback method to wait profile "
++                    "deletion"
++                )
++                return
++            else:
++                self._ctx.fail(
++                    NmstateLibnmError(f"{action} failed with error: {e}")
++                )
++                return
+         except Exception as e:
+             self._ctx.fail(NmstateLibnmError(f"{action} failed: error={e}"))
+             return
+ 
+         if success:
++            self._fallback_checker_cleanup()
+             self._ctx.finish_async(action)
+         else:
+             self._ctx.fail(
+@@ -518,6 +538,16 @@ class ConnectionProfile:
+                 )
+             )
+ 
++    def _delete_fallback_checker_callback(self, action):
++        if self.profile:
++            for nm_profile in self._ctx.client.get_connections():
++                if nm_profile.get_uuid() == self.profile.get_uuid():
++                    return GLib.SOURCE_CONTINUE
++
++        self._fallback_checker_cleanup()
++        self._ctx.finish_async(action)
++        return GLib.SOURCE_REMOVE
++
+     def _reset_profile(self):
+         self._con_profile = None
+ 
+-- 
+2.27.0
+
diff --git a/SOURCES/BZ_1916073_retry_on_failure_when_activate.patch b/SOURCES/BZ_1916073_retry_on_failure_when_activate.patch
new file mode 100644
index 0000000..da7f372
--- /dev/null
+++ b/SOURCES/BZ_1916073_retry_on_failure_when_activate.patch
@@ -0,0 +1,58 @@
+From 013860d2576a34a277178e6afba0935498dc4f72 Mon Sep 17 00:00:00 2001
+From: Fernando Fernandez Mancera <ffmancera@riseup.net>
+Date: Wed, 3 Feb 2021 11:59:05 +0100
+Subject: [PATCH] connection: retry on profile activation if libnm error
+ happened
+
+When activating a profile if NetworkManager fails during the activation,
+Nmstate should retry it once.
+
+Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
+---
+ libnmstate/nm/connection.py | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
+index 5b9a3aee2..45cb69019 100644
+--- a/libnmstate/nm/connection.py
++++ b/libnmstate/nm/connection.py
+@@ -180,7 +180,8 @@ def activate(self):
+                 "BUG: Cannot activate a profile with empty profile id and "
+                 "empty NM.Device"
+             )
+-        user_data = action
++        retry = True
++        user_data = action, retry
+         self._ctx.register_async(action)
+         self._ctx.client.activate_connection_async(
+             self.profile,
+@@ -267,7 +268,7 @@ def _active_connection_callback(self, src_object, result, user_data):
+         if self._ctx.is_cancelled():
+             self._activation_clean_up()
+             return
+-        action = user_data
++        action, retry = user_data
+ 
+         try:
+             nm_act_con = src_object.activate_connection_finish(result)
+@@ -279,6 +280,20 @@ def _active_connection_callback(self, src_object, result, user_data):
+                 )
+                 return
+             else:
++                if retry:
++                    retry = False
++                    user_data = action, retry
++                    specific_object = None
++                    logging.debug(f"Action {action} failed, trying again.")
++                    self._ctx.client.activate_connection_async(
++                        self.profile,
++                        self.nmdevice,
++                        specific_object,
++                        self._ctx.cancellable,
++                        self._active_connection_callback,
++                        user_data,
++                    )
++                    return
+                 self._ctx.fail(
+                     NmstateLibnmError(f"{action} failed: error={e}")
+                 )
diff --git a/SOURCES/BZ_1918712_use_uuid_for_vlan_vxlan_parent.patch b/SOURCES/BZ_1918712_use_uuid_for_vlan_vxlan_parent.patch
new file mode 100644
index 0000000..3c42770
--- /dev/null
+++ b/SOURCES/BZ_1918712_use_uuid_for_vlan_vxlan_parent.patch
@@ -0,0 +1,214 @@
+From a85b3dddf82f9e71774229740fbae6ea843d86d6 Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Mon, 18 Jan 2021 15:55:43 +0800
+Subject: [PATCH 1/2] ifaces: Don't validate undesired interface for overbook
+
+There is no need to validate overbooked interface for undesired
+controller interface.
+
+Unit test case included.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/ifaces/ifaces.py | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
+index 7723f43..ee75125 100644
+--- a/libnmstate/ifaces/ifaces.py
++++ b/libnmstate/ifaces/ifaces.py
+@@ -437,6 +437,8 @@ class Ifaces:
+         """
+         slave_master_map = {}
+         for iface in self._ifaces.values():
++            if not (iface.is_changed or iface.is_desired) or not iface.is_up:
++                continue
+             for slave_name in iface.slaves:
+                 cur_master = slave_master_map.get(slave_name)
+                 if cur_master:
+-- 
+2.27.0
+
+
+From 644d8e5f5072caaba7151e66f211eceb02ae79c3 Mon Sep 17 00:00:00 2001
+From: Gris Ge <fge@redhat.com>
+Date: Thu, 21 Jan 2021 20:43:34 +0800
+Subject: [PATCH 2/2] nm vlan/vxlan: Use uuid for VLAN/VxLAN parent
+
+When parent of VLAN/VxLAN is holding the same name of OVS bridge or OVS
+port, NetworkManager will fail with error failed to find interface index
+of that parent.
+
+The root cause is NetworkManager try to use user space interface as
+VLAN/VxLAN parent.
+
+To workaround that, use profile UUID for `NM.SettingVlan.props.parent`
+and `NM.SettingVxlan.props.parent`.
+
+Integration test case included.
+
+Signed-off-by: Gris Ge <fge@redhat.com>
+---
+ libnmstate/nm/applier.py    | 98 +++++++++++++++++++++++++++++++++++--
+ libnmstate/nm/connection.py |  7 +++
+ 2 files changed, 100 insertions(+), 5 deletions(-)
+
+diff --git a/libnmstate/nm/applier.py b/libnmstate/nm/applier.py
+index 26a057f..8e38df5 100644
+--- a/libnmstate/nm/applier.py
++++ b/libnmstate/nm/applier.py
+@@ -66,8 +66,6 @@ MASTER_IFACE_TYPES = (
+ 
+ 
+ def apply_changes(context, net_state, save_to_disk):
+-    con_profiles = []
+-
+     if (
+         not save_to_disk
+         and _has_ovs_interface_desired_or_changed(net_state)
+@@ -87,6 +85,10 @@ def apply_changes(context, net_state, save_to_disk):
+         _create_proxy_ifaces_desired_state(ifaces_desired_state)
+     )
+ 
++    # A list of tuple holding both current ConnectionProfile and new/updated
++    # ConnectionProfile.
++    pending_con_profiles = []
++
+     for iface_desired_state in filter(
+         lambda s: s.get(Interface.STATE) != InterfaceState.ABSENT,
+         ifaces_desired_state,
+@@ -131,7 +133,7 @@ def apply_changes(context, net_state, save_to_disk):
+                 # anything besides state:up and not been marked as changed.
+                 # We don't need to do this once we support querying on-disk
+                 # configure
+-                con_profiles.append(cur_con_profile)
++                pending_con_profiles.append((cur_con_profile, None))
+                 continue
+         new_con_profile = _build_connection_profile(
+             context,
+@@ -143,12 +145,25 @@ def apply_changes(context, net_state, save_to_disk):
+             set_conn = new_con_profile.profile.get_setting_connection()
+             set_conn.props.interface_name = iface_desired_state[Interface.NAME]
+         if cur_con_profile and cur_con_profile.profile:
+-            cur_con_profile.update(new_con_profile, save_to_disk)
+-            con_profiles.append(new_con_profile)
++            pending_con_profiles.append((cur_con_profile, new_con_profile))
+         else:
+             # Missing connection, attempting to create a new one.
++            pending_con_profiles.append((None, new_con_profile))
++
++    pending_con_profiles = _use_uuid_for_parent(
++        context, pending_con_profiles, save_to_disk
++    )
++
++    con_profiles = []
++    for cur_con_profile, new_con_profile in pending_con_profiles:
++        if cur_con_profile and new_con_profile:
++            cur_con_profile.update(new_con_profile, save_to_disk)
++            con_profiles.append(new_con_profile)
++        elif cur_con_profile is None and new_con_profile:
+             new_con_profile.add(save_to_disk)
+             con_profiles.append(new_con_profile)
++        elif cur_con_profile:
++            con_profiles.append(cur_con_profile)
+     context.wait_all_finish()
+ 
+     _set_ifaces_admin_state(context, ifaces_desired_state, con_profiles)
+@@ -655,3 +670,76 @@ def _mark_nm_external_subordinate_changed(context, net_state):
+                     subordinate_iface = net_state.ifaces.get(subordinate)
+                     if subordinate_iface:
+                         subordinate_iface.mark_as_changed()
++
++
++def _use_uuid_for_parent(context, pending_con_profiles, save_to_disk):
++    """
++    When parent of VLAN/VxLAN is holding the same name with
++    OVS bridge or OVS port, we should use UUID instead of interface name
++    """
++    new_pending_con_profiles = []
++    kernel_iface_name_to_uuid = {}
++    for cur_nm_profile in context.client.get_connections():
++        connection_type = cur_nm_profile.get_connection_type()
++        if connection_type not in (
++            NM.SETTING_OVS_BRIDGE_SETTING_NAME,
++            NM.SETTING_OVS_PORT_SETTING_NAME,
++        ):
++            kernel_iface_name_to_uuid[
++                cur_nm_profile.get_interface_name()
++            ] = cur_nm_profile.get_uuid()
++    # Override existing kernel_iface_name_to_uuid with pending changes.
++    for cur_con_profile, new_con_profile in pending_con_profiles:
++        if new_con_profile and new_con_profile.profile:
++            uuid = new_con_profile.profile.get_uuid()
++            connection_type = new_con_profile.profile.get_connection_type()
++            iface_name = new_con_profile.profile.get_interface_name()
++        elif cur_con_profile and cur_con_profile.profile:
++            uuid = cur_con_profile.profile.get_uuid()
++            connection_type = cur_con_profile.profile.get_connection_type()
++            iface_name = cur_con_profile.profile.get_interface_name()
++        else:
++            continue
++
++        if connection_type not in (
++            NM.SETTING_OVS_BRIDGE_SETTING_NAME,
++            NM.SETTING_OVS_PORT_SETTING_NAME,
++        ):
++            kernel_iface_name_to_uuid[iface_name] = uuid
++
++    for cur_con_profile, new_con_profile in pending_con_profiles:
++        new_pending_con_profiles.append((cur_con_profile, new_con_profile))
++        if not new_con_profile:
++            continue
++        nm_profile = new_con_profile.profile
++        if not nm_profile:
++            continue
++        connection_type = nm_profile.get_connection_type()
++        nm_setting = None
++        if connection_type == NM.SETTING_VLAN_SETTING_NAME:
++            nm_setting = nm_profile.get_setting_vlan()
++        elif connection_type == NM.SETTING_VXLAN_SETTING_NAME:
++            nm_setting = nm_profile.get_setting_vxlan()
++        else:
++            continue
++        if not nm_setting:
++            continue
++        parent_iface_name = nm_setting.props.parent
++        parent_uuid = kernel_iface_name_to_uuid.get(parent_iface_name)
++        if parent_uuid:
++            updated_con_profile = connection.ConnectionProfile(context)
++            new_nm_settings = []
++            for cur_nm_setting in nm_profile.get_settings():
++                new_nm_setting = cur_nm_setting.duplicate()
++                if new_nm_setting.get_name() in (
++                    NM.SETTING_VLAN_SETTING_NAME,
++                    NM.SETTING_VXLAN_SETTING_NAME,
++                ):
++                    new_nm_setting.props.parent = parent_uuid
++                new_nm_settings.append(new_nm_setting)
++            updated_con_profile.create(new_nm_settings)
++            new_pending_con_profiles.pop()
++            new_pending_con_profiles.append(
++                (cur_con_profile, updated_con_profile)
++            )
++    return new_pending_con_profiles
+diff --git a/libnmstate/nm/connection.py b/libnmstate/nm/connection.py
+index af7296f..5b9a3ae 100644
+--- a/libnmstate/nm/connection.py
++++ b/libnmstate/nm/connection.py
+@@ -63,6 +63,13 @@ class ConnectionProfile:
+         if self.con_id:
+             self.profile = self._ctx.client.get_connection_by_id(self.con_id)
+ 
++    def import_by_uuid(self, uuid):
++        for nm_profile in self._ctx.client.get_connections():
++            if nm_profile.get_uuid() == uuid:
++                self.profile = nm_profile
++                return
++        logging.debug(f"Failed to find {uuid} profile")
++
+     def update(self, con_profile, save_to_disk=True):
+         flags = NM.SettingsUpdate2Flags.BLOCK_AUTOCONNECT
+         if save_to_disk:
+-- 
+2.27.0
+
diff --git a/SPECS/nmstate.spec b/SPECS/nmstate.spec
index 5ee980f..c7f3b10 100644
--- a/SPECS/nmstate.spec
+++ b/SPECS/nmstate.spec
@@ -4,7 +4,7 @@
 
 Name:           nmstate
 Version:        0.3.4
-Release:        17%{?dist}
+Release:        25%{?dist}
 Summary:        Declarative network manager API
 License:        LGPLv2+
 URL:            https://github.com/%{srcname}/%{srcname}
@@ -24,6 +24,11 @@ Patch10:        BZ_1890497-nm-bond-Ignore-ad_actor_system-00-00-00-00-00-00.patc
 Patch11:        BZ_1890497-nm.ipv6-call-clear_routing_rules-when-creating-the-s.patch
 Patch12:        BZ_1901571_do_not_check_ovs_daemon_when_showing.patch
 Patch13:        BZ_1904889-do-not-remove-unmanaged-ovs-bridge.patch
+Patch14:        BZ_1910193-support-multiple-gateways.patch
+Patch15:        BZ_1908724-sriov-use-verification-retry-to-wait-VF-been-created.patch
+Patch16:        BZ_1916073_better-handling-for-timeout.patch
+Patch17:        BZ_1918712_use_uuid_for_vlan_vxlan_parent.patch
+Patch18:        BZ_1916073_retry_on_failure_when_activate.patch
 BuildArch:      noarch
 BuildRequires:  python3-devel
 BuildRequires:  python3-setuptools
@@ -94,6 +99,30 @@ gpgv2 --keyring ./gpgkey-mantainers.gpg %{SOURCE1} %{SOURCE0}
 %{python3_sitelib}/%{libname}/plugins/__pycache__/nmstate_plugin_ovsdb*
 
 %changelog
+* Fri Feb 05 2021 Gris Ge <fge@redhat.com> - 0.3.4-25
+- Remove patch for fixing the autoconnect on existing profile. RHBZ#1918712
+
+* Thu Feb 04 2021 Gris Ge <fge@redhat.com> - 0.3.4-24
+- New patch fixing activation failure with 1000 interfaces. RHBZ#1916073
+
+* Wed Feb 03 2021 Gris Ge <fge@redhat.com> - 0.3.4-23
+- Enforcing autoconnect on existing profile. RHBZ#1918712
+
+* Fri Jan 22 2021 Gris Ge <fge@redhat.com> - 0.3.4-22
+- Fix creating VLAN/VxLAN over interface also used for OVS. RHBZ#1918712
+
+* Sun Jan 17 2021 Gris Ge <fge@redhat.com> - 0.3.4-21
+- Additional patch for profile deactivation and deletion. RHBZ#1916073
+
+* Thu Jan 14 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-20
+- Better handling for timeout on activation. RHBZ#1916073
+
+* Mon Jan 04 2021 Fernando Fernandez Mancera <ferferna@redhat.com> - 0.3.4-19
+- Use verification retry to wait SR-IOV VF been created. RHBZ#1908724
+
+* Wed Dec 23 2020 Gris Ge <fge@redhat.com> - 0.3.4-18
+- Support multiple gateways. RHBZ#1910193
+
 * Mon Dec 07 2020 Gris Ge <fge@redhat.com> - 0.3.4-17
 - Rebuild to retrigger the CI gating. RHBZ#1904889