Merge "Test cases for Policy V3 API-New"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index a55df76..fd8cbb5 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -91,7 +91,7 @@
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = 'nature'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff --git a/doc/source/index.rst b/doc/source/index.rst
index e37e250..1ca7344 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -3,6 +3,7 @@
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
+===================================
 Tempest Testing Project
 ===================================
 
@@ -14,10 +15,15 @@
    overview
 
 
-Field Guide:
+-------------------------------
+Field Guides
+-------------------------------
+Tempest contains tests of many different types, the field guides
+attempt to explain these in a way that makes it easy to understand
+where your test contributions should go.
 
 .. toctree::
-   :maxdepth: 2
+   :maxdepth: 1
 
    field_guide/index
    field_guide/api
@@ -28,6 +34,7 @@
    field_guide/whitebox
 
 
+==================
 Indices and tables
 ==================
 
diff --git a/etc/logging.conf.sample b/etc/logging.conf.sample
index 5c1ea5f..685dd36 100644
--- a/etc/logging.conf.sample
+++ b/etc/logging.conf.sample
@@ -1,30 +1,41 @@
 [loggers]
-keys=root
-
-[formatters]
-keys=normal,debug
+keys=root,tempest
 
 [handlers]
-keys=file,devel
+keys=file,syslog,devel
+
+[formatters]
+keys=default,tests
 
 [logger_root]
 level=NOTSET
+handlers=syslog
+
+[logger_tempest]
+level=DEBUG
 handlers=file
+qualname=tempest
 
 [handler_file]
 class=FileHandler
 level=DEBUG
-formatter=normal
+formatter=tests
 args=('tempest.log', 'w')
 
+[handler_syslog]
+class=handlers.SysLogHandler
+level=ERROR
+formatter = default
+args = ('/dev/log', handlers.SysLogHandler.LOG_USER)
+
 [handler_devel]
 class=StreamHandler
 level=DEBUG
-formatter=debug
+formatter=default
 args=(sys.stdout,)
 
-[formatter_normal]
-format=%(asctime)s %(levelname)s %(message)s
+[formatter_default]
+format=%(name)s: %(levelname)s: %(message)s
 
-[formatter_debug]
-format=%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s
+[formatter_tests]
+class = tempest.common.log.TestsFormatter
diff --git a/run_tests.sh b/run_tests.sh
index 56a6e6e..d5b2494 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -75,6 +75,16 @@
     export TEMPEST_CONFIG=`basename "$config_file"`
 fi
 
+if [ $logging -eq 1 ]; then
+    if [ ! -f "$logging_config" ]; then
+        echo "No such logging config file: $logging_config"
+        exit 1
+    fi
+    logging_config=`readlink -f "$logging_config"`
+    export TEMPEST_LOG_CONFIG_DIR=`dirname "$logging_config"`
+    export TEMPEST_LOG_CONFIG=`basename "$logging_config"`
+fi
+
 cd `dirname "$0"`
 
 export NOSE_WITH_OPENSTACK=1
@@ -84,14 +94,6 @@
 export NOSE_OPENSTACK_SHOW_ELAPSED=1
 export NOSE_OPENSTACK_STDOUT=1
 
-if [ $logging -eq 1 ]; then
-    if [ ! -f "$logging_config" ]; then
-        echo "No such logging config file: $logging_config"
-        exit
-    fi
-    noseargs="$noseargs --logging-config=$logging_config"
-fi
-
 if [ $no_site_packages -eq 1 ]; then
   installvenvopts="--no-site-packages"
 fi
diff --git a/tempest/README.rst b/tempest/README.rst
index d506bc6..fa29fe2 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -1,5 +1,6 @@
-Tempest Field Guide
-========
+============
+Tempest Field Guide Overview
+============
 
 Tempest is designed to be useful for a large number of different
 environments. This includes being useful for gating commits to
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index 6cd6f69..98f198d 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -15,10 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
-
 from tempest import clients
