Merge "Code cleanup of object storage tests"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 7920ab5..9e93759 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -285,3 +285,26 @@
 
 # Status change wait interval
 build_interval = 1
+
+[orchestration]
+# Status change wait interval
+build_interval = 1
+
+# Status change wait timout. This may vary across environments as some some
+# tests spawn full VMs, which could be slow if the test is already in a VM.
+build_timeout = 300
+
+# Whether or not Heat is expected to be available
+heat_available = false
+
+# Instance type for tests. Needs to be big enough for a
+# full OS plus the test workload
+instance_type = m1.tiny
+
+# Name of heat-cfntools enabled image to use when launching test instances
+# If not specified, tests that spawn instances will not run
+#image_ref = ubuntu-vm-heat-cfntools
+
+# Name of existing keypair to launch servers with. The default is not to specify
+# any key, which will generate a keypair for each test class
+#keypair_name = heat_key
diff --git a/tempest/clients.py b/tempest/clients.py
index 9b2c1f5..037a1c4 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -40,6 +40,7 @@
 from tempest.services.compute.json.security_groups_client import \
     SecurityGroupsClientJSON
 from tempest.services.compute.json.servers_client import ServersClientJSON
+from tempest.services.compute.json.services_client import ServicesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
 from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
@@ -59,6 +60,7 @@
 from tempest.services.compute.xml.security_groups_client \
     import SecurityGroupsClientXML
 from tempest.services.compute.xml.servers_client import ServersClientXML
+from tempest.services.compute.xml.services_client import ServicesClientXML
 from tempest.services.compute.xml.volumes_extensions_client import \
     VolumesExtensionsClientXML
 from tempest.services.identity.json.identity_client import IdentityClientJSON
@@ -86,6 +88,8 @@
 from tempest.services.object_storage.object_client import ObjectClient
 from tempest.services.object_storage.object_client import \
     ObjectClientCustomizedHeader
+from tempest.services.orchestration.json.orchestration_client import \
+    OrchestrationClient
 from tempest.services.volume.json.admin.volume_types_client import \
     VolumeTypesClientJSON
 from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
@@ -207,6 +211,11 @@
     "xml": AggregatesClientXML,
 }
 
+SERVICES_CLIENT = {
+    "json": ServicesClientJSON,
+    "xml": ServicesClientXML,
+}
+
 
 class Manager(object):
 
@@ -277,6 +286,7 @@
                 AVAILABILITY_ZONE_CLIENT[interface](*client_args)
             self.service_client = SERVICE_CLIENT[interface](*client_args)
             self.aggregates_client = AGGREGATES_CLIENT[interface](*client_args)
+            self.services_client = SERVICES_CLIENT[interface](*client_args)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
@@ -287,6 +297,7 @@
         self.image_client_v2 = ImageClientV2JSON(*client_args)
         self.container_client = ContainerClient(*client_args)
         self.object_client = ObjectClient(*client_args)
+        self.orchestration_client = OrchestrationClient(*client_args)
         self.ec2api_client = botoclients.APIClientEC2(*client_args)
         self.s3_client = botoclients.ObjectClientS3(*client_args)
         self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
@@ -337,3 +348,17 @@
                       conf.compute_admin.password,
                       conf.compute_admin.tenant_name,
                       interface=interface)
+
+
+class OrchestrationManager(Manager):
+    """
+    Manager object that uses the admin credentials for its
+    so that heat templates can create users
+    """
+    def __init__(self, interface='json'):
+        conf = config.TempestConfig()
+        base = super(OrchestrationManager, self)
+        base.__init__(conf.identity.admin_username,
+                      conf.identity.admin_password,
+                      conf.identity.admin_tenant_name,
+                      interface=interface)
diff --git a/tempest/config.py b/tempest/config.py
index d43c5d7..f5f56a8 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -351,6 +351,48 @@
     for opt in ObjectStoreConfig:
         conf.register_opt(opt, group='object-storage')
 
