Merge "[TF] Workaround for  PRODX-7986" into mcp/caracal
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index db84a7c..5a1de9c 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -546,7 +546,7 @@
         """
 
         if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
-            raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
+            LOG.info('Subnet CIDR already reserved: {0!r}'.format(
                 addr))
 
     @classmethod
@@ -1234,6 +1234,7 @@
     def setup_clients(cls):
         super(BaseAdminNetworkTest, cls).setup_clients()
         cls.admin_client = cls.os_admin.network_client
+        cls.admin_client.auth_provider.get_token()
         cls.identity_admin_client = cls.os_admin.projects_client
 
     @classmethod
diff --git a/neutron_tempest_plugin/api/test_subnetpools.py b/neutron_tempest_plugin/api/test_subnetpools.py
index eaaee33..a9e2303 100644
--- a/neutron_tempest_plugin/api/test_subnetpools.py
+++ b/neutron_tempest_plugin/api/test_subnetpools.py
@@ -28,6 +28,13 @@
 class SubnetPoolsTestBase(base.BaseAdminNetworkTest):
 
     @classmethod
+    def skip_checks(cls):
+        super(SubnetPoolsTestBase, cls).skip_checks()
+        if not utils.is_extension_enabled('default-subnetpools', 'network'):
+            msg = "default-subnetpools extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
     def resource_setup(cls):
         super(SubnetPoolsTestBase, cls).resource_setup()
         min_prefixlen = '29'
@@ -328,7 +335,6 @@
         self.assertIsNone(body['subnetpool']['address_scope_id'])
 
     @decorators.idempotent_id('4c6963c2-f54c-4347-b288-75d18421c4c4')
-    @utils.requires_ext(extension='default-subnetpools', service='network')
     def test_tenant_create_non_default_subnetpool(self):
         """Test creates a subnetpool, the "is_default" attribute is False."""
         created_subnetpool = self._create_subnetpool()
diff --git a/neutron_tempest_plugin/api/test_subnetpools_negative.py b/neutron_tempest_plugin/api/test_subnetpools_negative.py
index 934d3cd..a85014c 100644
--- a/neutron_tempest_plugin/api/test_subnetpools_negative.py
+++ b/neutron_tempest_plugin/api/test_subnetpools_negative.py
@@ -62,7 +62,6 @@
 
     @decorators.attr(type='negative')
     @decorators.idempotent_id('6ae09d8f-95be-40ed-b1cf-8b850d45bab5')
-    @utils.requires_ext(extension='default-subnetpools', service='network')
     def test_tenant_create_default_subnetpool(self):
         # 'default' subnetpool can only be created by admin.
         self.assertRaises(lib_exc.Forbidden, self._create_subnetpool,
diff --git a/neutron_tempest_plugin/bgpvpn/api/test_bgpvpn.py b/neutron_tempest_plugin/bgpvpn/api/test_bgpvpn.py
index 4610686..14fa320 100644
--- a/neutron_tempest_plugin/bgpvpn/api/test_bgpvpn.py
+++ b/neutron_tempest_plugin/bgpvpn/api/test_bgpvpn.py
@@ -257,6 +257,7 @@
         self.assertRaises(exceptions.BadRequest,
                           self.bgpvpn_admin_client.update_bgpvpn,
                           bgpvpn['bgpvpn']['id'], **updatedata)
+        self.delete_bgpvpn(self.bgpvpn_admin_client, bgpvpn['bgpvpn'])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f049ce21-d239-47c0-b13f-fb57a2a558ce')
@@ -279,6 +280,7 @@
                           self.bgpvpn_client.create_network_association,
                           uuidutils.generate_uuid(),
                           network['network']['id'])
+        self.delete_bgpvpn(self.bgpvpn_admin_client, bgpvpn['bgpvpn'])
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('078b2660-4adb-4c4c-abf0-b77bf0bface5')
@@ -306,6 +308,7 @@
                           self.bgpvpn_client.delete_network_association,
                           uuidutils.generate_uuid(),
                           association['network_association']['id'])
+        self.delete_bgpvpn(self.bgpvpn_admin_client, bgpvpn['bgpvpn'])
 
     @decorators.idempotent_id('de8d94b0-0239-4a48-9574-c3a4a4f7cacb')
     def test_associate_disassociate_router(self):
diff --git a/neutron_tempest_plugin/bgpvpn/base.py b/neutron_tempest_plugin/bgpvpn/base.py
index b436a5d..46c2c78 100644
--- a/neutron_tempest_plugin/bgpvpn/base.py
+++ b/neutron_tempest_plugin/bgpvpn/base.py
@@ -17,6 +17,7 @@
 from tempest.common import utils
 from tempest import config
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 
 from neutron_tempest_plugin.bgpvpn.services import bgpvpn_client
 
@@ -34,12 +35,6 @@
     bgpvpn_alt_client = None
 
     @classmethod
-    def resource_cleanup(cls):
-        for bgpvpn in cls.bgpvpns:
-            cls.bgpvpn_admin_client.delete_bgpvpn(bgpvpn['id'])
-        super(BaseBgpvpnTest, cls).resource_cleanup()
-
-    @classmethod
     def resource_setup(cls):
         cls.route_distinguishers = []
         cls.bgpvpns = []
@@ -87,9 +82,9 @@
 
         body = client.create_bgpvpn(**kwargs)
         bgpvpn = body['bgpvpn']
-        self.bgpvpns.append(bgpvpn)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.bgpvpn_admin_client.delete_bgpvpn, bgpvpn['id'])
         return bgpvpn
 
     def delete_bgpvpn(self, client, bgpvpn):
         client.delete_bgpvpn(bgpvpn['id'])
-        self.bgpvpns.remove(bgpvpn)
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/manager.py b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
index 398c764..f5e9bf2 100644
--- a/neutron_tempest_plugin/bgpvpn/scenario/manager.py
+++ b/neutron_tempest_plugin/bgpvpn/scenario/manager.py
@@ -14,19 +14,70 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+
+import netaddr
 from oslo_log import log
 
+from tempest.common import compute
 from tempest.common import utils
+from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 from tempest.scenario import manager
 
+
 CONF = config.CONF
 
 LOG = log.getLogger(__name__)
 
+NET_A = 'A'
+NET_A_BIS = 'A-Bis'
+NET_B = 'B'
+NET_C = 'C'
+
+if "SUBNETPOOL_PREFIX_V4" in os.environ:
+    subnet_base = netaddr.IPNetwork(os.environ['SUBNETPOOL_PREFIX_V4'])
+    if subnet_base.prefixlen > 21:
+        raise Exception("if SUBNETPOOL_PREFIX_V4 is set, it needs to offer "
+                        "space for at least 8 /24 subnets")
+else:
+    subnet_base = netaddr.IPNetwork("10.100.0.0/16")
+
+
+def assign_24(idx):
+    # how many addresses in a /24:
+    range_size = 2 ** (32 - 24)
+    return netaddr.cidr_merge(
+        subnet_base[range_size * idx:range_size * (idx + 1)])[0]
+
+
+S1A = assign_24(1)
+S2A = assign_24(2)
+S1B = assign_24(4)
+S2B = assign_24(6)
+S1C = assign_24(6)
+NET_A_S1 = str(S1A)
+NET_A_S2 = str(S2A)
+NET_B_S1 = str(S1B)
+NET_B_S2 = str(S2B)
+NET_C_S1 = str(S1C)
+IP_A_S1_1 = str(S1A[10])
+IP_B_S1_1 = str(S1B[20])
+IP_C_S1_1 = str(S1C[30])
+IP_A_S1_2 = str(S1A[30])
+IP_B_S1_2 = str(S1B[40])
+IP_A_S1_3 = str(S1A[50])
+IP_B_S1_3 = str(S1B[60])
+IP_A_S2_1 = str(S2A[50])
+IP_B_S2_1 = str(S2B[60])
+IP_A_BIS_S1_1 = IP_A_S1_1
+IP_A_BIS_S1_2 = IP_A_S1_2
+IP_A_BIS_S1_3 = IP_A_S1_3
+IP_A_BIS_S2_1 = IP_A_S2_1
+
 
 class ScenarioTest(manager.NetworkScenarioTest):
     """Base class for scenario tests. Uses tempest own clients. """
@@ -147,9 +198,9 @@
     def _create_router(self, client=None, tenant_id=None,
                        namestart='router-smoke'):
         if not client:
-            client = self.admin_routers_client
+            client = self.routers_client
         if not tenant_id:
-            tenant_id = client.project_id
+            tenant_id = client.tenant_id
         name = data_utils.rand_name(namestart)
         result = client.create_router(name=name,
                                       admin_state_up=True,
@@ -160,3 +211,306 @@
                         client.delete_router,
                         router['id'])
         return router
+
+    def _create_security_group_for_test(self):
+        self.security_group = self.create_security_group(
+            project_id=self.bgpvpn_client.project_id)
+
+    def _create_networks_and_subnets(self, names=None, subnet_cidrs=None,
+                                     port_security=True):
+        if not names:
+            names = [NET_A, NET_B, NET_C]
+        if not subnet_cidrs:
+            subnet_cidrs = [[NET_A_S1], [NET_B_S1], [NET_C_S1]]
+        for (name, subnet_cidrs) in zip(names, subnet_cidrs):
+            network = super(NetworkScenarioTest, self).create_network(
+                namestart=name,
+                port_security_enabled=port_security)
+            self.networks[name] = network
+            self.subnets[name] = []
+            for (j, cidr) in enumerate(subnet_cidrs):
+                sub_name = "subnet-%s-%d" % (name, j + 1)
+                subnet = self._create_subnet_with_cidr(network,
+                                                       namestart=sub_name,
+                                                       cidr=cidr,
+                                                       ip_version=4)
+                self.subnets[name].append(subnet)
+
+    def _create_subnet_with_cidr(self, network, subnets_client=None,
+                                 namestart='subnet-smoke', **kwargs):
+        if not subnets_client:
+            subnets_client = self.subnets_client
+        tenant_cidr = kwargs.get('cidr')
+        subnet = dict(
+            name=data_utils.rand_name(namestart),
+            network_id=network['id'],
+            tenant_id=network['tenant_id'],
+            **kwargs)
+        result = subnets_client.create_subnet(**subnet)
+        self.assertIsNotNone(result, 'Unable to allocate tenant network')
+        subnet = result['subnet']
+        self.assertEqual(subnet['cidr'], tenant_cidr)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        subnets_client.delete_subnet, subnet['id'])
+        return subnet
+
+    def _create_fip_router(self, client=None, public_network_id=None,
+                           subnet_id=None):
+        router = self._create_router(client, namestart='router-')
+        router_id = router['id']
+        if public_network_id is None:
+            public_network_id = CONF.network.public_network_id
+        if client is None:
+            client = self.routers_client
+        kwargs = {'external_gateway_info': {'network_id': public_network_id}}
+        router = client.update_router(router_id, **kwargs)['router']
+        if subnet_id is not None:
+            client.add_router_interface(router_id, subnet_id=subnet_id)
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            client.remove_router_interface, router_id,
+                            subnet_id=subnet_id)
+        return router
+
+    def _associate_fip(self, server_index):
+        server = self.servers[server_index]
+        fip = self.create_floating_ip(
+            server, external_network_id=CONF.network.public_network_id,
+            port_id=self.ports[server['id']]['id'])
+        self.server_fips[server['id']] = fip
+        return fip
+
+    def _create_router_and_associate_fip(self, server_index, subnet):
+        router = self._create_fip_router(subnet_id=subnet['id'])
+        self._associate_fip(server_index)
+        return router
+
+    def _create_server(self, name, keypair, network, ip_address,
+                       security_group_ids, clients, port_security):
+        security_groups = []
+        if port_security:
+            security_groups = security_group_ids
+        create_port_body = {'fixed_ips': [{'ip_address': ip_address}],
+                            'namestart': 'port-smoke',
+                            'security_groups': security_groups}
+
+        port = super(NetworkScenarioTest, self).create_port(
+            network_id=network['id'],
+            client=clients.ports_client,
+            **create_port_body)
+
+        create_server_kwargs = {
+            'key_name': keypair['name'],
+            'networks': [{'uuid': network['id'], 'port': port['id']}]
+        }
+        body, servers = compute.create_test_server(
+            clients, wait_until='ACTIVE', name=name, **create_server_kwargs)
+        self.addCleanup(waiters.wait_for_server_termination,
+                        clients.servers_client, body['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        clients.servers_client.delete_server, body['id'])
+        server = clients.servers_client.show_server(body['id'])['server']
+        LOG.debug('Created server: %s with status: %s', server['id'],
+                  server['status'])
+        self.ports[server['id']] = port
+        return server
+
+    def _create_servers(self, ports_config=None, port_security=True):
+        keypair = self.create_keypair()
+        security_group_ids = [self.security_group['id']]
+        if not ports_config:
+            ports_config = [[self.networks[NET_A], IP_A_S1_1],
+                            [self.networks[NET_B], IP_B_S1_1]]
+
+        for (i, port_config) in enumerate(ports_config):
+            network = port_config[0]
+            server = self._create_server(
+                'server-' + str(i + 1), keypair, network, port_config[1],
+                security_group_ids, self.os_primary, port_security)
+            self.servers.append(server)
+            self.servers_keypairs[server['id']] = keypair
+            self.server_fixed_ips[server['id']] = (
+                server['addresses'][network['name']][0]['addr'])
+            self.assertTrue(self.servers_keypairs)
+
+    def _create_l3_bgpvpn(self, name='test-l3-bgpvpn', rts=None,
+                          import_rts=None, export_rts=None):
+        if rts is None and import_rts is None and export_rts is None:
+            rts = [self.RT1]
+        import_rts = import_rts or []
+        export_rts = export_rts or []
+        self.bgpvpn = self.create_bgpvpn(
+            self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id,
+            name=name, route_targets=rts, export_targets=export_rts,
+            import_targets=import_rts)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.bgpvpn_admin_client.delete_bgpvpn,
+                        self.bgpvpn['id'])
+        return self.bgpvpn
+
+    def _update_l3_bgpvpn(self, rts=None, import_rts=None, export_rts=None,
+                          bgpvpn=None):
+        bgpvpn = bgpvpn or self.bgpvpn
+        if rts is None:
+            rts = [self.RT1]
+        import_rts = import_rts or []
+        export_rts = export_rts or []
+        LOG.debug('Updating targets in BGPVPN %s', bgpvpn['id'])
+        self.bgpvpn_admin_client.update_bgpvpn(bgpvpn['id'],
+                                               route_targets=rts,
+                                               export_targets=export_rts,
+                                               import_targets=import_rts)
+
+    def _associate_all_nets_to_bgpvpn(self, bgpvpn=None):
+        bgpvpn = bgpvpn or self.bgpvpn
+        for network in self.networks.values():
+            self.bgpvpn_client.create_network_association(
+                bgpvpn['id'], network['id'])
+        LOG.debug('BGPVPN network associations completed')
+
+    def _setup_ssh_client(self, server):
+        server_fip = self.server_fips[server['id']][
+            'floating_ip_address']
+        private_key = self.servers_keypairs[server['id']][
+            'private_key']
+        ssh_client = self.get_remote_client(server_fip,
+                                            private_key=private_key,
+                                            server=server)
+        return ssh_client
+
+    def _setup_http_server(self, server_index):
+        server = self.servers[server_index]
+        ssh_client = self._setup_ssh_client(server)
+        ssh_client.exec_command("sudo nc -kl -p 80 -e echo '%s:%s' &"
+                                % (server['name'], server['id']))
+
+    def _setup_ip_forwarding(self, server_index):
+        server = self.servers[server_index]
+        ssh_client = self._setup_ssh_client(server)
+        ssh_client.exec_command("sudo sysctl -w net.ipv4.ip_forward=1")
+
+    def _setup_ip_address(self, server_index, cidr, device=None):
+        self._setup_range_ip_address(server_index, [cidr], device=None)
+
+    def _setup_range_ip_address(self, server_index, cidrs, device=None):
+        MAX_CIDRS = 50
+        if device is None:
+            device = 'lo'
+        server = self.servers[server_index]
+        ssh_client = self._setup_ssh_client(server)
+        for i in range(0, len(cidrs), MAX_CIDRS):
+            ips = ' '.join(cidrs[i:i + MAX_CIDRS])
+            ssh_client.exec_command(
+                ("for ip in {ips}; do sudo ip addr add $ip "
+                 "dev {dev}; done").format(ips=ips, dev=device))
+
+    def _check_l3_bgpvpn(self, from_server=None, to_server=None,
+                         should_succeed=True, validate_server=False):
+        to_server = to_server or self.servers[1]
+        destination_srv = None
+        if validate_server:
+            destination_srv = '%s:%s' % (to_server['name'], to_server['id'])
+        destination_ip = self.server_fixed_ips[to_server['id']]
+        self._check_l3_bgpvpn_by_specific_ip(from_server=from_server,
+                                             to_server_ip=destination_ip,
+                                             should_succeed=should_succeed,
+                                             validate_server=destination_srv)
+
+    def _check_l3_bgpvpn_by_specific_ip(self, from_server=None,
+                                        to_server_ip=None,
+                                        should_succeed=True,
+                                        validate_server=None,
+                                        repeat_validate_server=10):
+        from_server = from_server or self.servers[0]
+        from_server_ip = self.server_fips[from_server['id']][
+            'floating_ip_address']
+        if to_server_ip is None:
+            to_server_ip = self.server_fixed_ips[self.servers[1]['id']]
+        ssh_client = self._setup_ssh_client(from_server)
+        check_reachable = should_succeed or validate_server
+        msg = ""
+        if check_reachable:
+            msg = "Timed out waiting for {ip} to become reachable".format(
+                ip=to_server_ip)
+        else:
+            msg = ("Unexpected ping response from VM with IP address "
+                   "{dest} originated from VM with IP address "
+                   "{src}").format(dest=to_server_ip, src=from_server_ip)
+        try:
+            result = self._check_remote_connectivity(ssh_client,
+                                                     to_server_ip,
+                                                     check_reachable)
+            # if a negative connectivity check was unsuccessful (unexpected
+            # ping reply) then try to know more:
+            if not check_reachable and not result:
+                try:
+                    content = ssh_client.exec_command(
+                        "nc %s 80" % to_server_ip).strip()
+                    LOG.warning("Can connect to %s: %s", to_server_ip, content)
+                except Exception:
+                    LOG.warning("Could ping %s, but no http", to_server_ip)
+
+            self.assertTrue(result, msg)
+
+            if validate_server and result:
+                # repeating multiple times gives increased odds of avoiding
+                # false positives in the case where the dataplane does
+                # equal-cost multipath
+                for i in range(0, repeat_validate_server):
+                    real_dest = ssh_client.exec_command(
+                        "nc %s 80" % to_server_ip).strip()
+                    result = real_dest == validate_server
+                    self.assertTrue(
+                        should_succeed == result,
+                        ("Destination server name is '%s', expected is '%s'" %
+                         (real_dest, validate_server)))
+                    LOG.info("nc server name check %d successful", i)
+        except Exception:
+            LOG.exception("Error validating connectivity to %s "
+                          "from VM with IP address %s: %s",
+                          to_server_ip, from_server_ip, msg)
+            raise
+
+    def _associate_fip_and_check_l3_bgpvpn(self, subnet=None,
+                                           should_succeed=True):
+        if not subnet:
+            subnet = self.subnets[NET_A][0]
+        else:
+            subnet = self.subnets[subnet][0]
+
+        self.router = self._create_router_and_associate_fip(0, subnet)
+        self._check_l3_bgpvpn(should_succeed=should_succeed)
+
+    def _live_migrate(self, server_id, target_host, state,
+                      volume_backed=False):
+        # If target_host is None,
+        # check whether source host is different with
+        # the new host after migration.
+        if target_host is None:
+            source_host = self.get_host_for_server(server_id)
+        self._migrate_server_to(server_id, target_host, volume_backed)
+        waiters.wait_for_server_status(self.servers_client, server_id, state)
+        migration_list = (self.admin_migration_client.list_migrations()
+                          ['migrations'])
+        msg = ("Live Migration failed. Migrations list for Instance "
+               "%s: [" % server_id)
+        for live_migration in migration_list:
+            if (live_migration['instance_uuid'] == server_id):
+                msg += "\n%s" % live_migration
+        msg += "]"
+        if target_host is None:
+            self.assertNotEqual(source_host,
+                                self.get_host_for_server(server_id), msg)
+        else:
+            self.assertEqual(target_host, self.get_host_for_server(server_id),
+                             msg)
+
+    def _migrate_server_to(self, server_id, dest_host, volume_backed=False):
+        kwargs = dict()
+        block_migration = getattr(self, 'block_migration', None)
+        if self.block_migration is None:
+            block_migration = (CONF.compute_feature_enabled.
+                               block_migration_for_live_migration and
+                               not volume_backed)
+        self.admin_servers_client.live_migrate_server(
+            server_id, host=dest_host, block_migration=block_migration,
+            **kwargs)
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_advanced.py b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_advanced.py
new file mode 100644
index 0000000..d73ba36
--- /dev/null
+++ b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_advanced.py
@@ -0,0 +1,265 @@
+# Copyright 2021 Mirantis, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_log import log as logging
+from tempest.common import compute
+from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+import testtools
+
+from neutron_tempest_plugin.bgpvpn import base
+from neutron_tempest_plugin.bgpvpn.scenario import manager
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class TestBGPVPNAdvanced(base.BaseBgpvpnTest, manager.NetworkScenarioTest):
+    def setUp(self):
+        super(TestBGPVPNAdvanced, self).setUp()
+        self._create_security_group_for_test()
+
+    @decorators.idempotent_id("734213fb-8213-487d-9fe3-c8ff31758e18")
+    @utils.services("compute", "network")
+    @testtools.skipUnless(
+        CONF.bgpvpn.l2vpn_endpoint and CONF.bgpvpn.route_target,
+        "Required ip address of L2 endpoint and route target.",
+    )
+    def test_bgpvpn_l2vpn_endpoint(self):
+        """This test checks L2VPN connectivity.
+
+        1. Create network with respective subnet
+        2. Start up server
+        4. Associate network to a given L2 BGPVPN
+        5. Create router and connect it to network
+        6. Give a FIP to server
+        7. Check that server can ping l2vpn vtep/endpoint
+        """
+
+        self._create_networks_and_subnets(
+            subnet_cidr=CONF.bgpvpn.l2vpn_endpoint
+        )
+        self._create_server()
+        self._create_bgpvpn(
+            type="l2",
+            rts=CONF.bgpvpn.route_target,
+            vni=CONF.bgpvpn.route_target.split(":")[1],
+        )
+        self._associate_all_nets_to_bgpvpn()
+        self._associate_fip_and_check_bgpvpn(
+            CONF.bgpvpn.l2vpn_endpoint.split("/")[0]
+        )
+
+    @decorators.idempotent_id("124fe1bd-e18a-4482-9c52-855c81a58cd2")
+    @utils.services("compute", "network")
+    @testtools.skipUnless(
+        CONF.bgpvpn.l3vpn_endpoint and CONF.bgpvpn.route_target,
+        "Required ip address of L3 endpoint and route target.",
+    )
+    def test_bgpvpn_l3vpn_endpoint(self):
+        """This test checks L3VPN connectivity.
+
+        1. Create network with respective subnet
+        2. Start up server
+        4. Associate network to a given L3 BGPVPN
+        5. Create router and connect it to network
+        6. Give a FIP to server
+        7. Check that server can ping l3vpn endpoint
+        """
+
+        self._create_networks_and_subnets(
+            subnet_cidr=CONF.bgpvpn.l3vpn_subnet_cidr
+        )
+        self._create_server()
+        self._create_bgpvpn(rts=CONF.bgpvpn.route_target)
+        self._associate_all_nets_to_bgpvpn()
+        self._associate_fip_and_check_bgpvpn(
+            CONF.bgpvpn.l3vpn_endpoint.split("/")[0]
+        )
+
+    def _create_networks_and_subnets(
+        self, name="bgp", subnet_cidr=None, port_security=True
+    ):
+        self.network = super(manager.NetworkScenarioTest, self).create_network(
+            namestart=name, port_security_enabled=port_security
+        )
+        self.subnet = self._create_subnet_with_cidr(
+            self.network, cidr=subnet_cidr, ip_version=4
+        )
+        self._reserve_ip_address(subnet_cidr.split("/")[0], self.os_primary)
+
+    def _create_subnet_with_cidr(
+        self, network, subnets_client=None, namestart="subnet-bgp", **kwargs
+    ):
+        if not subnets_client:
+            subnets_client = self.subnets_client
+        subnet = dict(
+            name=data_utils.rand_name(namestart),
+            network_id=network["id"],
+            tenant_id=network["tenant_id"],
+            **kwargs
+        )
+        result = subnets_client.create_subnet(**subnet)
+        self.assertIsNotNone(result, "Unable to allocate tenant network")
+        subnet = result["subnet"]
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            subnets_client.delete_subnet,
+            subnet["id"],
+        )
+        return subnet
+
+    def _create_fip_router(
+        self, client=None, public_network_id=None, subnet_id=None
+    ):
+        router = self._create_router(client, namestart="router-")
+        router_id = router["id"]
+        if public_network_id is None:
+            public_network_id = CONF.network.public_network_id
+        if client is None:
+            client = self.routers_client
+        kwargs = {"external_gateway_info": {"network_id": public_network_id}}
+        router = client.update_router(router_id, **kwargs)["router"]
+        if subnet_id is not None:
+            client.add_router_interface(router_id, subnet_id=subnet_id)
+            self.addCleanup(
+                test_utils.call_and_ignore_notfound_exc,
+                client.remove_router_interface,
+                router_id,
+                subnet_id=subnet_id,
+            )
+        return router
+
+    def _create_router_and_associate_fip(self, subnet):
+        router = self._create_fip_router(subnet_id=subnet["id"])
+        self.server_fip = self.create_floating_ip(
+            self.server, external_network_id=CONF.network.public_network_id
+        )
+        return router
+
+    def _reserve_ip_address(self, ip_address, clients):
+        create_port_kwargs = {
+            "fixed_ips": [{"ip_address": ip_address}],
+            "namestart": "port-endpoint",
+        }
+        super(manager.NetworkScenarioTest, self).create_port(
+            network_id=self.network["id"],
+            client=clients.ports_client,
+            **create_port_kwargs
+        )
+
+    def _create_server(self, name="server-bgp", port_security=True):
+        self.keypair = self.create_keypair()
+        security_group_name = self.security_group["name"]
+        clients = self.os_primary
+
+        security_groups = {}
+        if port_security:
+            security_groups = {
+                "security_groups": [{"name": security_group_name}]
+            }
+
+        create_server_kwargs = {
+            "key_name": self.keypair["name"],
+            "networks": [{"uuid": self.network["id"]}],
+            **security_groups,
+        }
+        body, _ = compute.create_test_server(
+            clients, wait_until="ACTIVE", name=name, **create_server_kwargs
+        )
+        self.addCleanup(
+            waiters.wait_for_server_termination,
+            clients.servers_client,
+            body["id"],
+        )
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            clients.servers_client.delete_server,
+            body["id"],
+        )
+        self.server = clients.servers_client.show_server(body["id"])["server"]
+        LOG.debug(
+            "Created server: %s with status: %s",
+            self.server["id"],
+            self.server["status"],
+        )
+
+    def _create_bgpvpn(
+        self,
+        name="test-bgpvpn",
+        type="l3",
+        rts=None,
+        import_rts=None,
+        export_rts=None,
+        vni=None,
+    ):
+        import_rts = import_rts or []
+        export_rts = export_rts or []
+        self.bgpvpn = self.create_bgpvpn(
+            self.bgpvpn_admin_client,
+            tenant_id=self.bgpvpn_client.tenant_id,
+            name=name,
+            route_targets=rts,
+            export_targets=export_rts,
+            import_targets=import_rts,
+            type=type,
+            vni=vni,
+        )
+        return self.bgpvpn
+
+    def _associate_all_nets_to_bgpvpn(self, bgpvpn=None):
+        bgpvpn = bgpvpn or self.bgpvpn
+        self.bgpvpn_client.create_network_association(
+            bgpvpn["id"], self.network["id"]
+        )
+        LOG.debug("BGPVPN network associations completed")
+
+    def _setup_ssh_client(self, server):
+        server_fip = self.server_fip["floating_ip_address"]
+        private_key = self.keypair["private_key"]
+        ssh_client = self.get_remote_client(
+            server_fip, private_key=private_key
+        )
+        return ssh_client
+
+    def _check_bgpvpn(self, from_server=None, to_server_ip=None):
+        from_server = from_server or self.server
+        from_server_ip = self.server_fip["floating_ip_address"]
+        ssh_client = self._setup_ssh_client(from_server)
+        msg = "Timed out waiting for {ip} to become reachable".format(
+            ip=to_server_ip
+        )
+        try:
+            result = self._check_remote_connectivity(
+                ssh_client, to_server_ip, True
+            )
+            self.assertTrue(result, msg)
+        except Exception:
+            LOG.exception(
+                "Error validating connectivity to %s "
+                "from VM with IP address %s: %s",
+                to_server_ip,
+                from_server_ip,
+                msg,
+            )
+            raise
+
+    def _associate_fip_and_check_bgpvpn(self, to_server_ip=None):
+        self.router = self._create_router_and_associate_fip(self.subnet)
+        self._check_bgpvpn(to_server_ip=to_server_ip)
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_basic.py b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_basic.py
index 9cca602..1b0d6cc 100644
--- a/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_basic.py
+++ b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_basic.py
@@ -20,12 +20,8 @@
 from neutron_lib.utils import test
 from oslo_concurrency import lockutils
 from oslo_log import log as logging
-from tempest.common import compute
 from tempest.common import utils
-from tempest.common import waiters
 from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 from neutron_tempest_plugin.bgpvpn import base
@@ -108,20 +104,6 @@
         self.RT3 = self.new_rt()
         self.RT4 = self.new_rt()
 
-    @classmethod
-    def setup_clients(cls):
-        """This setup the service clients for the tests"""
-        super(TestBGPVPNBasic, cls).setup_clients()
-        cls.admin_security_group_client = cls.os_admin.security_groups_client
-        cls.admin_security_group_rule_client = (
-            cls.os_admin.security_group_rules_client)
-        cls.admin_routers_client = cls.os_admin.routers_client
-        cls.admin_ports_client = cls.os_admin.ports_client
-        cls.admin_networks_client = cls.os_admin.networks_client
-        cls.admin_subnets_client = cls.os_admin.subnets_client
-        cls.admin_fips_client = cls.os_admin.floating_ips_client
-        cls.admin_keys_client = cls.os_admin.keypairs_client
-
     @decorators.idempotent_id('afdd6cad-871a-4343-b97b-6319c76c815d')
     @utils.services('compute', 'network')
     def test_bgpvpn_basic(self):
@@ -178,7 +160,6 @@
         self._create_networks_and_subnets()
         self._create_servers()
         self.router_b = self._create_fip_router(
-            client=self.admin_routers_client,
             subnet_id=self.subnets[NET_B][0]['id'])
         self._create_l3_bgpvpn()
         self._associate_all_nets_to_bgpvpn()
@@ -202,13 +183,10 @@
         self._create_networks_and_subnets()
         self._create_servers()
         self.router_b = self._create_fip_router(
-            client=self.admin_routers_client,
             subnet_id=self.subnets[NET_B][0]['id'])
         self._create_l3_bgpvpn()
         self._associate_all_nets_to_bgpvpn()
-        self._delete_router(self.router_b,
-                            routers_client=self.admin_routers_client,
-                            ports_client=self.admin_ports_client)
+        self.delete_router(self.router_b)
         self._associate_fip_and_check_l3_bgpvpn()
 
     @decorators.idempotent_id('973ab26d-c7d8-4a32-9aa9-2d7e6f406135')
@@ -230,7 +208,6 @@
         self._create_l3_bgpvpn()
         self._associate_all_nets_to_bgpvpn()
         self.router_b = self._create_fip_router(
-            client=self.admin_routers_client,
             subnet_id=self.subnets[NET_B][0]['id'])
         self._associate_fip_and_check_l3_bgpvpn()
 
@@ -250,7 +227,6 @@
         """
         self._create_networks_and_subnets()
         self.router_b = self._create_fip_router(
-            client=self.admin_routers_client,
             subnet_id=self.subnets[NET_B][0]['id'])
         self._create_l3_bgpvpn()
         self._associate_all_nets_to_bgpvpn()
