Merge "Stop using not existing ShellCommandError exception class"
diff --git a/.zuul.yaml b/.zuul.yaml
index 7a3c106..ceb7bc2 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -248,8 +248,10 @@
     name: neutron-tempest-plugin-api-rocky
     nodeset: openstack-single-node-xenial
     parent: neutron-tempest-plugin-api
+    description: |
+      This job run on py2 for stable/rocky gate.
     override-checkout: stable/rocky
-    vars:
+    vars: &api_vars_rocky
       branch_override: stable/rocky
       # TODO(slaweq): find a way to put this list of extensions in
       # neutron repository and keep it different per branch,
@@ -316,6 +318,24 @@
         USE_PYTHON3: false
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_tempest) | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+    # NOTE(gmann): This job run on py2 for stable/rocky gate.
+    branches:
+      - stable/rocky
+
+
+- job:
+    name: neutron-tempest-plugin-api-rocky
+    nodeset: openstack-single-node-xenial
+    parent: neutron-tempest-plugin-api
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *api_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-api-stein
@@ -486,9 +506,7 @@
       tempest_test_regex: ^neutron_tempest_plugin\.scenario
       devstack_localrc:
         PHYSICAL_NETWORK: default
-        DOWNLOAD_DEFAULT_IMAGES: false
-        IMAGE_URLS: "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img"
-        DEFAULT_IMAGE_NAME: cirros-0.3.4-i386-disk
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
         ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
         ADVANCED_INSTANCE_TYPE: ds512M
         ADVANCED_INSTANCE_USER: ubuntu
@@ -553,15 +571,33 @@
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-rocky
     parent: neutron-tempest-plugin-scenario-openvswitch
+    description: |
+      This job run on py2 for stable/rocky gate.
     nodeset: openstack-single-node-xenial
     override-checkout: stable/rocky
-    vars:
+    vars: &scenario_vars_rocky
       branch_override: stable/rocky
       network_api_extensions: *api_extensions_rocky
       devstack_localrc:
         USE_PYTHON3: false
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+    branches:
+      - stable/rocky
+
+- job:
+    name: neutron-tempest-plugin-scenario-openvswitch-rocky
+    parent: neutron-tempest-plugin-scenario-openvswitch
+    nodeset: openstack-single-node-xenial
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *scenario_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-stein
@@ -620,14 +656,32 @@
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-rocky
     parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
     nodeset: openstack-single-node-xenial
+    description: |
+      This job run on py2 for stable/rocky gate.
     override-checkout: stable/rocky
-    vars:
+    vars: &openvswitch_vars_rocky
       branch_override: stable/rocky
       network_api_extensions: *api_extensions_rocky
       devstack_localrc:
         USE_PYTHON3: false
         NETWORK_API_EXTENSIONS: "{{ network_api_extensions | join(',') }}"
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+    branches:
+      - stable/rocky
+
+- job:
+    name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-rocky
+    parent: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid
+    nodeset: openstack-single-node-xenial
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *openvswitch_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-scenario-openvswitch-iptables_hybrid-stein
@@ -711,9 +765,11 @@
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-rocky
     parent: neutron-tempest-plugin-scenario-linuxbridge
+    description: |
+      This job run on py2 for stable/rocky gate.
     nodeset: openstack-single-node-xenial
     override-checkout: stable/rocky
-    vars:
+    vars: &linuxbridge_vars_rocky
       branch_override: stable/rocky
       network_api_extensions: *api_extensions_rocky
       devstack_localrc:
@@ -728,6 +784,22 @@
           $TEMPEST_CONFIG:
             neutron_plugin_options:
               q_agent: None
+    branches:
+      - stable/rocky
+
+- job:
+    name: neutron-tempest-plugin-scenario-linuxbridge-rocky
+    parent: neutron-tempest-plugin-scenario-linuxbridge
+    nodeset: openstack-single-node-xenial
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *linuxbridge_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-scenario-linuxbridge-stein
@@ -774,9 +846,7 @@
         USE_PYTHON3: true
         NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_dvr) | join(',') }}"
         PHYSICAL_NETWORK: default