+
+orchestration_group = cfg.OptGroup(name='orchestration',
+                                   title='Orchestration Service Options')
+
+OrchestrationGroup = [
+    cfg.StrOpt('catalog_type',
+               default='orchestration',
+               help="Catalog type of the Orchestration service."),
+    cfg.BoolOpt('allow_tenant_isolation',
+                default=False,
+                help="Allows test cases to create/destroy tenants and "
+                     "users. This option enables isolated test cases and "
+                     "better parallel execution, but also requires that "
+                     "OpenStack Identity API admin credentials are known."),
+    cfg.IntOpt('build_interval',
+               default=1,
+               help="Time in seconds between build status checks."),
+    cfg.IntOpt('build_timeout',
+               default=300,
+               help="Timeout in seconds to wait for a stack to build."),
+    cfg.BoolOpt('heat_available',
+                default=False,
+                help="Whether or not Heat is expected to be available"),
+    cfg.StrOpt('instance_type',
+               default='m1.tiny',
+               help="Instance type for tests. Needs to be big enough for a "
+                    "full OS plus the test workload"),
+    cfg.StrOpt('image_ref',
+               default=None,
+               help="Name of heat-cfntools enabled image to use when "
+                    "launching test instances."),
+    cfg.StrOpt('keypair_name',
+               default=None,
+               help="Name of existing keypair to launch servers with."),
+]
+
+
+def register_orchestration_opts(conf):
+    conf.register_group(orchestration_group)
+    for opt in OrchestrationGroup:
+        conf.register_opt(opt, group='orchestration')
+
 boto_group = cfg.OptGroup(name='boto',
                           title='EC2/S3 options')
 BotoConfig = [
@@ -485,6 +527,7 @@
         register_network_opts(cfg.CONF)
         register_volume_opts(cfg.CONF)
         register_object_storage_opts(cfg.CONF)
+        register_orchestration_opts(cfg.CONF)
         register_boto_opts(cfg.CONF)
         register_compute_admin_opts(cfg.CONF)
         register_stress_opts(cfg.CONF)
@@ -495,6 +538,7 @@
         self.network = cfg.CONF.network
         self.volume = cfg.CONF.volume
         self.object_storage = cfg.CONF['object-storage']
+        self.orchestration = cfg.CONF.orchestration
         self.boto = cfg.CONF.boto
         self.compute_admin = cfg.CONF['compute-admin']
         self.stress = cfg.CONF.stress
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 235a2e7..448fbdf 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -90,6 +90,11 @@
     message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
 
 
+class StackBuildErrorException(TempestException):
+    message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
+               "due to '%(stack_status_reason)s'")
+
+
 class BadRequest(RestClientException):
     message = "Bad request"
 
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
new file mode 100644
index 0000000..d054f72
--- /dev/null
+++ b/tempest/services/compute/json/services_client.py
@@ -0,0 +1,33 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# 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 json
+
+from tempest.common.rest_client import RestClient
+
+
+class ServicesClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ServicesClientJSON, self).__init__(config, username, password,
+                                                 auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_services(self):
+        resp, body = self.get("os-services")
+        body = json.loads(body)
+        return resp, body['services']
diff --git a/tempest/services/compute/xml/services_client.py b/tempest/services/compute/xml/services_client.py
new file mode 100644
index 0000000..ce23403
--- /dev/null
+++ b/tempest/services/compute/xml/services_client.py
@@ -0,0 +1,34 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# 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 lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class ServicesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ServicesClientXML, self).__init__(config, username, password,
+                                                auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_services(self):
+        resp, body = self.get("os-services", self.headers)
+        node = etree.fromstring(body)
+        body = [xml_to_json(x) for x in node.getchildren()]
+        return resp, body
diff --git a/tempest/services/orchestration/__init__.py b/tempest/services/orchestration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/orchestration/__init__.py
diff --git a/tempest/services/orchestration/json/__init__.py b/tempest/services/orchestration/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/orchestration/json/__init__.py
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
new file mode 100644
index 0000000..81162df
--- /dev/null
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -0,0 +1,99 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 IBM Corp.
+# 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 json
+import time
+import urllib
+
+from tempest.common import rest_client
+from tempest import exceptions
+
+
+class OrchestrationClient(rest_client.RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(OrchestrationClient, self).__init__(config, username, password,
+                                                  auth_url, tenant_name)
+        self.service = self.config.orchestration.catalog_type
+        self.build_interval = self.config.orchestration.build_interval
+        self.build_timeout = self.config.orchestration.build_timeout
+
+    def list_stacks(self, params=None):
+        """Lists all stacks for a user."""
+
+        uri = 'stacks'
+        if params:
+            uri += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(uri)
+        body = json.loads(body)
+        return resp, body
+
+    def create_stack(self, name, disable_rollback=True, parameters={},
+                     timeout_mins=60, template=None, template_url=None):
+        post_body = {
+            "stack_name": name,
+            "disable_rollback": disable_rollback,
+            "parameters": parameters,
+            "timeout_mins": timeout_mins,
+            "template": "HeatTemplateFormatVersion: '2012-12-12'\n"
+        }
+        if template:
+            post_body['template'] = template
+        if template_url:
+            post_body['template_url'] = template_url
+        body = json.dumps(post_body)
+        uri = 'stacks'
+        resp, body = self.post(uri, headers=self.headers, body=body)
+        return resp, body
+
+    def get_stack(self, stack_identifier):
+        """Returns the details of a single stack."""
+        url = "stacks/%s" % stack_identifier
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['stack']
+
+    def delete_stack(self, stack_identifier):
+        """Deletes the specified Stack."""
+        return self.delete("stacks/%s" % str(stack_identifier))
+
+    def wait_for_stack_status(self, stack_identifier, status, failure_status=(
+            'CREATE_FAILED',
+            'DELETE_FAILED',
+            'UPDATE_FAILED',
+            'ROLLBACK_FAILED')):
+        """Waits for a Volume to reach a given status."""
+        stack_status = None
+        start = int(time.time())
+
+        while stack_status != status:
+            resp, body = self.get_stack(stack_identifier)
+            stack_name = body['stack_name']
+            stack_status = body['stack_status']
+            if stack_status in failure_status:
+                raise exceptions.StackBuildErrorException(
+                    stack_identifier=stack_identifier,
+                    stack_status=stack_status,
+                    stack_status_reason=body['stack_status_reason'])
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = ('Stack %s failed to reach %s status within '
+                           'the required time (%s s).' %
+                           (stack_name, status, self.build_timeout))
+                raise exceptions.TimeoutException(message)
+            time.sleep(self.build_interval)
diff --git a/tempest/tests/compute/admin/test_quotas.py b/tempest/tests/compute/admin/test_quotas.py
index 8f520f9..5bb48d0 100644
--- a/tempest/tests/compute/admin/test_quotas.py
+++ b/tempest/tests/compute/admin/test_quotas.py
@@ -15,6 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 from tempest.tests.compute import base
@@ -30,6 +31,7 @@
         cls.client = cls.os.quotas_client
         cls.adm_client = cls.os_adm.quotas_client
         cls.identity_admin_client = cls._get_identity_admin_client()
+        cls.sg_client = cls.security_groups_client
 
         resp, tenants = cls.identity_admin_client.list_tenants()
 
@@ -157,6 +159,60 @@
                         instances=default_instances_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
+    @attr(type='negative')
+    def test_security_groups_exceed_limit(self):
+        # Negative test: Creation Security Groups over limit should FAIL
+
+        resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+        default_sg_quota = quota_set['security_groups']
+        sg_quota = 0  # Set the quota to zero to conserve resources
+
+        resp, quota_set =\
+            self.adm_client.update_quota_set(self.demo_tenant_id,
+                                             security_groups=sg_quota)
+
+        self.addCleanup(self.adm_client.update_quota_set,
+                        self.demo_tenant_id,
+                        security_groups=default_sg_quota)
+
+        # Check we cannot create anymore
+        self.assertRaises(exceptions.OverLimit,
+                          self.sg_client.create_security_group,
+                          "sg-overlimit", "sg-desc")
+
+    @attr(type='negative')
+    def test_security_groups_rules_exceed_limit(self):
+        # Negative test: Creation of Security Group Rules should FAIL
+        # when we reach limit maxSecurityGroupRules
+
+        resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+        default_sg_rules_quota = quota_set['security_group_rules']
+        sg_rules_quota = 0  # Set the quota to zero to conserve resources
+
+        resp, quota_set =\
+            self.adm_client.update_quota_set(
+                self.demo_tenant_id,
+                security_group_rules=sg_rules_quota)
+
+        self.addCleanup(self.adm_client.update_quota_set,
+                        self.demo_tenant_id,
+                        security_group_rules=default_sg_rules_quota)
+
+        s_name = rand_name('securitygroup-')
+        s_description = rand_name('description-')
+        resp, securitygroup =\
+            self.sg_client.create_security_group(s_name, s_description)
+        self.addCleanup(self.sg_client.delete_security_group,
+                        securitygroup['id'])
+
+        secgroup_id = securitygroup['id']
+        ip_protocol = 'tcp'
+
+        # Check we cannot create SG rule anymore
+        self.assertRaises(exceptions.OverLimit,
+                          self.sg_client.create_security_group_rule,
+                          secgroup_id, ip_protocol, 1025, 1025)
+
 
 class QuotasAdminTestXML(QuotasAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/tests/compute/admin/test_services.py b/tempest/tests/compute/admin/test_services.py
new file mode 100644
index 0000000..0577164
--- /dev/null
+++ b/tempest/tests/compute/admin/test_services.py
@@ -0,0 +1,52 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# 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 import exceptions
+from tempest.test import attr
+from tempest.tests.compute import base
+
+
+class ServicesAdminTestJSON(base.BaseComputeAdminTest):
+
+    """
+    Tests Services API. List and Enable/Disable require admin privileges.
+    """
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServicesAdminTestJSON, cls).setUpClass()
+        cls.client = cls.os_adm.services_client
+        cls.non_admin_client = cls.services_client
+
+    @attr(type='positive')
+    def test_list_services(self):
+        # List Compute services
+        resp, services = self.client.list_services()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(services) >= 2)
+
+    @attr(type='negative')
+    def test_list_services_with_non_admin_user(self):
+        # List Compute service with non admin user
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_client.list_services)
+
+
+class ServicesAdminTestXML(ServicesAdminTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index b313e0b..fbefe35 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -63,6 +63,7 @@
         cls.fixed_ips_client = os.fixed_ips_client
         cls.availability_zone_client = os.availability_zone_client
         cls.aggregates_client = os.aggregates_client
+        cls.services_client = os.services_client
         cls.build_interval = cls.config.compute.build_interval
         cls.build_timeout = cls.config.compute.build_timeout
         cls.ssh_user = cls.config.compute.ssh_user
diff --git a/tempest/tests/identity/admin/v3/test_endpoints.py b/tempest/tests/identity/admin/v3/test_endpoints.py
old mode 100755
new mode 100644
diff --git a/tempest/tests/volume/admin/test_multi_backend.py b/tempest/tests/volume/admin/test_multi_backend.py
index 3d5fae4..c50586c 100644
--- a/tempest/tests/volume/admin/test_multi_backend.py
+++ b/tempest/tests/volume/admin/test_multi_backend.py
@@ -22,6 +22,7 @@
 from tempest import config
 from tempest.services.volume.json.admin import volume_types_client
 from tempest.services.volume.json import volumes_client
+from tempest.test import attr
 from tempest.tests.volume import base
 
 LOG = logging.getLogger(__name__)
@@ -108,6 +109,7 @@
 
         super(VolumeMultiBackendTest, cls).tearDownClass()
 
+    @attr(type=['smoke', 'gate'])
     def test_multi_backend_enabled(self):
         # this test checks that multi backend is enabled for at least the
         # computes where the volumes created in setUp were made
@@ -131,6 +133,7 @@
                "%(volume_host2)s") % locals()
         self.assertTrue(len(volume_host2.split("@")) > 1, msg)
 