@@ -275,7 +251,6 @@
         self._create_l3_bgpvpn()
         self._associate_all_nets_to_bgpvpn()
         self.router_b = self._create_fip_router(
-            client=self.admin_routers_client,
             subnet_id=self.subnets[NET_B][0]['id'])
         self._create_servers()
         self._associate_fip_and_check_l3_bgpvpn()
@@ -365,10 +340,10 @@
             0, self.subnets[NET_A][0])
         self._create_l3_bgpvpn(rts=[], export_rts=[self.RT1],
                                import_rts=[self.RT2])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         self._check_l3_bgpvpn(should_succeed=False)
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_B]['id'])
         self._check_l3_bgpvpn(should_succeed=False)
         self._update_l3_bgpvpn(rts=[self.RT1], import_rts=[], export_rts=[])
@@ -409,13 +384,13 @@
                              [self.networks[NET_B], IP_B_S1_1],
                              [self.networks[NET_A], IP_A_S1_2],
                              [self.networks[NET_B], IP_B_S1_2]])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         self.router_a = self._create_router_and_associate_fip(
             0, self.subnets[NET_A][0])
         self._check_l3_bgpvpn(should_succeed=False)
         self._check_l3_bgpvpn(self.servers[0], self.servers[2])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_B]['id'])
         self.router_b = self._create_router_and_associate_fip(
             1, self.subnets[NET_B][0])
@@ -465,11 +440,11 @@
             0, self.subnets[NET_A][0])
         router_b = self._create_router_and_associate_fip(
             3, self.subnets[NET_B][0])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         self._check_l3_bgpvpn(should_succeed=False)
         self._check_l3_bgpvpn(self.servers[0], self.servers[2])
-        self.bgpvpn_admin_client.create_router_association(self.bgpvpn['id'],
+        self.bgpvpn_client.create_router_association(self.bgpvpn['id'],
                                                      router_b['id'])
         self._check_l3_bgpvpn(should_succeed=False)
         self._check_l3_bgpvpn(self.servers[3], self.servers[1])
@@ -534,16 +509,16 @@
                                   'local_pref': 100,
                                   'prefix': NET_C_S1}]
 
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
 
         port_id_1 = self.ports[self.servers[1]['id']]['id']
