Merge "validate server action 'console_output' attributes"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 28a4d1c..37d4d53 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -211,13 +211,14 @@
 # admin credentials are known. (boolean value)
 #allow_tenant_isolation=false
 
-# Valid primary image reference to be used in tests. (string
-# value)
-#image_ref={$IMAGE_ID}
+# Valid primary image reference to be used in tests. This is a
+# required option (string value)
+#image_ref=<None>
 
-# Valid secondary image reference to be used in tests. (string
-# value)
-#image_ref_alt={$IMAGE_ID_ALT}
+# Valid secondary image reference to be used in tests. This is
+# a required option, but if only one image is available
+# duplicate the value of image_ref above (string value)
+#image_ref_alt=<None>
 
 # Valid primary flavor to use in tests. (string value)
 #flavor_ref=1
@@ -241,7 +242,7 @@
 #image_alt_ssh_password=password
 
 # Time in seconds between build status checks. (integer value)
-#build_interval=10
+#build_interval=1
 
 # Timeout in seconds to wait for an instance to build.
 # (integer value)
@@ -332,6 +333,9 @@
 # admin credentials are known. (boolean value)
 #allow_tenant_isolation=false
 
+# Time in seconds between build status checks. (integer value)
+#build_interval=1
+
 
 [compute-admin]
 
@@ -705,7 +709,7 @@
 
 # Time in seconds between network operation status checks.
 # (integer value)
-#build_interval=10
+#build_interval=1
 
 
 [network-feature-enabled]
@@ -788,9 +792,6 @@
 # (string value)
 #endpoint_type=publicURL
 
-# Time in seconds between build status checks. (integer value)
-#build_interval=1
-
 # Timeout in seconds to wait for a stack to build. (integer
 # value)
 #build_timeout=1200
@@ -825,6 +826,10 @@
 # Catalog type of the Queuing service. (string value)
 #catalog_type=queuing
 
+# The maximum number of queue records per page when listing
+# queues (integer value)
+#max_queues_per_page=20
+
 
 [scenario]
 
@@ -980,7 +985,7 @@
 
 # Time in seconds between volume availability checks. (integer
 # value)
-#build_interval=10
+#build_interval=1
 
 # Timeout in seconds to wait for a volume to becomeavailable.
 # (integer value)
diff --git a/requirements.txt b/requirements.txt
index 75a61e7..4bf2bcf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,14 +5,14 @@
 testtools>=0.9.34
 lxml>=2.3
 boto>=2.12.0,!=2.13.0
-paramiko>=1.9.0
+paramiko>=1.13.0
 netaddr>=0.7.6
 python-glanceclient>=0.9.0
 python-keystoneclient>=0.8.0
 python-novaclient>=2.17.0
 python-neutronclient>=2.3.4,<3
 python-cinderclient>=1.0.6