+    @attr(type='gate')
     def test_backend_name_distinction(self):
         # this test checks that the two volumes created at setUp doesn't
         # belong to the same backend (if they are in the same backend, that
diff --git a/tempest/tests/volume/admin/test_volume_types.py b/tempest/tests/volume/admin/test_volume_types.py
index 13efca7..8fccd24 100644
--- a/tempest/tests/volume/admin/test_volume_types.py
+++ b/tempest/tests/volume/admin/test_volume_types.py
@@ -17,6 +17,7 @@
 
 from tempest.common.utils.data_utils import rand_name
 from tempest.services.volume.json.admin import volume_types_client
+from tempest.test import attr
 from tempest.tests.volume.base import BaseVolumeTest
 
 
@@ -37,6 +38,7 @@
                                                                auth_url,
                                                                adm_tenant)
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_type_list(self):
         # List Volume types.
         try:
@@ -46,6 +48,7 @@
         except Exception:
             self.fail("Could not list volume types")
 
+    @attr(type=['smoke', 'gate'])
     def test_create_get_delete_volume_with_volume_type_and_extra_specs(self):
         # Create/get/delete volume with volume_type and extra spec.
         try:
@@ -97,6 +100,7 @@
                 resp, _ = self.client.delete_volume_type(body['id'])
                 self.assertEqual(202, resp.status)
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_type_create_delete(self):
         # Create/Delete volume type.
         try:
@@ -119,6 +123,7 @@
         except Exception:
             self.fail("Could not create a volume_type")
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_type_create_get(self):
         # Create/get volume type.
         try:
diff --git a/tempest/tests/volume/admin/test_volume_types_extra_specs.py b/tempest/tests/volume/admin/test_volume_types_extra_specs.py
index 1cd7653..85edd64 100644
--- a/tempest/tests/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/tests/volume/admin/test_volume_types_extra_specs.py
@@ -16,6 +16,7 @@
 #    under the License.
 
 from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
 from tempest.tests.volume import base
 
 
@@ -33,6 +34,7 @@
         cls.client.delete_volume_type(cls.volume_type['id'])
         super(VolumeTypesExtraSpecsTest, cls).tearDownClass()
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_type_extra_specs_list(self):
         # List Volume types extra specs.
         try:
@@ -51,6 +53,7 @@
         except Exception:
             self.fail("Could not list volume types extra specs")
 
+    @attr(type=['gate'])
     def test_volume_type_extra_specs_update(self):
         # Update volume type extra specs
         try:
@@ -74,6 +77,7 @@
         except Exception:
             self.fail("Couldnt update volume type extra spec")
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_type_extra_spec_create_get_delete(self):
         # Create/Get/Delete volume type extra spec.
         try:
diff --git a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
index bd6e279..4a1a0b2 100644
--- a/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_extra_specs_negative.py
@@ -19,6 +19,7 @@
 
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.test import attr
 from tempest.tests.volume import base
 
 
@@ -39,6 +40,7 @@
         cls.client.delete_volume_type(cls.volume_type['id'])
         super(ExtraSpecsNegativeTest, cls).tearDownClass()
 
+    @attr(type='gate')
     def test_update_no_body(self):
         # Should not update volume type extra specs with no body
         extra_spec = {"spec1": "val2"}
@@ -46,6 +48,7 @@
                           self.client.update_volume_type_extra_specs,
                           self.volume_type['id'], extra_spec.keys()[0], None)
 
+    @attr(type='gate')
     def test_update_nonexistent_extra_spec_id(self):
         # Should not update volume type extra specs with nonexistent id.
         extra_spec = {"spec1": "val2"}
@@ -54,6 +57,7 @@
                           self.volume_type['id'], str(uuid.uuid4()),
                           extra_spec)
 
+    @attr(type='gate')
     def test_update_none_extra_spec_id(self):
         # Should not update volume type extra specs with none id.
         extra_spec = {"spec1": "val2"}
@@ -61,6 +65,7 @@
                           self.client.update_volume_type_extra_specs,
                           self.volume_type['id'], None, extra_spec)
 
+    @attr(type='gate')
     def test_update_multiple_extra_spec(self):
         # Should not update volume type extra specs with multiple specs as
             # body.
@@ -70,6 +75,7 @@
                           self.volume_type['id'], extra_spec.keys()[0],
                           extra_spec)
 
+    @attr(type='gate')
     def test_create_nonexistent_type_id(self):
         # Should not create volume type extra spec for nonexistent volume
             # type id.
@@ -78,18 +84,21 @@
                           self.client.create_volume_type_extra_specs,
                           str(uuid.uuid4()), extra_specs)
 
+    @attr(type='gate')
     def test_create_none_body(self):
         # Should not create volume type extra spec for none POST body.
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_volume_type_extra_specs,
                           self.volume_type['id'], None)
 
+    @attr(type='gate')
     def test_create_invalid_body(self):
         # Should not create volume type extra spec for invalid POST body.
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_volume_type_extra_specs,
                           self.volume_type['id'], ['invalid'])
 
+    @attr(type='gate')
     def test_delete_nonexistent_volume_type_id(self):
         # Should not delete volume type extra spec for nonexistent
             # type id.
@@ -98,12 +107,14 @@
                           self.client.delete_volume_type_extra_specs,
                           str(uuid.uuid4()), extra_specs.keys()[0])
 
+    @attr(type='gate')
     def test_list_nonexistent_volume_type_id(self):
         # Should not list volume type extra spec for nonexistent type id.
         self.assertRaises(exceptions.NotFound,
                           self.client.list_volume_types_extra_specs,
                           str(uuid.uuid4()))
 