-        body = self.bgpvpn_admin_client.create_port_association(
+        body = self.bgpvpn_client.create_port_association(
             self.bgpvpn['id'], port_id=port_id_1, routes=primary_port_routes)
         port_association_1 = body['port_association']
 
         port_id_2 = self.ports[self.servers[2]['id']]['id']
-        body = self.bgpvpn_admin_client.create_port_association(
+        body = self.bgpvpn_client.create_port_association(
             self.bgpvpn['id'], port_id=port_id_2, routes=alternate_port_routes)
         port_association_2 = body['port_association']
 
@@ -556,10 +531,10 @@
             to_server_ip=IP_C_S1_1,
             validate_server=destination_srv_1)
 
-        self.bgpvpn_admin_client.update_port_association(
+        self.bgpvpn_client.update_port_association(
             self.bgpvpn['id'], port_association_1['id'],
             routes=alternate_port_routes)
-        self.bgpvpn_admin_client.update_port_association(
+        self.bgpvpn_client.update_port_association(
             self.bgpvpn['id'], port_association_2['id'],
             routes=primary_port_routes)
 
@@ -602,9 +577,9 @@
                                           rts=[self.RT1])
         bgpvpn_a_bis = self._create_l3_bgpvpn(name='test-l3-bgpvpn-a-bis',
                                               rts=[self.RT2])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             bgpvpn_a['id'], self.networks[NET_A]['id'])
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             bgpvpn_a_bis['id'], self.networks[NET_A_BIS]['id'])
         self._create_servers([[self.networks[NET_A], IP_A_S1_1],
                              [self.networks[NET_A_BIS], IP_A_BIS_S1_2],
@@ -673,17 +648,18 @@
 
         self._setup_ip_forwarding(1)
         self._setup_ip_address(1, IP_C_S1_1)
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         port_id = self.ports[self.servers[1]['id']]['id']
         port_routes = [{'type': 'prefix',
                         'prefix': NET_C_S1}]
-        body = self.bgpvpn_admin_client.create_port_association(
-            self.bgpvpn['id'], port_id=port_id, routes=port_routes)
+        body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'],
+                                                          port_id=port_id,
+                                                          routes=port_routes)
         port_association = body['port_association']
         self._check_l3_bgpvpn_by_specific_ip(
             to_server_ip=IP_C_S1_1)
-        self.bgpvpn_admin_client.update_port_association(
+        self.bgpvpn_client.update_port_association(
             self.bgpvpn['id'], port_association['id'], routes=[])
         self._check_l3_bgpvpn_by_specific_ip(
             should_succeed=False, to_server_ip=IP_C_S1_1)
@@ -737,15 +713,16 @@
 
         self._setup_range_ip_address(1, LOOPBACKS)
 
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         port_id = self.ports[self.servers[1]['id']]['id']
         port_routes = [{'type': 'prefix',
                         'prefix': ip + "/32"}
                        for ip in LOOPBACKS]
 
-        body = self.bgpvpn_admin_client.create_port_association(
-            self.bgpvpn['id'], port_id=port_id, routes=port_routes)
+        body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'],
+                                                          port_id=port_id,
+                                                          routes=port_routes)
         port_association = body['port_association']
 
         for ip in random.sample(LOOPBACKS, SAMPLE_SIZE):
@@ -754,7 +731,7 @@
             self._check_l3_bgpvpn_by_specific_ip(
                 to_server_ip=ip)
 
-        self.bgpvpn_admin_client.update_port_association(
+        self.bgpvpn_client.update_port_association(
             self.bgpvpn['id'], port_association['id'], routes=[])
 
         for ip in SUB_LOOPBACKS:
@@ -763,6 +740,100 @@
             self._check_l3_bgpvpn_by_specific_ip(
                 should_succeed=False, to_server_ip=ip)
 
+    @decorators.idempotent_id('910e6d89-5703-4a5e-a5c7-0743ddffbeb7')
+    @utils.services('compute', 'network')
+    @utils.requires_ext(extension='bgpvpn-routes-control', service='network')
+    def test_bgpvpn_port_association_create_and_delete_association(self):
+        """This test checks port association in BGPVPN.
+
+        1. Create networks A and B with their respective subnets
+        2. Create L3 BGPVPN
+        3. Create router and connect it to network A
+        5. Start up server 1 in network A
+        6. Start up server 2 in network B
+        7. Give a FIP to server 1
+        LOG.debug("Check that server 1 cannot ping server's 2")
+        LOG.debug("Check that server 1 can ping server's 2")
+        import rpdb; rpdb.set_trace()
+        LOG.debug("Check that server 1 cannot ping server's 2")
+        8. Check that server 1 cannot ping server's 2
+        10. Associate network A to a given L3 BGPVPN
+        11. Associate port of server 2 to a given L3 BGPVPN
+        12. Check that server 1 can ping server's 2
+        13. Remove created before port association
+        14. Check that server 1 cannot ping server's 2
+        """
+        self._create_networks_and_subnets(port_security=False)
+        self._create_l3_bgpvpn()
+        self._create_servers([[self.networks[NET_A], IP_A_S1_1],
+                              [self.networks[NET_B], IP_B_S1_1]],
+                             port_security=False)
+        self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id'])
+        self._associate_fip(0)
+
+        # preliminary check that no connectivity to 192.168.0.1 initially
+        # exists
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=False, to_server_ip=IP_B_S1_1)
+        self.bgpvpn_client.create_network_association(
+            self.bgpvpn['id'], self.networks[NET_A]['id'])
+        port_id = self.ports[self.servers[1]['id']]['id']
+        body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'],
+                                                          port_id=port_id)
+        port_association = body['port_association']
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=True, to_server_ip=IP_B_S1_1)
+        self.bgpvpn_client.delete_port_association(
+            self.bgpvpn['id'], port_association['id'])
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=False, to_server_ip=IP_B_S1_1)
+
+    @decorators.idempotent_id('8de130c1-778a-4d86-913b-ff41be3c2f0b')
+    @utils.services('compute', 'network')
+    @utils.requires_ext(extension='bgpvpn-routes-control', service='network')
+    def test_bgpvpn_port_association_create_and_delete_bgpvpn(self):
+        """This test checks port association in BGPVPN.
+
+        1. Create networks A and B with their respective subnets
+        2. Create L3 BGPVPN
+        3. Create router and connect it to network A
+        5. Start up server 1 in network A
+        6. Start up server 2 in network B
+        7. Give a FIP to server 1
+        LOG.debug("Check that server 1 cannot ping server's 2")
+        LOG.debug("Check that server 1 can ping server's 2")
+        import rpdb; rpdb.set_trace()
+        LOG.debug("Check that server 1 cannot ping server's 2")
+        8. Check that server 1 cannot ping server's 2
+        10. Associate network A to a given L3 BGPVPN
+        11. Associate port of server 2 to a given L3 BGPVPN
+        12. Check that server 1 can ping server's 2
+        13. Remove created before bgpvpn
+        14. Check that server 1 cannot ping server's 2
+        """
+        self._create_networks_and_subnets(port_security=False)
+        self._create_l3_bgpvpn()
+        self._create_servers([[self.networks[NET_A], IP_A_S1_1],
+                              [self.networks[NET_B], IP_B_S1_1]],
+                             port_security=False)
+        self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id'])
+        self._associate_fip(0)
+
+        # preliminary check that no connectivity to 192.168.0.1 initially
+        # exists
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=False, to_server_ip=IP_B_S1_1)
+        self.bgpvpn_client.create_network_association(
+            self.bgpvpn['id'], self.networks[NET_A]['id'])
+        port_id = self.ports[self.servers[1]['id']]['id']
+        self.bgpvpn_client.create_port_association(self.bgpvpn['id'],
+                                                   port_id=port_id)
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=True, to_server_ip=IP_B_S1_1)
+        self.delete_bgpvpn(self.bgpvpn_admin_client, self.bgpvpn)
+        self._check_l3_bgpvpn_by_specific_ip(
+            should_succeed=False, to_server_ip=IP_B_S1_1)
+
     @decorators.idempotent_id('9c3280b5-0b32-4562-800c-0b50d9d52bfd')
     @utils.services('compute', 'network')
     @utils.requires_ext(extension='bgpvpn-routes-control', service='network')
@@ -801,17 +872,18 @@
 
         self._setup_ip_forwarding(1)
         self._setup_ip_address(1, IP_C_S1_1)
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         port_id = self.ports[self.servers[1]['id']]['id']
         port_routes = [{'type': 'prefix',
                         'prefix': NET_C_S1}]
-        body = self.bgpvpn_admin_client.create_port_association(
-            self.bgpvpn['id'], port_id=port_id, routes=port_routes)
+        body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'],
+                                                          port_id=port_id,
+                                                          routes=port_routes)
         port_association = body['port_association']
         self._check_l3_bgpvpn_by_specific_ip(
             to_server_ip=IP_C_S1_1)
-        self.bgpvpn_admin_client.delete_port_association(
+        self.bgpvpn_client.delete_port_association(
             self.bgpvpn['id'], port_association['id'])
         self._check_l3_bgpvpn_by_specific_ip(
             should_succeed=False, to_server_ip=IP_C_S1_1)
@@ -884,21 +956,21 @@
         self._setup_ip_forwarding(0)
 
         # connect network A to its BGPVPN
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             bgpvpn_a['id'], self.networks[NET_A]['id'])
 
         # connect network B to its BGPVPN
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             bgpvpn_b['id'], self.networks[NET_B]['id'])
 
         # connect network C to its BGPVPN
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             bgpvpn_c['id'], self.networks[NET_C]['id'])
 
         # create port associations for A->C traffic
         # (leak routes imported by BGPVPN B -- which happen to include the
         # routes net C -- into net A)