+from tempest.common import log as logging
 from tempest import config
 from tempest.exceptions import InvalidConfiguration
 
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 1eff4dd..acdfb74 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -43,7 +43,7 @@
                     filter(lambda y: y['service'] == 'compute', hosts_all))
         cls.host = hosts[0]
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_create_delete(self):
         # Create and delete an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -56,7 +56,7 @@
         self.assertEquals(200, resp.status)
         self.client.wait_for_resource_deletion(aggregate['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_create_delete_with_az(self):
         # Create and delete an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -70,7 +70,7 @@
         self.assertEquals(200, resp.status)
         self.client.wait_for_resource_deletion(aggregate['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_create_verify_entry_in_list(self):
         # Create an aggregate and ensure it is listed.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -83,7 +83,7 @@
                       map(lambda x: (x['id'], x['availability_zone']),
                           aggregates))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_create_get_details(self):
         # Create an aggregate and ensure its details are returned.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -96,7 +96,7 @@
         self.assertEquals(aggregate['availability_zone'],
                           body['availability_zone'])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_create_as_user(self):
         # Regular user is not allowed to create an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -104,7 +104,7 @@
                           self.user_client.create_aggregate,
                           aggregate_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_delete_as_user(self):
         # Regular user is not allowed to delete an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -115,13 +115,13 @@
                           self.user_client.delete_aggregate,
                           aggregate['id'])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_list_as_user(self):
         # Regular user is not allowed to list aggregates.
         self.assertRaises(exceptions.Unauthorized,
                           self.user_client.list_aggregates)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_get_details_as_user(self):
         # Regular user is not allowed to get aggregate details.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -132,19 +132,19 @@
                           self.user_client.get_aggregate,
                           aggregate['id'])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_delete_with_invalid_id(self):
         # Delete an aggregate with invalid id should raise exceptions.
         self.assertRaises(exceptions.NotFound,
                           self.client.delete_aggregate, -1)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_get_details_with_invalid_id(self):
         # Get aggregate details with invalid id should raise exceptions.
         self.assertRaises(exceptions.NotFound,
                           self.client.get_aggregate, -1)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_add_remove_host(self):
         # Add an host to the given aggregate and remove.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -165,7 +165,7 @@
                           body['availability_zone'])
         self.assertNotIn(self.host, body['hosts'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_add_host_list(self):
         # Add an host to the given aggregate and list.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -182,7 +182,7 @@
         self.assertEquals(None, agg['availability_zone'])
         self.assertIn(self.host, agg['hosts'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_add_host_get_details(self):
         # Add an host to the given aggregate and get details.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -196,7 +196,7 @@
         self.assertEquals(None, body['availability_zone'])
         self.assertIn(self.host, body['hosts'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_aggregate_add_host_create_server_with_az(self):
         # Add an host to the given aggregate and create a server.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -215,7 +215,7 @@
         resp, body = admin_servers_client.get_server(server['id'])
         self.assertEqual(self.host, body[self._host_key])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_add_non_exist_host(self):
         # Adding a non-exist host to an aggregate should raise exceptions.
         resp, hosts_all = self.os_adm.hosts_client.list_hosts()
@@ -232,7 +232,7 @@
         self.assertRaises(exceptions.NotFound, self.client.add_host,
                           aggregate['id'], non_exist_host)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_add_host_as_user(self):
         # Regular user is not allowed to add a host to an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -243,7 +243,7 @@
                           self.user_client.add_host,
                           aggregate['id'], self.host)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_aggregate_remove_host_as_user(self):
         # Regular user is not allowed to remove a host from an aggregate.
         aggregate_name = rand_name(self.aggregate_name_prefix)
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index db54011..ab2f2d9 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -34,14 +34,14 @@
         cls.client = cls.os_adm.availability_zone_client
         cls.non_adm_client = cls.availability_zone_client
 
-    @attr('positive')
+    @attr(type=['positive', 'gate'])
     def test_get_availability_zone_list(self):
         # List of availability zone
         resp, availability_zone = self.client.get_availability_zone_list()
         self.assertEqual(200, resp.status)
         self.assertTrue(len(availability_zone) > 0)
 
-    @attr('positive')
+    @attr(type=['positive', 'gate'])
     def test_get_availability_zone_list_detail(self):
         # List of availability zones and available services
         resp, availability_zone = \
@@ -49,7 +49,7 @@
         self.assertEqual(200, resp.status)
         self.assertTrue(len(availability_zone) > 0)
 
-    @attr('positive')
+    @attr(type=['positive', 'gate'])
     def test_get_availability_zone_list_with_non_admin_user(self):
         # List of availability zone with non admin user
         resp, availability_zone = \
@@ -57,7 +57,7 @@
         self.assertEqual(200, resp.status)
         self.assertTrue(len(availability_zone) > 0)
 
-    @attr('negative')
+    @attr(type=['negative', 'gate'])
     def test_get_availability_zone_list_detail_with_non_admin_user(self):
         # List of availability zones and available services with non admin user
         self.assertRaises(
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 4194b7e..8424709 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -51,43 +51,43 @@
 class FixedIPsTestJson(FixedIPsBase):
     _interface = 'json'
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_fixed_ip_details(self):
         resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
         self.assertEqual(fixed_ip['address'], self.ip)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_fixed_ip_details_with_non_admin_user(self):
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.get_fixed_ip_details, self.ip)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_set_reserve(self):
         body = {"reserve": "None"}
         resp, body = self.client.reserve_fixed_ip(self.ip, body)
         self.assertEqual(resp.status, 202)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_set_unreserve(self):
         body = {"unreserve": "None"}
         resp, body = self.client.reserve_fixed_ip(self.ip, body)
         self.assertEqual(resp.status, 202)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_reserve_with_non_admin_user(self):
         body = {"reserve": "None"}
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, body)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_unreserve_with_non_admin_user(self):
         body = {"unreserve": "None"}
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, body)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_reserve_with_invalid_ip(self):
         # NOTE(maurosr): since this exercises the same code snippet, we do it
         # only for reserve action
@@ -96,7 +96,7 @@
                           self.client.reserve_fixed_ip,
                           "my.invalid.ip", body)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_fixed_ip_with_invalid_action(self):
         body = {"invalid_action": "None"}
         self.assertRaises(exceptions.BadRequest,
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 95457b9..47097ed 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -53,7 +53,7 @@
         self.assertEqual(resp.status, 202)
         self.client.wait_for_resource_deletion(flavor_id)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_create_flavor(self):
         # Create a flavor and ensure it is listed
         # This operation requires the user to have 'admin' role
@@ -92,7 +92,7 @@
         self.assertEqual(resp.status, 200)
         self.assertEqual(flavor['name'], flavor_name)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_create_flavor_verify_entry_in_list_details(self):
         # Create a flavor and ensure it's details are listed
         # This operation requires the user to have 'admin' role
@@ -117,7 +117,7 @@
                 flag = True
         self.assertTrue(flag)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_flavor_details_for_deleted_flavor(self):
         # Delete a flavor and ensure it is not listed
         # Create a test flavor
@@ -151,6 +151,7 @@
                 flag = False
         self.assertTrue(flag)
 
+    @attr(type='gate')
     def test_create_list_flavor_without_extra_data(self):
         #Create a flavor and ensure it is listed
         #This operation requires the user to have 'admin' role
@@ -192,7 +193,7 @@
                 flag = True
         self.assertTrue(flag)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_flavor_not_public_verify_entry_not_in_list_details(self):
         #Create a flavor with os-flavor-access:is_public false should not
         #be present in list_details.
@@ -216,6 +217,7 @@
                 flag = True
         self.assertFalse(flag)
 
+    @attr(type='gate')
     def test_list_public_flavor_with_other_user(self):
         #Create a Flavor with public access.
         #Try to List/Get flavor with another user
@@ -239,7 +241,7 @@
                 flag = True
         self.assertTrue(flag)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_is_public_string_variations(self):
         flavor_id_not_public = rand_int_id(start=1000)
         flavor_name_not_public = rand_name(self.flavor_name_prefix)
@@ -282,13 +284,13 @@
         _test_string_variations(['t', 'true', 'yes', '1'],
                                 flavor_name_public)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_invalid_is_public_string(self):
         self.assertRaises(exceptions.BadRequest,
                           self.client.list_flavors_with_detail,
                           {'is_public': 'invalid'})
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_flavor_as_user(self):
         flavor_name = rand_name(self.flavor_name_prefix)
         new_flavor_id = rand_int_id(start=1000)
@@ -299,7 +301,7 @@
                           new_flavor_id, ephemeral=self.ephemeral,
                           swap=self.swap, rxtx=self.rxtx)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_flavor_as_user(self):
         self.assertRaises(exceptions.Unauthorized,
                           self.user_client.delete_flavor,
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 85648a9..f4b969d 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -50,7 +50,7 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @attr('positive')
+    @attr(type=['positive', 'gate'])
     def test_flavor_access_add_remove(self):
         #Test to add and remove flavor access to a given tenant.
         flavor_name = rand_name(self.flavor_name_prefix)
@@ -87,7 +87,7 @@
         self.assertEqual(resp.status, 200)
         self.assertNotIn(new_flavor['id'], map(lambda x: x['id'], flavors))
 
-    @attr('negative')
+    @attr(type=['negative', 'gate'])
     def test_flavor_non_admin_add(self):
         #Test to add flavor access as a user without admin privileges.
         flavor_name = rand_name(self.flavor_name_prefix)
@@ -103,7 +103,7 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @attr('negative')
+    @attr(type=['negative', 'gate'])
     def test_flavor_non_admin_remove(self):
         #Test to remove flavor access as a user without admin privileges.
         flavor_name = rand_name(self.flavor_name_prefix)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 357a787..db376b5 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -60,6 +60,7 @@
         resp, body = cls.client.delete_flavor(cls.flavor['id'])
         super(FlavorsExtraSpecsTestJSON, cls).tearDownClass()
 
+    @attr(type='gate')
     def test_flavor_set_get_unset_keys(self):
         #Test to SET, GET UNSET flavor extra spec as a user
         #with admin privileges.
@@ -80,7 +81,7 @@
             self.client.unset_flavor_extra_spec(self.flavor['id'], "key1")
         self.assertEqual(unset_resp.status, 200)
 
-    @attr('negative')
+    @attr(type=['negative', 'gate'])
     def test_flavor_non_admin_set_keys(self):
         #Test to SET flavor extra spec as a user without admin privileges.
         specs = {"key1": "value1", "key2": "value2"}
@@ -89,6 +90,7 @@
                           self.flavor['id'],
                           specs)
 
+    @attr(type='gate')
     def test_flavor_non_admin_get_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
@@ -99,7 +101,7 @@
         for key in specs:
             self.assertEquals(body[key], specs[key])
 
-    @attr('negative')
+    @attr(type=['negative', 'gate'])
     def test_flavor_non_admin_unset_keys(self):
         specs = {"key1": "value1", "key2": "value2"}
         set_resp, set_body = self.client.set_flavor_extra_spec(
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index ba21dc5..1266405 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -70,6 +70,7 @@
         self.assertEqual(200, resp.status)
         self.assertEqual(expected_quota_set, quota_set)
 
+    @attr(type='gate')
     def test_update_all_quota_resources_for_tenant(self):
         self.skipTest("This test require the change in nova component "
                       "https://review.openstack.org/#/c/25887/, the related "
@@ -103,6 +104,7 @@
                              "defaults")
 
     #TODO(afazekas): merge these test cases
+    @attr(type='gate')
     def test_get_updated_quotas(self):
         # Verify that GET shows the updated quota set
         self.adm_client.update_quota_set(self.demo_tenant_id,
@@ -123,6 +125,7 @@
             self.assertEqual(200, resp.status, "Failed to reset quota "
                              "defaults")
 
+    @attr(type='gate')
     def test_create_server_when_cpu_quota_is_full(self):
         self.skipTest("This test require the change in nova component "
                       "https://review.openstack.org/#/c/25887/, the related "
@@ -141,6 +144,7 @@
                         cores=default_vcpu_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
+    @attr(type='gate')
     def test_create_server_when_memory_quota_is_full(self):
         self.skipTest("This test require the change in nova component "
                       "https://review.openstack.org/#/c/25887/, the related "
@@ -161,7 +165,7 @@
 
 #TODO(afazekas): Add test that tried to update the quota_set as a regular user
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_server_when_instances_quota_is_full(self):
         self.skipTest("This test require the change in nova component "
                       "https://review.openstack.org/#/c/25887/, the related "
@@ -179,7 +183,7 @@
                         instances=default_instances_quota)
         self.assertRaises(exceptions.OverLimit, self.create_server)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
 
@@ -200,7 +204,7 @@
                           self.sg_client.create_security_group,
                           "sg-overlimit", "sg-desc")
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
         # when we reach limit maxSecurityGroupRules
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index aacfe8a..db7928a 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -34,14 +34,14 @@
         cls.client = cls.os_adm.services_client
         cls.non_admin_client = cls.services_client
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     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')
+    @attr(type=['negative', 'gate'])
     def test_list_services_with_non_admin_user(self):
         # List Compute service with non admin user
         self.assertRaises(exceptions.Unauthorized,
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 5f0df3c..b19b9d9 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -15,11 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import time
 
 from tempest.api import compute
 from tempest import clients
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 import tempest.test
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index d51d9d8..a60cbe9 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -50,27 +50,27 @@
         resp, flavor = self.client.get_flavor_details(self.flavor_ref)
         self.assertEqual(self.flavor_ref, int(flavor['id']))
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_non_existant_flavor(self):
         # flavor details are not returned for non existant flavors
         self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
                           999)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_limit_results(self):
         # Only the expected number of flavors should be returned
         params = {'limit': 1}
         resp, flavors = self.client.list_flavors(params)
         self.assertEqual(1, len(flavors))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_detailed_limit_results(self):
         # Only the expected number of flavors (detailed) should be returned
         params = {'limit': 1}
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertEqual(1, len(flavors))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_using_marker(self):
         # The list of flavors should start from the provided marker
         resp, flavors = self.client.list_flavors()
@@ -81,7 +81,7 @@
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
                          'The list of flavors did not start after the marker.')
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_detailed_using_marker(self):
         # The list of flavors should start from the provided marker
         resp, flavors = self.client.list_flavors_with_detail()
@@ -92,7 +92,7 @@
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
                          'The list of flavors did not start after the marker.')
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_detailed_filter_by_min_disk(self):
         # The detailed list of flavors should be filtered by disk space
         resp, flavors = self.client.list_flavors_with_detail()
@@ -103,7 +103,7 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_detailed_filter_by_min_ram(self):
         # The detailed list of flavors should be filtered by RAM
         resp, flavors = self.client.list_flavors_with_detail()
@@ -114,7 +114,7 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_filter_by_min_disk(self):
         # The list of flavors should be filtered by disk space
         resp, flavors = self.client.list_flavors_with_detail()
@@ -125,7 +125,7 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_flavors_filter_by_min_ram(self):
         # The list of flavors should be filtered by RAM
         resp, flavors = self.client.list_flavors_with_detail()
@@ -136,19 +136,19 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_invalid_minRam_filter(self):
         self.assertRaises(exceptions.BadRequest,
                           self.client.list_flavors_with_detail,
                           {'minRam': 'invalid'})
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_invalid_minDisk_filter(self):
         self.assertRaises(exceptions.BadRequest,
                           self.client.list_flavors_with_detail,
                           {'minDisk': 'invalid'})
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_flavor_details_for_invalid_flavor_id(self):
         # Ensure 404 returned for non-existant flavor ID
         self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 8263dc2..c2e6bd0 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -56,7 +56,7 @@
         resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
         super(FloatingIPsTestJSON, cls).tearDownClass()
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_allocate_floating_ip(self):
         # Positive test:Allocation of a new floating IP to a project
         # should be successful
@@ -73,7 +73,7 @@
             #Deleting the floating IP which is created in this method
             self.client.delete_floating_ip(floating_ip_id_allocated)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_allocate_floating_ip_from_nonexistent_pool(self):
         # Positive test:Allocation of a new floating IP from a nonexistent_pool
         #to a project should fail
@@ -81,7 +81,7 @@
                           self.client.create_floating_ip,
                           "non_exist_pool")
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_delete_floating_ip(self):
         # Positive test:Deletion of valid floating IP from project
         # should be successful
@@ -96,7 +96,7 @@
         # Check it was really deleted.
         self.client.wait_for_resource_deletion(floating_ip_body['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_associate_disassociate_floating_ip(self):
         # Positive test:Associate and disassociate the provided floating IP
         # to a specific server should be successful
@@ -112,7 +112,7 @@
             self.server_id)
         self.assertEqual(202, resp.status)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_nonexistant_floating_ip(self):
         # Negative test:Deletion of a nonexistent floating IP
         # from project should fail
@@ -121,7 +121,7 @@
         self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
                           self.non_exist_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_associate_nonexistant_floating_ip(self):
         # Negative test:Association of a non existent floating IP
         # to specific server should fail
@@ -130,7 +130,7 @@
                           self.client.associate_floating_ip_to_server,
                           "0.0.0.0", self.server_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_dissociate_nonexistant_floating_ip(self):
         # Negative test:Dissociation of a non existent floating IP should fail
         # Dissociating non existent floating IP
@@ -138,7 +138,7 @@
                           self.client.disassociate_floating_ip_from_server,
                           "0.0.0.0", self.server_id)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_associate_already_associated_floating_ip(self):
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
@@ -170,7 +170,7 @@
                           self.client.disassociate_floating_ip_from_server,
                           self.floating_ip, self.server_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_associate_ip_to_server_without_passing_floating_ip(self):
         # Negative test:Association of empty floating IP to specific server
         # should raise NotFound exception
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 017659e..d77b0a5 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -42,7 +42,7 @@
             cls.client.delete_floating_ip(cls.floating_ip_id[i])
         super(FloatingIPDetailsTestJSON, cls).tearDownClass()
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_floating_ips(self):
         # Positive test:Should return the list of floating IPs
         resp, body = self.client.list_floating_ips()
@@ -53,7 +53,7 @@
         for i in range(3):
             self.assertTrue(self.floating_ip[i] in floating_ips)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_get_floating_ip_details(self):
         # Positive test:Should be able to GET the details of floatingIP
         #Creating a floating IP for which details are to be checked
@@ -77,7 +77,7 @@
         finally:
             self.client.delete_floating_ip(floating_ip_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_nonexistant_floating_ip_details(self):
         # Negative test:Should not be able to GET the details
         # of nonexistant floating IP
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 6b167a8..7b8e1cc 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -52,12 +52,14 @@
         resp, _ = self.client.set_image_metadata(self.image_id, meta)
         self.assertEqual(resp.status, 200)
 
+    @attr(type='gate')
     def test_list_image_metadata(self):
         # All metadata key/value pairs for an image should be returned
         resp, resp_metadata = self.client.list_image_metadata(self.image_id)
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_set_image_metadata(self):
         # The metadata for the image should match the new values
         req_metadata = {'meta2': 'value2', 'meta3': 'value3'}
@@ -67,6 +69,7 @@
         resp, resp_metadata = self.client.list_image_metadata(self.image_id)
         self.assertEqual(req_metadata, resp_metadata)
 
+    @attr(type='gate')
     def test_update_image_metadata(self):
         # The metadata for the image should match the updated values
         req_metadata = {'key1': 'alt1', 'key3': 'value3'}
@@ -77,12 +80,14 @@
         expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_get_image_metadata_item(self):
         # The value for a specific metadata key should be returned
         resp, meta = self.client.get_image_metadata_item(self.image_id,
                                                          'key2')
         self.assertTrue('value2', meta['key2'])
 
+    @attr(type='gate')
     def test_set_image_metadata_item(self):
         # The value provided for the given meta item should be set for
         # the image
@@ -93,6 +98,7 @@
         expected = {'key1': 'alt', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_delete_image_metadata_item(self):
         # The metadata value/key pair should be deleted from the image
         resp, body = self.client.delete_image_metadata_item(self.image_id,
@@ -101,34 +107,34 @@
         expected = {'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_nonexistant_image_metadata(self):
         # Negative test: List on nonexistant image
         # metadata should not happen
         self.assertRaises(exceptions.NotFound, self.client.list_image_metadata,
                           999)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_nonexistant_image_metadata(self):
         # Negative test:An update should not happen for a nonexistant image
         meta = {'key1': 'alt1', 'key2': 'alt2'}
         self.assertRaises(exceptions.NotFound,
                           self.client.update_image_metadata, 999, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_nonexistant_image_metadata_item(self):
         # Negative test: Get on nonexistant image should not happen
         self.assertRaises(exceptions.NotFound,
                           self.client.get_image_metadata_item, 999, 'key2')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_nonexistant_image_metadata(self):
         # Negative test: Metadata should not be set to a nonexistant image
         meta = {'key1': 'alt1', 'key2': 'alt2'}
         self.assertRaises(exceptions.NotFound, self.client.set_image_metadata,
                           999, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_nonexistant_image_metadata_item(self):
         # Negative test: Metadata item should not be set to a
         # nonexistant image
@@ -137,7 +143,7 @@
                           self.client.set_image_metadata_item, 999, 'key1',
                           meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_nonexistant_image_metadata_item(self):
         # Negative test: Shouldnt be able to delete metadata
         # item from nonexistant image
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 0647d06..1ef30d4 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -64,7 +64,7 @@
         self.image_ids.append(image_id)
         return resp, body
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_from_deleted_server(self):
         # An image should not be created if the server instance is removed
         resp, server = self.create_server(wait_until='ACTIVE')
@@ -79,7 +79,7 @@
                           self.__create_image__,
                           server['id'], name, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_from_invalid_server(self):
         # An image should not be created with invalid server id
         # Create a new image with invalid server id
@@ -90,7 +90,7 @@
         self.assertRaises(exceptions.NotFound, self.__create_image__,
                           '!@#$%^&*()', name, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_when_server_is_terminating(self):
         # Return an error when creating image of server that is terminating
         resp, server = self.create_server(wait_until='ACTIVE')
@@ -101,7 +101,7 @@
                           server['id'], snapshot_name)
 
     @testtools.skip("Until Bug #1039739 is fixed")
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_when_server_is_rebooting(self):
         # Return error when creating an image of server that is rebooting
         resp, server = self.create_server()
@@ -111,7 +111,7 @@
         self.assertRaises(exceptions.Duplicate, self.client.create_image,
                           server['id'], snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_uuid_35_characters_or_less(self):
         # Return an error if Image ID passed is 35 characters or less
         snapshot_name = rand_name('test-snap-')
@@ -119,7 +119,7 @@
         self.assertRaises(exceptions.NotFound, self.client.create_image,
                           test_uuid, snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_uuid_37_characters_or_more(self):
         # Return an error if Image ID passed is 37 characters or more
         snapshot_name = rand_name('test-snap-')
@@ -127,13 +127,13 @@
         self.assertRaises(exceptions.NotFound, self.client.create_image,
                           test_uuid, snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_with_invalid_image_id(self):
         # An image should not be deleted with invalid image id
         self.assertRaises(exceptions.NotFound, self.client.delete_image,
                           '!@$%^&*()')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
 
@@ -141,24 +141,24 @@
         self.assertRaises(exceptions.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_blank_id(self):
         # Return an error while trying to delete an image with blank Id
         self.assertRaises(exceptions.NotFound, self.client.delete_image, '')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
         image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
         self.assertRaises(exceptions.NotFound, self.client.delete_image,
                           image_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_negative_image_id(self):
         # Return an error while trying to delete an image with negative id
         self.assertRaises(exceptions.NotFound, self.client.delete_image, -1)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_id_is_over_35_character_limit(self):
         # Return an error while trying to delete image with id over limit
         self.assertRaises(exceptions.NotFound, self.client.delete_image,
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index e5e02a7..c7f0b23 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -62,8 +62,8 @@
                 cls.alt_manager = clients.AltManager()
             cls.alt_client = cls.alt_manager.images_client
 
-    @attr(type='negative')
     @testtools.skip("Until Bug #1006725 is fixed")
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_multibyte_character_image_name(self):
         # Return an error if the image name has multi-byte characters
         snapshot_name = rand_name('\xef\xbb\xbf')
@@ -71,7 +71,7 @@
                           self.client.create_image, self.server['id'],
                           snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_invalid_metadata(self):
         # Return an error when creating image with invalid metadata
         snapshot_name = rand_name('test-snap-')
@@ -79,7 +79,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server['id'], snapshot_name, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_metadata_over_limits(self):
         # Return an error when creating image with meta data over 256 chars
         snapshot_name = rand_name('test-snap-')
@@ -87,9 +87,9 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server['id'], snapshot_name, meta)
 
-    @attr(type='negative')
     @testtools.skipUnless(compute.MULTI_USER,
                           'Need multiple users for this test.')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_of_another_tenant(self):
         # Return an error while trying to delete another tenant's image
         self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
@@ -104,9 +104,9 @@
         self.assertRaises(exceptions.NotFound,
                           self.alt_client.delete_image, image_id)
 
-    @attr(type='smoke')
     @testtools.skipUnless(compute.CREATE_IMAGE_ENABLED,
                           'Environment unable to create images.')
+    @attr(type='smoke')
     def test_create_delete_image(self):
 
         # Create a new image
@@ -133,9 +133,9 @@
         self.assertEqual('204', resp['status'])
         self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
 
-    @attr(type='negative')
     @testtools.skipUnless(compute.MULTI_USER,
                           'Need multiple users for this test.')
+    @attr(type=['negative', 'gate'])
     def test_create_image_for_server_in_another_tenant(self):
         # Creating image of another tenant's server should be return error
 
@@ -143,7 +143,7 @@
         self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
                           self.server['id'], snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_second_image_when_first_image_is_being_saved(self):
         # Disallow creating another image when first image is being saved
 
@@ -161,7 +161,7 @@
                           self.server['id'], alt_snapshot_name)
         self.client.wait_for_image_status(image_id, 'ACTIVE')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_image_specify_name_over_256_chars(self):
         # Return an error if snapshot name over 256 characters is passed
 
@@ -169,7 +169,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_image,
                           self.server['id'], snapshot_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_image_that_is_not_yet_active(self):
         # Return an error while trying to delete an image what is creating
 
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 4ba8e99..471a75a 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -78,13 +78,13 @@
         cls.client.delete_image(cls.image3_id)
         super(ListImageFiltersTestJSON, cls).tearDownClass()
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_image_not_existing(self):
         # Check raises a NotFound
         self.assertRaises(exceptions.NotFound, self.client.get_image,
                           "nonexistingimageid")
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_status(self):
         # The list of images should contain only images with the
         # provided status
@@ -95,7 +95,7 @@
         self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
         self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_name(self):
         # List of all images should contain the expected images filtered
         # by name
@@ -106,7 +106,7 @@
         self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
         self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_server_id(self):
         # The images should contain images filtered by server id
         params = {'server': self.server1['id']}
@@ -118,7 +118,7 @@
         self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
         self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_server_ref(self):
         # The list of servers should be filtered by server ref
         server_links = self.server2['links']
@@ -135,7 +135,7 @@
             self.assertTrue(any([i for i in images
                                  if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_type(self):
         # The list of servers should be filtered by image type
         params = {'type': 'snapshot'}
@@ -146,7 +146,7 @@
         self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
         self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_limit_results(self):
         # Verify only the expected number of results are returned
         params = {'limit': '1'}
@@ -155,7 +155,7 @@
         #ref: Question #224349
         self.assertEqual(1, len([x for x in images if 'id' in x]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_filter_by_changes_since(self):
         # Verify only updated images are returned in the detailed list
 
@@ -166,7 +166,7 @@
         found = any([i for i in images if i['id'] == self.image3_id])
         self.assertTrue(found)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_filter_by_status(self):
         # Detailed list of all images should only contain images
         # with the provided status
@@ -177,7 +177,7 @@
         self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
         self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_filter_by_name(self):
         # Detailed list of all images should contain the expected
         # images filtered by name
@@ -188,7 +188,7 @@
         self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
         self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_limit_results(self):
         # Verify only the expected number of results (with full details)
         # are returned
@@ -196,7 +196,7 @@
         resp, images = self.client.list_images_with_detail(params)
         self.assertEqual(1, len(images))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_filter_by_server_ref(self):
         # Detailed list of servers should be filtered by server ref
         server_links = self.server2['links']
@@ -213,7 +213,7 @@
             self.assertTrue(any([i for i in images
                                  if i['id'] == self.image3_id]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_filter_by_type(self):
         # The detailed list of servers should be filtered by image type
         params = {'type': 'snapshot'}
@@ -225,7 +225,7 @@
         self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
         self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_images_with_detail_filter_by_changes_since(self):
         # Verify an update image is returned
 
@@ -235,7 +235,7 @@
         resp, images = self.client.list_images_with_detail(params)
         self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_nonexistant_image(self):
         # Negative test: GET on non existant image should fail
         self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 15a23e5..4c0398e 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -29,7 +29,7 @@
         super(KeyPairsTestJSON, cls).setUpClass()
         cls.client = cls.keypairs_client
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_keypairs_create_list_delete(self):
         # Keypairs created should be available in the response list
         #Create 3 keypairs
@@ -63,7 +63,7 @@
             resp, _ = self.client.delete_keypair(keypair['name'])
             self.assertEqual(202, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_keypair_create_delete(self):
         # Keypair should be created, verified and deleted
         k_name = rand_name('keypair-')
@@ -79,7 +79,7 @@
         resp, _ = self.client.delete_keypair(k_name)
         self.assertEqual(202, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_get_keypair_detail(self):
         # Keypair should be created, Got details by name and deleted
         k_name = rand_name('keypair-')
@@ -102,7 +102,7 @@
             resp, _ = self.client.delete_keypair(k_name)
             self.assertEqual(202, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_keypair_create_with_pub_key(self):
         # Keypair should be created with a given public key
         k_name = rand_name('keypair-')
@@ -126,7 +126,7 @@
         resp, _ = self.client.delete_keypair(k_name)
         self.assertEqual(202, resp.status)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_keypair_create_with_invalid_pub_key(self):
         # Keypair should not be created with a non RSA public key
         k_name = rand_name('keypair-')
@@ -134,14 +134,14 @@
         self.assertRaises(exceptions.BadRequest,
                           self.client.create_keypair, k_name, pub_key)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_keypair_delete_nonexistant_key(self):
         # Non-existant key deletion should throw a proper error
         k_name = rand_name("keypair-non-existant-")
         self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
                           k_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_with_empty_public_key(self):
         # Keypair should not be created with an empty public key
         k_name = rand_name("keypair-")
@@ -149,7 +149,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
                           k_name, pub_key)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
         # Keypair should not be created when public key bits are too long
         k_name = rand_name("keypair-")
@@ -157,7 +157,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
                           k_name, pub_key)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_with_duplicate_name(self):
         # Keypairs with duplicate names should not be created
         k_name = rand_name('keypair-')
@@ -169,20 +169,20 @@
         resp, _ = self.client.delete_keypair(k_name)
         self.assertEqual(202, resp.status)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_with_empty_name_string(self):
         # Keypairs with name being an empty string should not be created
         self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
                           '')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_with_long_keynames(self):
         # Keypairs with name longer than 255 chars should not be created
         k_name = 'keypair-'.ljust(260, '0')
         self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
                           k_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_keypair_invalid_name(self):
         # Keypairs with name being an invalid name should not be created
         k_name = 'key_/.\@:'
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index b2c496b..beae122 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -29,6 +29,7 @@
         cls.client = cls.limits_client
         cls.server_client = cls.servers_client
 
+    @attr(type='gate')
     def test_absLimits_get(self):
         # To check if all limits are present in the response
         resp, absolute_limits = self.client.get_absolute_limits()
@@ -48,7 +49,7 @@
                          "Failed to find element %s in absolute limits list"
                          % ', '.join(ele for ele in missing_elements))
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_max_image_meta_exceed_limit(self):
         #We should not create vm with image meta over maxImageMeta limit
         # Get max limit value
@@ -63,8 +64,9 @@
 
         self.assertRaises(exceptions.OverLimit,
                           self.server_client.create_server,
-                          name='test', meta=meta_data, flavor_ref='84',
-                          image_ref='9e6a2e3b-1601-42a5-985f-c3a2f93a5ec3')
+                          name='test', meta=meta_data,
+                          flavor_ref=self.flavor_ref,
+                          image_ref=self.image_ref)
 
 
 class AbsoluteLimitsTestXML(AbsoluteLimitsTestJSON):
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index ad311d9..15af0f2 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -29,7 +29,7 @@
         super(SecurityGroupRulesTestJSON, cls).setUpClass()
         cls.client = cls.security_groups_client
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_group_rules_create(self):
         # Positive test: Creation of Security Group rule
         # should be successfull
@@ -52,7 +52,7 @@
         self.addCleanup(self.client.delete_security_group_rule, rule['id'])
         self.assertEqual(200, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_group_rules_create_with_optional_arguments(self):
         # Positive test: Creation of Security Group rule
         # with optional arguments
@@ -91,7 +91,7 @@
         self.addCleanup(self.client.delete_security_group_rule, rule['id'])
         self.assertEqual(200, resp.status)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_create_with_invalid_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid Parent group id
@@ -104,7 +104,7 @@
                           self.client.create_security_group_rule,
                           parent_group_id, ip_protocol, from_port, to_port)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_create_with_invalid_ip_protocol(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid ip_protocol
@@ -124,7 +124,7 @@
                           self.client.create_security_group_rule,
                           parent_group_id, ip_protocol, from_port, to_port)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_create_with_invalid_from_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid from_port
@@ -143,7 +143,7 @@
                           self.client.create_security_group_rule,
                           parent_group_id, ip_protocol, from_port, to_port)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_create_with_invalid_to_port(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid from_port
@@ -162,7 +162,7 @@
                           self.client.create_security_group_rule,
                           parent_group_id, ip_protocol, from_port, to_port)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_create_with_invalid_port_range(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid port range.
@@ -181,7 +181,7 @@
                           self.client.create_security_group_rule,
                           secgroup_id, ip_protocol, from_port, to_port)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_rules_delete_with_invalid_id(self):
         # Negative test: Deletion of Security Group rule should be FAIL
         # with invalid rule id
@@ -189,7 +189,7 @@
                           self.client.delete_security_group_rule,
                           rand_name('999'))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_group_rules_list(self):
         # Positive test: Created Security Group rules should be
         # in the list of all rules
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 75afe0d..5f3a37e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -33,7 +33,7 @@
         resp, _ = self.client.delete_security_group(securitygroup_id)
         self.assertEqual(202, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_groups_create_list_delete(self):
         # Positive test:Should return the list of Security Groups
         #Create 3 Security Groups
@@ -61,7 +61,7 @@
 
     #TODO(afazekas): scheduled for delete,
     #test_security_group_create_get_delete covers it
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_group_create_delete(self):
         # Security Group should be created, verified and deleted
         s_name = rand_name('securitygroup-')
@@ -80,7 +80,7 @@
                          "The created Security Group name is "
                          "not equal to the requested name")
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_security_group_create_get_delete(self):
         # Security Group should be created, fetched and deleted
         s_name = rand_name('securitygroup-')
@@ -104,7 +104,7 @@
                          "The fetched Security Group is different "
                          "from the created Group")
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_get_nonexistant_group(self):
         # Negative test:Should not be able to GET the details
         # of nonexistant Security Group
@@ -120,7 +120,7 @@
         self.assertRaises(exceptions.NotFound, self.client.get_security_group,
                           non_exist_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_create_with_invalid_group_name(self):
         # Negative test: Security Group should not be created with group name
         # as an empty string/with white spaces/chars more than 255
@@ -138,7 +138,7 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_create_with_invalid_group_description(self):
         # Negative test:Security Group should not be created with description
         # as an empty string/with white spaces/chars more than 255
@@ -155,7 +155,7 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
         # be created
@@ -172,7 +172,7 @@
                           self.client.create_security_group, s_name,
                           s_description)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_the_default_security_group(self):
         # Negative test:Deletion of the "default" Security Group should Fail
         default_security_group_id = None
@@ -186,7 +186,7 @@
                           self.client.delete_security_group,
                           default_security_group_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_nonexistant_security_group(self):
         # Negative test:Deletion of a nonexistant Security Group should Fail
         security_group_id = []
@@ -201,13 +201,14 @@
         self.assertRaises(exceptions.NotFound,
                           self.client.delete_security_group, non_exist_id)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_security_group_without_passing_id(self):
         # Negative test:Deletion of a Security Group with out passing ID
         # should Fail
         self.assertRaises(exceptions.NotFound,
                           self.client.delete_security_group, '')
 
+    @attr(type='gate')
     def test_server_security_groups(self):
         # Checks that security groups may be added and linked to a server
         # and not deleted if the server is active.
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8977cad..113ac78 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.test import attr
 
 import time
 
@@ -92,6 +93,7 @@
 
         self.assertEqual(sorted(list1), sorted(list2))
 
+    @attr(type='gate')
     def test_create_list_show_delete_interfaces(self):
         server, ifs = self._create_server_get_interfaces()
         interface_count = len(ifs)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index f06a0cc..f605485 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -28,7 +28,6 @@
 from tempest.test import attr
 
 
-@attr(type='smoke')
 class ServersTestJSON(base.BaseComputeTest):
     _interface = 'json'
     run_ssh = tempest.config.TempestConfig().compute.run_ssh
@@ -92,15 +91,15 @@
         found = any([i for i in servers if i['id'] == self.server['id']])
         self.assertTrue(found)
 
-    @attr(type='positive')
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+    @attr(type=['positive', 'gate'])
     def test_can_log_into_created_server(self):
         # Check that the user can authenticate with the generated password
         linux_client = RemoteClient(self.server, self.ssh_user, self.password)
         self.assertTrue(linux_client.can_authenticate())
 
-    @attr(type='positive')
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+    @attr(type=['positive', 'gate'])
     def test_verify_created_server_vcpus(self):
         # Verify that the number of vcpus reported by the instance matches
         # the amount stated by the flavor
@@ -108,15 +107,14 @@
         linux_client = RemoteClient(self.server, self.ssh_user, self.password)
         self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
 
-    @attr(type='positive')
     @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+    @attr(type=['positive', 'gate'])
     def test_host_name_is_same_as_server_name(self):
         # Verify the instance host name is the same as the server name
         linux_client = RemoteClient(self.server, self.ssh_user, self.password)
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
 
-@attr(type='positive')
 class ServersTestManualDisk(ServersTestJSON):
     disk_config = 'MANUAL'
 
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 64fb7d3..a0ed009 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -33,7 +33,7 @@
         super(ServerDiskConfigTestJSON, cls).setUpClass()
         cls.client = cls.os.servers_client
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_rebuild_server_with_manual_disk_config(self):
         # A server should be rebuilt using the manual disk config option
         resp, server = self.create_server(disk_config='AUTO',
@@ -57,7 +57,7 @@
         #Delete the server
         resp, body = self.client.delete_server(server['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_rebuild_server_with_auto_disk_config(self):
         # A server should be rebuilt using the auto disk config option
         resp, server = self.create_server(disk_config='MANUAL',
@@ -81,8 +81,8 @@
         #Delete the server
         resp, body = self.client.delete_server(server['id'])
 
-    @attr(type='positive')
     @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+    @attr(type=['positive', 'gate'])
     def test_resize_server_from_manual_to_auto(self):
         # A server should be resized from manual to auto disk config
         resp, server = self.create_server(disk_config='MANUAL',
@@ -101,8 +101,8 @@
         #Delete the server
         resp, body = self.client.delete_server(server['id'])
 
-    @attr(type='positive')
     @testtools.skipUnless(compute.RESIZE_AVAILABLE, 'Resize not available.')
+    @attr(type=['positive', 'gate'])
     def test_resize_server_from_auto_to_manual(self):
         # A server should be resized from auto to manual disk config
         resp, server = self.create_server(disk_config='AUTO',
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 81fd26c..e5da0a2 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -31,7 +31,7 @@
         cls.request_id = resp['x-compute-request-id']
         cls.server_id = server['id']
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_instance_actions(self):
         # List actions of the provided server
         resp, body = self.client.reboot(self.server_id, 'HARD')
@@ -43,7 +43,7 @@
         self.assertTrue(any([i for i in body if i['action'] == 'create']))
         self.assertTrue(any([i for i in body if i['action'] == 'reboot']))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_get_instance_action(self):
         # Get the action details of the provided server
         resp, body = self.client.get_instance_action(self.server_id,
@@ -52,13 +52,13 @@
         self.assertEqual(self.server_id, body['instance_uuid'])
         self.assertEqual('create', body['action'])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_instance_actions_invalid_server(self):
         # List actions of the invalid server id
         self.assertRaises(exceptions.NotFound,
                           self.client.list_instance_actions, 'server-999')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_instance_action_invalid_request(self):
         # Get the action details of the provided server with invalid request
         self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 637a86f..dbebf5e 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -84,7 +84,7 @@
         super(ListServerFiltersTestJSON, cls).tearDownClass()
 
     @utils.skip_unless_attr('multiple_images', 'Only one image found')
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filter_by_image(self):
         # Filter the list of servers by image
         params = {'image': self.image_ref}
@@ -95,7 +95,7 @@
         self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filter_by_flavor(self):
         # Filter the list of servers by flavor
         params = {'flavor': self.flavor_ref_alt}
@@ -106,7 +106,7 @@
         self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filter_by_server_name(self):
         # Filter the list of servers by server name
         params = {'name': self.s1_name}
@@ -117,7 +117,7 @@
         self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filter_by_server_status(self):
         # Filter the list of servers by server status
         params = {'status': 'active'}
@@ -128,7 +128,7 @@
         self.assertIn(self.s2['id'], map(lambda x: x['id'], servers))
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filter_by_limit(self):
         # Verify only the expected number of servers are returned
         params = {'limit': 1}
@@ -137,7 +137,7 @@
         self.assertEqual(1, len([x for x in servers['servers'] if 'id' in x]))
 
     @utils.skip_unless_attr('multiple_images', 'Only one image found')
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_detailed_filter_by_image(self):
         # Filter the detailed list of servers by image
         params = {'image': self.image_ref}
@@ -148,7 +148,7 @@
         self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_detailed_filter_by_flavor(self):
         # Filter the detailed list of servers by flavor
         params = {'flavor': self.flavor_ref_alt}
@@ -159,7 +159,7 @@
         self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_detailed_filter_by_server_name(self):
         # Filter the detailed list of servers by server name
         params = {'name': self.s1_name}
@@ -170,7 +170,7 @@
         self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_detailed_filter_by_server_status(self):
         # Filter the detailed list of servers by server status
         params = {'status': 'active'}
@@ -182,7 +182,7 @@
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
         self.assertEqual(['ACTIVE'] * 3, [x['status'] for x in servers])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filtered_by_name_wildcard(self):
         # List all servers that contains 'server' in name
         params = {'name': 'server'}
@@ -205,7 +205,7 @@
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
     @testtools.skip('Until Bug #1170718 is resolved.')
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filtered_by_ip(self):
         # Filter servers by ip
         # Here should be listed 1 server
@@ -218,7 +218,7 @@
         self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
         self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_filtered_by_ip_regex(self):
         # Filter servers by regex ip
         # List all servers filtered by part of ip address.
@@ -232,7 +232,7 @@
         self.assertIn(self.s2_name, map(lambda x: x['name'], servers))
         self.assertIn(self.s3_name, map(lambda x: x['name'], servers))
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_servers_detailed_limit_results(self):
         # Verify only the expected number of detailed results are returned
         params = {'limit': 1}
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 2c15c99..0f35ee5 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -20,6 +20,7 @@
 from tempest.api.compute import base
 from tempest import clients
 from tempest import exceptions
+from tempest.test import attr
 
 
 class ListServersNegativeTestJSON(base.BaseComputeTest):
@@ -91,6 +92,7 @@
                                                ignore_error=True)
         cls.deleted_fixtures.append(srv)
 
+    @attr(type='gate')
     def test_list_servers_with_a_deleted_server(self):
         # Verify deleted servers do not show by default in list servers
         # List servers and verify server not returned
@@ -102,6 +104,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], actual)
 
+    @attr(type='gate')
     def test_list_servers_by_non_existing_image(self):
         # Listing servers for a non existing image returns empty list
         non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
@@ -110,6 +113,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
+    @attr(type='gate')
     def test_list_servers_by_non_existing_flavor(self):
         # Listing servers by non existing flavor returns empty list
         non_existing_flavor = 1234
@@ -118,6 +122,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
+    @attr(type='gate')
     def test_list_servers_by_non_existing_server_name(self):
         # Listing servers for a non existent server name returns empty list
         non_existing_name = 'junk_server_1234'
@@ -126,6 +131,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
+    @attr(type='gate')
     def test_list_servers_status_non_existing(self):
         # Return an empty list when invalid status is specified
         non_existing_status = 'BALONEY'
@@ -134,6 +140,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
+    @attr(type='gate')
     def test_list_servers_by_limits(self):
         # List servers by specifying limits
         resp, body = self.client.list_servers({'limit': 1})
@@ -141,22 +148,26 @@
         #when _interface='xml', one element for servers_links in servers
         self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
 
+    @attr(type='gate')
     def test_list_servers_by_limits_greater_than_actual_count(self):
         # List servers by specifying a greater value for limit
         resp, body = self.client.list_servers({'limit': 100})
         self.assertEqual('200', resp['status'])
         self.assertEqual(len(self.existing_fixtures), len(body['servers']))
 
+    @attr(type='gate')
     def test_list_servers_by_limits_pass_string(self):
         # Return an error if a string value is passed for limit
         self.assertRaises(exceptions.BadRequest, self.client.list_servers,
                           {'limit': 'testing'})
 
+    @attr(type='gate')
     def test_list_servers_by_limits_pass_negative_value(self):
         # Return an error if a negative value for limit is passed
         self.assertRaises(exceptions.BadRequest, self.client.list_servers,
                           {'limit': -1})
 
+    @attr(type='gate')
     def test_list_servers_by_changes_since(self):
         # Servers are listed by specifying changes-since date
         changes_since = {'changes-since': '2011-01-01T12:34:00Z'}
@@ -167,11 +178,13 @@
                         len(self.deleted_fixtures))
         self.assertEqual(num_expected, len(body['servers']))
 
+    @attr(type='gate')
     def test_list_servers_by_changes_since_invalid_date(self):
         # Return an error when invalid date format is passed
         self.assertRaises(exceptions.BadRequest, self.client.list_servers,
                           {'changes-since': '2011/01/01'})
 
+    @attr(type='gate')
     def test_list_servers_by_changes_since_future_date(self):
         # Return an empty list when a date in the future is passed
         changes_since = {'changes-since': '2051-01-01T12:34:00Z'}
@@ -179,6 +192,7 @@
         self.assertEqual('200', resp['status'])
         self.assertEqual(0, len(body['servers']))
 
+    @attr(type='gate')
     def test_list_servers_detail_server_is_deleted(self):
         # Server details are not listed for a deleted server
         deleted_ids = [s['id'] for s in self.deleted_fixtures]
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index 476a767..a705dd3 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -60,7 +60,7 @@
 
         return resp, body
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_multiple_create(self):
         resp, body = self._create_multiple_servers(wait_until='ACTIVE',
                                                    min_count=1,
@@ -71,31 +71,31 @@
         self.assertEqual('202', resp['status'])
         self.assertFalse('reservation_id' in body)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_min_count_less_than_one(self):
         invalid_min_count = 0
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           min_count=invalid_min_count)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_min_count_non_integer(self):
         invalid_min_count = 2.5
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           min_count=invalid_min_count)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_max_count_less_than_one(self):
         invalid_max_count = 0
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           max_count=invalid_max_count)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_max_count_non_integer(self):
         invalid_max_count = 2.5
         self.assertRaises(exceptions.BadRequest, self._create_multiple_servers,
                           max_count=invalid_max_count)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_max_count_less_than_min_count(self):
         min_count = 3
         max_count = 2
@@ -103,7 +103,7 @@
                           min_count=min_count,
                           max_count=max_count)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_multiple_create_with_reservation_return(self):
         resp, body = self._create_multiple_servers(wait_until='ACTIVE',
                                                    min_count=1,
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index e8f41ec..228dc45 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -51,9 +51,9 @@
         cls.client = cls.servers_client
         cls.rebuild_servers()
 
-    @attr(type='smoke')
     @testtools.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
                           'Change password not available.')
+    @attr(type='gate')
     def test_change_server_password(self):
         # The server's password should be set to the provided password
         new_password = 'Newpass1234'
@@ -86,8 +86,8 @@
             new_boot_time = linux_client.get_boot_time()
             self.assertGreater(new_boot_time, boot_time)
 
-    @attr(type='smoke')
     @testtools.skip('Until Bug #1014647 is dealt with.')
+    @attr(type='smoke')
     def test_reboot_server_soft(self):
         # The server should be signaled to reboot gracefully
         if self.run_ssh:
@@ -147,8 +147,8 @@
             if int(current_flavor) == self.flavor_ref else self.flavor_ref
         return int(current_flavor), int(new_flavor_ref)
 
-    @attr(type='smoke')
     @testtools.skipIf(not resize_available, 'Resize not available.')
+    @attr(type='smoke')
     def test_resize_server_confirm(self):
         # The server's RAM and disk space should be modified to that of
         # the provided flavor
@@ -166,8 +166,8 @@
         resp, server = self.client.get_server(self.server_id)
         self.assertEqual(new_flavor_ref, int(server['flavor']['id']))
 
-    @attr(type='positive')
     @testtools.skipIf(not resize_available, 'Resize not available.')
+    @attr(type=['positive', 'gate'])
     def test_resize_server_revert(self):
         # The server's RAM and disk space should return to its original
         # values after a resize is reverted
@@ -195,13 +195,13 @@
                 required time (%s s).' % (self.server_id, self.build_timeout)
                 raise exceptions.TimeoutException(message)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_reboot_nonexistent_server_soft(self):
         # Negative Test: The server reboot on non existent server should return
         # an error
         self.assertRaises(exceptions.NotFound, self.client.reboot, 999, 'SOFT')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rebuild_nonexistent_server(self):
         # Negative test: The server rebuild for a non existing server
         # should not be allowed
@@ -217,7 +217,7 @@
                           personality=personality,
                           adminPass='rebuild')
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
         # for a given server_id and number of lines
@@ -230,7 +230,7 @@
             self.assertEqual(lines, 10)
         self.wait_for(get_output)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_console_output_invalid_server_id(self):
         # Negative test: Should not be able to get the console output
         # for an invalid server_id
@@ -238,8 +238,8 @@
                           self.servers_client.get_console_output,
                           '!@#$%^&*()', 10)
 
-    @attr(type='positive')
     @testtools.skip('Until tempest Bug #1014683 is fixed.')
+    @attr(type=['positive', 'gate'])
     def test_get_console_output_server_id_in_reboot_status(self):
         # Positive test:Should be able to GET the console output
         # for a given server_id in reboot status
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 7cb78e9..3cd0a91 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -30,13 +30,13 @@
 
         resp, cls.server = cls.create_server(wait_until='ACTIVE')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_server_addresses_invalid_server_id(self):
         # List addresses request should fail if server id not in system
         self.assertRaises(exceptions.NotFound, self.client.list_addresses,
                           '999')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_server_addresses_by_network_neg(self):
         # List addresses by network should fail if network name not valid
         self.assertRaises(exceptions.NotFound,
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 664a0c0..442d30c 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -42,6 +42,7 @@
         resp, _ = self.client.set_server_metadata(self.server_id, meta)
         self.assertEqual(resp.status, 200)
 
+    @attr(type='gate')
     def test_list_server_metadata(self):
         # All metadata key/value pairs for a server should be returned
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
@@ -51,6 +52,7 @@
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_set_server_metadata(self):
         # The server's metadata should be replaced with the provided values
         #Create a new set of metadata for the server
@@ -64,6 +66,7 @@
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
         self.assertEqual(resp_metadata, req_metadata)
 
+    @attr(type='gate')
     def test_server_create_metadata_key_too_long(self):
         # Attempt to start a server with a meta-data key that is > 255
         # characters
@@ -78,7 +81,7 @@
 
         # no teardown - all creates should fail
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_metadata_key_error(self):
         # Blank key should trigger an error.
         meta = {'': 'data1'}
@@ -86,6 +89,7 @@
                           self.create_server,
                           meta=meta)
 
+    @attr(type='gate')
     def test_update_server_metadata(self):
         # The server's metadata values should be updated to the
         # provided values
@@ -99,6 +103,7 @@
         expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_update_metadata_empty_body(self):
         # The original metadata should not be lost if empty metadata body is
         # passed
@@ -108,12 +113,14 @@
         expected = {'key1': 'value1', 'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_get_server_metadata_item(self):
         # The value for a specic metadata key should be returned
         resp, meta = self.client.get_server_metadata_item(self.server_id,
                                                           'key2')
         self.assertTrue('value2', meta['key2'])
 
+    @attr(type='gate')
     def test_set_server_metadata_item(self):
         # The item's value should be updated to the provided value
         #Update the metadata value
@@ -127,6 +134,7 @@
         expected = {'key1': 'value1', 'key2': 'value2', 'nova': 'alt'}
         self.assertEqual(expected, resp_metadata)
 
+    @attr(type='gate')
     def test_delete_server_metadata_item(self):
         # The metadata value/key pair should be deleted from the server
         resp, meta = self.client.delete_server_metadata_item(self.server_id,
@@ -138,20 +146,20 @@
         expected = {'key2': 'value2'}
         self.assertEqual(expected, resp_metadata)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_nonexistant_server_metadata_item(self):
         # Negative test: GET on nonexistant server should not succeed
         self.assertRaises(exceptions.NotFound,
                           self.client.get_server_metadata_item, 999, 'test2')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_nonexistant_server_metadata(self):
         # Negative test:List metadata on a non existant server should
         # not succeed
         self.assertRaises(exceptions.NotFound,
                           self.client.list_server_metadata, 999)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_server_metadata_item_incorrect_uri_key(self):
         # Raise BadRequest if key in uri does not match
         # the key passed in body.
@@ -161,7 +169,7 @@
                           self.client.set_server_metadata_item,
                           self.server_id, 'key', meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_nonexistant_server_metadata(self):
         # Negative test: Set metadata on a non existant server should not
         # succeed
@@ -169,14 +177,14 @@
         self.assertRaises(exceptions.NotFound,
                           self.client.set_server_metadata, 999, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_nonexistant_server_metadata(self):
         # Negative test: An update should not happen for a nonexistant image
         meta = {'key1': 'value1', 'key2': 'value2'}
         self.assertRaises(exceptions.NotFound,
                           self.client.update_server_metadata, 999, meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_metadata_key_error(self):
         # Blank key should trigger an error.
         meta = {'': 'data1'}
@@ -184,7 +192,7 @@
                           self.client.update_server_metadata,
                           self.server_id, meta=meta)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_nonexistant_server_metadata_item(self):
         # Negative test: Should not be able to delete metadata item from a
         #  nonexistant server
@@ -193,7 +201,7 @@
         self.assertRaises(exceptions.NotFound,
                           self.client.delete_server_metadata_item, 999, 'd')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_set_server_metadata_too_long(self):
         # Raise a 413 OverLimit exception while exceeding metadata items limit
         # for tenant.
@@ -206,7 +214,7 @@
                           self.client.set_server_metadata,
                           self.server_id, req_metadata)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_server_metadata_too_long(self):
         # Raise a 413 OverLimit exception while exceeding metadata items limit
         # for tenant.
@@ -219,7 +227,7 @@
                           self.client.update_server_metadata,
                           self.server_id, req_metadata)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_all_metadata_field_error(self):
         # Raise a bad request error for blank key.
         # set_server_metadata will replace all metadata with new value
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index b0ee9e7..4744bf5 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -31,6 +31,7 @@
         cls.client = cls.servers_client
         cls.user_client = cls.limits_client
 
+    @attr(type='gate')
     def test_personality_files_exceed_limit(self):
         # Server creation should fail if greater than the maximum allowed
         # number of files are injected into the server.
@@ -45,7 +46,7 @@
         self.assertRaises(exceptions.OverLimit, self.create_server,
                           personality=personality)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_can_create_server_with_max_number_personality_files(self):
         # Server should be created successfully if maximum allowed number of
         # files is injected into the server during creation.
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index a7410ec..7e0ac85 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -120,19 +120,19 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rescued_vm_reboot(self):
         self.assertRaises(exceptions.Duplicate, self.servers_client.reboot,
                           self.rescue_id, 'HARD')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rescued_vm_rebuild(self):
         self.assertRaises(exceptions.Duplicate,
                           self.servers_client.rebuild,
                           self.rescue_id,
                           self.image_ref_alt)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rescued_vm_attach_volume(self):
         # Rescue the server
         self.servers_client.rescue_server(self.server_id, self.password)
@@ -146,7 +146,7 @@
                           self.volume_to_attach['id'],
                           device='/dev/%s' % self.device)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rescued_vm_detach_volume(self):
         # Attach the volume to the server
         self.servers_client.attach_volume(self.server_id,
@@ -169,7 +169,7 @@
                           self.server_id,
                           self.volume_to_detach['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_rescued_vm_associate_dissociate_floating_ip(self):
         # Rescue the server
         self.servers_client.rescue_server(
@@ -189,7 +189,7 @@
                                                         self.server_id)
         self.assertEqual(202, resp.status)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_rescued_vm_add_remove_security_group(self):
         # Rescue the server
         self.servers_client.rescue_server(
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index f86ac07..57baae6 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -32,7 +32,7 @@
         self.clear_servers()
         super(ServersTestJSON, self).tearDown()
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_create_server_with_admin_password(self):
         # If an admin password is provided on server creation, the server's
         # root password should be set to that password.
@@ -41,6 +41,7 @@
         # Verify the password is set correctly in the response
         self.assertEqual('testpassword', server['adminPass'])
 
+    @attr(type='gate')
     def test_create_with_existing_server_name(self):
         # Creating a server with a name that already exists is allowed
 
@@ -59,7 +60,7 @@
         name2 = server['name']
         self.assertEqual(name1, name2)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_create_specify_keypair(self):
         # Specify a keypair while creating a server
 
@@ -72,7 +73,7 @@
         resp, server = self.client.get_server(server['id'])
         self.assertEqual(key_name, server['key_name'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_update_server_name(self):
         # The server name should be changed to the the provided value
         resp, server = self.create_server(wait_until='ACTIVE')
@@ -87,7 +88,7 @@
         resp, server = self.client.get_server(server['id'])
         self.assertEqual('newname', server['name'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_update_access_server_address(self):
         # The server's access addresses should reflect the provided values
         resp, server = self.create_server(wait_until='ACTIVE')
@@ -104,6 +105,7 @@
         self.assertEqual('1.1.1.1', server['accessIPv4'])
         self.assertEqual('::babe:202:202', server['accessIPv6'])
 
+    @attr(type='gate')
     def test_delete_server_while_in_building_state(self):
         # Delete a server while it's VM state is Building
         resp, server = self.create_server(wait_until='BUILD')
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index b369179..5f53080 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -35,7 +35,7 @@
         cls.alt_os = clients.AltManager()
         cls.alt_client = cls.alt_os.servers_client
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_server_name_blank(self):
         # Create a server with name parameter empty
 
@@ -43,7 +43,7 @@
                           self.create_server,
                           name='')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_personality_file_contents_not_encoded(self):
         # Use an unencoded file when creating a server with personality
 
@@ -55,7 +55,7 @@
                           self.create_server,
                           personality=person)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_with_invalid_image(self):
         # Create a server with an unknown image
 
@@ -63,7 +63,7 @@
                           self.create_server,
                           image_id=-1)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_with_invalid_flavor(self):
         # Create a server with an unknown flavor
 
@@ -71,7 +71,7 @@
                           self.create_server,
                           flavor=-1,)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_invalid_access_ip_v4_address(self):
         # An access IPv4 address must match a valid address pattern
 
@@ -79,7 +79,7 @@
         self.assertRaises(exceptions.BadRequest,
                           self.create_server, accessIPv4=IPv4)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_invalid_ip_v6_address(self):
         # An access IPv6 address must match a valid address pattern
 
@@ -88,7 +88,7 @@
         self.assertRaises(exceptions.BadRequest,
                           self.create_server, accessIPv6=IPv6)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_reboot_deleted_server(self):
         # Reboot a deleted server
         resp, server = self.create_server()
@@ -98,7 +98,7 @@
         self.assertRaises(exceptions.NotFound, self.client.reboot,
                           self.server_id, 'SOFT')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_rebuild_deleted_server(self):
         # Rebuild a deleted server
 
@@ -111,7 +111,7 @@
                           self.client.rebuild,
                           self.server_id, self.image_ref_alt)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_numeric_server_name(self):
         # Create a server with a numeric name
         if self.__class__._interface == "xml":
@@ -122,7 +122,7 @@
                           self.create_server,
                           name=server_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_server_name_length_exceeds_256(self):
         # Create a server with name length exceeding 256 characters
 
@@ -131,7 +131,7 @@
                           self.create_server,
                           name=server_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_with_invalid_network_uuid(self):
         # Pass invalid network uuid while creating a server
 
@@ -141,7 +141,7 @@
                           self.create_server,
                           networks=networks)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_with_non_existant_keypair(self):
         # Pass a non existant keypair while creating a server
 
@@ -150,7 +150,7 @@
                           self.create_server,
                           key_name=key_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_server_metadata_exceeds_length_limit(self):
         # Pass really long metadata while creating a server
 
@@ -159,7 +159,7 @@
                           self.create_server,
                           meta=metadata)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_name_of_non_existent_server(self):
         # Update name of a non-existent server
 
@@ -169,7 +169,7 @@
         self.assertRaises(exceptions.NotFound, self.client.update_server,
                           server_name, name=new_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_server_set_empty_name(self):
         # Update name of the server to an empty string
 
@@ -179,7 +179,7 @@
         self.assertRaises(exceptions.BadRequest, self.client.update_server,
                           server_name, name=new_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_server_of_another_tenant(self):
         # Update name of a server that belongs to another tenant
 
@@ -189,7 +189,7 @@
                           self.alt_client.update_server, server['id'],
                           name=new_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_update_server_name_length_exceeds_256(self):
         # Update name of server exceed the name length limit
 
@@ -200,14 +200,14 @@
                           server['id'],
                           name=new_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_non_existent_server(self):
         # Delete a non existent server
 
         self.assertRaises(exceptions.NotFound, self.client.delete_server,
                           '999erra43')
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_a_server_of_another_tenant(self):
         # Delete a server that belongs to another tenant
         try:
@@ -218,20 +218,20 @@
         finally:
             self.client.delete_server(server['id'])
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_server_pass_negative_id(self):
         # Pass an invalid string parameter to delete server
 
         self.assertRaises(exceptions.NotFound, self.client.delete_server, -1)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_server_pass_id_exceeding_length_limit(self):
         # Pass a server ID that exceeds length limit to delete server
 
         self.assertRaises(exceptions.NotFound, self.client.delete_server,
                           sys.maxint + 1)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_create_with_nonexistent_security_group(self):
         # Create a server with a nonexistent security group
 
@@ -240,7 +240,7 @@
                           self.create_server,
                           security_groups=security_groups)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_get_non_existent_server(self):
         # Get a non existent server details
 
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index bdec1cb..47d6169 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -23,7 +23,6 @@
 from tempest.test import attr
 
 
-@attr(type='smoke')
 class VirtualInterfacesTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
@@ -34,7 +33,7 @@
         resp, server = cls.create_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_virtual_interfaces(self):
         # Positive test:Should be able to GET the virtual interfaces list
         # for a given server_id
@@ -49,7 +48,7 @@
             self.assertTrue(netaddr.valid_mac(mac_address),
                             "Invalid mac address detected.")
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_virtual_interfaces_invalid_server_id(self):
         # Negative test: Should not be able to GET virtual interfaces
         # for an invalid server_id
@@ -59,6 +58,5 @@
                           invalid_server_id)
 
 
-@attr(type='smoke')
 class VirtualInterfacesTestXML(VirtualInterfacesTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index b0d6a0b..1a65a20 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -21,6 +21,7 @@
 from tempest.common.utils.data_utils import parse_image_id
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.test import attr
 
 
 class AuthorizationTestJSON(base.BaseComputeTest):
@@ -89,26 +90,31 @@
             cls.security_client.delete_security_group(cls.security_group['id'])
         super(AuthorizationTestJSON, cls).tearDownClass()
 
+    @attr(type='gate')
     def test_get_server_for_alt_account_fails(self):
         # A GET request for a server on another user's account should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.get_server,
                           self.server['id'])
 
+    @attr(type='gate')
     def test_delete_server_for_alt_account_fails(self):
         # A DELETE request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.delete_server,
                           self.server['id'])
 
+    @attr(type='gate')
     def test_update_server_for_alt_account_fails(self):
         # An update server request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.update_server,
                           self.server['id'], name='test')
 
+    @attr(type='gate')
     def test_list_server_addresses_for_alt_account_fails(self):
         # A list addresses request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.list_addresses,
                           self.server['id'])
 
+    @attr(type='gate')
     def test_list_server_addresses_by_network_for_alt_account_fails(self):
         # A list address/network request for another user's server should fail
         server_id = self.server['id']
@@ -116,6 +122,7 @@
                           self.alt_client.list_addresses_by_network, server_id,
                           'public')
 
+    @attr(type='gate')
     def test_list_servers_with_alternate_tenant(self):
         # A list on servers from one tenant should not
         # show on alternate tenant
@@ -125,37 +132,44 @@
         alt_server_ids = [s['id'] for s in body['servers']]
         self.assertNotIn(self.server['id'], alt_server_ids)
 
+    @attr(type='gate')
     def test_change_password_for_alt_account_fails(self):
         # A change password request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.change_password,
                           self.server['id'], 'newpass')
 
+    @attr(type='gate')
     def test_reboot_server_for_alt_account_fails(self):
         # A reboot request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.reboot,
                           self.server['id'], 'HARD')
 
+    @attr(type='gate')
     def test_rebuild_server_for_alt_account_fails(self):
         # A rebuild request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.rebuild,
                           self.server['id'], self.image_ref_alt)
 
+    @attr(type='gate')
     def test_resize_server_for_alt_account_fails(self):
         # A resize request for another user's server should fail
         self.assertRaises(exceptions.NotFound, self.alt_client.resize,
                           self.server['id'], self.flavor_ref_alt)
 
+    @attr(type='gate')
     def test_create_image_for_alt_account_fails(self):
         # A create image request for another user's server should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_images_client.create_image,
                           self.server['id'], 'testImage')
 
+    @attr(type='gate')
     def test_create_server_with_unauthorized_image(self):
         # Server creation with another user's image should fail
         self.assertRaises(exceptions.BadRequest, self.alt_client.create_server,
                           'test', self.image['id'], self.flavor_ref)
 
+    @attr(type='gate')
     def test_create_server_fails_when_tenant_incorrect(self):
         # A create server request should fail if the tenant id does not match
         # the current user
@@ -170,6 +184,7 @@
             # Reset the base_url...
             self.alt_client.base_url = saved_base_url
 
+    @attr(type='gate')
     def test_create_keypair_in_analt_user_tenant(self):
         # A create keypair request should fail if the tenant id does not match
         # the current user
@@ -192,29 +207,34 @@
                 self.fail("Create keypair request should not happen "
                           "if the tenant id does not match the current user")
 
+    @attr(type='gate')
     def test_get_keypair_of_alt_account_fails(self):
         # A GET request for another user's keypair should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_keypairs_client.get_keypair,
                           self.keypairname)
 
+    @attr(type='gate')
     def test_delete_keypair_of_alt_account_fails(self):
         # A DELETE request for another user's keypair should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_keypairs_client.delete_keypair,
                           self.keypairname)
 
+    @attr(type='gate')
     def test_get_image_for_alt_account_fails(self):
         # A GET request for an image on another user's account should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_images_client.get_image, self.image['id'])
 
+    @attr(type='gate')
     def test_delete_image_for_alt_account_fails(self):
         # A DELETE request for another user's image should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_images_client.delete_image,
                           self.image['id'])
 
+    @attr(type='gate')
     def test_create_security_group_in_analt_user_tenant(self):
         # A create security group request should fail if the tenant id does not
         # match the current user
@@ -238,18 +258,21 @@
                 self.fail("Create Security Group request should not happen if"
                           "the tenant id does not match the current user")
 
+    @attr(type='gate')
     def test_get_security_group_of_alt_account_fails(self):
         # A GET request for another user's security group should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_security_client.get_security_group,
                           self.security_group['id'])
 