-        DOWNLOAD_DEFAULT_IMAGES: false
-        IMAGE_URLS: "http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img"
-        DEFAULT_IMAGE_NAME: cirros-0.3.4-i386-disk
+        IMAGE_URLS: https://cloud-images.ubuntu.com/releases/xenial/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
         ADVANCED_IMAGE_NAME: ubuntu-16.04-server-cloudimg-amd64-disk1
         ADVANCED_INSTANCE_TYPE: ds512M
         ADVANCED_INSTANCE_USER: ubuntu
@@ -911,14 +981,36 @@
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario-rocky
     parent: neutron-tempest-plugin-dvr-multinode-scenario
+    description: |
+      This job run on py2 for stable/rocky gate.
     nodeset: openstack-two-node-xenial
     override-checkout: stable/rocky
-    vars:
+    vars: &multinode_scenario_vars_rocky
       branch_override: stable/rocky
       network_api_extensions_common: *api_extensions_rocky
       devstack_localrc:
         USE_PYTHON3: false
         TEMPEST_PLUGINS: /opt/stack/neutron-tempest-plugin
+    branches:
+      - stable/rocky
+
+- job:
+    name: neutron-tempest-plugin-dvr-multinode-scenario-rocky
+    parent: neutron-tempest-plugin-dvr-multinode-scenario
+    nodeset: openstack-two-node-xenial
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *multinode_scenario_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    group-vars:
+      subnode:
+        devstack_localrc:
+          USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-dvr-multinode-scenario-stein
@@ -948,8 +1040,6 @@
     vars:
       devstack_localrc:
         DESIGNATE_BACKEND_DRIVER: bind9
-        DOWNLOAD_DEFAULT_IMAGES: false
-        IMAGE_URLS: http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-i386-disk.img,
         Q_AGENT: openvswitch
         # In this job advanced image is not needed, so it's name should be
         # empty
@@ -1004,14 +1094,32 @@
 - job:
     name: neutron-tempest-plugin-designate-scenario-rocky
     parent: neutron-tempest-plugin-designate-scenario
+    description: |
+      This job run on py2 for stable/rocky gate.
     nodeset: openstack-single-node-xenial
     override-checkout: stable/rocky
-    vars:
+    vars: &designate_scenario_vars_rocky
       branch_override: stable/rocky
       network_api_extensions_common: *api_extensions_rocky
       devstack_localrc:
         USE_PYTHON3: false
         TEMPEST_PLUGINS: '"/opt/stack/designate-tempest-plugin /opt/stack/neutron-tempest-plugin"'
+    branches:
+      - stable/rocky
+
+- job:
+    name: neutron-tempest-plugin-designate-scenario-rocky
+    parent: neutron-tempest-plugin-designate-scenario
+    nodeset: openstack-single-node-xenial
+    description: |
+      This job run on py3 for other than stable/rocky gate
+      which is nothing but neutron-tempest-pluign master gate.
+    override-checkout: stable/rocky
+    vars:
+      <<: *designate_scenario_vars_rocky
+      devstack_localrc:
+        USE_PYTHON3: True
+    branches: ^(?!stable/rocky).*$
 
 - job:
     name: neutron-tempest-plugin-designate-scenario-stein
diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py
index 0891bb6..f8cb339 100644
--- a/neutron_tempest_plugin/api/base.py
+++ b/neutron_tempest_plugin/api/base.py
@@ -733,10 +733,10 @@
 
     @classmethod
     def create_qos_policy(cls, name, description=None, shared=False,
-                          tenant_id=None, is_default=False):
+                          project_id=None, is_default=False):
         """Wrapper utility that returns a test QoS policy."""
         body = cls.admin_client.create_qos_policy(
-            name, description, shared, tenant_id, is_default)
+            name, description, shared, project_id, is_default)
         qos_policy = body['policy']
         cls.qos_policies.append(qos_policy)
         return qos_policy