+    @attr(type='gate')
     def test_get_nonexistent_volume_type_id(self):
         # Should not get volume type extra spec for nonexistent type id.
         extra_specs = {"spec1": "val1"}
@@ -111,6 +122,7 @@
                           self.client.get_volume_type_extra_specs,
                           str(uuid.uuid4()), extra_specs.keys()[0])
 
+    @attr(type='gate')
     def test_get_nonexistent_extra_spec_id(self):
         # Should not get volume type extra spec for nonexistent extra spec
             # id.
diff --git a/tempest/tests/volume/admin/test_volume_types_negative.py b/tempest/tests/volume/admin/test_volume_types_negative.py
index daf804d..bd358b8 100644
--- a/tempest/tests/volume/admin/test_volume_types_negative.py
+++ b/tempest/tests/volume/admin/test_volume_types_negative.py
@@ -18,12 +18,14 @@
 import uuid
 
 from tempest import exceptions
+from tempest.test import attr
 from tempest.tests.volume import base
 
 
 class VolumeTypesNegativeTest(base.BaseVolumeAdminTest):
     _interface = 'json'
 
+    @attr(type='gate')
     def test_create_with_nonexistent_volume_type(self):
         # Should not be able to create volume with nonexistent volume_type.
         self.assertRaises(exceptions.NotFound,
@@ -31,16 +33,19 @@
                           display_name=str(uuid.uuid4()),
                           volume_type=str(uuid.uuid4()))
 
+    @attr(type='gate')
     def test_create_with_empty_name(self):
         # Should not be able to create volume type with an empty name.
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_volume_type, '')
 
+    @attr(type='gate')
     def test_get_nonexistent_type_id(self):
         # Should not be able to get volume type with nonexistent type id.
         self.assertRaises(exceptions.NotFound, self.client.get_volume_type,
                           str(uuid.uuid4()))
 
+    @attr(type='gate')
     def test_delete_nonexistent_type_id(self):
         # Should not be able to delete volume type with nonexistent type id.
         self.assertRaises(exceptions.NotFound, self.client.delete_volume_type,
diff --git a/tempest/tests/volume/test_volumes_actions.py b/tempest/tests/volume/test_volumes_actions.py
index e6eb8d8..5396fa4 100644
--- a/tempest/tests/volume/test_volumes_actions.py
+++ b/tempest/tests/volume/test_volumes_actions.py
@@ -52,7 +52,7 @@
 
         super(VolumesActionsTest, cls).tearDownClass()
 
-    @attr(type='smoke')
+    @attr(type=['smoke', 'gate'])
     def test_attach_detach_volume_to_instance(self):
         # Volume is attached and detached successfully from an instance
         try:
@@ -70,6 +70,7 @@
             self.assertEqual(202, resp.status)
             self.client.wait_for_volume_status(self.volume['id'], 'available')
 
+    @attr(type='gate')
     def test_get_volume_attachment(self):
         # Verify that a volume's attachment information is retrieved
         mountpoint = '/dev/vdc'
diff --git a/tempest/tests/volume/test_volumes_get.py b/tempest/tests/volume/test_volumes_get.py
index 8e80e18..fdaf09b 100644
--- a/tempest/tests/volume/test_volumes_get.py
+++ b/tempest/tests/volume/test_volumes_get.py
@@ -78,7 +78,7 @@
                 self.assertEqual(202, resp.status)
                 self.client.wait_for_resource_deletion(volume['id'])
 
-    @attr(type='positive')
+    @attr(type='gate')
     def test_volume_get_metadata_none(self):
         # Create a volume without passing metadata, get details, and delete
         try:
@@ -105,11 +105,11 @@
                 self.assertEqual(202, resp.status)
                 self.client.wait_for_resource_deletion(volume['id'])
 
-    @attr(type='smoke')
+    @attr(type=['smoke', 'gate'])
     def test_volume_create_get_delete(self):
         self._volume_create_get_delete(image_ref=None)
 
-    @attr(type='smoke')
+    @attr(type=['smoke', 'gate'])
     def test_volume_from_image(self):
         self._volume_create_get_delete(image_ref=self.config.compute.image_ref)
 
diff --git a/tempest/tests/volume/test_volumes_list.py b/tempest/tests/volume/test_volumes_list.py
index a8fedb9..2468705 100644
--- a/tempest/tests/volume/test_volumes_list.py
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -76,7 +76,7 @@
             cls.client.wait_for_resource_deletion(volid)
         super(VolumesListTest, cls).tearDownClass()
 
-    @attr(type='smoke')
+    @attr(type=['smoke', 'gate'])
     def test_volume_list(self):
         # Get a list of Volumes
         # Fetch all volumes
@@ -89,7 +89,7 @@
                          ', '.join(m_vol['display_name']
                                    for m_vol in missing_vols))
 
-    @attr(type='smoke')
+    @attr(type='gate')
     def test_volume_list_with_details(self):
         # Get a list of Volumes with details
         # Fetch all Volumes
diff --git a/tempest/tests/volume/test_volumes_negative.py b/tempest/tests/volume/test_volumes_negative.py
index c7d4374..f02bb3f 100644
--- a/tempest/tests/volume/test_volumes_negative.py
+++ b/tempest/tests/volume/test_volumes_negative.py
@@ -17,6 +17,7 @@
 
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.test import attr
 from tempest.tests.volume import base
 
 
@@ -28,6 +29,7 @@
         super(VolumesNegativeTest, cls).setUpClass()
         cls.client = cls.volumes_client
 
+    @attr(type='gate')
     def test_volume_get_nonexistant_volume_id(self):
         # Should not be able to get a nonexistant volume
         #Creating a nonexistant volume id
@@ -43,6 +45,7 @@
         self.assertRaises(exceptions.NotFound, self.client.get_volume,
                           non_exist_id)
 
+    @attr(type='gate')
     def test_volume_delete_nonexistant_volume_id(self):
         # Should not be able to delete a nonexistant Volume
         # Creating nonexistant volume id
@@ -58,6 +61,7 @@
         self.assertRaises(exceptions.NotFound, self.client.delete_volume,
                           non_exist_id)
 
+    @attr(type='gate')
     def test_create_volume_with_invalid_size(self):
         # Should not be able to create volume with invalid size
         # in request
@@ -66,6 +70,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_volume,
                           size='#$%', display_name=v_name, metadata=metadata)
 
+    @attr(type='gate')
     def test_create_volume_with_out_passing_size(self):
         # Should not be able to create volume without passing size
         # in request
@@ -74,6 +79,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_volume,
                           size='', display_name=v_name, metadata=metadata)
 
+    @attr(type='gate')
     def test_create_volume_with_size_zero(self):
         # Should not be able to create volume with size zero
         v_name = rand_name('Volume-')
@@ -81,20 +87,24 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_volume,
                           size='0', display_name=v_name, metadata=metadata)
 
+    @attr(type='gate')
     def test_get_invalid_volume_id(self):
         # Should not be able to get volume with invalid id
         self.assertRaises(exceptions.NotFound, self.client.get_volume,
                           '#$%%&^&^')
 
+    @attr(type='gate')
     def test_get_volume_without_passing_volume_id(self):
         # Should not be able to get volume when empty ID is passed
         self.assertRaises(exceptions.NotFound, self.client.get_volume, '')
 
+    @attr(type='gate')
     def test_delete_invalid_volume_id(self):
         # Should not be able to delete volume when invalid ID is passed
         self.assertRaises(exceptions.NotFound, self.client.delete_volume,
                           '!@#$%^&*()')
 
+    @attr(type='gate')
     def test_delete_volume_without_passing_volume_id(self):
         # Should not be able to delete volume when empty ID is passed
         self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
diff --git a/tempest/tests/volume/test_volumes_snapshots.py b/tempest/tests/volume/test_volumes_snapshots.py
index ba8ba6c..edc02ac 100644
--- a/tempest/tests/volume/test_volumes_snapshots.py
+++ b/tempest/tests/volume/test_volumes_snapshots.py
@@ -37,7 +37,7 @@
     def tearDownClass(cls):
         super(VolumesSnapshotTest, cls).tearDownClass()
 
-    @attr(type='smoke')
+    @attr(type=['smoke', 'gate'])
     def test_snapshot_create_get_delete(self):
         # Create a snapshot, get some of the details and then deletes it
         resp, snapshot = self.snapshots_client.create_snapshot(
@@ -52,6 +52,7 @@
         self.snapshots_client.delete_snapshot(snapshot['id'])
         self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
 
+    @attr(type=['smoke', 'gate'])
     def test_volume_from_snapshot(self):
         # Create a temporary snap using wrapper method from base, then
         # create a snap based volume, check resp code and deletes it