+    @attr(type='gate')
     def test_delete_security_group_of_alt_account_fails(self):
         # A DELETE request for another user's security group should fail
         self.assertRaises(exceptions.NotFound,
                           self.alt_security_client.delete_security_group,
                           self.security_group['id'])
 
+    @attr(type='gate')
     def test_create_security_group_rule_in_analt_user_tenant(self):
         # A create security group rule request should fail if the tenant id
         # does not match the current user
@@ -278,6 +301,7 @@
                           "happen if the tenant id does not match the"
                           " current user")
 
+    @attr(type='gate')
     def test_delete_security_group_rule_of_alt_account_fails(self):
         # A DELETE request for another user's security group rule
         # should fail
@@ -285,6 +309,7 @@
                           self.alt_security_client.delete_security_group_rule,
                           self.rule['id'])
 
+    @attr(type='gate')
     def test_set_metadata_of_alt_account_server_fails(self):
         # A set metadata for another user's server should fail
         req_metadata = {'meta1': 'data1', 'meta2': 'data2'}
@@ -293,6 +318,7 @@
                           self.server['id'],
                           req_metadata)
 
+    @attr(type='gate')
     def test_set_metadata_of_alt_account_image_fails(self):
         # A set metadata for another user's image should fail
         req_metadata = {'meta1': 'value1', 'meta2': 'value2'}