-python-heatclient>=0.2.3
+python-heatclient>=0.2.9
 python-ironicclient
 python-saharaclient>=0.6.0
 python-swiftclient>=2.0.2
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
deleted file mode 100644
index 4b845b1..0000000
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import json
-import testtools
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.common.utils.linux import remote_client
-from tempest import config
-from tempest import exceptions
-from tempest.openstack.common import log as logging
-from tempest import test
-
-CONF = config.CONF
-LOG = logging.getLogger(__name__)
-
-
-class ServerCfnInitTestJSON(base.BaseOrchestrationTest):
-    existing_keypair = CONF.orchestration.keypair_name is not None
-
-    @classmethod
-    @test.safe_setup
-    def setUpClass(cls):
-        super(ServerCfnInitTestJSON, cls).setUpClass()
-        if not CONF.orchestration.image_ref:
-            raise cls.skipException("No image available to test")
-        template = cls.load_template('cfn_init_signal')
-        stack_name = data_utils.rand_name('heat')
-        if CONF.orchestration.keypair_name:
-            keypair_name = CONF.orchestration.keypair_name
-        else:
-            cls.keypair = cls._create_keypair()
-            keypair_name = cls.keypair['name']
-
-        # create the stack
-        cls.stack_identifier = cls.create_stack(
-            stack_name,
-            template,
-            parameters={
-                'key_name': keypair_name,
-                'flavor': CONF.orchestration.instance_type,
-                'image': CONF.orchestration.image_ref,
-                'network': cls._get_default_network()['id'],
-                'timeout': CONF.orchestration.build_timeout
-            })
-
-    @test.attr(type='slow')
-    @testtools.skipIf(existing_keypair, 'Server ssh tests are disabled.')
-    def test_can_log_into_created_server(self):
-
-        sid = self.stack_identifier
-        rid = 'SmokeServer'
-
-        # wait for create to complete.
-        self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
-        resp, body = self.client.get_resource(sid, rid)
-        self.assertEqual('CREATE_COMPLETE', body['resource_status'])
-
-        # fetch the IP address from servers client, since we can't get it
-        # from the stack until stack create is complete
-        resp, server = self.servers_client.get_server(
-            body['physical_resource_id'])
-
-        # Check that the user can authenticate with the generated password
-        linux_client = remote_client.RemoteClient(server, 'ec2-user',
-                                                  pkey=self.keypair[
-                                                      'private_key'])
-        linux_client.validate_authentication()
-
-    @test.attr(type='slow')
-    def test_all_resources_created(self):
-        sid = self.stack_identifier
-        self.client.wait_for_resource_status(
-            sid, 'WaitHandle', 'CREATE_COMPLETE')
-        self.client.wait_for_resource_status(
-            sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
-        self.client.wait_for_resource_status(
-            sid, 'SmokeKeys', 'CREATE_COMPLETE')
-        self.client.wait_for_resource_status(
-            sid, 'CfnUser', 'CREATE_COMPLETE')
-        self.client.wait_for_resource_status(
-            sid, 'SmokeServer', 'CREATE_COMPLETE')
-        try:
-            self.client.wait_for_resource_status(
-                sid, 'WaitCondition', 'CREATE_COMPLETE')
-        except (exceptions.StackResourceBuildErrorException,
-                exceptions.TimeoutException) as e:
-            # attempt to log the server console to help with debugging
-            # the cause of the server not signalling the waitcondition
-            # to heat.
-            resp, body = self.client.get_resource(sid, 'SmokeServer')
-            server_id = body['physical_resource_id']
-            LOG.debug('Console output for %s', server_id)
-            resp, output = self.servers_client.get_console_output(
-                server_id, None)
-            LOG.debug(output)
-            raise e
-
-        # wait for create to complete.
-        self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
-        # This is an assert of great significance, as it means the following
-        # has happened:
-        # - cfn-init read the provided metadata and wrote out a file
-        # - a user was created and credentials written to the server
-        # - a cfn-signal was built which was signed with provided credentials
-        # - the wait condition was fulfilled and the stack has changed state
-        wait_status = json.loads(
-            self.get_stack_output(sid, 'WaitConditionStatus'))
-        self.assertEqual('smoke test complete', wait_status['00000'])
diff --git a/tempest/api/queuing/base.py b/tempest/api/queuing/base.py
index 6c22719..5649619 100644
--- a/tempest/api/queuing/base.py
+++ b/tempest/api/queuing/base.py
@@ -50,6 +50,42 @@
 
     @classmethod
     def delete_queue(cls, queue_name):
-        """Wrapper utility that returns a test queue."""
+        """Wrapper utility that deletes a test queue."""
         resp, body = cls.client.delete_queue(queue_name)
         return resp, body
+
+    @classmethod
+    def check_queue_exists(cls, queue_name):
+        """Wrapper utility that checks the existence of a test queue."""
+        resp, body = cls.client.get_queue(queue_name)
+        return resp, body
+
+    @classmethod
+    def check_queue_exists_head(cls, queue_name):
+        """Wrapper utility checks the head of a queue via http HEAD."""
+        resp, body = cls.client.head_queue(queue_name)
+        return resp, body
+
+    @classmethod
+    def list_queues(cls):
+        """Wrapper utility that lists queues."""
+        resp, body = cls.client.list_queues()
+        return resp, body
+
+    @classmethod
+    def get_queue_stats(cls, queue_name):
+        """Wrapper utility that returns the queue stats."""
+        resp, body = cls.client.get_queue_stats(queue_name)
+        return resp, body
+
+    @classmethod
+    def get_queue_metadata(cls, queue_name):
+        """Wrapper utility that gets a queue metadata."""
+        resp, body = cls.client.get_queue_metadata(queue_name)
+        return resp, body
+
+    @classmethod
+    def set_queue_metadata(cls, queue_name, rbody):
+        """Wrapper utility that sets the metadata of a queue."""
+        resp, body = cls.client.set_queue_metadata(queue_name, rbody)
+        return resp, body
diff --git a/tempest/api/queuing/test_queues.py b/tempest/api/queuing/test_queues.py
index 4d03f7e..e43178a 100644
--- a/tempest/api/queuing/test_queues.py
+++ b/tempest/api/queuing/test_queues.py
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 import logging
+from six import moves
+from testtools import matchers
 
 from tempest.api.queuing import base
 from tempest.common.utils import data_utils
@@ -43,18 +45,86 @@
     @classmethod
     def setUpClass(cls):
         super(TestManageQueue, cls).setUpClass()
-        cls.queue_name = data_utils.rand_name('Queues-Test')
-        # Create Queue
-        cls.client.create_queue(cls.queue_name)
+        cls.queues = list()
+        for _ in moves.xrange(5):
+            queue_name = data_utils.rand_name('Queues-Test')
+            cls.queues.append(queue_name)
+            # Create Queue
+            cls.client.create_queue(queue_name)
 
     @test.attr(type='smoke')
     def test_delete_queue(self):
         # Delete Queue
-        resp, body = self.delete_queue(self.queue_name)
+        queue_name = self.queues.pop()
+        resp, body = self.delete_queue(queue_name)
         self.assertEqual('204', resp['status'])
         self.assertEqual('', body)
 
+    @test.attr(type='smoke')
+    def test_check_queue_existence(self):
+        # Checking Queue Existence
+        for queue_name in self.queues:
+            resp, body = self.check_queue_exists(queue_name)
+            self.assertEqual('204', resp['status'])
+            self.assertEqual('', body)
+
+    @test.attr(type='smoke')
+    def test_check_queue_head(self):
+        # Checking Queue Existence by calling HEAD
+        for queue_name in self.queues:
+            resp, body = self.check_queue_exists_head(queue_name)
+            self.assertEqual('204', resp['status'])
+            self.assertEqual('', body)
+
+    @test.attr(type='smoke')
+    def test_list_queues(self):
+        # Listing queues
+        resp, body = self.list_queues()
+        self.assertEqual(len(body['queues']), len(self.queues))
+        for item in body['queues']:
+            self.assertIn(item['name'], self.queues)
+
+    @test.attr(type='smoke')
+    def test_get_queue_stats(self):
+        # Retrieve random queue
+        queue_name = self.queues[data_utils.rand_int_id(0,
+                                                        len(self.queues) - 1)]
+        # Get Queue Stats for a newly created Queue
+        resp, body = self.get_queue_stats(queue_name)
+        msgs = body['messages']
+        for element in ('free', 'claimed', 'total'):
+            self.assertEqual(0, msgs[element])
+        for element in ('oldest', 'newest'):
+            self.assertNotIn(element, msgs)
+
+    @test.attr(type='smoke')
+    def test_set_and_get_queue_metadata(self):
+        # Retrieve random queue
+        queue_name = self.queues[data_utils.rand_int_id(0,
+                                                        len(self.queues) - 1)]
+        # Check the Queue has no metadata
+        resp, body = self.get_queue_metadata(queue_name)
+        self.assertEqual('200', resp['status'])
+        self.assertThat(body, matchers.HasLength(0))
+        # Create metadata
+        key3 = [0, 1, 2, 3, 4]
+        key2 = data_utils.rand_name('value')
+        req_body1 = dict()
+        req_body1[data_utils.rand_name('key3')] = key3
+        req_body1[data_utils.rand_name('key2')] = key2
+        req_body = dict()
+        req_body[data_utils.rand_name('key1')] = req_body1
+        # Set Queue Metadata
+        resp, body = self.set_queue_metadata(queue_name, req_body)
+        self.assertEqual('204', resp['status'])
+        self.assertEqual('', body)
+        # Get Queue Metadata
+        resp, body = self.get_queue_metadata(queue_name)
+        self.assertEqual('200', resp['status'])
+        self.assertThat(body, matchers.Equals(req_body))
+
     @classmethod
     def tearDownClass(cls):
-        cls.client.delete_queue(cls.queue_name)
+        for queue_name in cls.queues:
+            cls.client.delete_queue(queue_name)
         super(TestManageQueue, cls).tearDownClass()
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 531e145..ecd8836 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -15,6 +15,7 @@
 #    under the License.
 
 from tempest.api.volume import base
+from tempest.common.utils import data_utils
 from tempest import test
 
 QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes']
@@ -99,6 +100,27 @@
         self.assertEqual(quota_usage['gigabytes']['in_use'] + 1,
                          new_quota_usage['gigabytes']['in_use'])
 
+    @test.attr(type='gate')
+    def test_delete_quota(self):
+        # Admin can delete the resource quota set for a tenant
+        tenant_name = data_utils.rand_name('quota_tenant_')
+        identity_client = self.os_adm.identity_client
+        tenant = identity_client.create_tenant(tenant_name)[1]
+        tenant_id = tenant['id']
+        self.addCleanup(identity_client.delete_tenant, tenant_id)
+        _, quota_set_default = self.quotas_client.get_default_quota_set(
+            tenant_id)
+        volume_default = quota_set_default['volumes']
+
+        self.quotas_client.update_quota_set(tenant_id,
+                                            volumes=(int(volume_default) + 5))
+
+        resp, _ = self.quotas_client.delete_quota_set(tenant_id)
+        self.assertEqual(200, resp.status)
+
+        _, quota_set_new = self.quotas_client.get_quota_set(tenant_id)
+        self.assertEqual(volume_default, quota_set_new['volumes'])
+
 
 class VolumeQuotasAdminTestXML(VolumeQuotasAdminTestJSON):
     _interface = "xml"
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 4d11d24..67d0203 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -109,6 +109,7 @@
         cls.backups_client = cls.os.backups_client
         cls.volume_services_client = cls.os.volume_services_client
         cls.volumes_extension_client = cls.os.volumes_extension_client
+        cls.availability_zone_client = cls.os.volume_availability_zone_client
 
     @classmethod
     def create_volume(cls, size=1, **kwargs):
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
new file mode 100644
index 0000000..1db7b7b
--- /dev/null
+++ b/tempest/api/volume/test_availability_zone.py
@@ -0,0 +1,40 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.volume import base
+from tempest import test
+
+
+class AvailabilityZoneTestJSON(base.BaseVolumeV1Test):
+
+    """
+    Tests Availability Zone API List
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(AvailabilityZoneTestJSON, cls).setUpClass()
+        cls.client = cls.availability_zone_client
+
+    @test.attr(type='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)
+
+
+class AvailabilityZoneTestXML(AvailabilityZoneTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api_schema/compute/v2/security_groups.py b/tempest/api_schema/compute/v2/security_groups.py
index 8b4bead..9a852e5 100644
--- a/tempest/api_schema/compute/v2/security_groups.py
+++ b/tempest/api_schema/compute/v2/security_groups.py
@@ -80,6 +80,10 @@
     }
 }
 
+delete_security_group = {
+    'status_code': [202]
+}
+
 create_security_group_rule = {
     'status_code': [200],
     'response_body': {
diff --git a/tempest/api_schema/queuing/__init__.py b/tempest/api_schema/queuing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/queuing/__init__.py
diff --git a/tempest/api_schema/queuing/v1/__init__.py b/tempest/api_schema/queuing/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/queuing/v1/__init__.py
diff --git a/tempest/api_schema/queuing/v1/queues.py b/tempest/api_schema/queuing/v1/queues.py
new file mode 100644
index 0000000..4630e1c
--- /dev/null
+++ b/tempest/api_schema/queuing/v1/queues.py
@@ -0,0 +1,98 @@
+
+# Copyright (c) 2014 Rackspace, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+list_link = {
+    'type': 'object',
+    'properties': {
+        'rel': {'type': 'string'},
+        'href': {
+            'type': 'string',
+            'format': 'uri'
+        }
+    },
+    'required': ['href', 'rel']
+}
+
+list_queue = {
+    'type': 'object',
+    'properties': {
+        'name': {'type': 'string'},
+        'href': {
+            'type': 'string',
+            'format': 'uri'
+        },
+        'metadata': {'type': 'object'}
+    },
+    'required': ['name', 'href']
+}
+
+list_queues = {
+    'status_code': [200, 204],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'links': {
+                'type': 'array',
+                'items': list_link,
+                'maxItems': 1
+            },
+            'queues': {
+                'type': 'array',
+                'items': list_queue
+            }
+        },
+        'required': ['links', 'queues']
+    }
+}
+
+message_link = {
+    'type': 'object',
+    'properties': {
+        'href': {
+            'type': 'string',
+            'format': 'uri'
+        },
+        'age': {'type': 'number'},
+        'created': {
+            'type': 'string',
+            'format': 'date-time'
+        }
+    },
+    'required': ['href', 'age', 'created']
+}
+
+messages = {
+    'type': 'object',
+    'properties': {
+        'free': {'type': 'number'},
+        'claimed': {'type': 'number'},
+        'total': {'type': 'number'},
+        'oldest': message_link,
+        'newest': message_link
+    },
+    'required': ['free', 'claimed', 'total']
+}
+
+queue_stats = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'messages': messages
+        },
+        'required': ['messages']
+    }
+}
diff --git a/tempest/clients.py b/tempest/clients.py
index 37049d6..7745d54 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -168,6 +168,8 @@
     VolumesServicesClientJSON
 from tempest.services.volume.json.admin.volume_types_client import \
     VolumeTypesClientJSON
+from tempest.services.volume.json.availability_zone_client import \
+    VolumeAvailabilityZoneClientJSON
 from tempest.services.volume.json.backups_client import BackupsClientJSON
 from tempest.services.volume.json.extensions_client import \
     ExtensionsClientJSON as VolumeExtensionClientJSON
@@ -183,6 +185,8 @@
     VolumesServicesClientXML
 from tempest.services.volume.xml.admin.volume_types_client import \
     VolumeTypesClientXML
+from tempest.services.volume.xml.availability_zone_client import \
+    VolumeAvailabilityZoneClientXML
 from tempest.services.volume.xml.backups_client import BackupsClientXML
 from tempest.services.volume.xml.extensions_client import \
     ExtensionsClientXML as VolumeExtensionClientXML
@@ -262,6 +266,8 @@
                     self.auth_provider)
             self.token_client = TokenClientXML()
             self.token_v3_client = V3TokenClientXML()
+            self.volume_availability_zone_client = \
+                VolumeAvailabilityZoneClientXML(self.auth_provider)
 
         elif self.interface == 'json':
             self.certificates_client = CertificatesClientJSON(
@@ -358,6 +364,8 @@
             self.negative_client = rest_client.NegativeRestClient(
                 self.auth_provider)
             self.negative_client.service = service
+            self.volume_availability_zone_client = \
+                VolumeAvailabilityZoneClientJSON(self.auth_provider)
 
         else:
             msg = "Unsupported interface type `%s'" % interface
diff --git a/tempest/common/commands.py b/tempest/common/commands.py
index c31a038..6580c65 100644
--- a/tempest/common/commands.py
+++ b/tempest/common/commands.py
@@ -28,15 +28,13 @@
     args = shlex.split(cmd)
     subprocess_args = {'stdout': subprocess.PIPE,
                        'stderr': subprocess.STDOUT}
-    try:
-        proc = subprocess.Popen(['/usr/bin/sudo', '-n'] + args,
-                                **subprocess_args)
-        return proc.communicate()[0]
-        if proc.returncode != 0:
-            LOG.error(cmd + "returned with: " +
-                      proc.returncode + "exit status")
-    except subprocess.CalledProcessError as e:
-        LOG.error("command output:\n%s" % e.output)
+    proc = subprocess.Popen(['/usr/bin/sudo', '-n'] + args,
+                            **subprocess_args)
+    stdout = proc.communicate()[0]
+    if proc.returncode != 0:
+        LOG.error(("Command {0} returned with exit status {1},"
+                   "output {2}").format(cmd, proc.returncode, stdout))
+    return stdout
 
 
 def ip_addr_raw():
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 10223a0..3c527f5 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -238,6 +238,14 @@
                 return resp[i]
         return ""
 
+    def _log_request_start(self, method, req_url, req_headers={},
+                           req_body=None):
+        caller_name = misc_utils.find_test_caller()
+        trace_regex = CONF.debug.trace_requests
+        if trace_regex and re.search(trace_regex, caller_name):
+            self.LOG.debug('Starting Request (%s): %s %s' %
+                           (caller_name, method, req_url))
+
     def _log_request(self, method, req_url, resp,
                      secs="", req_headers={},
                      req_body=None, resp_body=None):
@@ -364,6 +372,7 @@
 
         # Do the actual request, and time it
         start = time.time()
+        self._log_request_start(method, req_url)
         resp, resp_body = self.http_obj.request(
             req_url, method, headers=req_headers, body=req_body)
         end = time.time()
diff --git a/tempest/config.py b/tempest/config.py
index f9be90d..1049f67 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -137,11 +137,12 @@
                      "better parallel execution, but also requires that "
                      "OpenStack Identity API admin credentials are known."),
     cfg.StrOpt('image_ref',
-               default="{$IMAGE_ID}",
-               help="Valid primary image reference to be used in tests."),
+               help="Valid primary image reference to be used in tests. "
+                    "This is a required option"),
     cfg.StrOpt('image_ref_alt',
-               default="{$IMAGE_ID_ALT}",
-               help="Valid secondary image reference to be used in tests."),
+               help="Valid secondary image reference to be used in tests. "
+                    "This is a required option, but if only one image is "
+                    "available duplicate the value of image_ref above"),
     cfg.StrOpt('flavor_ref',
                default="1",
                help="Valid primary flavor to use in tests."),
@@ -163,7 +164,7 @@
                help="Password used to authenticate to an instance using "
                     "the alternate image."),
     cfg.IntOpt('build_interval',
-               default=10,
+               default=1,
                help="Time in seconds between build status checks."),
     cfg.IntOpt('build_timeout',
                default=300,
@@ -411,7 +412,7 @@
                help="Timeout in seconds to wait for network operation to "
                     "complete."),
     cfg.IntOpt('build_interval',
-               default=10,
+               default=1,
                help="Time in seconds between network operation status "
                     "checks."),
 ]
@@ -436,6 +437,10 @@
     cfg.StrOpt('catalog_type',
                default='queuing',
                help='Catalog type of the Queuing service.'),
+    cfg.IntOpt('max_queues_per_page',
+               default=20,
+               help='The maximum number of queue records per page when '
+                    'listing queues'),
 ]
 
 volume_group = cfg.OptGroup(name='volume',
@@ -443,7 +448,7 @@
 
 VolumeGroup = [
     cfg.IntOpt('build_interval',
-               default=10,
+               default=1,
                help='Time in seconds between volume availability checks.'),
     cfg.IntOpt('build_timeout',
                default=300,
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e057c74..f83a4b8 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -16,9 +16,12 @@
 
 import logging
 import os
+import re
 import six
 import subprocess
+import time
 
+from heatclient import exc as heat_exceptions
 import netaddr
 from neutronclient.common import exceptions as exc
 from novaclient import exceptions as nova_exceptions
@@ -32,6 +35,7 @@
 from tempest import config
 from tempest import exceptions
 from tempest.openstack.common import log
+from tempest.openstack.common import timeutils
 import tempest.test
 
 CONF = config.CONF
@@ -1069,3 +1073,98 @@
         for net in networks['networks']:
             if net['name'] == CONF.compute.fixed_network_name:
                 return net
+
+    @staticmethod
+    def _stack_output(stack, output_key):
+        """Return a stack output value for a given key."""
+        return next((o['output_value'] for o in stack.outputs
+                    if o['output_key'] == output_key), None)
+
+    def _ping_ip_address(self, ip_address, should_succeed=True):
+        cmd = ['ping', '-c1', '-w1', ip_address]
+
+        def ping():
+            proc = subprocess.Popen(cmd,
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE)
+            proc.wait()
+            return (proc.returncode == 0) == should_succeed
+
+        return tempest.test.call_until_true(
+            ping, CONF.orchestration.build_timeout, 1)
+
+    def _wait_for_resource_status(self, stack_identifier, resource_name,
+                                  status, failure_pattern='^.*_FAILED$'):
+        """Waits for a Resource to reach a given status."""
+        fail_regexp = re.compile(failure_pattern)
+        build_timeout = CONF.orchestration.build_timeout
+        build_interval = CONF.orchestration.build_interval
+
+        start = timeutils.utcnow()
+        while timeutils.delta_seconds(start,
+                                      timeutils.utcnow()) < build_timeout:
+            try:
+                res = self.client.resources.get(
+                    stack_identifier, resource_name)
+            except heat_exceptions.HTTPNotFound:
+                # ignore this, as the resource may not have
+                # been created yet
+                pass
+            else:
+                if res.resource_status == status:
+                    return
+                if fail_regexp.search(res.resource_status):
+                    raise exceptions.StackResourceBuildErrorException(
+                        resource_name=res.resource_name,
+                        stack_identifier=stack_identifier,
+                        resource_status=res.resource_status,
+                        resource_status_reason=res.resource_status_reason)
+            time.sleep(build_interval)
+
+        message = ('Resource %s failed to reach %s status within '
+                   'the required time (%s s).' %
+                   (res.resource_name, status, build_timeout))
+        raise exceptions.TimeoutException(message)
+
+    def _wait_for_stack_status(self, stack_identifier, status,
+                               failure_pattern='^.*_FAILED$'):
+        """
+        Waits for a Stack to reach a given status.
+
+        Note this compares the full $action_$status, e.g
+        CREATE_COMPLETE, not just COMPLETE which is exposed
+        via the status property of Stack in heatclient
+        """
+        fail_regexp = re.compile(failure_pattern)
+        build_timeout = CONF.orchestration.build_timeout
+        build_interval = CONF.orchestration.build_interval
+
+        start = timeutils.utcnow()
+        while timeutils.delta_seconds(start,
+                                      timeutils.utcnow()) < build_timeout:
+            try:
+                stack = self.client.stacks.get(stack_identifier)
+            except heat_exceptions.HTTPNotFound:
+                # ignore this, as the stackource may not have
+                # been created yet
+                pass
+            else:
+                if stack.stack_status == status:
+                    return
+                if fail_regexp.search(stack.stack_status):
+                    raise exceptions.StackBuildErrorException(
+                        stack_identifier=stack_identifier,
+                        stack_status=stack.stack_status,
+                        stack_status_reason=stack.stack_status_reason)
+            time.sleep(build_interval)
+
+        message = ('Stack %s failed to reach %s status within '
+                   'the required time (%s s).' %
+                   (stack.stack_name, status, build_timeout))
+        raise exceptions.TimeoutException(message)
+
+    def _stack_delete(self, stack_identifier):
+        try:
+            self.client.stacks.delete(stack_identifier)
+        except heat_exceptions.HTTPNotFound:
+            pass
diff --git a/tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml b/tempest/scenario/orchestration/cfn_init_signal.yaml
similarity index 97%
rename from tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml
rename to tempest/scenario/orchestration/cfn_init_signal.yaml
index fa5345e..c95aabf 100644
--- a/tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml
+++ b/tempest/scenario/orchestration/cfn_init_signal.yaml
@@ -62,7 +62,7 @@
           #!/bin/bash -v
           /opt/aws/bin/cfn-init
           /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" \
-              "WaitHandle"
+              --id smoke_status "WaitHandle"
   WaitHandle:
     Type: AWS::CloudFormation::WaitConditionHandle
   WaitCondition:
diff --git a/tempest/scenario/orchestration/test_server_cfn_init.py b/tempest/scenario/orchestration/test_server_cfn_init.py
new file mode 100644
index 0000000..36e6126
--- /dev/null
+++ b/tempest/scenario/orchestration/test_server_cfn_init.py
@@ -0,0 +1,130 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+from tempest.scenario import manager
+from tempest import test
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class CfnInitScenarioTest(manager.OrchestrationScenarioTest):
+
+    def setUp(self):
+        super(CfnInitScenarioTest, self).setUp()
+        if not CONF.orchestration.image_ref:
+            raise self.skipException("No image available to test")
+        self.client = self.orchestration_client
+        self.template_name = 'cfn_init_signal.yaml'
+
+    def assign_keypair(self):
+        self.stack_name = self._stack_rand_name()
+        if CONF.orchestration.keypair_name:
+            self.keypair = None
+            self.keypair_name = CONF.orchestration.keypair_name
+        else:
+            self.keypair = self.create_keypair()
+            self.keypair_name = self.keypair.id
+
+    def launch_stack(self):
+        net = self._get_default_network()
+        self.parameters = {
+            'key_name': self.keypair_name,
+            'flavor': CONF.orchestration.instance_type,
+            'image': CONF.orchestration.image_ref,
+            'timeout': CONF.orchestration.build_timeout,
+            'network': net['id'],
+        }
+
+        # create the stack
+        self.template = self._load_template(__file__, self.template_name)
+        self.client.stacks.create(
+            stack_name=self.stack_name,
+            template=self.template,
+            parameters=self.parameters)
+
+        self.stack = self.client.stacks.get(self.stack_name)
+        self.stack_identifier = '%s/%s' % (self.stack_name, self.stack.id)
+        self.addCleanup(self._stack_delete, self.stack_identifier)
+
+    def check_stack(self):
+        sid = self.stack_identifier
+        self._wait_for_resource_status(
+            sid, 'WaitHandle', 'CREATE_COMPLETE')
+        self._wait_for_resource_status(
+            sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
+        self._wait_for_resource_status(
+            sid, 'SmokeKeys', 'CREATE_COMPLETE')
+        self._wait_for_resource_status(
+            sid, 'CfnUser', 'CREATE_COMPLETE')
+        self._wait_for_resource_status(
+            sid, 'SmokeServer', 'CREATE_COMPLETE')
+
+        server_resource = self.client.resources.get(sid, 'SmokeServer')
+        server_id = server_resource.physical_resource_id
+        server = self.compute_client.servers.get(server_id)
+        server_ip = server.networks[CONF.compute.network_for_ssh][0]
+
+        if not self._ping_ip_address(server_ip):
+            self._log_console_output(servers=[server])
+            self.fail(
+                "Timed out waiting for %s to become reachable" % server_ip)
+
+        try:
+            self._wait_for_resource_status(
+                sid, 'WaitCondition', 'CREATE_COMPLETE')
+        except (exceptions.StackResourceBuildErrorException,
+                exceptions.TimeoutException) as e:
+            raise e
+        finally:
+            # attempt to log the server console regardless of WaitCondition
+            # going to complete. This allows successful and failed cloud-init
+            # logs to be compared
+            self._log_console_output(servers=[server])
+
+        self._wait_for_stack_status(sid, 'CREATE_COMPLETE')
+
+        stack = self.client.stacks.get(sid)
+
+        # This is an assert of great significance, as it means the following
+        # has happened:
+        # - cfn-init read the provided metadata and wrote out a file
+        # - a user was created and credentials written to the server
+        # - a cfn-signal was built which was signed with provided credentials
+        # - the wait condition was fulfilled and the stack has changed state
+        wait_status = json.loads(
+            self._stack_output(stack, 'WaitConditionStatus'))
+        self.assertEqual('smoke test complete', wait_status['smoke_status'])
+
+        if self.keypair:
+            # Check that the user can authenticate with the generated
+            # keypair
+            try:
+                linux_client = self.get_remote_client(
+                    server_ip, username='ec2-user')
+                linux_client.validate_authentication()
+            except (exceptions.ServerUnreachable,
+                    exceptions.SSHTimeout) as e:
+                self._log_console_output(servers=[server])
+                raise e
+
+    @test.attr(type='slow')
+    @test.services('orchestration', 'compute')
+    def test_server_cfn_init(self):
+        self.assign_keypair()
+        self.launch_stack()
+        self.check_stack()
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index c19baf3..a86f3df 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -88,7 +88,10 @@
 
     def delete_security_group(self, security_group_id):
         """Deletes the provided Security Group."""
-        return self.delete('os-security-groups/%s' % str(security_group_id))
+        resp, body = self.delete(
+            'os-security-groups/%s' % str(security_group_id))
+        self.validate_response(schema.delete_security_group, resp, body)
+        return resp, body
 
     def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
                                    to_port, **kwargs):
diff --git a/tempest/services/queuing/json/queuing_client.py b/tempest/services/queuing/json/queuing_client.py
index 4a0c495..e5978f5 100644
--- a/tempest/services/queuing/json/queuing_client.py
+++ b/tempest/services/queuing/json/queuing_client.py
@@ -15,6 +15,7 @@
 
 import json
 
+from tempest.api_schema.queuing.v1 import queues as queues_schema
 from tempest.common import rest_client
 from tempest import config
 
@@ -33,6 +34,7 @@
         uri = '{0}/queues'.format(self.uri_prefix)
         resp, body = self.get(uri)
         body = json.loads(body)
+        self.validate_response(queues_schema.list_queues, resp, body)
         return resp, body
 
     def create_queue(self, queue_name):
@@ -43,16 +45,32 @@
     def get_queue(self, queue_name):
         uri = '{0}/queues/{1}'.format(self.uri_prefix, queue_name)
         resp, body = self.get(uri)
-        body = json.loads(body)
         return resp, body
 
     def head_queue(self, queue_name):
         uri = '{0}/queues/{1}'.format(self.uri_prefix, queue_name)
         resp, body = self.head(uri)
-        body = json.loads(body)
         return resp, body
 
     def delete_queue(self, queue_name):
         uri = '{0}/queues/{1}'.format(self.uri_prefix, queue_name)
         resp = self.delete(uri)
         return resp
+
+    def get_queue_stats(self, queue_name):
+        uri = '{0}/queues/{1}/stats'.format(self.uri_prefix, queue_name)
+        resp, body = self.get(uri)
+        body = json.loads(body)
+        self.validate_response(queues_schema.queue_stats, resp, body)
+        return resp, body
+
+    def get_queue_metadata(self, queue_name):
+        uri = '{0}/queues/{1}/metadata'.format(self.uri_prefix, queue_name)
+        resp, body = self.get(uri)
+        body = json.loads(body)
+        return resp, body
+
+    def set_queue_metadata(self, queue_name, rbody):
+        uri = '{0}/queues/{1}/metadata'.format(self.uri_prefix, queue_name)
+        resp, body = self.put(uri, body=json.dumps(rbody))
+        return resp, body
diff --git a/tempest/services/volume/json/admin/volume_quotas_client.py b/tempest/services/volume/json/admin/volume_quotas_client.py
index ea9c92e..961c7da 100644
--- a/tempest/services/volume/json/admin/volume_quotas_client.py
+++ b/tempest/services/volume/json/admin/volume_quotas_client.py
@@ -77,3 +77,7 @@
         post_body = jsonutils.dumps({'quota_set': post_body})
         resp, body = self.put('os-quota-sets/%s' % tenant_id, post_body)
         return resp, self._parse_resp(body)
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        return self.delete('os-quota-sets/%s' % tenant_id)
diff --git a/tempest/services/volume/json/availability_zone_client.py b/tempest/services/volume/json/availability_zone_client.py
new file mode 100644
index 0000000..6839d3a
--- /dev/null
+++ b/tempest/services/volume/json/availability_zone_client.py
@@ -0,0 +1,34 @@
+# Copyright 2014 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from tempest.common import rest_client
+from tempest import config
+
+CONF = config.CONF
+
+
+class VolumeAvailabilityZoneClientJSON(rest_client.RestClient):
+
+    def __init__(self, auth_provider):
+        super(VolumeAvailabilityZoneClientJSON, self).__init__(
+            auth_provider)
+        self.service = CONF.volume.catalog_type
+
+    def get_availability_zone_list(self):
+        resp, body = self.get('os-availability-zone')
+        body = json.loads(body)
+        return resp, body['availabilityZoneInfo']
diff --git a/tempest/services/volume/xml/admin/volume_quotas_client.py b/tempest/services/volume/xml/admin/volume_quotas_client.py
index dd1423f..a38410b 100644
--- a/tempest/services/volume/xml/admin/volume_quotas_client.py
+++ b/tempest/services/volume/xml/admin/volume_quotas_client.py
@@ -68,3 +68,7 @@
                               str(xml.Document(element)))
         body = xml.xml_to_json(etree.fromstring(body))
         return resp, self._format_quota(body)
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        return self.delete('os-quota-sets/%s' % tenant_id)
diff --git a/tempest/services/volume/xml/availability_zone_client.py b/tempest/services/volume/xml/availability_zone_client.py
new file mode 100644
index 0000000..e4a004a
--- /dev/null
+++ b/tempest/services/volume/xml/availability_zone_client.py
@@ -0,0 +1,39 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from lxml import etree
+
+from tempest.common import rest_client
+from tempest.common import xml_utils
+from tempest import config
+
+CONF = config.CONF
+
+
+class VolumeAvailabilityZoneClientXML(rest_client.RestClient):
+    TYPE = "xml"
+
+    def __init__(self, auth_provider):
+        super(VolumeAvailabilityZoneClientXML, self).__init__(
+            auth_provider)
+        self.service = CONF.volume.catalog_type
+
+    def _parse_array(self, node):
+        return [xml_utils.xml_to_json(x) for x in node]
+
+    def get_availability_zone_list(self):
+        resp, body = self.get('os-availability-zone')
+        availability_zone = self._parse_array(etree.fromstring(body))
+        return resp, availability_zone
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 33b8d6e..b2eb18d 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -193,7 +193,6 @@
             instance.terminate()
         self.cancelResourceCleanUp(rcuk)
 
-    @test.skip_because(bug="1098891")
     @test.attr(type='smoke')
     def test_run_terminate_instance(self):
         # EC2 run, terminate immediately
@@ -211,7 +210,7 @@
             pass
         except exception.EC2ResponseError as exc:
             if self.ec2_error_code.\
-                client.InvalidInstanceID.NotFound.match(exc):
+                client.InvalidInstanceID.NotFound.match(exc) is None:
                 pass
             else:
                 raise
diff --git a/test-requirements.txt b/test-requirements.txt
index 8d64167..942a7c3 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 hacking>=0.8.0,<0.9
 # needed for doc build
 docutils==0.9.1
-sphinx>=1.1.2,<1.2
+sphinx>=1.2.1,<1.3
 python-subunit>=0.0.18
 oslosphinx
 mox>=0.5.3