-        self.bgpvpn_admin_client.create_port_association(
+        self.bgpvpn_client.create_port_association(
             bgpvpn_to_a['id'],
             port_id=self.ports[vm2['id']]['id'],
             routes=[{'type': 'bgpvpn',
@@ -908,7 +980,7 @@
         # create port associations for C->A traffic
         # (leak routes imported by BGPVPN B -- which happen to include the
         # routes from net A -- into net C)
-        body = self.bgpvpn_admin_client.create_port_association(
+        body = self.bgpvpn_client.create_port_association(
             bgpvpn_to_c['id'],
             port_id=self.ports[vm2['id']]['id'],
             routes=[{'type': 'bgpvpn',
@@ -932,7 +1004,7 @@
                                              should_succeed=True)
 
         # remove port association 1
-        self.bgpvpn_admin_client.delete_port_association(self.bgpvpn['id'],
+        self.bgpvpn_client.delete_port_association(self.bgpvpn['id'],
                                                    port_association['id'])
 
         # check that connectivity is actually interrupted
@@ -956,7 +1028,7 @@
         """
         self._create_networks_and_subnets()
         self._create_l3_bgpvpn()
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         self._create_servers()
         self._associate_fip_and_check_l3_bgpvpn(should_succeed=False)
@@ -1025,10 +1097,10 @@
         """
         self._create_networks_and_subnets()
         self._create_l3_bgpvpn()
-        body = self.bgpvpn_admin_client.create_network_association(
+        body = self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
         assoc_b = body['network_association']
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_B]['id'])
         self._create_servers()
         self._associate_fip_and_check_l3_bgpvpn()
@@ -1058,10 +1130,10 @@
         router_b = self._create_fip_router(
             subnet_id=self.subnets[NET_B][0]['id'])
         self._create_l3_bgpvpn()
-        self.bgpvpn_admin_client.create_network_association(
+        self.bgpvpn_client.create_network_association(
             self.bgpvpn['id'], self.networks[NET_A]['id'])
-        body = self.bgpvpn_admin_client.create_router_association(
-            self.bgpvpn['id'], router_b['id'])
+        body = self.bgpvpn_client.create_router_association(self.bgpvpn['id'],
+                                                            router_b['id'])
         assoc_b = body['router_association']
         self._create_servers()
         self._associate_fip_and_check_l3_bgpvpn()
@@ -1119,282 +1191,3 @@
         self._associate_fip_and_check_l3_bgpvpn()
         self._update_l3_bgpvpn(rts=[], import_rts=[], export_rts=[])
         self._check_l3_bgpvpn(should_succeed=False)
-
-    def _create_security_group_for_test(self):
-        self.security_group = self.create_security_group(
-            project_id=self.bgpvpn_admin_client.project_id,
-            security_groups_client=self.admin_security_group_client,
-            security_group_rules_client=self.admin_security_group_rule_client)
-
-    def _create_networks_and_subnets(self, names=None, subnet_cidrs=None,
-                                     port_security=True):
-        if not names:
-            names = [NET_A, NET_B, NET_C]
-        if not subnet_cidrs:
-            subnet_cidrs = [[NET_A_S1], [NET_B_S1], [NET_C_S1]]
-        for (name, subnet_cidrs) in zip(names, subnet_cidrs):
-            network = super(manager.NetworkScenarioTest,
-                            self).create_network(namestart=name,
-                    port_security_enabled=port_security,
-                    networks_client=self.admin_networks_client)
-            self.networks[name] = network
-            self.subnets[name] = []
-            for (j, cidr) in enumerate(subnet_cidrs):
-                sub_name = "subnet-%s-%d" % (name, j + 1)
-                subnet = self._create_subnet_with_cidr(
-                    network, namestart=sub_name, cidr=cidr, ip_version=4,
-                    subnets_client=self.admin_subnets_client)
-                self.subnets[name].append(subnet)
-
-    def _create_subnet_with_cidr(self, network, subnets_client=None,
-                                 namestart='subnet-smoke', **kwargs):
-        if not subnets_client:
-            subnets_client = self.subnets_client
-        tenant_cidr = kwargs.get('cidr')
-        subnet = dict(
-            name=data_utils.rand_name(namestart),
-            network_id=network['id'],
-            tenant_id=network['tenant_id'],
-            **kwargs)
-        result = subnets_client.create_subnet(**subnet)
-        self.assertIsNotNone(result, 'Unable to allocate tenant network')
-        subnet = result['subnet']
-        self.assertEqual(subnet['cidr'], tenant_cidr)
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        subnets_client.delete_subnet, subnet['id'])
-        return subnet
-
-    def _create_fip_router(self, client=None, public_network_id=None,
-                           subnet_id=None):
-        if not client:
-            client = self.admin_routers_client
-        router = self._create_router(client, namestart='router-')
-        router_id = router['id']
-        if public_network_id is None:
-            public_network_id = CONF.network.public_network_id
-        if client is None:
-            client = self.routers_client
-        kwargs = {'external_gateway_info': {'network_id': public_network_id}}
-        router = client.update_router(router_id, **kwargs)['router']
-        if subnet_id is not None:
-            client.add_router_interface(router_id, subnet_id=subnet_id)
-            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                            client.remove_router_interface, router_id,
-                            subnet_id=subnet_id)
-        return router
-
-    def _associate_fip(self, server_index):
-        server = self.servers[server_index]
-        fip = self.create_floating_ip(
-            server, external_network_id=CONF.network.public_network_id,
-            port_id=self.ports[server['id']]['id'],
-            client=self.admin_fips_client)
-        self.server_fips[server['id']] = fip
-        return fip
-
-    def _create_router_and_associate_fip(self, server_index, subnet):
-        router = self._create_fip_router(client=self.admin_routers_client,
-                                         subnet_id=subnet['id'])
-        self._associate_fip(server_index)
-        return router
-
-    def _create_server(self, name, keypair, network, ip_address,
-                       security_group_ids, clients, port_security):
-        security_groups = []
-        if port_security:
-            security_groups = security_group_ids
-        create_port_body = {'fixed_ips': [{'ip_address': ip_address}],
-                            'namestart': 'port-smoke',
-                            'security_groups': security_groups}
-
-        port = super(manager.NetworkScenarioTest,
-                    self).create_port(network_id=network['id'],
-                                    client=self.admin_ports_client,
-                                    **create_port_body)
-
-        create_server_kwargs = {
-            'key_name': keypair['name'],
-            'networks': [{'uuid': network['id'], 'port': port['id']}]
-        }
-        body, servers = compute.create_test_server(
-            clients, wait_until='ACTIVE', name=name, **create_server_kwargs)
-        self.addCleanup(waiters.wait_for_server_termination,
-                        clients.servers_client, body['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        clients.servers_client.delete_server, body['id'])
-        server = clients.servers_client.show_server(body['id'])['server']
-        LOG.debug('Created server: %s with status: %s', server['id'],
-                  server['status'])
-        self.ports[server['id']] = port
-        return server
-
-    def _create_servers(self, ports_config=None, port_security=True):
-        keypair = self.create_keypair(client=self.admin_keys_client)
-        security_group_ids = [self.security_group['id']]
-        if ports_config is None:
-            ports_config = [[self.networks[NET_A], IP_A_S1_1],
-                            [self.networks[NET_B], IP_B_S1_1]]
-        for (i, port_config) in enumerate(ports_config):
-            network = port_config[0]
-            server = self._create_server(
-                'server-' + str(i + 1), keypair, network, port_config[1],
-                security_group_ids, self.os_admin, port_security)
-            self.servers.append(server)
-            self.servers_keypairs[server['id']] = keypair
-            self.server_fixed_ips[server['id']] = (
-                server['addresses'][network['name']][0]['addr'])
-            self.assertTrue(self.servers_keypairs)
-
-    def _create_l3_bgpvpn(self, name='test-l3-bgpvpn', rts=None,
-                          import_rts=None, export_rts=None):
-        if rts is None and import_rts is None and export_rts is None:
-            rts = [self.RT1]
-        import_rts = import_rts or []
-        export_rts = export_rts or []
-        self.bgpvpn = self.create_bgpvpn(
-            self.bgpvpn_admin_client,
-            tenant_id=self.bgpvpn_admin_client.project_id,
-            name=name, route_targets=rts, export_targets=export_rts,
-            import_targets=import_rts)
-        return self.bgpvpn
-
-    def _update_l3_bgpvpn(self, rts=None, import_rts=None, export_rts=None,
-                          bgpvpn=None):
-        bgpvpn = bgpvpn or self.bgpvpn
-        if rts is None:
-            rts = [self.RT1]
-        import_rts = import_rts or []
-        export_rts = export_rts or []
-        LOG.debug('Updating targets in BGPVPN %s', bgpvpn['id'])
-        self.bgpvpn_admin_client.update_bgpvpn(bgpvpn['id'],
-                                               route_targets=rts,
-                                               export_targets=export_rts,
-                                               import_targets=import_rts)
-
-    def _associate_all_nets_to_bgpvpn(self, bgpvpn=None):
-        bgpvpn = bgpvpn or self.bgpvpn
-        for network in self.networks.values():
-            self.bgpvpn_admin_client.create_network_association(
-                bgpvpn['id'], network['id'])
-        LOG.debug('BGPVPN network associations completed')
-
-    def _setup_ssh_client(self, server):
-        server_fip = self.server_fips[server['id']][
-            'floating_ip_address']
-        private_key = self.servers_keypairs[server['id']][
-            'private_key']
-        ssh_client = self.get_remote_client(server_fip,
-                                            private_key=private_key,
-                                            server=server)
-        return ssh_client
-
-    def _setup_http_server(self, server_index):
-        server = self.servers[server_index]
-        ssh_client = self._setup_ssh_client(server)
-        ssh_client.exec_command("sudo nc -kl -p 80 -e echo '%s:%s' &"
-                                % (server['name'], server['id']))
-
-    def _setup_ip_forwarding(self, server_index):
-        server = self.servers[server_index]
-        ssh_client = self._setup_ssh_client(server)
-        ssh_client.exec_command("sudo sysctl -w net.ipv4.ip_forward=1")
-
-    def _setup_ip_address(self, server_index, cidr, device=None):
-        self._setup_range_ip_address(server_index, [cidr], device=None)
-
-    def _setup_range_ip_address(self, server_index, cidrs, device=None):
-        MAX_CIDRS = 50
-        if device is None:
-            device = 'lo'
-        server = self.servers[server_index]
-        ssh_client = self._setup_ssh_client(server)
-        for i in range(0, len(cidrs), MAX_CIDRS):
-            ips = ' '.join(cidrs[i:i + MAX_CIDRS])
-            ssh_client.exec_command(
-                ("for ip in {ips}; do sudo ip addr add $ip "
-                 "dev {dev}; done").format(ips=ips, dev=device))
-
-    def _check_l3_bgpvpn(self, from_server=None, to_server=None,
-                         should_succeed=True, validate_server=False):
-        to_server = to_server or self.servers[1]
-        destination_srv = None
-        if validate_server:
-            destination_srv = '%s:%s' % (to_server['name'], to_server['id'])
-        destination_ip = self.server_fixed_ips[to_server['id']]
-        self._check_l3_bgpvpn_by_specific_ip(from_server=from_server,
-                                             to_server_ip=destination_ip,
-                                             should_succeed=should_succeed,
-                                             validate_server=destination_srv)
-
-    def _check_l3_bgpvpn_by_specific_ip(self, from_server=None,
-                                        to_server_ip=None,
-                                        should_succeed=True,
-                                        validate_server=None,
-                                        repeat_validate_server=10):
-        from_server = from_server or self.servers[0]
-        from_server_ip = self.server_fips[from_server['id']][
-            'floating_ip_address']
-        if to_server_ip is None:
-            to_server_ip = self.server_fixed_ips[self.servers[1]['id']]
-        ssh_client = self._setup_ssh_client(from_server)
-        check_reachable = should_succeed or validate_server
-        msg = ""
-        if check_reachable:
-            msg = "Timed out waiting for {ip} to become reachable".format(
-                ip=to_server_ip)
-        else:
-            msg = ("Unexpected ping response from VM with IP address "
-                   "{dest} originated from VM with IP address "
-                   "{src}").format(dest=to_server_ip, src=from_server_ip)
-        try:
-            result = self._check_remote_connectivity(ssh_client,
-                                                     to_server_ip,
-                                                     check_reachable)
-            # if a negative connectivity check was unsuccessful (unexpected
-            # ping reply) then try to know more:
-            if not check_reachable and not result:
-                try:
-                    content = ssh_client.exec_command(
-                        "nc %s 80" % to_server_ip).strip()
-                    LOG.warning("Can connect to %s: %s", to_server_ip, content)
-                except Exception:
-                    LOG.warning("Could ping %s, but no http", to_server_ip)
-
-            self.assertTrue(result, msg)
-
-            if validate_server and result:
-                # repeating multiple times gives increased odds of avoiding
-                # false positives in the case where the dataplane does
-                # equal-cost multipath
-                for i in range(0, repeat_validate_server):
-                    real_dest = ssh_client.exec_command(
-                        "nc %s 80" % to_server_ip).strip()
-                    result = real_dest == validate_server
-                    self.assertTrue(
-                        should_succeed == result,
-                        ("Destination server name is '%s', expected is '%s'" %
-                         (real_dest, validate_server)))
-                    LOG.info("nc server name check %d successful", i)
-        except Exception:
-            LOG.exception("Error validating connectivity to %s "
-                          "from VM with IP address %s: %s",
-                          to_server_ip, from_server_ip, msg)
-            raise
-
-    def _associate_fip_and_check_l3_bgpvpn(self, should_succeed=True):
-        subnet = self.subnets[NET_A][0]
-        self.router = self._create_router_and_associate_fip(0, subnet)
-        self._check_l3_bgpvpn(should_succeed=should_succeed)
-
-    def _delete_router(self, router, routers_client=None, ports_client=None):
-        if not routers_client:
-            routers_client = self.routers_client
-        if not ports_client:
-            ports_client = self.ports_client
-        ports_rsp = ports_client.list_ports(device_id=router['id'])
-        interfaces = ports_rsp['ports']
-        for i in interfaces:
-            test_utils.call_and_ignore_notfound_exc(
-                routers_client.remove_router_interface, router['id'],
-                subnet_id=i['fixed_ips'][0]['subnet_id'])
-        routers_client.delete_router(router['id'])
diff --git a/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_migration.py b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_migration.py
new file mode 100644
index 0000000..24f99dd
--- /dev/null
+++ b/neutron_tempest_plugin/bgpvpn/scenario/test_bgpvpn_migration.py
@@ -0,0 +1,136 @@
+# Copyright 2016 Cisco Systems, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+
+import netaddr
+from oslo_concurrency import lockutils
+from oslo_log import log as logging
+import tempest.api.compute.base as base2
+from tempest.common import utils
+from tempest import config
+from tempest.lib import decorators
+
+from neutron_tempest_plugin.bgpvpn import base
+from neutron_tempest_plugin.bgpvpn.scenario import manager
+
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+NET_D = 'D'
+NET_E = 'E'
+NET_F = 'F'
+
+
+if "SUBNETPOOL_PREFIX_V4" in os.environ:
+    subnet_base = netaddr.IPNetwork(os.environ['SUBNETPOOL_PREFIX_V4'])
+    if subnet_base.prefixlen > 21:
+        raise Exception("if SUBNETPOOL_PREFIX_V4 is set, it needs to offer "
+                        "space for at least 8 /24 subnets")
+else:
+    subnet_base = netaddr.IPNetwork("10.200.0.0/16")
+
+
+def assign_24(idx):
+    # how many addresses in a /24:
+    range_size = 2 ** (32 - 24)
+    return netaddr.cidr_merge(
+        subnet_base[range_size * idx:range_size * (idx + 1)])[0]
+
+
+S1D = assign_24(1)
+S1E = assign_24(4)
+S1F = assign_24(6)
+NET_D_S1 = str(S1D)
+NET_E_S1 = str(S1E)
+NET_F_S1 = str(S1F)
+IP_D_S1_1 = str(S1D[10])
+IP_E_S1_1 = str(S1E[20])
+IP_F_S1_1 = str(S1F[30])
+
+
+class TestBGPVPNMigration(base.BaseBgpvpnTest, manager.NetworkScenarioTest,
+                          base2.BaseV2ComputeAdminTest):
+    block_migration = None
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestBGPVPNMigration, cls).setUpClass()
+        cls._rt_index = 0
+        cls.admin_migration_client = cls.os_admin.migrations_client
+
+    @classmethod
+    @lockutils.synchronized('bgpvpn')
+    def new_rt(cls):
+        cls._rt_index += 1
+        return "64513:%d" % cls._rt_index
+
+    def setUp(self):
+        super(TestBGPVPNMigration, self).setUp()
+        self.servers_keypairs = {}
+        self.servers = []
+        self.server_fixed_ips = {}
+        self.ports = {}
+        self.networks = {}
+        self.subnets = {}
+        self.server_fips = {}
+        self._create_security_group_for_test()
+        self.RT1 = self.new_rt()
+
+    @decorators.idempotent_id('8a5a6fac-313c-464b-9c5c-77eca377ff1a')
+    @utils.services('compute', 'network')
+    def test_bgpvpn_livemigration(self):
+        """This test checks connection between VMs with BGPVPN
+
+         after live migration
+
+        1. Create networks D, E and F with their respective subnets
+        2. Associate network D, E and network F to a given L3 BGPVPN
+        3. Start up server 1 in network D
+        4. Start up server 2 in network E
+        5. Create router and connect it to network D
+        6. Give a FIP to server 1
+        7. Check that server 1 can ping server 2
+        8. Live migrate server 1
+        9. Check that server 1 can ping server 2
+        10. Live migrate server 2
+        11. Check that server 1 can ping server 2
+        """
+        self._create_networks_and_subnets(names=[NET_D, NET_E, NET_F],
+                                          subnet_cidrs=[[NET_D_S1], [NET_E_S1],
+                                                        [NET_F_S1]])
+        self._create_l3_bgpvpn()
+        self._associate_all_nets_to_bgpvpn()
+        self._create_servers(ports_config=[
+            [self.networks[NET_D], IP_D_S1_1],
+            [self.networks[NET_E], IP_E_S1_1],
+            [self.networks[NET_F], IP_F_S1_1]
+        ])
+        self._associate_fip_and_check_l3_bgpvpn(subnet=NET_D)
+        # Migrate both VMs one by one and check connection
+        for i in range(2):
+            source_host = self.get_host_for_server(self.servers[i]['id'])
+            if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+                # not to specify a host so that the scheduler will pick one
+                destination_host = None
+            else:
+                destination_host = \
+                    self.get_host_other_than(self.servers[i]['id'])
+            LOG.info("Live migrate from source %s to destination %s",
+                     source_host, destination_host)
+            self._live_migrate(self.servers[i]['id'],
+                               destination_host, 'ACTIVE')
+            self._check_l3_bgpvpn()
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index a6d5c09..3f8ccd5 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -184,6 +184,22 @@
                default=200,
                help=("Maximum number for the range of "
                      "assigned number for distinguishers.")),
+    cfg.StrOpt('l3vpn_endpoint',
+               default=None,
+               help=("IP Address/Mask, which will be used to "
+                     "test the l3vpn connectivity.")),
+    cfg.StrOpt('l3vpn_subnet_cidr',
+               default="192.168.0.254/24",
+               help=("IP Address/Mask, which will be used as private network"
+                     "test the l3vpn connectivity.")),
+    cfg.StrOpt('l2vpn_endpoint',
+               default=None,
+               help=("IP Address/Mask, which will be used to "
+                     "test the l2vpn connectivity.")),
+    cfg.StrOpt('route_target',
+               default=None,
+               help=("Route-target (RT) extended community attributes "
+                     "identify the VPN membership of routes.")),
 ]
 
 bgpvpn_group = cfg.OptGroup(name="bgpvpn", title=("Networking-Bgpvpn Service "
@@ -191,6 +207,47 @@
 CONF.register_group(bgpvpn_group)
 CONF.register_opts(BgpvpnGroup, group="bgpvpn")
 
+
+DynamicRoutingGroup = [
+    cfg.StrOpt('frr_docker_image',
+               default='quay.io/frrouting/frr:8.5.0',
+               help=("Docker image with frr.")),
+    cfg.ListOpt('frr_provider_ipv4_ips',
+                default=["10.11.12.71/24", "10.11.12.72/24", "10.11.12.73/24",
+                         "10.11.12.74/24", "10.11.12.75/24", "10.11.12.76/24"],
+                help=('List of ip addresses to bind frr containers. Require '
+                     'at least 3 items per class to run tests simulteniously. '
+                     'The addresses should be assigned on interface with '
+                     'tempest node.')),
+    cfg.ListOpt('frr_provider_ipv6_ips',
+                default=["2001:db8:a000::7001/64", "2001:db8:a000::7002/64",
+                         "2001:db8:a000::7003/64", "2001:db8:a000::7004/64",
+                         "2001:db8:a000::7005/64", "2001:db8:a000::7006/64"],
+                help=('List of ip addresses to bind frr containers. Require '
+                     'at least 3 items per class to run tests simulteniously. '
+                     'The addresses should be assigned on interface with '
+                     'tempest node.')),
+    cfg.BoolOpt('frr_bgp_ipv6_enabled',
+                default=False,
+                help=("Run dynamic routing ipv6 tests.")),
+    cfg.IntOpt('frr_bgp_timeout',
+               default=600,
+               help=('Timeout for bgp operation like setup neighborship '
+                    'or route update.')),
+    cfg.StrOpt('frr_bgp_ipv4_control_cidr',
+               default='10.0.0.0/8',
+               help=("CIDR for bgp control network on gateway nodes. Is "
+                     "used as allowed range for dynamic neighbors.")),
+    cfg.StrOpt('frr_bgp_ipv6_control_cidr',
+               default='2001:db8::/32',
+               help=("CIDR for bgp control network on gateway nodes. Is "
+                     "used as allowed range for dynamic neighbors.")),
+]
+dynamic_routing_group = cfg.OptGroup(name="dynamic_routing",
+                                     title=("Networking-DNR Service Options"))
+CONF.register_group(dynamic_routing_group)
+CONF.register_opts(DynamicRoutingGroup, group="dynamic_routing")
+
 # TODO(slaweq): This config option is added to avoid running fwaas tests twice
 # on stable branches till stable/stein. We need to remove this config option
 # once stable/stein is EOL. Fwaas tempest plugin has been merged into
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py b/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
index 5cdd524..c5c1646 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/api/test_bgp_speaker_extensions.py
@@ -229,22 +229,56 @@
     def test_get_advertised_routes_null_address_scope(self):
         self.useFixture(fixtures.LockFixture('gateway_network_binding'))
         bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
+
+        addr_scope1 = self.create_address_scope(
+                                 'get-advertised-routes-null-address-scope',
+                                 ip_version=4)
+        ext_net = self.create_shared_network(**{'router:external': True})
+        ext_subnetpool = self.create_subnetpool(
+                                 'get-advertised-routes-null-address-pool-ext',
+                                 is_admin=True,
+                                 default_prefixlen=24,
+                                 address_scope_id=addr_scope1['id'],
+                                 prefixes=['8.0.0.0/8'])
+        self.create_subnet({'id': ext_net['id']},
+                   cidr=netaddr.IPNetwork('8.1.0.0/24'),
+                   ip_version=4,
+                   client=self.admin_client,
+                   subnetpool_id=ext_subnetpool['id'])
+        ext_gw_info = {'network_id': ext_net['id']}
+        router = self.create_router(ext_gw_info)
+        self.admin_routers.append(router)
         bgp_speaker_id = bgp_speaker['id']
         self.bgp_adm_client.add_bgp_gateway_network(bgp_speaker_id,
-                                                  self.ext_net_id)
+                                                  ext_net['id'])
         routes = self.bgp_adm_client.get_bgp_advertised_routes(bgp_speaker_id)
         self.assertEqual(0, len(routes['advertised_routes']))
 
     @decorators.idempotent_id('cae9cdb1-ad65-423c-9604-d4cd0073616e')
     def test_get_advertised_routes_floating_ips(self):
         self.useFixture(fixtures.LockFixture('gateway_network_binding'))
+        addr_scope1 = self.create_address_scope(
+                                 'get-advertised-routes-floating-ip-scope',
+                                 ip_version=4)
+        ext_net = self.create_shared_network(**{'router:external': True})
+        ext_subnetpool = self.create_subnetpool(
+                                 'get-advertised-routes-floating-ip-ext',
+                                 is_admin=True,
+                                 default_prefixlen=24,
+                                 address_scope_id=addr_scope1['id'],
+                                 prefixes=['8.0.0.0/8'])
+        self.create_subnet({'id': ext_net['id']},
+                   cidr=netaddr.IPNetwork('8.2.0.0/24'),
+                   ip_version=4,
+                   client=self.admin_client,
+                   subnetpool_id=ext_subnetpool['id'])
         bgp_speaker = self.create_bgp_speaker(**self.default_bgp_speaker_args)
         bgp_speaker_id = bgp_speaker['id']
         self.bgp_adm_client.add_bgp_gateway_network(bgp_speaker_id,
-                                                    self.ext_net_id)
+                                                    ext_net['id'])
         tenant_net = self.create_network()
         tenant_subnet = self.create_subnet(tenant_net)
-        ext_gw_info = {'network_id': self.ext_net_id}
+        ext_gw_info = {'network_id': ext_net['id']}
         router = self.create_router(ext_gw_info)
         self.admin_routers.append(router)
         self.admin_client.add_router_interface_with_subnet_id(
@@ -253,7 +287,7 @@
         self.admin_routerports.append({'router_id': router['id'],
                                        'subnet_id': tenant_subnet['id']})
         tenant_port = self.create_port(tenant_net)
-        floatingip = self.create_floatingip(self.ext_net_id)
+        floatingip = self.create_floatingip(ext_net['id'])
         self.admin_floatingips.append(floatingip)
         self.client.update_floatingip(floatingip['id'],
                                       port_id=tenant_port['id'])
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/frr/__init__.py b/neutron_tempest_plugin/neutron_dynamic_routing/frr/__init__.py
new file mode 100644
index 0000000..8505995
--- /dev/null
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/frr/__init__.py
@@ -0,0 +1,185 @@
+# Copyright 2023 Mirantis Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import abc
+import json
+import os
+
+import docker
+import jinja2
+from tempest import config
+import tenacity
+
+
+CONF = config.CONF
+
+
+class GenericConnector:
+    @abc.abstractmethod
+    def exec(self, cmd):
+        pass
+
+
+class ContainerConnector(GenericConnector):
+    def __init__(self, container):
+        self.ctn = container
+
+    def exec(self, cmd):
+        res_cmd = f"vtysh -d bgpd  -c '{cmd}'"
+        return self.ctn.exec_run(res_cmd)
+
+
+class BGPClient:
+    def __init__(self, connector):
+        self.connector = connector
+
+    def exec(self, cmd):
+        res = self.connector.exec(cmd)
+        if res.exit_code == 0:
+            return res.output
+        raise Exception(f"Failed to run command {cmd}")
+
+    def show_bgp_neighbors(self, *args):
+        cmd = ["show", "bgp", "neighbors", "json"]
+        cmd.extend(args)
+        return json.loads(self.exec(" ".join(cmd)))
+
+    def show_bgp_ipv4(self):
+        cmd = ["show", "bgp", "ipv4", "json"]
+        return json.loads(self.exec(" ".join(cmd)))
+
+    def show_bgp_ipv6(self):
+        cmd = ["show", "bgp", "ipv6", "json"]
+        return json.loads(self.exec(" ".join(cmd)))
+
+    def show_bgp(self):
+        cmd = ["show", "bgp", "json"]
+        return json.loads(self.exec(" ".join(cmd)))
+
+
+class FrrBGPContainer:
+    conf_dir = "/etc/frr"
+
+    def __init__(self, name, image, bgpd, daemons=None):
+        self.image = image
+        self.name = name
+        self.config_dir = os.path.join(CONF.state_path, f"ctn_base/{name}/")
+        self.volumes = [f"{self.config_dir}:{self.conf_dir}"]
+        self.daemons = daemons or {
+            "bgpd": {"enabled": "yes"},
+            "vtysh": {"enabled": "yes"},
+        }
+        self.bgpd = bgpd
+        self.docker_client = docker.from_env()
+        self._create_config_debian()
+        self.ctn = None
+        self._bgp_client = None
+
+    @property
+    def bgp_client(self):
+        if self._bgp_client is None:
+            self._bgp_client = BGPClient(ContainerConnector(self.ctn))
+        return self._bgp_client
+
+    def _create_config_debian(self):
+        environment = jinja2.Environment(
+            loader=jinja2.FileSystemLoader(
+                os.path.join(os.path.dirname(__file__), "templates")
+            )
+        )
+        template = environment.get_template("bgpd.conf")
+        if not os.path.exists(self.config_dir):
+            os.makedirs(self.config_dir)
+        for cfg_file in ["daemons", "vtysh.conf", "bgpd.conf"]:
+            with open(
+                os.path.join(self.config_dir, cfg_file),
+                mode="w",
+                encoding="utf-8",
+            ) as conf:
+                template = environment.get_template(cfg_file)
+                data = template.render(bgpd=self.bgpd, daemons=self.daemons)
+                conf.write(data)
+
+    def run(self, wait=True):
+        self.docker_client.images.pull(self.image)
+        self.ctn = self.docker_client.containers.create(
+            image=self.image,
+            name=self.name,
+            volumes=self.volumes,
+            privileged=True,
+            network_mode="host",
+        )
+        self.ctn.start()
+        if wait:
+            self._wait_running()
+
+    @tenacity.retry(
+        retry=tenacity.retry_if_result(lambda val: val is not True),
+        wait=tenacity.wait_random(min=5, max=15),
+        stop=tenacity.stop_after_delay(60),
+    )
+    def _wait_running(self):
+        self.ctn.reload()
+        if self.ctn.status == "running":
+            return True
+
+    def exec_on_ctn(self, cmd, capture=True, detach=False):
+        self.ctn.exec_run(cmd, detach=detach)
+
+    @tenacity.retry(
+        retry=tenacity.retry_if_result(lambda val: val is not True),
+        wait=tenacity.wait_random(min=5, max=15),
+        stop=tenacity.stop_after_delay(CONF.dynamic_routing.frr_bgp_timeout),
+    )
+    def bgp_check_neighbor_state(self, nei_ident, expected_state):
+        res = self.bgp_client.show_bgp_neighbors()
+        neighbor_states = [
+            nei.get("bgpState") == expected_state
+            for nei in res.values()
+            if nei.get("peerGroup") == nei_ident
+        ]
+        return len(neighbor_states) > 0 and all(neighbor_states)
+
+    @tenacity.retry(
+        retry=tenacity.retry_if_result(lambda val: val is not True),
+        wait=tenacity.wait_random(min=5, max=15),
+        stop=tenacity.stop_after_delay(CONF.dynamic_routing.frr_bgp_timeout),
+    )
+    def bgp_check_neighbor_absent(self, nei_ident):
+        res = self.bgp_client.show_bgp_neighbors()
+        neighbors = [
+            nei for nei in res.values() if nei.get("peerGroup") == nei_ident
+        ]
+        return len(neighbors) == 0
+
+    @tenacity.retry(
+        retry=tenacity.retry_if_result(lambda val: val is not True),
+        wait=tenacity.wait_random(min=5, max=15),
+        stop=tenacity.stop_after_delay(CONF.dynamic_routing.frr_bgp_timeout),
+    )
+    def bgp_check_rib(self, ip_version, cidr, nexthop=None):
+        res = getattr(self.bgp_client, f"show_bgp_{ip_version}")()
+        should = {"cidr": False}
+        if nexthop:
+            should["nexthop"] = False
+
+        for _cidr, routes in res.get("routes", {}).items():
+            if cidr == _cidr:
+                should["cidr"] = True
+            for route in routes:
+                if nexthop:
+                    for hop_data in route.get("nexthops", []):
+                        if nexthop == hop_data.get("ip"):
+                            should["nexthop"] = True
+        return all(should.values())
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/frr/constants.py b/neutron_tempest_plugin/neutron_dynamic_routing/frr/constants.py
new file mode 100644
index 0000000..f2b96d8
--- /dev/null
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/frr/constants.py
@@ -0,0 +1,7 @@
+# Various states of bgp state machine.
+BGP_FSM_IDLE = "Idle"
+BGP_FSM_CONNECT = "Connect"
+BGP_FSM_ACTIVE = "Active"
+BGP_FSM_OPEN_SENT = "OpenSent"
+BGP_FSM_OPEN_CONFIRM = "OpenConfirm"
+BGP_FSM_ESTABLISHED = "Established"
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/bgpd.conf b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/bgpd.conf
new file mode 100644
index 0000000..27d8fab
--- /dev/null
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/bgpd.conf
@@ -0,0 +1,23 @@
+!
+router bgp {{ bgpd.bgp.as_number }}
+ bgp router-id {{ bgpd.bgp.router_id }}
+ {%- for neighbor in bgpd.bgp.neighbors %}
+ {%- if neighbor.peer_group %}
+ neighbor {{ neighbor.address }} peer-group
+ {%- endif %}
+ neighbor {{ neighbor.address }} remote-as {{ neighbor.as_number }}
+ {%- if neighbor.get('passive', false) %}
+ neighbor {{ neighbor.address }} passive
+ {%- endif %}
+ {%- endfor %}
+ {%- if bgpd.bgp.get('listen', {}).get('limit') %}
+ bgp listen limit {{ bgpd.bgp.listen.limit }}
+ {%- endif %}
+ {%- for listen_range in bgpd.bgp.get('listen', {}).get('ranges', []) %}
+ bgp listen range {{ listen_range.cidr }} peer-group {{ listen_range.peer_group }}
+ {%- endfor %}
+ {%- if bgpd.bgp.ebgp_requires_policy is false %}
+ no bgp ebgp-requires-policy
+ {%- endif %}
+ neighbor dnr timers 10 30
+!
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/daemons b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/daemons
new file mode 100644
index 0000000..314e231
--- /dev/null
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/daemons
@@ -0,0 +1,33 @@
+zebra=no
+bgpd={{ daemons.bgpd.enabled }}
+ospfd=no
+ospf6d=no
+ripd=no
+ripngd=no
+isisd=no
+babeld=no
+
+#
+# If this option is set the /etc/init.d/frr script automatically loads
+# the config via "vtysh -b" when the servers are started.
+# Check /etc/pam.d/frr if you intend to use "vtysh"!
+#
+vtysh_enable={{ daemons.vtysh.enabled }}
+zebra_options="  -A 127.0.0.1 -s 90000000"
+bgpd_options="   -A 127.0.0.1 --listenon {{ daemons.bgpd.listenon }}"
+ospfd_options="  -A 127.0.0.1"
+ospf6d_options=" -A ::1"
+ripd_options="   -A 127.0.0.1"
+ripngd_options=" -A ::1"
+isisd_options="  -A 127.0.0.1"
+pimd_options="   -A 127.0.0.1"
+ldpd_options="   -A 127.0.0.1"
+nhrpd_options="  -A 127.0.0.1"
+eigrpd_options=" -A 127.0.0.1"
+babeld_options=" -A 127.0.0.1"
+sharpd_options=" -A 127.0.0.1"
+pbrd_options="   -A 127.0.0.1"
+staticd_options="-A 127.0.0.1"
+bfdd_options="   -A 127.0.0.1"
+fabricd_options="-A 127.0.0.1"
+vrrpd_options="  -A 127.0.0.1"
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/vtysh.conf b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/vtysh.conf
new file mode 100644
index 0000000..e0ab9cb
--- /dev/null
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/frr/templates/vtysh.conf
@@ -0,0 +1 @@
+service integrated-vtysh-config
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/README b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/README
index 44990bd..14957d3 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/README
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/README
@@ -14,23 +14,19 @@
        | dragent |           |
        +---------+           |
             |                |
-            | +--------------+
-            | |
-       +--------+
-       | docker |
-       | bridge |
-       +--------+
-            |
-            +-----------+------------+-------
+            |                |
+            |                |
+            |                |
+            +-----------+----+-------+-------
                         |            |
                    +---------+  +---------+
-       docker      | quagga1 |  | quagga2 | ...
+       docker      |   frr1  |  |  frr1   | ...
        container   +---------+  +---------+
 
 
-docker container environment is provided by test tool of os-ken.
+docker container environment is provided by test tools with help of
+python-docker library.
 It has the following functions:
-- build and remove a container image.
 - run, stop and remove a container.
-- some operations to quagga container.
-- get some information from quagga container.
+- some operations to frr container.
+- get some information from frr container.
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
index c7023af..2655804 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base.py
@@ -16,11 +16,10 @@
 
 import collections
 import threading
-import time
 
+import docker
 import netaddr
 
-from os_ken.tests.integrated.common import docker_base as ctn_base
 from tempest.common import utils
 from tempest import config
 
@@ -36,10 +35,6 @@
 SubNet = collections.namedtuple('SubNet', 'name, cidr, mask')
 Router = collections.namedtuple('Router', 'name, gw')
 AS = collections.namedtuple('AS', 'asn, router_id, adv_net')
-CHECKTIME = 180
-CHECKTIME_INFO = 60
-CHECKTIME_INT = 1
-BRIDGE_TYPE = ctn_base.BRIDGE_TYPE_DOCKER
 
 
 def _setup_client_args(auth_provider):
@@ -110,7 +105,7 @@
         for ctn in cls.containers:
             try:
                 ctn.stop()
-            except ctn_base.CommandError:
+            except docker.errors.APIError:
                 pass
             ctn.remove()
         for br in cls.bridges:
@@ -249,122 +244,6 @@
                                                      peer_id)
         return (speaker_id, peer_ids)
 
-    def get_remote_as_state(self, l_as, r_as,
-                            expected_state,
-                            init_state=ctn_base.BGP_FSM_IDLE,
-                            checktime=CHECKTIME,
-                            checktime_int=CHECKTIME_INT):
-        neighbor_state = init_state
-        for i in range(0, checktime):
-            neighbor_state = r_as.get_neighbor_state(l_as)
-            if neighbor_state == expected_state:
-                break
-            time.sleep(checktime_int)
-        return neighbor_state
-
-    def check_remote_as_state(self, l_as, r_as,
-                              expected_state,
-                              init_state=ctn_base.BGP_FSM_IDLE,
-                              checktime=CHECKTIME,
-                              checktime_int=CHECKTIME_INT):
-        neighbor_state = self.get_remote_as_state(l_as, r_as,
-                                                  expected_state,
-                                                  init_state=init_state,
-                                                  checktime=checktime,
-                                                  checktime_int=checktime_int)
-        self.assertEqual(neighbor_state, expected_state)
-
-    def get_remote_as_of_state_ok(self, l_as, r_ass,
-                                  expected_state,
-                                  init_state=ctn_base.BGP_FSM_IDLE,
-                                  checktime=CHECKTIME,
-                                  checktime_int=CHECKTIME_INT):
-        neighbor_state = init_state
-        ras_list = []
-        ras_max = len(r_ass)
-        for r_as in r_ass:
-            ras_list.append({'as': r_as, 'check': False})
-        ok_ras = []
-        for i in range(0, checktime):
-            for ras in ras_list:
-                if ras['check']:
-                    continue
-                neighbor_state = ras['as'].get_neighbor_state(l_as)
-                if neighbor_state == expected_state:
-                    ras['check'] = True
-                    ok_ras.append(ras['as'])
-            if len(ok_ras) >= ras_max:
-                break
-            time.sleep(checktime_int)
-        return ok_ras
-
-    def check_multi_remote_as_state(self, l_as, r_ass,
-                                    expected_state,
-                                    init_state=ctn_base.BGP_FSM_IDLE,
-                                    checktime=CHECKTIME,
-                                    checktime_int=CHECKTIME_INT):
-        ok_ras = self.get_remote_as_of_state_ok(
-            l_as, r_ass,
-            expected_state,
-            init_state=init_state,
-            checktime=checktime,
-            checktime_int=checktime_int)
-        self.assertEqual(len(ok_ras), len(r_ass))
-
-    def get_remote_as_rib(self, r_as, prefix, rf, key, expected_item,
-                          checktime=CHECKTIME_INFO,
-                          checktime_int=CHECKTIME_INT):
-        item = None
-        for i in range(0, checktime):
-            rib = r_as.get_global_rib(prefix=prefix, rf=rf)
-            if rib and key in rib[0]:
-                if expected_item == rib[0][key]:
-                    item = rib[0][key]
-                    break
-            time.sleep(checktime_int)
-        return item
-
-    def check_remote_as_rib(self, r_as, prefix, rf, key, expected_item,
-                            checktime=CHECKTIME_INFO,
-                            checktime_int=CHECKTIME_INT):
-        item = self.get_remote_as_rib(r_as=r_as, prefix=prefix, rf=rf,
-                                      key=key, expected_item=expected_item,
-                                      checktime=checktime,
-                                      checktime_int=checktime_int)
-        self.assertEqual(expected_item, item)
-
-    def get_remote_as_of_rib_ok(self, r_ass, prefix, rf, key, expected_item,
-                                checktime=CHECKTIME_INFO,
-                                checktime_int=CHECKTIME_INT):
-        ras_list = []
-        ras_max = len(r_ass)
-        for r_as in r_ass:
-            ras_list.append({'as': r_as, 'check': False})
-        ok_ras = []
-        for i in range(0, checktime):
-            for ras in ras_list:
-                if ras['check']:
-                    continue
-                rib = r_as.get_global_rib(prefix=prefix, rf=rf)
-                if rib and key in rib[0]:
-                    if expected_item == rib[0][key]:
-                        ras['check'] = True
-                        ok_ras.append(ras['as'])
-            if len(ok_ras) >= ras_max:
-                break
-            time.sleep(checktime_int)
-        return ok_ras
-
-    def check_multi_remote_as_rib(self, r_ass, prefix, rf, key, expected_item,
-                                  checktime=CHECKTIME_INFO,
-                                  checktime_int=CHECKTIME_INT):
-        ok_ras = self.get_remote_as_of_rib_ok(
-                   r_ass=r_ass, prefix=prefix, rf=rf,
-                   key=key, expected_item=expected_item,
-                   checktime=checktime,
-                   checktime_int=checktime_int)
-        self.assertEqual(len(ok_ras), len(r_ass))
-
     def get_next_hop(self, speaker_id, dest_addr):
         routes = self.bgp_adm_client.get_bgp_advertised_routes(speaker_id)
         next_hop = ''
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base_test_proto.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base_test_proto.py
index 643620f..305ab25 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base_test_proto.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/base_test_proto.py
@@ -16,10 +16,9 @@
 
 from tempest import config
 
+from neutron_tempest_plugin.neutron_dynamic_routing.frr import constants
 from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base
 
-from os_ken.tests.integrated.common import docker_base as ctn_base
-
 CONF = config.CONF
 
 
@@ -41,8 +40,8 @@
             ext_net_id,
             self.bgp_speaker_args,
             [self.bgp_peer_args[0]])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
+        self.r_ass[0].bgp_check_neighbor_state("dnr",
+                                               constants.BGP_FSM_ESTABLISHED)
 
     def _test_check_advertised_tenant_network(self, ip_version):
         self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
@@ -60,12 +59,12 @@
             ext_net_id,
             self.bgp_speaker_args,
             [self.bgp_peer_args[0]])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
+
+        self.r_ass[0].bgp_check_neighbor_state("dnr",
+                                               constants.BGP_FSM_ESTABLISHED)
         rf = 'ipv' + str(ip_version)
-        self.check_remote_as_rib(self.r_ass[0], TNet.cidr, rf,
-                                 'nexthop',
-                                 self.get_next_hop(speaker_id, TNet.cidr))
+        self.r_ass[0].bgp_check_rib(rf, TNet.cidr,
+                                    self.get_next_hop(speaker_id, TNet.cidr))
 
     def _test_check_advertised_multiple_tenant_network(self, ip_version):
         self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
@@ -88,13 +87,14 @@
             ext_net_id,
             self.bgp_speaker_args,
             [self.bgp_peer_args[0]])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
+
+        self.r_ass[0].bgp_check_neighbor_state("dnr",
+                                               constants.BGP_FSM_ESTABLISHED)
+
         rf = 'ipv' + str(ip_version)
         for cidr in tnets_cidr:
-            self.check_remote_as_rib(self.r_ass[0], cidr, rf,
-                                     'nexthop',
-                                     self.get_next_hop(speaker_id, cidr))
+            self.r_ass[0].bgp_check_rib(rf, cidr,
+                                        self.get_next_hop(speaker_id, cidr))
 
     def _test_check_neighbor_established_with_multiple_peers(
             self, ip_version):
@@ -115,8 +115,8 @@
             ext_net_id,
             self.bgp_speaker_args,
             self.bgp_peer_args)
-        self.check_multi_remote_as_state(self.dr, self.r_ass,
-                                         ctn_base.BGP_FSM_ESTABLISHED)
+        for r_as in self.r_ass:
+            r_as.bgp_check_neighbor_state("dnr", constants.BGP_FSM_ESTABLISHED)
 
     def _test_check_advertised_tenant_network_with_multiple_peers(
             self, ip_version):
@@ -137,9 +137,10 @@
             ext_net_id,
             self.bgp_speaker_args,
             self.bgp_peer_args)