@@ -300,6 +326,7 @@
                           self.alt_images_client.set_image_metadata,
                           self.image['id'], req_metadata)
 
+    @attr(type='gate')
     def test_get_metadata_of_alt_account_server_fails(self):
         # A get metadata for another user's server should fail
         req_metadata = {'meta1': 'data1'}
@@ -310,6 +337,7 @@
                           self.alt_client.get_server_metadata_item,
                           self.server['id'], 'meta1')
 
+    @attr(type='gate')
     def test_get_metadata_of_alt_account_image_fails(self):
         # A get metadata for another user's image should fail
         req_metadata = {'meta1': 'value1'}
@@ -321,6 +349,7 @@
                           self.alt_images_client.get_image_metadata_item,
                           self.image['id'], 'meta1')
 
+    @attr(type='gate')
     def test_delete_metadata_of_alt_account_server_fails(self):
         # A delete metadata for another user's server should fail
         req_metadata = {'meta1': 'data1'}
@@ -331,6 +360,7 @@
                           self.alt_client.delete_server_metadata_item,
                           self.server['id'], 'meta1')
 
+    @attr(type='gate')
     def test_delete_metadata_of_alt_account_image_fails(self):
         # A delete metadata for another user's image should fail
         req_metadata = {'meta1': 'data1'}