diff --git a/neutron_tempest_plugin/api/test_qos.py b/neutron_tempest_plugin/api/test_qos.py
index 9f9ce67..0fc7b15 100644
--- a/neutron_tempest_plugin/api/test_qos.py
+++ b/neutron_tempest_plugin/api/test_qos.py
@@ -65,11 +65,8 @@
         body = self.admin_client.show_qos_policy(policy['id'])
         show_policy = body['policy']
         self.assertIn('project_id', show_policy)
-        self.assertIn('tenant_id', show_policy)
         self.assertEqual(self.admin_client.tenant_id,
                          show_policy['project_id'])
-        self.assertEqual(self.admin_client.tenant_id,
-                         show_policy['tenant_id'])
 
     @decorators.idempotent_id('f8d20e92-f06d-4805-b54f-230f77715815')
     def test_list_policy_filter_by_name(self):
@@ -90,7 +87,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
                                         shared=False,
-                                        tenant_id=self.admin_client.tenant_id)
+                                        project_id=self.admin_client.tenant_id)
         self.admin_client.update_qos_policy(policy['id'],
                                             description='test policy desc2',
                                             shared=True)
@@ -106,7 +103,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
                                         shared=False,
-                                        tenant_id=self.client.tenant_id)
+                                        project_id=self.client.tenant_id)
         self.assertRaises(
             exceptions.Forbidden,
             self.client.update_qos_policy,
@@ -117,7 +114,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
                                         shared=False,
-                                        tenant_id=self.admin_client.tenant_id)
+                                        project_id=self.admin_client.tenant_id)
         self.assertRaises(
             exceptions.NotFound,
             self.client.update_qos_policy,
@@ -128,7 +125,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='',
                                         shared=True,
-                                        tenant_id=self.admin_client.tenant_id)
+                                        project_id=self.admin_client.tenant_id)
 
         self.admin_client.update_qos_policy(policy['id'],
                                             description='test policy desc2')
@@ -351,7 +348,7 @@
             name='test-policy-shared',
             description='shared policy',
             shared=True,
-            tenant_id=self.admin_client.tenant_id)
+            project_id=self.admin_client.tenant_id)
         obtained_policy = self.client.show_qos_policy(policy['id'])['policy']
         self.assertEqual(obtained_policy, policy)
 
@@ -366,7 +363,7 @@
     def test_default_policy_creating_network_without_policy(self):
         project_id = self.create_project()['id']
         policy = self.create_qos_policy(name='test-policy',
-                                        tenant_id=project_id,
+                                        project_id=project_id,
                                         is_default=True)
         network = self.create_network('test network', client=self.admin_client,
                                       project_id=project_id)
@@ -378,10 +375,10 @@
     def test_default_policy_creating_network_with_policy(self):
         project_id = self.create_project()['id']
         self.create_qos_policy(name='test-policy',
-                               tenant_id=project_id,
+                               project_id=project_id,
                                is_default=True)
         policy = self.create_qos_policy(name='test-policy',
-                                        tenant_id=project_id)
+                                        project_id=project_id)
         network = self.create_network('test network', client=self.admin_client,
                                       project_id=project_id,
                                       qos_policy_id=policy['id'])
@@ -393,7 +390,7 @@
     def test_user_create_port_with_admin_qos_policy(self):
         qos_policy = self.create_qos_policy(
             name='test-policy',
-            tenant_id=self.admin_client.tenant_id,
+            project_id=self.admin_client.tenant_id,
             shared=False)
         network = self.create_network(
             'test network', client=self.admin_client,
@@ -536,7 +533,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='test policy',
                                         shared=False,
-                                        tenant_id=self.client.tenant_id)
+                                        project_id=self.client.tenant_id)
         rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
                                                     max_kbps=1,
                                                     max_burst_kbps=1,
@@ -551,7 +548,7 @@
         policy = self.create_qos_policy(name='test-policy',
                                         description='test policy',
                                         shared=False,
-                                        tenant_id=self.admin_client.tenant_id)
+                                        project_id=self.admin_client.tenant_id)
         rule = self.create_qos_bandwidth_limit_rule(policy_id=policy['id'],
                                                     max_kbps=1,
                                                     max_burst_kbps=1,
@@ -698,23 +695,23 @@
         super(RbacSharedQosPoliciesTest, cls).resource_setup()
         cls.client2 = cls.os_alt.network_client
 
-    def _create_qos_policy(self, tenant_id=None):
+    def _create_qos_policy(self, project_id=None):
         args = {'name': data_utils.rand_name('test-policy'),
                 'description': 'test policy',
                 'shared': False,
-                'tenant_id': tenant_id}
+                'project_id': project_id}
         qos_policy = self.admin_client.create_qos_policy(**args)['policy']
         self.addCleanup(self.admin_client.delete_qos_policy, qos_policy['id'])
 
         return qos_policy
 
-    def _make_admin_policy_shared_to_tenant_id(self, tenant_id):
+    def _make_admin_policy_shared_to_project_id(self, project_id):
         policy = self._create_qos_policy()
         rbac_policy = self.admin_client.create_rbac_policy(
             object_type='qos_policy',
             object_id=policy['id'],
             action='access_as_shared',
-            target_tenant=tenant_id,
+            target_tenant=project_id,
         )['rbac_policy']
 
         return {'policy': policy, 'rbac_policy': rbac_policy}
@@ -733,7 +730,7 @@
         qos_pol = self.create_qos_policy(
             name=data_utils.rand_name('test-policy'),
             description='test-shared-policy', shared=False,
-            tenant_id=self.admin_client.tenant_id)
+            project_id=self.admin_client.tenant_id)
         self.assertNotIn(qos_pol, self.client2.list_qos_policies()['policies'])
 
         # test update shared False -> True
@@ -764,7 +761,7 @@
         self.assertNotIn(qos_pol, self.client2.list_qos_policies()['policies'])
 
     def _create_net_bound_qos_rbacs(self):
-        res = self._make_admin_policy_shared_to_tenant_id(
+        res = self._make_admin_policy_shared_to_project_id(
             self.client.tenant_id)
         qos_policy, rbac_for_client_tenant = res['policy'], res['rbac_policy']
 
@@ -782,22 +779,22 @@
         return rbac_for_client_tenant, rbac_wildcard
 
     @decorators.idempotent_id('328b1f70-d424-11e5-a57f-54ee756c66df')
-    def test_net_bound_shared_policy_wildcard_and_tenant_id_wild_remove(self):
+    def test_net_bound_shared_policy_wildcard_and_project_id_wild_remove(self):
         client_rbac, wildcard_rbac = self._create_net_bound_qos_rbacs()
         # globally unshare the qos-policy, the specific share should remain
         self.admin_client.delete_rbac_policy(wildcard_rbac['id'])
         self.client.list_rbac_policies(id=client_rbac['id'])
 
     @decorators.idempotent_id('1997b00c-0c75-4e43-8ce2-999f9fa555ee')
-    def test_net_bound_shared_policy_wildcard_and_tenant_id_wild_remains(self):
+    def test_net_bound_shared_policy_wildcard_and_projectid_wild_remains(self):
         client_rbac, wildcard_rbac = self._create_net_bound_qos_rbacs()
         # remove client_rbac policy the wildcard share should remain
         self.admin_client.delete_rbac_policy(client_rbac['id'])
         self.client.list_rbac_policies(id=wildcard_rbac['id'])
 
     @decorators.idempotent_id('2ace9adc-da6e-11e5-aafe-54ee756c66df')
-    def test_policy_sharing_with_wildcard_and_tenant_id(self):
-        res = self._make_admin_policy_shared_to_tenant_id(
+    def test_policy_sharing_with_wildcard_and_project_id(self):
+        res = self._make_admin_policy_shared_to_project_id(
             self.client.tenant_id)
         qos_policy, rbac = res['policy'], res['rbac_policy']
         qos_pol = self.client.show_qos_policy(qos_policy['id'])['policy']
@@ -820,7 +817,7 @@
 
     @decorators.idempotent_id('9f85c76a-a350-11e5-8ae5-54ee756c66df')
     def test_policy_target_update(self):
-        res = self._make_admin_policy_shared_to_tenant_id(
+        res = self._make_admin_policy_shared_to_project_id(
             self.client.tenant_id)
         # change to client2
         update_res = self.admin_client.update_rbac_policy(
@@ -834,7 +831,7 @@
 
     @decorators.idempotent_id('a9b39f46-a350-11e5-97c7-54ee756c66df')
     def test_network_presence_prevents_policy_rbac_policy_deletion(self):
-        res = self._make_admin_policy_shared_to_tenant_id(
+        res = self._make_admin_policy_shared_to_project_id(
             self.client2.tenant_id)
         qos_policy_id = res['policy']['id']
         self._create_network(qos_policy_id, self.client2)
@@ -886,14 +883,14 @@
             object_type='qos_policy', object_id=policy['id'],
             action='access_as_shared', target_tenant=self.client2.tenant_id)
         field_args = (('id',), ('id', 'action'), ('object_type', 'object_id'),
-                      ('tenant_id', 'target_tenant'))
+                      ('project_id', 'target_tenant'))
         for fields in field_args:
             res = self.admin_client.list_rbac_policies(fields=fields)
             self.assertEqual(set(fields), set(res['rbac_policies'][0].keys()))
 
     @decorators.idempotent_id('c10d993a-a350-11e5-9c7a-54ee756c66df')
     def test_rbac_policy_show(self):
-        res = self._make_admin_policy_shared_to_tenant_id(
+        res = self._make_admin_policy_shared_to_project_id(
             self.client.tenant_id)
         p1 = res['rbac_policy']
         p2 = self.admin_client.create_rbac_policy(
@@ -928,7 +925,7 @@
 
     @decorators.idempotent_id('cd7d755a-a350-11e5-a344-54ee756c66df')
     def test_regular_client_blocked_from_sharing_anothers_policy(self):
-        qos_policy = self._make_admin_policy_shared_to_tenant_id(
+        qos_policy = self._make_admin_policy_shared_to_project_id(
             self.client.tenant_id)['policy']
         with testtools.ExpectedException(exceptions.BadRequest):
             self.client.create_rbac_policy(
diff --git a/neutron_tempest_plugin/api/test_security_groups.py b/neutron_tempest_plugin/api/test_security_groups.py
index cdd7017..c2e63da 100644
--- a/neutron_tempest_plugin/api/test_security_groups.py
+++ b/neutron_tempest_plugin/api/test_security_groups.py
@@ -272,19 +272,19 @@
             name=data_utils.rand_name('test-sg'),
             project={'id': self.admin_client.tenant_id})
 
-    def _make_admin_sg_shared_to_tenant_id(self, tenant_id):
+    def _make_admin_sg_shared_to_project_id(self, project_id):
         sg = self._create_security_group()
         rbac_policy = self.admin_client.create_rbac_policy(
             object_type='security_group',
             object_id=sg['id'],
             action='access_as_shared',
-            target_tenant=tenant_id,
+            target_tenant=project_id,
         )['rbac_policy']
         return {'security_group': sg, 'rbac_policy': rbac_policy}
 
     @decorators.idempotent_id('2a41eb8f-2a35-11e9-bae9-acde48001122')
     def test_policy_target_update(self):
-        res = self._make_admin_sg_shared_to_tenant_id(
+        res = self._make_admin_sg_shared_to_project_id(
             self.client.tenant_id)
         # change to client2
         update_res = self.admin_client.update_rbac_policy(
@@ -298,7 +298,7 @@
 
     @decorators.idempotent_id('2a619a8a-2a35-11e9-90d9-acde48001122')
     def test_port_presence_prevents_policy_rbac_policy_deletion(self):
-        res = self._make_admin_sg_shared_to_tenant_id(
+        res = self._make_admin_sg_shared_to_project_id(
             self.client2.tenant_id)
         sg_id = res['security_group']['id']
         net = self.create_network(client=self.client2)
@@ -341,14 +341,14 @@
             object_type='security_group', object_id=sg['id'],
             action='access_as_shared', target_tenant=self.client2.tenant_id)
         field_args = (('id',), ('id', 'action'), ('object_type', 'object_id'),
-                      ('tenant_id', 'target_tenant'))
+                      ('project_id', 'target_tenant'))
         for fields in field_args:
             res = self.admin_client.list_rbac_policies(fields=fields)
             self.assertEqual(set(fields), set(res['rbac_policies'][0].keys()))
 
     @decorators.idempotent_id('2abf8f9e-2a35-11e9-85f7-acde48001122')
     def test_rbac_policy_show(self):
-        res = self._make_admin_sg_shared_to_tenant_id(
+        res = self._make_admin_sg_shared_to_project_id(
             self.client.tenant_id)
         p1 = res['rbac_policy']
         p2 = self.admin_client.create_rbac_policy(
@@ -384,7 +384,7 @@
 
     @decorators.idempotent_id('2aff3900-2a35-11e9-96b3-acde48001122')
     def test_regular_client_blocked_from_sharing_anothers_policy(self):
-        sg = self._make_admin_sg_shared_to_tenant_id(
+        sg = self._make_admin_sg_shared_to_project_id(
             self.client.tenant_id)['security_group']
         with testtools.ExpectedException(exceptions.BadRequest):
             self.client.create_rbac_policy(
diff --git a/neutron_tempest_plugin/api/test_security_groups_negative.py b/neutron_tempest_plugin/api/test_security_groups_negative.py
index 1fcbd18..24e2289 100644
--- a/neutron_tempest_plugin/api/test_security_groups_negative.py
+++ b/neutron_tempest_plugin/api/test_security_groups_negative.py
@@ -15,11 +15,13 @@
 
 from neutron_lib import constants
 from neutron_lib.db import constants as db_const
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
 from neutron_tempest_plugin.api import base
 from neutron_tempest_plugin.api import base_security_groups
+from neutron_tempest_plugin.api import test_security_groups
 
 
 LONG_NAME_NG = 'x' * (db_const.NAME_FIELD_SIZE + 1)
@@ -84,6 +86,41 @@
                           self.os_primary.network_client.delete_security_group,
                           security_group_id=security_group['id'])
 
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('867d67c3-7e26-4288-a27b-e3d0649ee54b')
+    def test_assign_sec_group_twice(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        sg = self.create_security_group()
+        self.assertRaises(lib_exc.BadRequest,
+                          self.update_port,
+                          port,
+                          **{'security_groups': [sg['id'], sg['id']]})
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('d5ecb408-eb7e-47c1-a56f-353967dbd1c2')
+    def test_assign_nonexistent_sec_group(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        self.assertRaises(lib_exc.NotFound,
+                          self.update_port,
+                          port,
+                          **{'security_groups': [data_utils.rand_uuid()]})
+
+    @decorators.attr(type='negative')
+    @decorators.idempotent_id('98ef378d-81a2-43f6-bb6f-735c04cdef91')
+    def test_no_sec_group_changes_after_assignment_failure(self):
+        net = self.create_network()
+        port = self.create_port(net)
+        sg_list_before_failure = port['security_groups']
+        self.assertRaises(lib_exc.NotFound,
+                          self.update_port,
+                          port,
+                          **{'security_groups': [data_utils.rand_uuid()]})
+        port_details_new = self.client.show_port(port['id'])['port']
+        sg_list_after_failure = port_details_new['security_groups']
+        self.assertEqual(sg_list_before_failure, sg_list_after_failure)
+
 
 class NegativeSecGroupIPv6Test(NegativeSecGroupTest):
     _ip_version = constants.IP_VERSION_6
@@ -114,3 +151,22 @@
     def test_create_security_group_rule_with_ipv6_protocol_integers(self):
         self._test_create_security_group_rule_with_bad_protocols(
             base_security_groups.V6_PROTOCOL_INTS)
+
+
+class NegativeSecGroupQuotaTest(test_security_groups.BaseSecGroupQuota):
+
+    credentials = ['primary', 'admin']
+    required_extensions = ['security-group', 'quotas']
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('63f00cba-fcf5-4000-a3ee-eca58a1795c1')
+    def test_create_excess_sg(self):
+        self._set_sg_quota(0)
+        self.assertRaises(lib_exc.Conflict, self.create_security_group)
+
+    @decorators.attr(type=['negative'])
+    @decorators.idempotent_id('90a83445-bbc2-49d8-8c85-a111c08cd7fb')
+    def test_sg_quota_incorrect_values(self):
+        values = [-2, 2147483648, "value"]
+        for value in values:
+            self.assertRaises(lib_exc.BadRequest, self._set_sg_quota, value)
diff --git a/neutron_tempest_plugin/config.py b/neutron_tempest_plugin/config.py
index 2f2f913..28d6b76 100644
--- a/neutron_tempest_plugin/config.py
+++ b/neutron_tempest_plugin/config.py
@@ -123,6 +123,12 @@
                 default=False,
                 help='Allow creation of shared resources.'
                      'The default value is false.'),
+    cfg.BoolOpt('is_igmp_snooping_enabled',
+                default=False,
+                help='Indicates whether IGMP snooping is enabled or not. '
+                     'If True, multicast test(s) will assert that multicast '
+                     'traffic is not being flooded to all ports. Defaults '
+                     'to False.'),
 ]
 
 # TODO(amuller): Redo configuration options registration as part of the planned
diff --git a/neutron_tempest_plugin/scenario/test_multicast.py b/neutron_tempest_plugin/scenario/test_multicast.py
index a39a0c3..566ac95 100644
--- a/neutron_tempest_plugin/scenario/test_multicast.py
+++ b/neutron_tempest_plugin/scenario/test_multicast.py
@@ -111,6 +111,14 @@
            'result_file': result_file}
 
 
+def get_unregistered_script(group, result_file):
+    return """#!/bin/bash
+export LC_ALL=en_US.UTF-8
+tcpdump -i any -s0 -vv host %(group)s -vvneA -s0 -l &> %(result_file)s &
+    """ % {'group': group,
+           'result_file': result_file}
+
+
 class BaseMulticastTest(object):
 
     credentials = ['primary']
@@ -125,6 +133,7 @@
     multicast_message = "Big Bang"
     receiver_output_file = "/tmp/receiver_mcast_out"
     sender_output_file = "/tmp/sender_mcast_out"
+    unregistered_output_file = "/tmp/unregistered_mcast_out"
 
     @classmethod
     def skip_checks(cls):
@@ -198,16 +207,16 @@
         server['ssh_client'] = ssh.Client(server['fip']['floating_ip_address'],
                                           self.username,
                                           pkey=self.keypair['private_key'])
-        self._check_python_installed_on_server(server['ssh_client'],
-                                               server['id'])
+        self._check_cmd_installed_on_server(server['ssh_client'],
+                                            server['id'], PYTHON3_BIN)
         return server
 
-    def _check_python_installed_on_server(self, ssh_client, server_id):
+    def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
         try:
-            ssh_client.execute_script('which %s' % PYTHON3_BIN)
+            ssh_client.execute_script('which %s' % cmd)
         except exceptions.SSHScriptFailed:
             raise self.skipException(
-                "%s is not available on server %s" % (PYTHON3_BIN, server_id))
+                "%s is not available on server %s" % (cmd, server_id))
 
     def _prepare_sender(self, server, mcast_address):
         check_script = get_sender_script(
@@ -226,10 +235,23 @@
             server['fip']['floating_ip_address'],
             self.username,
             pkey=self.keypair['private_key'])
-        self._check_python_installed_on_server(ssh_client, server['id'])
+        self._check_cmd_installed_on_server(ssh_client, server['id'],
+                                            PYTHON3_BIN)
         server['ssh_client'].execute_script(
             'echo "%s" > ~/multicast_traffic_receiver.py' % check_script)
 
+    def _prepare_unregistered(self, server, mcast_address):
+        check_script = get_unregistered_script(
+            group=mcast_address, result_file=self.unregistered_output_file)
+        ssh_client = ssh.Client(
+            server['fip']['floating_ip_address'],
+            self.username,
+            pkey=self.keypair['private_key'])
+        self._check_cmd_installed_on_server(ssh_client, server['id'],
+                                            'tcpdump')
+        server['ssh_client'].execute_script(
+            'echo "%s" > ~/unregistered_traffic_receiver.sh' % check_script)
+
     @test.unstable_test("bug 1850288")
     @decorators.idempotent_id('113486fc-24c9-4be4-8361-03b1c9892867')
     def test_multicast_between_vms_on_same_network(self):
@@ -241,9 +263,26 @@
         receivers = [self._create_server() for _ in range(1)]
         # Sender can be also receiver of multicast traffic
         receivers.append(sender)
-        self._check_multicast_conectivity(sender=sender, receivers=receivers)
+        unregistered = self._create_server()
+        self._check_multicast_conectivity(sender=sender, receivers=receivers,
+                                          unregistered=unregistered)
 
-    def _check_multicast_conectivity(self, sender, receivers):
+    def _is_multicast_traffic_expected(self, mcast_address):
+        """Checks if multicast traffic is expected to arrive.
+
+        Checks if multicast traffic is expected to arrive to the
+        unregistered VM.
+
+        If IGMP snooping is enabled, multicast traffic should not be
+        flooded unless the destination IP is in the range of 224.0.0.X
+        [0].
+
+        [0] https://tools.ietf.org/html/rfc4541 (See section 2.1.2)
+        """
+        return (mcast_address.startswith('224.0.0') or not
+                CONF.neutron_plugin_options.is_igmp_snooping_enabled)
+
+    def _check_multicast_conectivity(self, sender, receivers, unregistered):
         """Test multi-cast messaging between two servers
 
         [Sender server] -> ... some network topology ... -> [Receiver server]
@@ -257,6 +296,12 @@
                     path=file_path))
             return msg in result
 
+        self._prepare_unregistered(unregistered, mcast_address)
+
+        # Run the unregistered node script
+        unregistered['ssh_client'].execute_script(
+            "bash ~/unregistered_traffic_receiver.sh", become_root=True)
+
         self._prepare_sender(sender, mcast_address)
         receiver_ids = []
         for receiver in receivers:
@@ -295,6 +340,18 @@
         for receiver_id in receiver_ids:
             self.assertIn(receiver_id, replies_result)
 
+        # Kill the tcpdump command running on the unregistered node so
+        # tcpdump flushes its output to the output file
+        unregistered['ssh_client'].execute_script(
+            "killall tcpdump && sleep 2", become_root=True)
+
+        unregistered_result = unregistered['ssh_client'].execute_script(
+            "cat {path} || echo '{path} not exists yet'".format(
+                path=self.unregistered_output_file))
+        num_of_pckt = (1 if self._is_multicast_traffic_expected(mcast_address)
+                       else 0)
+        self.assertIn('%d packets captured' % num_of_pckt, unregistered_result)
+
 
 class MulticastTestIPv4(BaseMulticastTest, base.BaseTempestTestCase):
 
diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py
index 8f79b5d..521e2be 100644
--- a/neutron_tempest_plugin/services/network/json/network_client.py
+++ b/neutron_tempest_plugin/services/network/json/network_client.py
@@ -601,7 +601,7 @@
         return service_client.ResponseBody(resp, body)
 
     def create_qos_policy(self, name, description=None, shared=False,
-                          tenant_id=None, is_default=False):
+                          project_id=None, is_default=False):
         uri = '%s/qos/policies' % self.uri_prefix
         post_data = {
             'policy': {
@@ -612,8 +612,8 @@
         }
         if description is not None:
             post_data['policy']['description'] = description
-        if tenant_id is not None:
-            post_data['policy']['tenant_id'] = tenant_id
+        if project_id is not None:
+            post_data['policy']['project_id'] = project_id
         resp, body = self.post(uri, self.serialize(post_data))
         body = self.deserialize_single(body)
         self.expected_success(201, resp.status)
diff --git a/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml b/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml
new file mode 100644
index 0000000..032be1f
--- /dev/null
+++ b/releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    Enhanced the ``test_multicast_between_vms_on_same_network`` adding
+    IGMP test coverage to it. A new VM running tcpdump is spawned as
+    part of the test to verify whether the traffic is reaching it or not.
+upgrade:
+  - |
+    Add a new configuration option called ``is_igmp_snooping_enabled``
+    to enable/disable IGMP testing as part of the
+    ``test_multicast_between_vms_on_same_network`` test case.