-        self.check_multi_remote_as_state(self.dr, self.r_ass,
-                                         ctn_base.BGP_FSM_ESTABLISHED)
+        for r_as in self.r_ass:
+            r_as.bgp_check_neighbor_state("dnr", constants.BGP_FSM_ESTABLISHED)
+
         rf = 'ipv' + str(ip_version)
-        next_hop = self.get_next_hop(speaker_id, TNet.cidr)
-        self.check_multi_remote_as_rib(self.r_ass, TNet.cidr, rf,
-                                       'nexthop', next_hop)
+        for r_as in self.r_ass:
+            r_as.bgp_check_rib(rf, TNet.cidr,
+                               self.get_next_hop(speaker_id, TNet.cidr))
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/__init__.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/__init__.py
+++ /dev/null
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/base.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/base.py
deleted file mode 100644
index dd170e7..0000000
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/base.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright (C) 2016 VA Linux Systems Japan K.K.
-# Copyright (C) 2016 Fumihiko Kakuma <kakuma at valinux co jp>
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from os_ken.tests.integrated.common import docker_base as ctn_base
-from os_ken.tests.integrated.common import quagga
-
-from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base
-
-
-class BgpSpeakerBasicTestJSONBase(base.BgpSpeakerScenarioTestJSONBase):
-
-    RAS_MAX = 3
-    public_gw = '192.168.20.1'
-    MyScope = base.Scope(name='my-scope')
-    PNet = base.Net(name='', net='172.24.6.0', mask=24,
-                    cidr='172.24.6.0/24', router=None)
-    PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask,
-                      prefixes=[PNet.net + '/8'])
-    PSubNet = base.SubNet(name='', cidr=PNet.cidr, mask=PNet.mask)
-    TPool = base.Pool(name='tenant-test-pool', prefixlen=28,
-                      prefixes=['10.10.0.0/16'])
-    L_AS = base.AS(asn='64512', router_id='192.168.0.2', adv_net='')
-    ras_l = [
-        base.AS(asn='64522', router_id='192.168.0.12',
-                adv_net='192.168.162.0/24'),
-        base.AS(asn='64523', router_id='192.168.0.13',
-                adv_net='192.168.163.0/24'),
-        base.AS(asn='64524', router_id='192.168.0.14',
-                adv_net='192.168.164.0/24')
-    ]
-
-    bgp_speaker_args = {
-        'local_as': L_AS.asn,
-        'ip_version': 4,
-        'name': 'my-bgp-speaker1',
-        'advertise_floating_ip_host_routes': True,
-        'advertise_tenant_networks': True
-    }
-    bgp_peer_args = [
-        {'remote_as': ras_l[0].asn,
-         'name': 'my-bgp-peer1',
-         'peer_ip': None,
-         'auth_type': 'none'},
-        {'remote_as': ras_l[1].asn,
-         'name': 'my-bgp-peer2',
-         'peer_ip': None,
-         'auth_type': 'none'},
-        {'remote_as': ras_l[2].asn,
-         'name': 'my-bgp-peer3',
-         'peer_ip': None,
-         'auth_type': 'none'}
-    ]
-
-    def setUp(self):
-        super(BgpSpeakerBasicTestJSONBase, self).setUp()
-
-    @classmethod
-    def resource_setup_container(cls):
-        cls.brdc = ctn_base.Bridge(name='br-docker-basic',
-                                   subnet='192.168.20.0/24',
-                                   start_ip='192.168.20.128',
-                                   end_ip='192.168.20.254',
-                                   self_ip=True,
-                                   fixed_ip=cls.public_gw + '/24',
-                                   br_type=base.BRIDGE_TYPE)
-        cls.bridges.append(cls.brdc)
-        # This is dummy container object for a dr service.
-        # This keeps data which passes to a quagga container.
-        cls.dr = ctn_base.BGPContainer(name='dummy-dr-basic',
-                                       asn=int(cls.L_AS.asn),
-                                       router_id=cls.L_AS.router_id)
-        cls.dr.set_addr_info(bridge='br-docker-basic', ipv4=cls.public_gw)
-        # quagga container
-        cls.dockerimg = ctn_base.DockerImage()
-        cls.q_img = cls.dockerimg.create_quagga(check_exist=True)
-        cls.images.append(cls.q_img)
-        for i in range(cls.RAS_MAX):
-            qg = quagga.QuaggaBGPContainer(name='q-basic-' + str(i + 1),
-                                           asn=int(cls.ras_l[i].asn),
-                                           router_id=cls.ras_l[i].router_id,
-                                           ctn_image_name=cls.q_img)
-            cls.containers.append(qg)
-            cls.r_ass.append(qg)
-            qg.add_route(cls.ras_l[i].adv_net)
-            qg.run(wait=True)
-            cls.r_as_ip.append(cls.brdc.addif(qg))
-            qg.add_peer(cls.dr, bridge=cls.brdc.name,
-                        peer_info={'passive': True})
-
-        cls.tnet_gen = cls.get_subnet(start='10.10.1.0',
-                                      end='10.10.255.0',
-                                      step=256)
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_4byte_asn.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_4byte_asn.py
deleted file mode 100644
index f492ede..0000000
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_4byte_asn.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.common import utils
-from tempest import config
-from tempest.lib import decorators
-
-from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base
-from neutron_tempest_plugin.neutron_dynamic_routing.scenario\
-    import base_test_proto as test_base
-
-from os_ken.tests.integrated.common import docker_base as ctn_base
-from os_ken.tests.integrated.common import quagga
-
-CONF = config.CONF
-
-
-class BgpSpeaker4byteASNTest(test_base.BgpSpeakerProtoTestBase):
-
-    RAS_MAX = 3
-    ip_version = 4
-    public_gw = '192.168.10.1'
-    MyScope = base.Scope(name='my-scope')
-    PNet = base.Net(name='', net='172.24.6.0', mask=24,
-                    cidr='172.24.6.0/24', router=None)
-    PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask,
-                      prefixes=[PNet.net + '/8'])
-    PSubNet = base.SubNet(name='', cidr=PNet.cidr, mask=PNet.mask)
-    TPool = base.Pool(name='tenant-test-pool', prefixlen=28,
-                      prefixes=['10.10.0.0/16'])
-    L_AS = base.AS(asn='4200000000', router_id='192.168.0.3', adv_net='')
-    ras_l = [
-        base.AS(asn='4210000000', router_id='192.168.0.12',
-                adv_net='192.168.162.0/24'),
-        base.AS(asn='64522', router_id='192.168.0.13',
-                adv_net='192.168.163.0/24'),
-        base.AS(asn='4230000000', router_id='192.168.0.14',
-                adv_net='192.168.164.0/24')
-    ]
-
-    bgp_speaker_args = {
-        'local_as': L_AS.asn,
-        'ip_version': ip_version,
-        'name': 'my-bgp-speaker1',
-        'advertise_floating_ip_host_routes': True,
-        'advertise_tenant_networks': True
-    }
-    bgp_peer_args = [
-        {'remote_as': ras_l[0].asn,
-         'name': 'my-bgp-peer1',
-         'peer_ip': None,
-         'auth_type': 'none'},
-        {'remote_as': ras_l[1].asn,
-         'name': 'my-bgp-peer2',
-         'peer_ip': None,
-         'auth_type': 'none'},
-        {'remote_as': ras_l[2].asn,
-         'name': 'my-bgp-peer3',
-         'peer_ip': None,
-         'auth_type': 'none'}
-    ]
-
-    @classmethod
-    @utils.requires_ext(extension="bgp_4byte_asn", service="network")
-    def resource_setup(cls):
-        super(BgpSpeaker4byteASNTest, cls).resource_setup()
-
-    @classmethod
-    def resource_setup_container(cls):
-        cls.brdc = ctn_base.Bridge(name='br-docker-4byte-asn',
-                                   subnet='192.168.10.0/24',
-                                   start_ip='192.168.10.128',
-                                   end_ip='192.168.10.254',
-                                   self_ip=True,
-                                   fixed_ip=cls.public_gw + '/24',
-                                   br_type=base.BRIDGE_TYPE)
-        cls.bridges.append(cls.brdc)
-        # This is dummy container object for a dr service.
-        # This keeps data which passes to a quagga container.
-        cls.dr = ctn_base.BGPContainer(name='dummy-dr-4byte-asn',
-                                       asn=int(cls.L_AS.asn),
-                                       router_id=cls.L_AS.router_id)
-        cls.dr.set_addr_info(bridge='br-docker-4byte-asn', ipv4=cls.public_gw)
-        # quagga container
-        cls.dockerimg = ctn_base.DockerImage()
-        cls.q_img = cls.dockerimg.create_quagga(check_exist=True)
-        cls.images.append(cls.q_img)
-        for i in range(cls.RAS_MAX):
-            qg = quagga.QuaggaBGPContainer(name='q-4byte-asn-' + str(i + 1),
-                                           asn=int(cls.ras_l[i].asn),
-                                           router_id=cls.ras_l[i].router_id,
-                                           ctn_image_name=cls.q_img)
-            cls.containers.append(qg)
-            cls.r_ass.append(qg)
-            qg.add_route(cls.ras_l[i].adv_net)
-            qg.run(wait=True)
-            cls.r_as_ip.append(cls.brdc.addif(qg))
-            qg.add_peer(cls.dr, bridge=cls.brdc.name,
-                        peer_info={'passive': True})
-        cls.tnet_gen = cls.get_subnet(start='10.10.1.0', end='10.10.255.0',
-                                      step=256)
-
-    @decorators.idempotent_id('9f18c931-a59e-4a27-939b-21124995ffe2')
-    def test_check_neighbor_established(self):
-        self._test_check_neighbor_established(self.ip_version)
-
-    @decorators.idempotent_id('73466aa5-7d1d-4f9f-8fb4-4100fad2dffe')
-    def test_check_advertised_tenant_network(self):
-        self._test_check_advertised_tenant_network(self.ip_version)
-
-    @decorators.idempotent_id('c3158328-2f69-4aa2-b1b7-5a06ab58afaf')
-    def test_check_advertised_multiple_tenant_network(self):
-        self._test_check_advertised_multiple_tenant_network(self.ip_version)
-
-    @decorators.idempotent_id('212a3d82-ac50-43dc-b657-030b1133643e')
-    def test_check_neighbor_established_with_multiple_peers(self):
-        self._test_check_neighbor_established_with_multiple_peers(
-            self.ip_version)
-
-    @decorators.idempotent_id('c72411c8-ea79-495d-bdbd-a10159642676')
-    def test_check_advertised_tenant_network_with_multiple_peers(self):
-        self._test_check_advertised_tenant_network_with_multiple_peers(
-            self.ip_version)
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_basic.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_basic.py
deleted file mode 100644
index 90a6815..0000000
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/test_basic.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright (C) 2016 VA Linux Systems Japan K.K.
-# Copyright (C) 2016 Fumihiko Kakuma <kakuma at valinux co jp>
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from os_ken.tests.integrated.common import docker_base as ctn_base
-from tempest import config
-from tempest.lib import decorators
-
-from neutron_tempest_plugin.neutron_dynamic_routing.scenario\
-    import base as s_base
-from neutron_tempest_plugin.neutron_dynamic_routing.scenario.basic import base
-
-CONF = config.CONF
-
-
-class BgpSpeakerBasicTest(base.BgpSpeakerBasicTestJSONBase):
-
-    @decorators.idempotent_id('cc615252-c6cb-4d75-a70e-608fb2c3736a')
-    def test_schedule_added_speaker(self):
-        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
-        num, subnet = next(self.tnet_gen)
-        mask = '/' + str(self.TPool.prefixlen)
-        TNet = s_base.Net(name='', net=subnet, mask=self.TPool.prefixlen,
-                          cidr=subnet + mask, router=None)
-        TSubNet = s_base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
-        MyRouter = s_base.Router(name='my-router' + str(num), gw='')
-        ext_net_id = self.create_bgp_network(
-            4, self.MyScope,
-            self.PNet, self.PPool, self.PSubNet,
-            self.TPool, [[TNet, TSubNet, MyRouter]])
-        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
-            ext_net_id,
-            self.bgp_speaker_args,
-            [self.bgp_peer_args[0]])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
-
-    @decorators.idempotent_id('ce98c33c-0ffa-49ae-b365-da836406793b')
-    def test_unschedule_deleted_speaker(self):
-        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
-        num, subnet = next(self.tnet_gen)
-        mask = '/' + str(self.TPool.prefixlen)
-        TNet = s_base.Net(name='', net=subnet, mask=self.TPool.prefixlen,
-                          cidr=subnet + mask, router=None)
-        TSubNet = s_base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
-        MyRouter = s_base.Router(name='my-router' + str(num), gw='')
-        ext_net_id = self.create_bgp_network(
-            4, self.MyScope,
-            self.PNet, self.PPool, self.PSubNet,
-            self.TPool, [[TNet, TSubNet, MyRouter]])
-        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
-            ext_net_id,
-            self.bgp_speaker_args,
-            [self.bgp_peer_args[0]],
-            auto_delete=False)
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
-        self.delete_bgp_speaker(speaker_id)
-        self.delete_bgp_peer(peers_ids[0])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ACTIVE,
-                                   init_state=ctn_base.BGP_FSM_ESTABLISHED)
-
-    @decorators.idempotent_id('aa6c565c-ded3-413b-8dc9-3928b3b0e38f')
-    def test_remove_add_speaker_agent(self):
-        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
-        num, subnet = next(self.tnet_gen)
-        mask = '/' + str(self.TPool.prefixlen)
-        TNet = s_base.Net(name='', net=subnet, mask=self.TPool.prefixlen,
-                          cidr=subnet + mask, router=None)
-        TSubNet = s_base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
-        MyRouter = s_base.Router(name='my-router' + str(num), gw='')
-        ext_net_id = self.create_bgp_network(
-            4, self.MyScope,
-            self.PNet, self.PPool, self.PSubNet,
-            self.TPool, [[TNet, TSubNet, MyRouter]])
-        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
-            ext_net_id,
-            self.bgp_speaker_args,
-            [self.bgp_peer_args[0]])
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
-        agent_list = self.bgp_client.list_dragents_for_bgp_speaker(
-            speaker_id)['agents']
-        self.assertEqual(1, len(agent_list))
-        agent_id = agent_list[0]['id']
-        self.bgp_client.remove_bgp_speaker_from_dragent(agent_id, speaker_id)
-        # NOTE(tidwellr) This assertion can fail due to the fact that BGP
-        # speakers are auto-scheduled. The BGP process can quickly return to
-        # ACTIVE status before this gets asserted. Let's see how it goes with
-        # this commented out.
-        # self.check_remote_as_state(self.dr, self.r_ass[0],
-        #                           ctn_base.BGP_FSM_ACTIVE,
-        #                           init_state=ctn_base.BGP_FSM_ESTABLISHED)
-
-        # Ignore errors re-associating the BGP speaker, auto-scheduling may
-        # have already added it to an agent. The next assertion is what
-        # matters.
-        self.bgp_client.add_bgp_speaker_to_dragent(agent_id, speaker_id,
-                                                   ignore_errors=True)
-        self.check_remote_as_state(self.dr, self.r_ass[0],
-                                   ctn_base.BGP_FSM_ESTABLISHED)
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv4/test_ipv4.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv4/test_ipv4.py
index 158b7ad..5a9deae 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv4/test_ipv4.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv4/test_ipv4.py
@@ -14,24 +14,24 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
 
+from neutron_tempest_plugin.neutron_dynamic_routing import frr
+from neutron_tempest_plugin.neutron_dynamic_routing.frr import constants
 from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base
 from neutron_tempest_plugin.neutron_dynamic_routing.scenario\
     import base_test_proto as test_base
 
-from os_ken.tests.integrated.common import docker_base as ctn_base
-from os_ken.tests.integrated.common import quagga
-
 CONF = config.CONF
 
 
 class BgpSpeakerIpv4Test(test_base.BgpSpeakerProtoTestBase):
 
     RAS_MAX = 3
+    IP_ALLOCATION_MULTIPLIER = 0
     ip_version = 4
-    public_gw = '192.168.11.1'
     MyScope = base.Scope(name='my-scope')
     PNet = base.Net(name='', net='172.24.6.0', mask=24,
                     cidr='172.24.6.0/24', router=None)
@@ -73,42 +73,71 @@
     ]
 
     def setUp(self):
+        for ctn in self.containers:
+            ctn.restart()
         super(BgpSpeakerIpv4Test, self).setUp()
 
     @classmethod
+    def skip_checks(cls):
+        super().skip_checks()
+        if CONF.production:
+            raise cls.skipException('Skip on production environment.')
+
+    @classmethod
     def resource_setup_container(cls):
-        cls.brdc = ctn_base.Bridge(name='br-docker-ipv4',
-                                   subnet='192.168.11.0/24',
-                                   start_ip='192.168.11.128',
-                                   end_ip='192.168.11.254',
-                                   self_ip=True,
-                                   fixed_ip=cls.public_gw + '/24',
-                                   br_type=base.BRIDGE_TYPE)
-        cls.bridges.append(cls.brdc)
-        # This is dummy container object for a dr service.
-        # This keeps data which passes to a quagga container.
-        cls.dr = ctn_base.BGPContainer(name='dr', asn=int(cls.L_AS.asn),
-                                       router_id=cls.L_AS.router_id)
-        cls.dr.set_addr_info(bridge='br-docker-ipv4', ipv4=cls.public_gw)
-        # quagga container
-        cls.dockerimg = ctn_base.DockerImage()
-        cls.q_img = cls.dockerimg.create_quagga(check_exist=True)
-        cls.images.append(cls.q_img)
+        # frr container
         for i in range(cls.RAS_MAX):