@@ -342,6 +372,7 @@
                           self.alt_images_client.delete_image_metadata_item,
                           self.image['id'], 'meta1')
 
+    @attr(type='gate')
     def test_get_console_output_of_alt_account_server_fails(self):
         # A Get Console Output for another user's server should fail
         self.assertRaises(exceptions.NotFound,
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 5bc4a30..4893e3d 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -23,7 +23,7 @@
 class ExtensionsTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_extensions(self):
         # List of all extensions
         resp, extensions = self.extensions_client.list_extensions()
diff --git a/tempest/api/compute/test_live_block_migration.py b/tempest/api/compute/test_live_block_migration.py
index 15ca129..84fd653 100644
--- a/tempest/api/compute/test_live_block_migration.py
+++ b/tempest/api/compute/test_live_block_migration.py
@@ -97,9 +97,9 @@
             self.volumes_client.wait_for_volume_status(volume_id, 'available')
         self.volumes_client.delete_volume(volume_id)
 
-    @attr(type='positive')
     @testtools.skipIf(not CONF.compute.live_migration_available,
                       'Live migration not available')
+    @attr(type='gate')
     def test_live_block_migration(self):
         # Live block migrate an instance to another host
         if len(self._get_compute_hostnames()) < 2:
@@ -114,6 +114,7 @@
 
     @testtools.skipIf(not CONF.compute.live_migration_available,
                       'Live migration not available')
+    @attr(type='gate')
     def test_invalid_host_for_migration(self):
         # Migrating to an invalid host should not change the status
         server_id = self._get_an_active_server()
@@ -123,12 +124,12 @@
                           server_id, target_host)
         self.assertEquals('ACTIVE', self._get_server_status(server_id))
 
