Merge "Replace test.attr() with decorators.attr()"
diff --git a/neutron/tests/tempest/api/admin/test_quotas.py b/neutron/tests/tempest/api/admin/test_quotas.py
index ef11851..85aecb9 100644
--- a/neutron/tests/tempest/api/admin/test_quotas.py
+++ b/neutron/tests/tempest/api/admin/test_quotas.py
@@ -29,21 +29,18 @@
 
     @classmethod
     def resource_setup(cls):
-        if not CONF.identity_feature_enabled.api_v2_admin:
-            # TODO(ihrachys) adopt to v3
-            raise cls.skipException('Identity v2 admin not available')
         super(QuotasTestBase, cls).resource_setup()
 
     def _create_tenant(self):
         # Add a tenant to conduct the test
         test_tenant = data_utils.rand_name('test_tenant_')
         test_description = data_utils.rand_name('desc_')
-        tenant = self.identity_admin_client.create_tenant(
+        project = self.identity_admin_clientv3.create_project(
             name=test_tenant,
-            description=test_description)['tenant']
+            description=test_description)['project']
         self.addCleanup(
-            self.identity_admin_client.delete_tenant, tenant['id'])
-        return tenant
+            self.identity_admin_clientv3.delete_project, project['id'])
+        return project
 
     def _setup_quotas(self, project_id, **new_quotas):
         # Change quotas for tenant
diff --git a/neutron/tests/tempest/api/base.py b/neutron/tests/tempest/api/base.py
index d0b8382..84746fb 100644
--- a/neutron/tests/tempest/api/base.py
+++ b/neutron/tests/tempest/api/base.py
@@ -430,6 +430,7 @@
         super(BaseAdminNetworkTest, cls).setup_clients()
         cls.admin_client = cls.os_adm.network_client
         cls.identity_admin_client = cls.os_adm.tenants_client
+        cls.identity_admin_clientv3 = cls.os_admin.projects_client
 
     @classmethod
     def create_metering_label(cls, name, description):
diff --git a/neutron/tests/tempest/api/clients.py b/neutron/tests/tempest/api/clients.py
index c6f41d0..949ce2e 100644
--- a/neutron/tests/tempest/api/clients.py
+++ b/neutron/tests/tempest/api/clients.py
@@ -16,6 +16,7 @@
 from tempest.lib.services.compute import keypairs_client
 from tempest.lib.services.compute import servers_client
 from tempest.lib.services.identity.v2 import tenants_client
+from tempest.lib.services.identity.v3 import projects_client
 from tempest import manager
 
 from neutron.tests.tempest import config
@@ -85,3 +86,6 @@
         # Client uses admin endpoint type of Keystone API v2
         self.tenants_client = tenants_client.TenantsClient(self.auth_provider,
                                                            **params_v2_admin)
+        # Client uses admin endpoint type of Keystone API v3
+        self.projects_client = projects_client.ProjectsClient(
+            self.auth_provider, **params_v2_admin)
diff --git a/neutron/tests/tempest/api/test_qos.py b/neutron/tests/tempest/api/test_qos.py
index f35abd8..7735710 100644
--- a/neutron/tests/tempest/api/test_qos.py
+++ b/neutron/tests/tempest/api/test_qos.py
@@ -31,6 +31,12 @@
 
     required_extensions = ['qos']
 
+    @staticmethod
+    def _get_driver_details(rule_type_details, driver_name):
+        for driver in rule_type_details['drivers']:
+            if driver['name'] == driver_name:
+                return driver
+
     @decorators.idempotent_id('108fbdf7-3463-4e47-9871-d07f3dcf5bbb')
     def test_create_policy(self):
         policy = self.create_qos_policy(name='test-policy',
@@ -172,6 +178,31 @@
         for rule in actual_list_rule_types:
             self.assertEqual(tuple(expected_rule_keys), tuple(rule.keys()))
 
+    @decorators.idempotent_id('8ececa21-ef97-4904-a152-9f04c90f484d')
+    def test_show_rule_type_details_as_user(self):
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.show_qos_rule_type,
+            qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
+
+    @decorators.idempotent_id('d0a2460b-7325-481f-a531-050bd96ab25e')
+    def test_show_rule_type_details_as_admin(self):
+        # Since returned rule types depend on loaded backend drivers this test
+        # is checking only if returned keys are same as expected keys
+
+        # In theory, we could make the test conditional on which ml2 drivers
+        # are enabled in gate, but that option doesn't seem to be
+        # available through tempest.lib framework
+        expected_rule_type_details_keys = ['type', 'drivers']
+
+        rule_type_details = self.admin_client.show_qos_rule_type(
+            qos_consts.RULE_TYPE_BANDWIDTH_LIMIT).get("rule_type")
+
+        # Verify that only required fields present in rule details
+        self.assertEqual(
+            sorted(tuple(expected_rule_type_details_keys)),
+            sorted(tuple(rule_type_details.keys())))
+
     def _disassociate_network(self, client, network_id):
         updated_network = client.update_network(network_id,
                                                 qos_policy_id=None)
diff --git a/neutron/tests/tempest/scenario/base.py b/neutron/tests/tempest/scenario/base.py
index 5ccbd1c..ac18ab5 100644
--- a/neutron/tests/tempest/scenario/base.py
+++ b/neutron/tests/tempest/scenario/base.py
@@ -192,10 +192,10 @@
         waiters.wait_for_server_status(self.os_primary.servers_client,
                                        self.server['server']['id'],
                                        constants.SERVER_STATUS_ACTIVE)
-        port = self.client.list_ports(network_id=self.network['id'],
-                                     device_id=self.server[
-                                          'server']['id'])['ports'][0]
-        self.fip = self.create_and_associate_floatingip(port['id'])
+        self.port = self.client.list_ports(network_id=self.network['id'],
+                                           device_id=self.server[
+                                               'server']['id'])['ports'][0]
+        self.fip = self.create_and_associate_floatingip(self.port['id'])
 
     def check_connectivity(self, host, ssh_user, ssh_key, servers=None):
         ssh_client = ssh.Client(host, ssh_user, pkey=ssh_key)
diff --git a/neutron/tests/tempest/scenario/test_portsecurity.py b/neutron/tests/tempest/scenario/test_portsecurity.py
new file mode 100644
index 0000000..76b23a4
--- /dev/null
+++ b/neutron/tests/tempest/scenario/test_portsecurity.py
@@ -0,0 +1,53 @@
+# 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 tempest.lib import decorators
+
+from neutron.tests.tempest import config
+from neutron.tests.tempest.scenario import base
+
+CONF = config.CONF
+
+
+class PortSecurityTest(base.BaseTempestTestCase):
+    credentials = ['primary']
+    required_extensions = ['port-security']
+
+    @decorators.idempotent_id('61ab176e-d48b-42b7-b38a-1ba571ecc033')
+    def test_port_security_removed_added(self):
+        """Test connection works after port security has been removed
+
+        Initial test that vm is accessible. Then port security is removed,
+        checked connectivity. Port security is added back and checked
+        connectivity again.
+        """
+        self.setup_network_and_server()
+        self.check_connectivity(self.fip['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+        sec_group_id = self.security_groups[0]['id']
+
+        self.port = self.update_port(port=self.port,
+                                     port_security_enabled=False,
+                                     security_groups=[])
+        self.check_connectivity(self.fip['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
+
+        self.port = self.update_port(port=self.port,
+                                     port_security_enabled=True,
+                                     security_groups=[sec_group_id])
+        self.check_connectivity(self.fip['floating_ip_address'],
+                                CONF.validation.image_ssh_user,
+                                self.keypair['private_key'])
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 7a05da1..39f2c38 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -728,6 +728,14 @@
         body = jsonutils.loads(body)
         return service_client.ResponseBody(resp, body)
 
+    def show_qos_rule_type(self, rule_type_name):
+        uri = '%s/qos/rule-types/%s' % (
+            self.uri_prefix, rule_type_name)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = jsonutils.loads(body)
+        return service_client.ResponseBody(resp, body)
+
     def create_trunk(self, parent_port_id, subports,
                      tenant_id=None, name=None, admin_state_up=None,
                      description=None):