-            qg = quagga.QuaggaBGPContainer(name='q' + str(i + 1),
-                                           asn=int(cls.ras_l[i].asn),
-                                           router_id=cls.ras_l[i].router_id,
-                                           ctn_image_name=cls.q_img)
-            cls.containers.append(qg)
-            cls.r_ass.append(qg)
-            qg.add_route(cls.ras_l[i].adv_net)
+            container_number = i + 1
+            bgp_listenon = CONF.dynamic_routing.frr_provider_ipv4_ips[
+                cls.IP_ALLOCATION_MULTIPLIER + container_number - 1
+            ].split('/')[0]
+            bgpd = {
+                'bgp': {
+                    'as_number': int(cls.ras_l[i].asn),
+                    'router_id': bgp_listenon,
+                    'neighbors': [
+                        {
+                            'peer_group': True,
+                            'address': 'dnr',
+                            'as_number': cls.L_AS.asn,
+                            'passive': True,
+                        }
+                    ],
+                    'listen': {
+                        'ranges': [
+                            {'cidr': CONF.dynamic_routing.
+                             frr_bgp_ipv4_control_cidr,
+                             'peer_group': 'dnr'}
+                        ],
+                        'limit': 10,
+                    },
+                    'ebgp_requires_policy': False,
+                }
+            }
+            daemons = {
+                "bgpd": {
+                    "enabled": "yes",
+                    "listenon": bgp_listenon,
+                },
+                "vtysh": {"enabled": "yes"},
+            }
+            qg = frr.FrrBGPContainer(
+                "q" + str(container_number),
+                CONF.dynamic_routing.frr_docker_image,
+                bgpd=bgpd,
+                daemons=daemons,
+            )
             qg.run(wait=True)
-            cls.r_as_ip.append(cls.brdc.addif(qg))
-            qg.add_peer(cls.dr, bridge=cls.brdc.name,
-                        peer_info={'passive': True})
+            cls.containers.append(qg.ctn)
+            cls.r_ass.append(qg)
+            cls.r_as_ip.append(
+                CONF.dynamic_routing.frr_provider_ipv4_ips[
+                    cls.IP_ALLOCATION_MULTIPLIER + container_number - 1]
+            )
         cls.tnet_gen = cls.get_subnet(start='10.10.1.0', end='10.10.255.0',
                                       step=256)
 
+    @decorators.attr(type='smoke')
     @decorators.idempotent_id('7f2acbc2-ff88-4a63-aa02-a2f9feb3f5b0')
     def test_check_neighbor_established(self):
         self._test_check_neighbor_established(self.ip_version)
@@ -130,3 +159,246 @@
     def test_check_advertised_tenant_network_with_multiple_peers(self):
         self._test_check_advertised_tenant_network_with_multiple_peers(
             self.ip_version)
+
+    @decorators.idempotent_id('cc615252-c6cb-4d75-a70e-608fb2c3736a')
+    def test_schedule_added_speaker(self):
+        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
+        num, subnet = next(self.tnet_gen)
+        mask = '/' + str(self.TPool.prefixlen)
+        TNet = base.Net(
+            name='',
+            net=subnet,
+            mask=self.TPool.prefixlen,
+            cidr=subnet + mask,
+            router=None,
+        )
+        TSubNet = base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
+        MyRouter = base.Router(name='my-router' + str(num), gw='')
+        ext_net_id = self.create_bgp_network(
+            4,
+            self.MyScope,
+            self.PNet,
+            self.PPool,
+            self.PSubNet,
+            self.TPool,
+            [[TNet, TSubNet, MyRouter]],
+        )
+        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
+            ext_net_id, self.bgp_speaker_args, [self.bgp_peer_args[0]]
+        )
+        self.r_ass[0].bgp_check_neighbor_state(
+            'dnr', constants.BGP_FSM_ESTABLISHED
+        )
+
+    @decorators.idempotent_id('ce98c33c-0ffa-49ae-b365-da836406793b')
+    def test_unschedule_deleted_speaker(self):
+        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
+        num, subnet = next(self.tnet_gen)
+        mask = '/' + str(self.TPool.prefixlen)
+        TNet = base.Net(
+            name='',
+            net=subnet,
+            mask=self.TPool.prefixlen,
+            cidr=subnet + mask,
+            router=None,
+        )
+        TSubNet = base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
+        MyRouter = base.Router(name='my-router' + str(num), gw='')
+        ext_net_id = self.create_bgp_network(
+            4,
+            self.MyScope,
+            self.PNet,
+            self.PPool,
+            self.PSubNet,
+            self.TPool,
+            [[TNet, TSubNet, MyRouter]],
+        )
+        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
+            ext_net_id,
+            self.bgp_speaker_args,
+            [self.bgp_peer_args[0]],
+            auto_delete=False,
+        )
+        self.r_ass[0].bgp_check_neighbor_state(
+            'dnr', constants.BGP_FSM_ESTABLISHED
+        )
+        self.delete_bgp_speaker(speaker_id)
+        self.delete_bgp_peer(peers_ids[0])
+        self.r_ass[0].bgp_check_neighbor_absent('dnr')
+
+    @decorators.idempotent_id('aa6c565c-ded3-413b-8dc9-3928b3b0e38f')
+    def test_remove_add_speaker_agent(self):
+        self.bgp_peer_args[0]['peer_ip'] = self.r_as_ip[0].split('/')[0]
+        num, subnet = next(self.tnet_gen)
+        mask = '/' + str(self.TPool.prefixlen)
+        TNet = base.Net(
+            name='',
+            net=subnet,
+            mask=self.TPool.prefixlen,
+            cidr=subnet + mask,
+            router=None,
+        )
+        TSubNet = base.SubNet(name='', cidr=TNet.cidr, mask=TNet.mask)
+        MyRouter = base.Router(name='my-router' + str(num), gw='')
+        ext_net_id = self.create_bgp_network(
+            4,
+            self.MyScope,
+            self.PNet,
+            self.PPool,
+            self.PSubNet,
+            self.TPool,
+            [[TNet, TSubNet, MyRouter]],
+        )
+        speaker_id, peers_ids = self.create_and_add_peers_to_speaker(
+            ext_net_id, self.bgp_speaker_args, [self.bgp_peer_args[0]]
+        )
+        self.r_ass[0].bgp_check_neighbor_state(
+            'dnr', constants.BGP_FSM_ESTABLISHED
+        )
+        agent_list = self.bgp_client.list_dragents_for_bgp_speaker(speaker_id)[
+            'agents'
+        ]
+        self.assertEqual(1, len(agent_list))
+        agent_id = agent_list[0]['id']
+        self.bgp_client.remove_bgp_speaker_from_dragent(agent_id, speaker_id)
+        self.r_ass[0].bgp_check_neighbor_absent('dnr')
+        self.bgp_client.add_bgp_speaker_to_dragent(
+            agent_id, speaker_id, ignore_errors=True
+        )
+        self.r_ass[0].bgp_check_neighbor_state(
+            'dnr', constants.BGP_FSM_ESTABLISHED
+        )
+
+
+class BgpSpeaker4byteASNTest(test_base.BgpSpeakerProtoTestBase):
+
+    RAS_MAX = 3
+    IP_ALLOCATION_MULTIPLIER = 3
+    ip_version = 4
+    MyScope = base.Scope(name='my-scope')
+    PNet = base.Net(name='', net='172.24.6.0', mask=24, cidr='172.24.6.0/24',
+                    router=None)
+    PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask,
+                      prefixes=[PNet.net + '/8'])
+    PSubNet = base.SubNet(name='', cidr=PNet.cidr, mask=PNet.mask)
+    TPool = base.Pool(name='tenant-test-pool', prefixlen=28,
+                      prefixes=['10.10.0.0/16'])
+    L_AS = base.AS(asn='4200000000', router_id='192.168.0.3', adv_net='')
+    ras_l = [
+        base.AS(asn='4210000000', router_id='192.168.0.12',
+                adv_net='192.168.162.0/24'),
+        base.AS(asn='64522', router_id='192.168.0.13',
+                adv_net='192.168.163.0/24'),
+        base.AS(asn='4230000000', router_id='192.168.0.14',
+                adv_net='192.168.164.0/24'),
+    ]
+
+    bgp_speaker_args = {
+        'local_as': L_AS.asn,
+        'ip_version': ip_version,
+        'name': 'my-bgp-speaker1',
+        'advertise_floating_ip_host_routes': True,
+        'advertise_tenant_networks': True,
+    }
+    bgp_peer_args = [
+        {'remote_as': ras_l[0].asn,
+         'name': 'my-bgp-peer1',
+         'peer_ip': None,
+         'auth_type': 'none'},
+        {'remote_as': ras_l[1].asn,
+         'name': 'my-bgp-peer2',
+         'peer_ip': None,
+         'auth_type': 'none'},
+        {'remote_as': ras_l[2].asn,
+         'name': 'my-bgp-peer3',
+         'peer_ip': None,
+         'auth_type': 'none'},
+    ]
+
+    @classmethod
+    @utils.requires_ext(extension='bgp_4byte_asn', service='network')
+    def resource_setup(cls):
+        super(BgpSpeaker4byteASNTest, cls).resource_setup()
+
+    @classmethod
+    def skip_checks(cls):
+        super().skip_checks()
+        if CONF.production:
+            raise cls.skipException('Skip on production environment.')
+
+    @classmethod
+    def resource_setup_container(cls):
+        # frr container
+        for i in range(cls.RAS_MAX):
+            container_number = i + 1
+            bgp_listenon = CONF.dynamic_routing.frr_provider_ipv4_ips[
+                cls.IP_ALLOCATION_MULTIPLIER + container_number - 1
+            ].split('/')[0]
+            bgpd = {
+                'bgp': {
+                    'as_number': int(cls.ras_l[i].asn),
+                    'router_id': bgp_listenon,
+                    'neighbors': [
+                        {
+                            'peer_group': True,
+                            'address': 'dnr',
+                            'as_number': cls.L_AS.asn,
+                            'passive': True,
+                        }
+                    ],
+                    'listen': {
+                        'ranges': [
+                            {'cidr': CONF.dynamic_routing.
+                             frr_bgp_ipv4_control_cidr,
+                             'peer_group': 'dnr'}
+                        ],
+                        'limit': 10,
+                    },
+                    'ebgp_requires_policy': False,
+                }
+            }
+            daemons = {
+                "bgpd": {
+                    "enabled": "yes",
+                    "listenon": bgp_listenon,
+                },
+                "vtysh": {"enabled": "yes"},
+            }
+            qg = frr.FrrBGPContainer(
+                'q-4byte-asn-' + str(container_number),
+                CONF.dynamic_routing.frr_docker_image,
+                bgpd=bgpd,
+                daemons=daemons,
+            )
+            qg.run(wait=True)
+            cls.containers.append(qg.ctn)
+            cls.r_ass.append(qg)
+            cls.r_as_ip.append(
+                CONF.dynamic_routing.frr_provider_ipv4_ips[
+                    cls.IP_ALLOCATION_MULTIPLIER + container_number - 1]
+            )
+        cls.tnet_gen = cls.get_subnet(
+            start='10.10.1.0', end='10.10.255.0', step=256
+        )
+
+    @decorators.idempotent_id('9f18c931-a59e-4a27-939b-21124995ffe2')
+    def test_check_neighbor_established(self):
+        self._test_check_neighbor_established(self.ip_version)
+
+    @decorators.idempotent_id('73466aa5-7d1d-4f9f-8fb4-4100fad2dffe')
+    def test_check_advertised_tenant_network(self):
+        self._test_check_advertised_tenant_network(self.ip_version)
+
+    @decorators.idempotent_id('c3158328-2f69-4aa2-b1b7-5a06ab58afaf')
+    def test_check_advertised_multiple_tenant_network(self):
+        self._test_check_advertised_multiple_tenant_network(self.ip_version)
+
+    @decorators.idempotent_id('212a3d82-ac50-43dc-b657-030b1133643e')
+    def test_check_neighbor_established_with_multiple_peers(self):
+        self._test_check_neighbor_established_with_multiple_peers(
+            self.ip_version)
+
+    @decorators.idempotent_id('c72411c8-ea79-495d-bdbd-a10159642676')
+    def test_check_advertised_tenant_network_with_multiple_peers(self):
+        self._test_check_advertised_tenant_network_with_multiple_peers(
+            self.ip_version)
diff --git a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv6/test_ipv6.py b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv6/test_ipv6.py
index 937b38d..d83fb7c 100644
--- a/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv6/test_ipv6.py
+++ b/neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv6/test_ipv6.py
@@ -17,12 +17,11 @@
 from tempest import config
 from tempest.lib import decorators
 
+from neutron_tempest_plugin.neutron_dynamic_routing import frr
 from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base
 from neutron_tempest_plugin.neutron_dynamic_routing.scenario\
     import base_test_proto as test_base
 
-from os_ken.tests.integrated.common import docker_base as ctn_base
-from os_ken.tests.integrated.common import quagga
 
 CONF = config.CONF
 
@@ -30,9 +29,8 @@
 class BgpSpeakerIpv6Test(test_base.BgpSpeakerProtoTestBase):
 
     RAS_MAX = 3
+    IP_ALLOCATION_MULTIPLIER = 0
     ip_version = 6
-    public_gw = '2001:db8:a000::1'
-    MyScope = base.Scope(name='my-scope')
     PNet = base.Net(name='', net='2001:db8::', mask=64,
                     cidr='2001:db8::/64', router=None)
     PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask,
@@ -76,36 +74,53 @@
         super(BgpSpeakerIpv6Test, self).setUp()
 
     @classmethod
+    def skip_checks(cls):
+        super().skip_checks()
+        if not CONF.dynamic_routing.frr_bgp_ipv6_enabled:
+            raise cls.skipException("Dynamic routing IPv6 tests not enabled.")
+        if CONF.production:
+            raise cls.skipException("Skip on production environment.")
+
+    @classmethod
     def resource_setup_container(cls):
-        cls.brdc = ctn_base.Bridge(name='br-docker-ipv6',
-                                   subnet='2001:db8:a000::/64',
-                                   start_ip='2001:db8:a000::8000',
-                                   end_ip='2001:db8:a000::fffe',
-                                   self_ip=True,
-                                   fixed_ip=cls.public_gw + '/64',
-                                   br_type=base.BRIDGE_TYPE)
-        cls.bridges.append(cls.brdc)
-        # This is dummy container object for a dr service.
-        # This keeps data which passes to a quagga container.
-        cls.dr = ctn_base.BGPContainer(name='dr', asn=int(cls.L_AS.asn),
-                                       router_id=cls.L_AS.router_id)
-        cls.dr.set_addr_info(bridge='br-docker-ipv6', ipv6=cls.public_gw)
-        # quagga container
-        cls.dockerimg = ctn_base.DockerImage()
-        cls.q_img = cls.dockerimg.create_quagga(check_exist=True)
-        cls.images.append(cls.q_img)
+        # frr container
         for i in range(cls.RAS_MAX):
-            qg = quagga.QuaggaBGPContainer(name='q' + str(i + 1),
-                                           asn=int(cls.ras_l[i].asn),
-                                           router_id=cls.ras_l[i].router_id,
-                                           ctn_image_name=cls.q_img)
-            cls.containers.append(qg)
-            cls.r_ass.append(qg)
-            qg.add_route(cls.ras_l[i].adv_net, route_info={'rf': 'ipv6'})
+            container_number = i + 1
+            bgpd = {
+                'bgp': {
+                    'as_number': int(cls.ras_l[i].asn),
+                    'router_id': cls.ras_l[i].router_id,
+                    'neighbors': [
+                        {
+                            'peer_group': True,
+                            'address': 'dnr',
+                            'as_number': cls.L_AS.asn,
+                            'passive': True,
+                        }
+                    ],
+                    'listen': {
+                        'ranges': [
+                            {'cidr': CONF.dynamic_routing.
+                             frr_bgp_ipv6_control_cidr,
+                             'peer_group': 'dnr'}
+                        ],
+                        'limit': 10,
+                    },
+                    'ebgp_requires_policy': False,
+                }
+            }
+            qg = frr.FrrBGPContainer(
+                "qv6" + str(container_number),
+                CONF.dynamic_routing.frr_docker_image,
+                bgpd=bgpd,
+            )
             qg.run(wait=True)
-            cls.r_as_ip.append(cls.brdc.addif(qg))
-            qg.add_peer(cls.dr, bridge=cls.brdc.name, v6=True,
-                        peer_info={'passive': True})
+            cls.containers.append(qg.ctn)
+            cls.r_ass.append(qg)
+            cls.r_as_ip.append(
+                CONF.dynamic_routing.frr_provider_ips[
+                    cls.IP_ALLOCATION_MULTIPLIER + container_number - 1]
+            )
         cls.tnet_gen = cls.get_subnet(start='2001:db8:8000:1::',
                                       end='2001:db8:8000:ffff::',
                                       step=65536 * 65536 * 65536 * 65536)
diff --git a/neutron_tempest_plugin/scenario/test_dhcp.py b/neutron_tempest_plugin/scenario/test_dhcp.py
index d0545e2..31d2930 100644
--- a/neutron_tempest_plugin/scenario/test_dhcp.py
+++ b/neutron_tempest_plugin/scenario/test_dhcp.py
@@ -66,7 +66,7 @@
         test_domain = "test.domain"
         extra_dhcp_opts = [
             {'opt_name': 'domain-name',
-             'opt_value': '"%s"' % test_domain}]
+             'opt_value': '%s' % test_domain}]
         port = self.create_port(
             network=self.network, name=self.rand_name,
             security_groups=[self.security_group['id']],
diff --git a/neutron_tempest_plugin/scenario/test_dns_integration.py b/neutron_tempest_plugin/scenario/test_dns_integration.py
index d520aa2..7fce12f 100644
--- a/neutron_tempest_plugin/scenario/test_dns_integration.py
+++ b/neutron_tempest_plugin/scenario/test_dns_integration.py
@@ -17,11 +17,9 @@
 
 import testtools
 
-from oslo_log import log
 from tempest.common import utils
 from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -32,8 +30,6 @@
 
 
 CONF = config.CONF
-LOG = log.getLogger(__name__)
-
 
 # Note(jh): Need to do a bit of juggling here in order to avoid failures
 # when designate_tempest_plugin is not available
@@ -45,15 +41,36 @@
     DNSMixin = object
 
 
+def rand_zone_name(name='', prefix='rand', suffix=None):
+    """Generate a random zone name
+
+    :param str name: The name that you want to include
+    :param prefix: the exact text to start the string. Defaults to "rand"
+    :param suffix: the exact text to end the string
+    :return: a random zone name e.g. example.org.
+    :rtype: string
+    """
+
+    if suffix is None:
+        suffix = '.{}.'.format(CONF.dns.tld_suffix)
+    name = data_utils.rand_name(name=name, prefix=prefix)
+    return name + suffix
+
+
 class BaseDNSIntegrationTests(base.BaseTempestTestCase, DNSMixin):
     credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
         super(BaseDNSIntegrationTests, cls).setup_clients()
-        cls.zone_client = cls.os_tempest.dns_v2.ZonesClient()
-        cls.recordset_client = cls.os_tempest.dns_v2.RecordsetClient()
+        cls.dns_client = cls.os_tempest.dns_v2.ZonesClient()
         cls.query_client.build_timeout = 30
+        if CONF.enforce_scope.designate:
+            cls.admin_tld_client = cls.os_system_admin.dns_v2.TldClient()
+            cls.rec_client = cls.os_system_admin.dns_v2.RecordsetClient()
+        else:
+            cls.admin_tld_client = cls.os_admin.dns_v2.TldClient()
+            cls.rec_client = cls.os_admin.dns_v2.RecordsetClient()
 
     @classmethod
     def skip_checks(cls):
@@ -63,16 +80,26 @@
             raise cls.skipException("Designate support is required")
         if not (dns_base and dns_waiters):
             raise cls.skipException("Designate tempest plugin is missing")
+        # Neutron creates zones for reverse lookups and designate requires
+        # related TLD if there is any
+        if CONF.production:
+            if CONF.dns.existing_tlds and not set([CONF.dns.tld_suffix,
+                    "arpa", "in-addr.arpa"]).issubset(CONF.dns.existing_tlds):
+                raise cls.skipException("Skip on production environment "
+                            "because it doesn't match TLD configuration.")
 
     @classmethod
     @utils.requires_ext(extension="dns-integration", service="network")
     def resource_setup(cls):
         super(BaseDNSIntegrationTests, cls).resource_setup()
-        cls.zone = cls.zone_client.create_zone()[1]
-        cls.addClassResourceCleanup(cls.zone_client.delete_zone,
+
+        zone_name = rand_zone_name(
+            name="dnsinttest", prefix='')
+        _, cls.zone = cls.dns_client.create_zone(name=zone_name)
+        cls.addClassResourceCleanup(cls.dns_client.delete_zone,
             cls.zone['id'], ignore_errors=lib_exc.NotFound)
         dns_waiters.wait_for_zone_status(
-            cls.zone_client, cls.zone['id'], 'ACTIVE')
+            cls.dns_client, cls.zone['id'], 'ACTIVE')
 
         cls.network = cls.create_network(dns_domain=cls.zone['name'])
         cls.subnet = cls.create_subnet(cls.network)
@@ -81,6 +108,10 @@
         cls.create_router_interface(cls.router['id'], cls.subnet['id'])
         cls.keypair = cls.create_keypair()
 
+    @classmethod
+    def resource_cleanup(cls):
+        super(BaseDNSIntegrationTests, cls).resource_cleanup()
+
     def _create_floatingip_with_dns(self, dns_name):
         return self.create_floatingip(client=self.os_primary.network_client,
                                       dns_name=dns_name,
@@ -99,79 +130,12 @@
         fip = self.create_floatingip(port=port)
         return {'port': port, 'fip': fip, 'server': server}
 
-    def _check_type_in_recordsets(self, zone_id, rec_type):
-        types = [rec['type'] for rec in self.recordset_client.list_recordset(
-            zone_id)[1]['recordsets']]
-        if rec_type in types:
-            return True
-        return False
-
-    def _wait_for_type_in_recordsets(self, zone_id, type):
-        test_utils.call_until_true(
-            func=self._check_type_in_recordsets, zone_id=zone_id,
-            rec_type=type, duration=self.query_client.build_timeout,
-            sleep_for=5)
-
-    def _check_recordset_deleted(
-            self, recordset_client, zone_id, recordset_id):
-        return test_utils.call_and_ignore_notfound_exc(
-            recordset_client.show_recordset, zone_id, recordset_id) is None
-
-    def _verify_designate_recordset(
-            self, address, found=True, record_type='A'):
-        if found:
-            self._wait_for_type_in_recordsets(self.zone['id'], record_type)
-            recordsets = self.recordset_client.list_recordset(
-                self.zone['id'])[1]['recordsets']
-            relevant_type = [rec for rec in recordsets if
-                             rec['type'] == record_type]
-            self.assertTrue(
-                relevant_type,
-                'Failed no {} type recordset has been detected in the '
-                'Designate DNS DB'.format(record_type))
-            rec_id = [rec['id'] for rec in relevant_type if address in
-                      str(rec['records'])][0]
-            self.assertTrue(
-                rec_id, 'Record of type:{} with IP:{} was not detected in '
-                        'the Designate DNS DB'.format(record_type, address))
-            dns_waiters.wait_for_recordset_status(
-                self.recordset_client, self.zone['id'], rec_id, 'ACTIVE')
-        else:
-            rec_id = None
-            recordsets = self.recordset_client.list_recordset(
-                self.zone['id'])[1]['recordsets']
-            relevant_type = [rec for rec in recordsets if
-                             rec['type'] == record_type]
-            if relevant_type:
-                rec_id = [rec['id'] for rec in relevant_type if
-                          address in str(rec['records'])][0]
-            if rec_id:
-                recordset_exists = test_utils.call_until_true(
-                    func=self._check_recordset_deleted,
-                    recordset_client=self.recordset_client,
-                    zone_id=self.zone['id'], recordset_id=rec_id,
-                    duration=self.query_client.build_timeout, sleep_for=5)
-                self.assertTrue(
-                    recordset_exists,
-                    'Failed, recordset type:{} and ID:{} is still exist in '
-                    'the Designate DNS DB'.format(record_type, rec_id))
-
     def _verify_dns_records(self, address, name, found=True, record_type='A'):
         client = self.query_client
         forward = name + '.' + self.zone['name']
         reverse = ipaddress.ip_address(address).reverse_pointer
-        record_types_to_check = [record_type, 'PTR']
-        for rec_type in record_types_to_check:
-            try:
-                if rec_type == 'PTR':
-                    dns_waiters.wait_for_query(
-                        client, reverse, rec_type, found)
-                else:
-                    dns_waiters.wait_for_query(
-                        client, forward, rec_type, found)
-            except Exception as e:
-                LOG.error(e)
-                self._verify_designate_recordset(address, found, rec_type)
+        dns_waiters.wait_for_query(client, forward, record_type, found)
+        dns_waiters.wait_for_query(client, reverse, 'PTR', found)
         if not found:
             return
         fwd_response = client.query(forward, record_type)
@@ -289,15 +253,15 @@
 
         name = data_utils.rand_name('test-domain')
         zone_name = "%s.%s.%s.zone." % (cls.client.user_id,
-                                        cls.client.project_id,
+                                        cls.client.tenant_id,
                                         name)
         dns_domain_template = "<user_id>.<project_id>.%s.zone." % name
 
-        cls.zone = cls.zone_client.create_zone(name=zone_name)[1]
-        cls.addClassResourceCleanup(cls.zone_client.delete_zone,
+        _, cls.zone = cls.dns_client.create_zone(name=zone_name)
+        cls.addClassResourceCleanup(cls.dns_client.delete_zone,
             cls.zone['id'], ignore_errors=lib_exc.NotFound)
         dns_waiters.wait_for_zone_status(
-            cls.zone_client, cls.zone['id'], 'ACTIVE')
+            cls.dns_client, cls.zone['id'], 'ACTIVE')
 
         cls.network = cls.create_network(dns_domain=dns_domain_template)
         cls.subnet = cls.create_subnet(cls.network,
diff --git a/neutron_tempest_plugin/vpnaas/api/base_vpnaas.py b/neutron_tempest_plugin/vpnaas/api/base_vpnaas.py
index 0e54380..3ccf48b 100644
--- a/neutron_tempest_plugin/vpnaas/api/base_vpnaas.py
+++ b/neutron_tempest_plugin/vpnaas/api/base_vpnaas.py
@@ -14,7 +14,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.api import base
 from neutron_tempest_plugin import config
@@ -138,6 +141,31 @@
         return ipsec_site_connection
 
     @classmethod
+    def wait_ipsec_site_connection_status(cls, site_id, status, timeout=None,
+                                          interval=None):
+        start_time = int(time.time())
+        timeout = timeout or cls.client.build_timeout
+        interval = interval or cls.client.build_interval
+        current_status = ""
+        while True:
+            if current_status.lower() == status.lower():
+                return
+
+            timed_out = int(time.time()) - start_time >= timeout
+            if timed_out:
+                message = ('IPSec site connection %(site_id)s failed to reach '
+                           'desired status %(desired_status)s, current state '
+                           '%(current_status)s in %(timeout)d' %
+                           {"site_id": site_id,
+                            "desired_status": status,
+                            "current_status": current_status,
+                            "timeout": timeout})
+                raise lib_exc.TimeoutException(message)
+            current_status = cls.client.show_ipsec_site_connection(
+                site_id)['ipsec_site_connection']['status']
+            time.sleep(interval)
+
+    @classmethod
     def create_endpoint_group(cls, name, type, endpoints):
         """Wrapper utility that returns a test ipsec policy."""
         body = cls.client.create_endpoint_group(
diff --git a/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py b/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
index 30a8674..78b9323 100644
--- a/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
+++ b/neutron_tempest_plugin/vpnaas/scenario/test_vpnaas.py
@@ -116,6 +116,13 @@
             cls.extra_subnet_attributes['ipv6_address_mode'] = 'slaac'
             cls.extra_subnet_attributes['ipv6_ra_mode'] = 'slaac'
 
+        left_v4_cidr = netaddr.IPNetwork('10.20.0.0/24')
+        left_v6_cidr = netaddr.IPNetwork('2001:db8:0:2::/64')
+        cls.left_cidr = left_v6_cidr if cls.inner_ipv6 else left_v4_cidr
+        right_v4_cidr = netaddr.IPNetwork('10.10.0.0/24')
+        right_v6_cidr = netaddr.IPNetwork('2001:db8:0:1::/64')
+        cls.right_cidr = right_v6_cidr if cls.inner_ipv6 else right_v4_cidr
+
         # LEFT
         cls.router = cls.create_router(
             data_utils.rand_name('left-router'),
@@ -123,14 +130,22 @@
             external_network_id=CONF.network.public_network_id)
         cls.network = cls.create_network(network_name='left-network')
         ip_version = 6 if cls.inner_ipv6 else 4
-        v4_cidr = netaddr.IPNetwork('10.20.0.0/24')
-        v6_cidr = netaddr.IPNetwork('2001:db8:0:2::/64')
-        cidr = v6_cidr if cls.inner_ipv6 else v4_cidr
-        cls.subnet = cls.create_subnet(
-            cls.network, ip_version=ip_version, cidr=cidr, name='left-subnet',
-            **cls.extra_subnet_attributes)
-        cls.create_router_interface(cls.router['id'], cls.subnet['id'])
+        is_distributed = cls.os_admin.network_client.show_router(
+            cls.router['id'])['router'].get('distributed')
 
+        cls.subnet = cls.create_subnet(
+            cls.network, ip_version=ip_version, cidr=cls.left_cidr,
+            name='left-subnet', **cls.extra_subnet_attributes)
+        cls.create_router_interface(cls.router['id'], cls.subnet['id'])
+        if is_distributed:
+            snat_port = cls.os_admin.network_client.list_ports(
+                device_id=cls.router['id'],
+                device_owner='network:router_centralized_snat')
+            snat_ip = cls._get_ip_on_subnet_for_port(
+                cls, snat_port['ports'][0], cls.subnet['id'])
+            cls.os_admin.network_client.update_subnet(
+                cls.subnet['id'], host_routes=[{"destination": cls.right_cidr,
+                                                "nexthop": snat_ip}])
         # Gives an internal IPv4 subnet for floating IP to the left server,
         # we use it to ssh into the left server.
         if cls.inner_ipv6:
@@ -164,15 +179,23 @@
             data_utils.rand_name('right-router'),
             admin_state_up=True,
             external_network_id=CONF.network.public_network_id)
+        is_distributed = cls.os_admin.network_client.show_router(
+            router['id'])['router'].get('distributed')
         network = cls.create_network(network_name='right-network')
-        v4_cidr = netaddr.IPNetwork('10.10.0.0/24')
-        v6_cidr = netaddr.IPNetwork('2001:db8:0:1::/64')
-        cidr = v6_cidr if cls.inner_ipv6 else v4_cidr
         ip_version = 6 if cls.inner_ipv6 else 4
         subnet = cls.create_subnet(
-            network, ip_version=ip_version, cidr=cidr, name='right-subnet',
-            **cls.extra_subnet_attributes)
+            network, ip_version=ip_version, cidr=cls.right_cidr,
+            name='right-subnet', **cls.extra_subnet_attributes)
         cls.create_router_interface(router['id'], subnet['id'])
+        if is_distributed:
+            snat_port = cls.os_admin.network_client.list_ports(
+                device_id=router['id'],
+                device_owner='network:router_centralized_snat')
+            snat_ip = cls._get_ip_on_subnet_for_port(
+                cls, snat_port['ports'][0], subnet['id'])
+            cls.os_admin.network_client.update_subnet(
+                subnet['id'], host_routes=[{"destination": cls.left_cidr,
+                                            "nexthop": snat_ip}])
 
         return network, subnet, router
 
@@ -207,6 +230,7 @@
             site['vpnservice'] = self.create_vpnservice(
                 site['subnet']['id'], site['router']['id'],
                 name=data_utils.rand_name('%s-vpnservice' % site['name']))
+        site_connections = []
         for i in range(0, 2):
             site = sites[i]
             vpnservice = site['vpnservice']
@@ -218,7 +242,7 @@
                     raise self.skipException(msg)
             else:
                 peer_address = peer['vpnservice']['external_v4_ip']
-            self.create_ipsec_site_connection(
+            site_connection = self.create_ipsec_site_connection(
                 self.ikepolicy['id'],
                 self.ipsecpolicy['id'],
                 vpnservice['id'],
@@ -228,6 +252,10 @@
                 psk=psk,
                 name=data_utils.rand_name(
                     '%s-ipsec-site-connection' % site['name']))
+            site_connections.append(site_connection)
+        for site_connection in site_connections:
+            self.wait_ipsec_site_connection_status(site_connection['id'],
+                                                   status="ACTIVE")
 
     def _get_ip_on_subnet_for_port(self, port, subnet_id):
         for fixed_ip in port['fixed_ips']:
@@ -237,7 +265,6 @@
             subnet_id, port)
         raise self.fail(msg)
 
-    @test.unstable_test("bug 1882220")
     def _test_vpnaas(self):
         # RIGHT
         right_server = self._create_server(network=self._right_network,
@@ -275,6 +302,7 @@
 class Vpnaas4in4(Vpnaas):
 
     @decorators.idempotent_id('aa932ab2-63aa-49cf-a2a0-8ae71ac2bc24')
+    @decorators.attr(type='smoke')
     def test_vpnaas(self):
         self._test_vpnaas()
 
@@ -288,6 +316,7 @@
     @testtools.skipIf(
         CONF.neutron_vpnaas_plugin_options.skip_4in6_6in4_tests,
         'VPNaaS 4in6 test is skipped.')
+    @test.unstable_test("bug 1882220")
     def test_vpnaas_4in6(self):
         self._test_vpnaas()
 
@@ -299,11 +328,9 @@
     @testtools.skipUnless(CONF.network_feature_enabled.ipv6,
                           'IPv6 tests are disabled.')
     @testtools.skipIf(
-        CONF.neutron_vpnaas_plugin_options.skip_4in6_6in4_tests,
-        'VPNaaS 6in4 test is skipped.')
-    @testtools.skipIf(
         CONF.neutron_vpnaas_plugin_options.skip_6in4_tests,
         'VPNaaS 6in4 test is skipped.')
+    @test.unstable_test("bug 1882220")
     def test_vpnaas_6in4(self):
         self._test_vpnaas()
 
@@ -318,5 +345,6 @@
     @testtools.skipIf(
         CONF.neutron_vpnaas_plugin_options.skip_6in6_tests,
         'VPNaaS 6in6 test is skipped.')
+    @test.unstable_test("bug 1882220")
     def test_vpnaas_6in6(self):
         self._test_vpnaas()
diff --git a/requirements.txt b/requirements.txt
index 9423079..481bb7f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,6 @@
 neutron-lib>=1.25.0 # Apache-2.0
 oslo.config>=5.2.0 # Apache-2.0
 netaddr>=0.7.18 # BSD
-os-ken>=0.3.0 # Apache-2.0
 oslo.log>=3.36.0 # Apache-2.0
 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
 oslo.utils>=3.33.0 # Apache-2.0
@@ -18,3 +17,4 @@
 testtools>=2.2.0 # MIT
 eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT
 debtcollector>=1.2.0 # Apache-2.0
+docker  # Apache-2.0