-    @attr(type='positive')
     @testtools.skipIf(not CONF.compute.live_migration_available or
                       not CONF.compute.use_block_migration_for_live_migration,
                       'Block Live migration not available')
     @testtools.skipIf(not CONF.compute.block_migrate_supports_cinder_iscsi,
                       'Block Live migration not configured for iSCSI')
+    @attr(type='gate')
     def test_iscsi_volume(self):
         # Live block migrate an instance to another host
         if len(self._get_compute_hostnames()) < 2:
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index a086c17..5d45251 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -69,8 +69,8 @@
 
         self.attached = True
 
-    @attr(type='positive')
     @testtools.skipIf(not run_ssh, 'SSH required for this test')
+    @attr(type=['positive', 'gate'])
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 4646ae2..7d3c075 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -75,7 +75,7 @@
                 #Checking if the deleted Volume still exists
                 self.client.wait_for_resource_deletion(volume['id'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_volume_get_metadata_none(self):
         # CREATE, GET empty metadata dict
         try:
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index b1ef2fd..d52349e 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
 
 
 class VolumesTestJSON(base.BaseComputeTest):
@@ -74,6 +75,7 @@
             cls.client.wait_for_resource_deletion(volume['id'])
         super(VolumesTestJSON, cls).tearDownClass()
 
+    @attr(type='gate')
     def test_volume_list(self):
         # Should return the list of Volumes
         # Fetch all Volumes
@@ -89,6 +91,7 @@
                          ', '.join(m_vol['displayName']
                                    for m_vol in missing_volumes))
 
+    @attr(type='gate')
     def test_volume_list_with_details(self):
         # Should return the list of Volumes with details
         #Fetch all Volumes
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index a4ecd0d..de214fc 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -18,6 +18,7 @@
 from tempest.api.compute import base
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.test import attr
 
 
 class VolumesNegativeTest(base.BaseComputeTest):
@@ -28,6 +29,7 @@
         super(VolumesNegativeTest, cls).setUpClass()
         cls.client = cls.volumes_extensions_client
 
+    @attr(type='gate')
     def test_volume_get_nonexistant_volume_id(self):
         # Negative: Should not be able to get details of 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):
         # Negative: Should not be able to delete 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):
         # Negative: 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):
         # Negative: 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):
         # Negative: Should not be able to create volume with size zero
         v_name = rand_name('Volume-')
@@ -81,21 +87,25 @@
         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):
         # Negative: 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):
         # Negative: 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):
         # Negative: 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):
         # Negative: Should not be able to delete volume when empty ID is passed
         self.assertRaises(exceptions.NotFound, self.client.delete_volume, '')
diff --git a/tempest/api/identity/__init__.py b/tempest/api/identity/__init__.py
index e5fdc1b..718aa15 100644
--- a/tempest/api/identity/__init__.py
+++ b/tempest/api/identity/__init__.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
+from tempest.common import log as logging
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/api/identity/admin/test_tenants.py b/tempest/api/identity/admin/test_tenants.py
index 815cfbe..e8625db 100644
--- a/tempest/api/identity/admin/test_tenants.py
+++ b/tempest/api/identity/admin/test_tenants.py
@@ -88,6 +88,7 @@
         self.assertRaises(exceptions.NotFound, self.client.delete_tenant,
                           'junk_tenant_123456abc')
 
+    @attr(type='gate')
     def test_tenant_create_with_description(self):
         # Create tenant with a description
         tenant_name = rand_name('tenant-')
@@ -109,6 +110,7 @@
         self.client.delete_tenant(tenant_id)
         self.data.tenants.remove(tenant)
 
+    @attr(type='gate')
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
         tenant_name = rand_name('tenant-')
@@ -126,6 +128,7 @@
         self.client.delete_tenant(tenant_id)
         self.data.tenants.remove(tenant)
 
+    @attr(type='gate')
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
         tenant_name = rand_name('tenant-')
@@ -182,12 +185,14 @@
         self.assertRaises(exceptions.BadRequest, self.client.create_tenant,
                           name='')
 
+    @attr(type='gate')
     def test_create_tenants_name_length_over_64(self):
         # Tenant name length should not be greater than 64 characters
         tenant_name = 'a' * 65
         self.assertRaises(exceptions.BadRequest, self.client.create_tenant,
                           tenant_name)
 
+    @attr(type='gate')
     def test_tenant_update_name(self):
         # Update name attribute of a tenant
         t_name1 = rand_name('tenant-')
@@ -215,6 +220,7 @@
         self.client.delete_tenant(t_id)
         self.data.tenants.remove(tenant)
 
+    @attr(type='gate')
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
         t_name = rand_name('tenant-')
@@ -243,6 +249,7 @@
         self.client.delete_tenant(t_id)
         self.data.tenants.remove(tenant)
 
+    @attr(type='gate')
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
         t_name = rand_name('tenant-')
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index abc311b..bbfadba 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -33,7 +33,7 @@
     alt_tenant = rand_name('test_tenant_')
     alt_description = rand_name('desc_')
 
-    @attr(type=['smoke', 'gate'])
+    @attr(type=['smoke'])
     def test_create_user(self):
         # Create a user
         self.data.setup_test_tenant()
@@ -77,8 +77,8 @@
                           self.data.test_user, self.data.test_password,
                           self.data.tenant['id'], self.data.test_email)
 
-    @attr(type='gate')
     @testtools.skip("Until Bug #999084 is fixed")
+    @attr(type='gate')
     def test_create_user_with_empty_password(self):
         # User with an empty password should not be created
         self.data.setup_test_tenant()
@@ -86,8 +86,8 @@
                           self.alt_user, '', self.data.tenant['id'],
                           self.alt_email)
 
-    @attr(type='gate')
     @testtools.skip("Until Bug #999084 is fixed")
+    @attr(type='gate')
     def test_create_user_with_long_password(self):
         # User having password exceeding max length should not be created
         self.data.setup_test_tenant()
@@ -95,8 +95,8 @@
                           self.alt_user, 'a' * 65, self.data.tenant['id'],
                           self.alt_email)
 
-    @attr(type='gate')
     @testtools.skip("Until Bug #999084 is fixed")
+    @attr(type='gate')
     def test_create_user_with_invalid_email_format(self):
         # Email format should be validated while creating a user
         self.data.setup_test_tenant()
@@ -125,7 +125,7 @@
         # Unset the token to allow further tests to generate a new token
         self.client.clear_auth()
 
-    @attr(type=['smoke', 'gate'])
+    @attr(type=['smoke'])
     def test_delete_user(self):
         # Delete a user
         self.data.setup_test_tenant()
@@ -150,7 +150,7 @@
         self.assertRaises(exceptions.NotFound, self.client.delete_user,
                           'junk12345123')
 
-    @attr(type=['smoke', 'gate'])
+    @attr(type=['smoke'])
     def test_user_authentication(self):
         # Valid user's token is authenticated
         self.data.setup_test_user()
@@ -225,7 +225,7 @@
         self.assertEqual('200', resp['status'])
         self.client.clear_auth()
 
-    @attr(type=['smoke', 'gate'])
+    @attr(type=['smoke'])
     def test_get_users(self):
         # Get a list of users and find the test user
         self.data.setup_test_user()
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index f12e957..e62d84b 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -14,9 +14,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
 from tempest import clients
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 import tempest.test
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index d4e0645..78c09e0 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -54,7 +54,7 @@
         cls.subnet = cls.create_subnet(cls.network)
         cls.cidr = cls.subnet['cidr']
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_create_delete_network_subnet(self):
         # Creates a network
         name = rand_name('network-')
@@ -83,7 +83,7 @@
         resp, body = self.client.delete_network(network['id'])
         self.assertEqual('204', resp['status'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_show_network(self):
         # Verifies the details of a network
         resp, body = self.client.show_network(self.network['id'])
@@ -92,7 +92,7 @@
         self.assertEqual(self.network['id'], network['id'])
         self.assertEqual(self.name, network['name'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_networks(self):
         # Verify the network exists in the list of all networks
         resp, body = self.client.list_networks()
@@ -100,7 +100,7 @@
         found = any(n for n in networks if n['id'] == self.network['id'])
         self.assertTrue(found)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_show_subnet(self):
         # Verifies the details of a subnet
         resp, body = self.client.show_subnet(self.subnet['id'])
@@ -109,7 +109,7 @@
         self.assertEqual(self.subnet['id'], subnet['id'])
         self.assertEqual(self.cidr, subnet['cidr'])
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_list_subnets(self):
         # Verify the subnet exists in the list of all subnets
         resp, body = self.client.list_subnets()
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 745de22..bf013ec 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -47,3 +47,31 @@
         except exceptions.EndpointNotFound:
             skip_msg = "No OpenStack Object Storage API endpoint"
             raise cls.skipException(skip_msg)
+
+    @classmethod
+    def delete_containers(cls, containers, container_client=None,
+                          object_client=None):
+        """Remove given containers and all objects in them.
+
+        The containers should be visible from the container_client given.
+        Will not throw any error if the containers don't exist.
+
+        :param containers: list of container names to remove
+        :param container_client: if None, use cls.container_client, this means
+            that the default testing user will be used (see 'username' in
+            'etc/tempest.conf')
+        :param object_client: if None, use cls.object_client
+        """
+        if container_client is None:
+            container_client = cls.container_client
+        if object_client is None:
+            object_client = cls.object_client
+        for cont in containers:
+            try:
+                objlist = container_client.list_all_container_objects(cont)
+                # delete every object in the container
+                for obj in objlist:
+                    object_client.delete_object(cont, obj['name'])
+                container_client.delete_container(cont)
+            except exceptions.NotFound:
+                pass
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index ddedfc6..d7b87d1 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -75,7 +75,7 @@
         resp, metadata = self.account_client.list_account_metadata()
         self.assertNotIn('x-account-meta-test-account-meta', resp)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_list_containers_with_non_authorized_user(self):
         # list containers using non-authorized user
 
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 0b4b57b..70f704f 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -29,15 +29,7 @@
 
     @classmethod
     def tearDownClass(cls):
-        for container in cls.containers:
-            objlist = \
-                cls.container_client.list_all_container_objects(container)
-            # delete every object in the container
-            for obj in objlist:
-                resp, _ = \
-                    cls.object_client.delete_object(container, obj['name'])
-            # delete the container
-            resp, _ = cls.container_client.delete_container(container)
+        cls.delete_containers(cls.containers)
 
     @attr(type='smoke')
     def test_create_container(self):
@@ -125,8 +117,3 @@
         self.assertEqual(resp['status'], '204')
         self.assertNotIn('x-container-meta-name', resp)
         self.assertNotIn('x-container-meta-description', resp)
-
-        # delete container
-        resp, _ = self.container_client.delete_container(container_name)
-        self.assertEqual(resp['status'], '204')
-        self.containers.remove(container_name)
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 3243990..a1c1c06 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -48,17 +48,11 @@
 
     @classmethod
     def tearDownClass(cls):
-        for cont_name, client in cls.clients.items():
-            objlist = client[0].list_all_container_objects(cont_name)
-            # delete every object in the container
-            if objlist:
-                for obj in objlist:
-                    resp, _ = client[1].delete_object(cont_name, obj['name'])
-            # delete the container
-            resp, _ = client[0].delete_container(cont_name)
+        for client in cls.clients.values():
+            cls.delete_containers(cls.containers, client[0], client[1])
 
     @testtools.skip('Until Bug #1093743 is resolved.')
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_container_synchronization(self):
         # container to container synchronization
         # to allow/accept sync requests to/from other accounts
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 1fe47ea..aaa2c64 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -23,6 +23,7 @@
 from tempest.common.utils.data_utils import arbitrary_string
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
+from tempest.test import attr
 
 
 class ObjectExpiryTest(base.BaseObjectTest):
@@ -39,17 +40,10 @@
         But delete action for the expired object is raising
         NotFound exception and also non empty container cannot be deleted.
         """
-        objlist = \
-            cls.container_client.list_all_container_objects(cls.container_name)
-        # delete every object in the container
-        if objlist:
-            for obj in objlist:
-                resp, _ = cls.object_client.delete_object(cls.container_name,
-                                                          obj['name'])
-        # delete the container
-        resp, _ = cls.container_client.delete_container(cls.container_name)
+        cls.delete_containers([cls.container_name])
 
     @testtools.skip('Until Bug #1069849 is resolved.')
+    @attr(type='gate')
     def test_get_object_after_expiry_time(self):
         #TODO(harika-vakadi): similar test case has to be created for
         # "X-Delete-At", after this test case works.
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 4d7ee74..72a1d51 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -32,6 +32,7 @@
         super(ObjectTest, cls).setUpClass()
         cls.container_name = rand_name(name='TestContainer')
         cls.container_client.create_container(cls.container_name)
+        cls.containers = [cls.container_name]
 
         cls.data.setup_test_user()
         resp, body = cls.token_client.auth(cls.data.test_user,
@@ -44,14 +45,7 @@
 
     @classmethod
     def tearDownClass(cls):
-        objlist = cls.container_client.list_all_container_objects(
-            cls.container_name)
-        # delete every object in the container
-        for obj in objlist:
-            resp, _ = cls.object_client.delete_object(cls.container_name,
-                                                      obj['name'])
-        # delete the container
-        resp, _ = cls.container_client.delete_container(cls.container_name)
+        cls.delete_containers(cls.containers)
         # delete the user setup created
         cls.data.teardown_all()
 
@@ -198,9 +192,11 @@
         # create a container to use as  asource container
         src_container_name = rand_name(name='TestSourceContainer')
         self.container_client.create_container(src_container_name)
+        self.containers.append(src_container_name)
         # create a container to use as a destination container
         dst_container_name = rand_name(name='TestDestinationContainer')
         self.container_client.create_container(dst_container_name)
+        self.containers.append(dst_container_name)
         # create object in source container
         object_name = rand_name(name='Object')
         data = arbitrary_string(size=len(object_name) * 2,
@@ -233,19 +229,8 @@
         except Exception as e:
             self.fail("Got exception :%s ; while copying"
                       " object across containers" % e)
-        finally:
-            # delete objects from respective containers
-            resp, _ = self.object_client.delete_object(dst_container_name,
-                                                       object_name)
-            resp, _ = self.object_client.delete_object(src_container_name,
-                                                       object_name)
-            # delete containers created in this method
-            resp, _ = self.container_client.delete_container(
-                src_container_name)
-            resp, _ = self.container_client.delete_container(
-                dst_container_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_write_object_without_using_creds(self):
         # trying to create object with empty headers
         object_name = rand_name(name='Object')
@@ -258,7 +243,7 @@
                           self.container_name, object_name, data,
                           metadata=obj_headers)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_object_without_using_creds(self):
         # create object
         object_name = rand_name(name='Object')
@@ -271,7 +256,7 @@
                           self.custom_object_client.delete_object,
                           self.container_name, object_name)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_write_object_with_non_authorized_user(self):
         # attempt to upload another file using non-authorized user
         object_name = rand_name(name='Object')
@@ -284,7 +269,7 @@
                           self.container_name, object_name, data,
                           metadata=self.custom_headers)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_read_object_with_non_authorized_user(self):
         object_name = rand_name(name='Object')
         data = arbitrary_string(size=len(object_name) * 5,
@@ -299,7 +284,7 @@
                           self.container_name, object_name,
                           metadata=self.custom_headers)
 
-    @attr(type='negative')
+    @attr(type=['negative', 'gate'])
     def test_delete_object_with_non_authorized_user(self):
         object_name = rand_name(name='Object')
         data = arbitrary_string(size=len(object_name) * 5,
@@ -314,7 +299,7 @@
                           metadata=self.custom_headers)
 
     @testtools.skip('Until Bug #1097137 is resolved.')
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_get_object_using_temp_url(self):
         # access object using temporary URL within expiration time
 
@@ -352,7 +337,7 @@
                 resp, _ = self.account_client.list_account_metadata()
                 self.assertNotIn('x-account-meta-temp-url-key', resp)
 
-    @attr(type='positive')
+    @attr(type=['positive', 'gate'])
     def test_object_upload_in_segments(self):
         # create object
         object_name = rand_name(name='LObject')
@@ -389,14 +374,7 @@
         self.container_client.create_container(self.container_name)
 
     def tearDown(self):
-        objlist = self.container_client.list_all_container_objects(
-            self.container_name)
-        # delete every object in the container
-        for obj in objlist:
-            resp, _ = self.object_client.delete_object(
-                self.container_name, obj['name'])
-        # delete the container
-        resp, _ = self.container_client.delete_container(self.container_name)
+        self.delete_containers([self.container_name])
         super(PublicObjectTest, self).tearDown()
 
     @attr(type='smoke')
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index d449c0a..cda3e4f 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -28,15 +28,7 @@
 
     @classmethod
     def tearDownClass(cls):
-        for container in cls.containers:
-            objlist = \
-                cls.container_client.list_all_container_objects(container)
-            # delete every object in the container
-            for obj in objlist:
-                resp, _ = \
-                    cls.object_client.delete_object(container, obj['name'])
-            # delete the container
-            resp, _ = cls.container_client.delete_container(container)
+        cls.delete_containers(cls.containers)
 
     def assertContainer(self, container, count, byte, versioned):
         resp, _ = self.container_client.list_container_metadata(container)
@@ -94,10 +86,3 @@
                              vers_container_name)
         self.assertContainer(vers_container_name, '0', '0',
                              'Missing Header')
-        # delete containers
-        resp, _ = self.container_client.delete_container(base_container_name)
-        self.assertEqual(resp['status'], '204')
-        self.containers.remove(base_container_name)
-        resp, _ = self.container_client.delete_container(vers_container_name)
-        self.assertEqual(resp['status'], '204')
-        self.containers.remove(vers_container_name)
diff --git a/tempest/api/orchestration/__init__.py b/tempest/api/orchestration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/orchestration/__init__.py
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
new file mode 100644
index 0000000..544558e
--- /dev/null
+++ b/tempest/api/orchestration/base.py
@@ -0,0 +1,110 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 logging
+import time
+
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+import tempest.test
+
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseOrchestrationTest(tempest.test.BaseTestCase):
+    """Base test case class for all Orchestration API tests."""
+
+    @classmethod
+    def setUpClass(cls):
+
+        os = clients.OrchestrationManager()
+        cls.orchestration_cfg = os.config.orchestration
+        if not cls.orchestration_cfg.heat_available:
+            raise cls.skipException("Heat support is required")
+
+        cls.os = os
+        cls.orchestration_client = os.orchestration_client
+        cls.keypairs_client = os.keypairs_client
+        cls.stacks = []
+
+    @classmethod
+    def _get_identity_admin_client(cls):
+        """
+        Returns an instance of the Identity Admin API client
+        """
+        os = clients.AdminManager(interface=cls._interface)
+        admin_client = os.identity_client
+        return admin_client
+
+    @classmethod
+    def _get_client_args(cls):
+
+        return (
+            cls.config,
+            cls.config.identity.admin_username,
+            cls.config.identity.admin_password,
+            cls.config.identity.uri
+        )
+
+    def create_stack(self, stack_name, template_data, parameters={}):
+        resp, body = self.client.create_stack(
+            stack_name,
+            template=template_data,
+            parameters=parameters)
+        self.assertEqual('201', resp['status'])
+        stack_id = resp['location'].split('/')[-1]
+        stack_identifier = '%s/%s' % (stack_name, stack_id)
+        self.stacks.append(stack_identifier)
+        return stack_identifier
+
+    @classmethod
+    def clear_stacks(cls):
+        for stack_identifier in cls.stacks:
+            try:
+                cls.orchestration_client.delete_stack(stack_identifier)
+            except Exception:
+                pass
+
+        for stack_identifier in cls.stacks:
+            try:
+                cls.orchestration_client.wait_for_stack_status(
+                    stack_identifier, 'DELETE_COMPLETE')
+            except Exception:
+                pass
+
+    def _create_keypair(self, namestart='keypair-heat-'):
+        kp_name = rand_name(namestart)
+        resp, body = self.keypairs_client.create_keypair(kp_name)
+        self.assertEqual(body['name'], kp_name)
+        return body
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.clear_stacks()
+
+    def wait_for(self, condition):
+        """Repeatedly calls condition() until a timeout."""
+        start_time = int(time.time())
+        while True:
+            try:
+                condition()
+            except Exception:
+                pass
+            else:
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                condition()
+                return
+            time.sleep(self.build_interval)
diff --git a/tempest/api/orchestration/stacks/__init__.py b/tempest/api/orchestration/stacks/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/orchestration/stacks/__init__.py
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
new file mode 100644
index 0000000..8847c08
--- /dev/null
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -0,0 +1,77 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 logging
+
+from tempest.api.orchestration import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+LOG = logging.getLogger(__name__)
+
+
+class StacksTestJSON(base.BaseOrchestrationTest):
+    _interface = 'json'
+
+    empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
+
+    @classmethod
+    def setUpClass(cls):
+        super(StacksTestJSON, cls).setUpClass()
+        cls.client = cls.orchestration_client
+
+    @attr(type='smoke')
+    def test_stack_list_responds(self):
+        resp, body = self.client.list_stacks()
+        stacks = body['stacks']
+        self.assertEqual('200', resp['status'])
+        self.assertIsInstance(stacks, list)
+
+    @attr(type='smoke')
+    def test_stack_crud_no_resources(self):
+        stack_name = rand_name('heat')
+
+        # count how many stacks to start with
+        resp, body = self.client.list_stacks()
+        stack_count = len(body['stacks'])
+
+        # create the stack
+        stack_identifier = self.create_stack(
+            stack_name, self.empty_template)
+
+        # wait for create complete (with no resources it should be instant)
+        self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
+
+        # stack count will increment by 1
+        resp, body = self.client.list_stacks()
+        self.assertEqual(stack_count + 1, len(body['stacks']),
+                         'Expected stack count to increment by 1')
+
+        # fetch the stack
+        resp, body = self.client.get_stack(stack_identifier)
+        self.assertEqual('CREATE_COMPLETE', body['stack_status'])
+
+        # fetch the stack by name
+        resp, body = self.client.get_stack(stack_name)
+        self.assertEqual('CREATE_COMPLETE', body['stack_status'])
+
+        # fetch the stack by id
+        stack_id = stack_identifier.split('/')[1]
+        resp, body = self.client.get_stack(stack_id)
+        self.assertEqual('CREATE_COMPLETE', body['stack_status'])
+
+        # delete the stack
+        resp = self.client.delete_stack(stack_identifier)
+        self.assertEqual('204', resp[0]['status'])
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index bbc1d97..2b2b1a1 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -15,10 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import testtools
 
 from tempest.api.volume import base
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import config
 from tempest.services.volume.json.admin import volume_types_client
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 978ec53..2839da4 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -15,10 +15,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import time
 
 from tempest import clients
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 import tempest.test
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index c013ae4..3a0c802 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,10 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import testtools
 
 from tempest.api.volume import base
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
 
diff --git a/tempest/clients.py b/tempest/clients.py
index f8c93fb..d78ce61 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -15,8 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
+from tempest.common import log as logging
 from tempest import config
 from tempest import exceptions
 from tempest.services import botoclients
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 4ddaf17..d19d216 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -19,7 +19,6 @@
 import hashlib
 import httplib
 import json
-import logging
 import posixpath
 import re
 import socket
@@ -35,6 +34,7 @@
 
 import OpenSSL
 
+from tempest.common import log as logging
 from tempest import exceptions as exc
 
 
diff --git a/tempest/common/log.py b/tempest/common/log.py
new file mode 100644
index 0000000..9b35723
--- /dev/null
+++ b/tempest/common/log.py
@@ -0,0 +1,116 @@
+# 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 ConfigParser
+import inspect
+import logging
+import logging.config
+import os
+import re
+
+from oslo.config import cfg
+
+
+_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
+_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
+
+_loggers = {}
+
+
+def getLogger(name='unknown'):
+    if len(_loggers) == 0:
+        loaded = _load_log_config()
+        getLogger.adapter = TestsAdapter if loaded else None
+
+    if name not in _loggers:
+        logger = logging.getLogger(name)
+        if getLogger.adapter:
+            _loggers[name] = getLogger.adapter(logger, name)
+        else:
+            _loggers[name] = logger
+
+    return _loggers[name]
+
+
+def _load_log_config():
+    conf_dir = os.environ.get('TEMPEST_LOG_CONFIG_DIR', None)
+    conf_file = os.environ.get('TEMPEST_LOG_CONFIG', None)
+    if not conf_dir or not conf_file:
+        return False
+
+    log_config = os.path.join(conf_dir, conf_file)
+    try:
+        logging.config.fileConfig(log_config)
+    except ConfigParser.Error, exc:
+        raise cfg.ConfigFileParseError(log_config, str(exc))
+    return True
+
+
+class TestsAdapter(logging.LoggerAdapter):
+
+    def __init__(self, logger, project_name):
+        self.logger = logger
+        self.project = project_name
+        self.regexp = re.compile(r"test_\w+\.py")
+
+    def __getattr__(self, key):
+        return getattr(self.logger, key)
+
+    def _get_test_name(self):
+        frames = inspect.stack()
+        for frame in frames:
+            binary_name = frame[1]
+            if self.regexp.search(binary_name) and 'self' in frame[0].f_locals:
+                return frame[0].f_locals.get('self').id()
+            elif frame[3] == '_run_cleanups':
+                #NOTE(myamazaki): method calling addCleanup
+                return frame[0].f_locals.get('self').case.id()
+            elif frame[3] in ['setUpClass', 'tearDownClass']:
+                #NOTE(myamazaki): setUpClass or tearDownClass
+                return "%s.%s.%s" % (frame[0].f_locals['cls'].__module__,
+                                     frame[0].f_locals['cls'].__name__,
+                                     frame[3])
+        return None
+
+    def process(self, msg, kwargs):
+        if 'extra' not in kwargs:
+            kwargs['extra'] = {}
+        extra = kwargs['extra']
+
+        test_name = self._get_test_name()
+        if test_name:
+            extra.update({'testname': test_name})
+        extra['extra'] = extra.copy()
+
+        return msg, kwargs
+
+
+class TestsFormatter(logging.Formatter):
+    def __init__(self, fmt=None, datefmt=None):
+        super(TestsFormatter, self).__init__()
+        self.default_format = _DEFAULT_LOG_FORMAT
+        self.testname_format =\
+            "%(asctime)s %(levelname)8s [%(testname)s] %(message)s"
+        self.datefmt = _DEFAULT_LOG_DATE_FORMAT
+
+    def format(self, record):
+        extra = record.__dict__.get('extra', None)
+        if extra and 'testname' in extra:
+            self._fmt = self.testname_format
+        else:
+            self._fmt = self.default_format
+        return logging.Formatter.format(self, record)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index fba3b0f..d81af83 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -19,11 +19,11 @@
 import hashlib
 import httplib2
 import json
-import logging
 from lxml import etree
 import re
 import time
 
+from tempest.common import log as logging
 from tempest import exceptions
 from tempest.services.compute.xml.common import xml_to_json
 
diff --git a/tempest/config.py b/tempest/config.py
index 6d6bc2b..8f3e574 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -15,12 +15,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import os
 import sys
 
 from oslo.config import cfg
 
+from tempest.common import log as logging
 from tempest.common.utils.misc import singleton
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 1e70108..353a9ac 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -17,30 +17,42 @@
 import re
 
 
-SKIP_DECORATOR = '@testtools.skip('
+PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'quantum']
+
+SKIP_DECORATOR_RE = re.compile(r'\s*@testtools.skip\((.*)\)')
+SKIP_STR_RE = re.compile(r'.*Bug #\d+.*')
+PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 
 
 def skip_bugs(physical_line):
     """Check skip lines for proper bug entries
 
-    T101: Bug not in skip line
-    T102: Bug in message formatted incorrectly
+    T101: skips must contain "Bug #<bug_number>"
     """
 
-    pos = physical_line.find(SKIP_DECORATOR)
+    res = SKIP_DECORATOR_RE.match(physical_line)
+    if res:
+        content = res.group(1)
+        res = SKIP_STR_RE.match(content)
+        if not res:
+            return (physical_line.find(content),
+                    'T101: skips must contain "Bug #<bug_number>"')
 
-    skip_re = re.compile(r'^\s*@testtools.skip.*')
 
-    if pos != -1 and skip_re.match(physical_line):
-        bug = re.compile(r'^.*\bbug\b.*', re.IGNORECASE)
-        if bug.match(physical_line) is None:
-            return (pos, 'T101: skips must have an associated bug')
+def import_no_clients_in_api(physical_line, filename):
+    """Check for client imports from tempest/api tests
 
-        bug_re = re.compile(r'.*skip\(.*Bug\s\#\d+', re.IGNORECASE)
+    T102: Cannot import OpenStack python clients
+    """
 
-        if bug_re.match(physical_line) is None:
-            return (pos, 'T102: Bug number formatted incorrectly')
+    if "tempest/api" in filename:
+        res = PYTHON_CLIENT_RE.match(physical_line)
+        if res:
+            return (physical_line.find(res.group(1)),
+                    ("T102: python clients import not allowed"
+                     " in tempest/api/* tests"))
 
 
 def factory(register):
     register(skip_bugs)
+    register(import_no_clients_in_api)
diff --git a/tempest/manager.py b/tempest/manager.py
index 047ad41..25e80ad 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -15,8 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
+from tempest.common import log as logging
 import tempest.config
 from tempest import exceptions
 # Tempest REST Fuzz testing client libs
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index e48157e..9ac0cc0 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -15,9 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
-
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest.scenario import manager
 
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index c5c6728..d318dd9 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -15,8 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest.scenario import manager
 
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index f7e8915..6d811a5 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -16,12 +16,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import time
 import urllib
 
 from lxml import etree
 
+from tempest.common import log as logging
 from tempest.common.rest_client import RestClientXML
 from tempest import exceptions
 from tempest.services.compute.xml.common import Document
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index a8fab7f..f0b1c28 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -18,12 +18,12 @@
 import copy
 import errno
 import json
-import logging
 import os
 import time
 import urllib
 
 from tempest.common import glance_http
+from tempest.common import log as logging
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
 
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index db614f1..17f6cba 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -13,10 +13,10 @@
 #    under the License.
 
 import json
-import logging
 import time
 import urllib
 
+from tempest.common import log as logging
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
 
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 2209fc7..410ed3e 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -12,12 +12,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import time
 import urllib
 
 from lxml import etree
 
+from tempest.common import log as logging
 from tempest.common.rest_client import RestClientXML
 from tempest import exceptions
 from tempest.services.compute.xml.common import Document
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
index 2c431ed..2fcdf2e 100644
--- a/tempest/stress/README.rst
+++ b/tempest/stress/README.rst
@@ -1,4 +1,4 @@
-Quanta Research Cambridge OpenStack Stress Test System
+Tempest Field Guide to Stress Tests
 ======================================================
 
 Nova is a distributed, asynchronous system that is prone to race condition
@@ -10,7 +10,7 @@
 
 Environment
 ------------
-This particular framework assumes your working Nova cluster understands Nova 
+This particular framework assumes your working Nova cluster understands Nova
 API 2.0. The stress tests can read the logs from the cluster. To enable this
 you have to provide the hostname to call 'nova-manage' and
 the private key and user name for ssh to the cluster in the
diff --git a/tempest/test.py b/tempest/test.py
index c69a94c..5f403b3 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -15,13 +15,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import time
 
 import nose.plugins.attrib
 import testresources
 import testtools
 
+from tempest.common import log as logging
 from tempest import config
 from tempest import manager
 
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index afa5c69..9ff628c 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -16,7 +16,7 @@
 #    under the License.
 
 import contextlib
-import logging
+import logging as orig_logging
 import os
 import re
 import urlparse
@@ -28,6 +28,7 @@
 import keystoneclient.exceptions
 
 import tempest.clients
+from tempest.common import log as logging
 from tempest.common.utils.file_utils import have_effective_read_access
 import tempest.config
 from tempest import exceptions
@@ -58,7 +59,7 @@
     A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
     boto_logger = logging.getLogger('boto')
     level = boto_logger.level
-    boto_logger.setLevel(logging.CRITICAL)  # suppress logging for these
+    boto_logger.setLevel(orig_logging.CRITICAL)  # suppress logging for these
 
     def _cred_sub_check(connection_data):
         if not id_matcher.match(connection_data["aws_access_key_id"]):
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index e8abe97..7480833 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -15,12 +15,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
 from boto import exception
 import testtools
 
 from tempest import clients
+from tempest.common import log as logging
 from tempest.common.utils.data_utils import rand_name
 from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest import exceptions
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index edf0180..c90c586 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -15,9 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
 from tempest import clients
+from tempest.common import log as logging
 from tempest.test import attr
 from tempest.thirdparty.boto.test import BotoTestCase
 
diff --git a/tempest/thirdparty/boto/utils/s3.py b/tempest/thirdparty/boto/utils/s3.py
index ea9869b..a309a12 100644
--- a/tempest/thirdparty/boto/utils/s3.py
+++ b/tempest/thirdparty/boto/utils/s3.py
@@ -16,13 +16,14 @@
 #    under the License.
 
 import contextlib
-import logging
 import os
 import re
 
 import boto
 import boto.s3.key
 
+from tempest.common import log as logging
+
 LOG = logging.getLogger(__name__)
 
 
diff --git a/tempest/thirdparty/boto/utils/wait.py b/tempest/thirdparty/boto/utils/wait.py
index 6cd17a9..6b3ef27 100644
--- a/tempest/thirdparty/boto/utils/wait.py
+++ b/tempest/thirdparty/boto/utils/wait.py
@@ -15,13 +15,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import re
 import time
 
 import boto.exception
 from testtools import TestCase
 
+from tempest.common import log as logging
 import tempest.config
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index a75edb0..aa58ab6 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
 import os
 import shlex
 import subprocess
@@ -23,6 +22,7 @@
 
 from sqlalchemy import create_engine, MetaData
 
+from tempest.common import log as logging
 from tempest.common.ssh import Client
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions