Merge "Add some tests for os_update_readonly_flag"
diff --git a/HACKING.rst b/HACKING.rst
index 377c647..a74ff73 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -186,3 +186,9 @@
 
 - Scenario tests
 - API tests that have a wide focus
+
+Sample Configuration File
+-------------------------
+The sample config file is autogenerated using a script. If any changes are made
+to the config variables in tempest then the sample config file must be
+regenerated. This can be done running the script: tools/generate_sample.sh
diff --git a/README.rst b/README.rst
index 0996022..96f6e4c 100644
--- a/README.rst
+++ b/README.rst
@@ -79,6 +79,9 @@
 document. The etc/tempest.conf.sample attempts to be a self
 documenting version of the configuration.
 
+The sample config file is auto generated using the script:
+tools/generate_sample.sh
+
 The most important pieces that are needed are the user ids, openstack
 endpoints, and basic flavors and images needed to run tests.
 
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index aef49dd..24ee5e1 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -31,6 +31,12 @@
       message: "Getting disk size of instance"
     - module: "nova.virt.libvirt.driver"
       message: "No such file or directory: '/opt/stack/data/nova/instances"
+    - module: "nova.compute.manager"
+      message: "error during stop\\(\\) in sync_power_state"
+    - module: "nova.compute.manager"
+      message: "Instance failed network setup after 1 attempt"
+    - module: "nova.compute.manager"
+      message: "Periodic sync_power_state task had an error"
 
 g-api:
     - module: "glance.store.sheepdog"
@@ -43,11 +49,19 @@
 
 ceilometer-acompute:
     - module: "ceilometer.compute.pollsters.disk"
+      message: "Unable to read from monitor: Connection reset by peer"
+    - module: "ceilometer.compute.pollsters.disk"
+      message: "Requested operation is not valid: domain is not running"
+    - module: "ceilometer.compute.pollsters.net"
       message: "Requested operation is not valid: domain is not running"
     - module: "ceilometer.compute.pollsters.disk"
       message: "Domain not found: no domain with matching uuid"
     - module: "ceilometer.compute.pollsters.net"
       message: "No module named libvirt"
+    - module: "ceilometer.compute.pollsters.net"
+      message: "Unable to write to monitor: Broken pipe"
+    - module: "ceilometer.compute.pollsters.cpu"
+      message: "Domain not found: no domain with matching uuid"
 
 ceilometer-alarm-evaluator:
     - module: "ceilometer.alarm.service"
@@ -85,6 +99,8 @@
       message: "takes exactly 4 arguments"
     - module: "nova.api.openstack"
       message: "Caught error: Instance .* could not be found"
+    - module: "nova.api.metadata.handler"
+      message: "Failed to get metadata for instance id:"
 
 n-cond:
     - module: "nova.notifications"
@@ -100,6 +116,12 @@
     - module: "nova.scheduler.filter_scheduler"
       message: "Error from last host: "
 
+n-net:
+    - module: "nova.openstack.common.rpc.amqp"
+      message: "Exception during message handling"
+    - module: "nova.openstack.common.rpc.common"
+      message: "'NoneType' object has no attribute '__getitem__'"
+
 c-api:
     - module: "cinder.api.middleware.fault"
       message: "Caught error: Volume .* could not be found"
@@ -116,6 +138,8 @@
     - module: "cinder.brick.local_dev.lvm"
       message: "/dev/dm-1: stat failed: No such file or directory"
     - module: "cinder.brick.local_dev.lvm"
+      message: "LV stack-volumes.*in use: not deactivating"
+    - module: "cinder.brick.local_dev.lvm"
       message: "Can't remove open logical volume"
 
 q-dhpc:
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index a4a877c..4c823ad 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -234,6 +234,7 @@
         cls.services_client = cls.os.services_v3_client
         cls.extensions_client = cls.os.extensions_v3_client
         cls.availability_zone_client = cls.os.availability_zone_v3_client
+        cls.interfaces_client = cls.os.interfaces_v3_client
 
     @classmethod
     def create_image_from_server(cls, server_id, **kwargs):
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
new file mode 100644
index 0000000..a599f06
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
@@ -0,0 +1,115 @@
+# 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 datetime
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+import time
+
+
+class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
+
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(TenantUsagesTestJSON, cls).setUpClass()
+        cls.adm_client = cls.os_adm.tenant_usages_client
+        cls.client = cls.os.tenant_usages_client
+        cls.identity_client = cls._get_identity_admin_client()
+
+        resp, tenants = cls.identity_client.list_tenants()
+        cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
+                         cls.client.tenant_name][0]
+
+        # Create a server in the demo tenant
+        resp, server = cls.create_test_server(wait_until='ACTIVE')
+        time.sleep(2)
+
+        now = datetime.datetime.now()
+        cls.start = cls._parse_strtime(now - datetime.timedelta(days=1))
+        cls.end = cls._parse_strtime(now + datetime.timedelta(days=1))
+
+    @classmethod
+    def _parse_strtime(cls, at):
+        # Returns formatted datetime
+        return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
+
+    @attr(type='gate')
+    def test_list_usage_all_tenants(self):
+        # Get usage for all tenants
+        params = {'start': self.start,
+                  'end': self.end,
+                  'detailed': int(bool(True))}
+        resp, tenant_usage = self.adm_client.list_tenant_usages(params)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(len(tenant_usage), 8)
+
+    @attr(type='gate')
+    def test_get_usage_tenant(self):
+        # Get usage for a specific tenant
+        params = {'start': self.start,
+                  'end': self.end}
+        resp, tenant_usage = self.adm_client.get_tenant_usage(
+            self.tenant_id, params)
+
+        self.assertEqual(200, resp.status)
+        self.assertEqual(len(tenant_usage), 8)
+
+    @attr(type='gate')
+    def test_get_usage_tenant_with_non_admin_user(self):
+        # Get usage for a specific tenant with non admin user
+        params = {'start': self.start,
+                  'end': self.end}
+        resp, tenant_usage = self.client.get_tenant_usage(
+            self.tenant_id, params)
+
+        self.assertEqual(200, resp.status)
+        self.assertEqual(len(tenant_usage), 8)
+
+    @attr(type=['negative', 'gate'])
+    def test_get_usage_tenant_with_empty_tenant_id(self):
+        # Get usage for a specific tenant empty
+        params = {'start': self.start,
+                  'end': self.end}
+        self.assertRaises(exceptions.NotFound,
+                          self.adm_client.get_tenant_usage,
+                          '', params)
+
+    @attr(type=['negative', 'gate'])
+    def test_get_usage_tenant_with_invalid_date(self):
+        # Get usage for tenant with invalid date
+        params = {'start': self.end,
+                  'end': self.start}
+        self.assertRaises(exceptions.BadRequest,
+                          self.adm_client.get_tenant_usage,
+                          self.tenant_id, params)
+
+    @attr(type=['negative', 'gate'])
+    def test_list_usage_all_tenants_with_non_admin_user(self):
+        # Get usage for all tenants with non admin user
+        params = {'start': self.start,
+                  'end': self.end,
+                  'detailed': int(bool(True))}
+        self.assertRaises(exceptions.Unauthorized,
+                          self.client.list_tenant_usages, params)
+
+
+class TenantUsagesTestXML(TenantUsagesTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_attach_interfaces.py b/tempest/api/compute/v3/servers/test_attach_interfaces.py
index a177cea..f208a4b 100644
--- a/tempest/api/compute/v3/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/v3/servers/test_attach_interfaces.py
@@ -19,15 +19,15 @@
 import time
 
 
-class AttachInterfacesTestJSON(base.BaseV2ComputeTest):
+class AttachInterfacesV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
         if not cls.config.service_available.neutron:
             raise cls.skipException("Neutron is required")
-        super(AttachInterfacesTestJSON, cls).setUpClass()
-        cls.client = cls.os.interfaces_client
+        super(AttachInterfacesV3TestJSON, cls).setUpClass()
+        cls.client = cls.interfaces_client
 
     def _check_interface(self, iface, port_id=None, network_id=None,
                          fixed_ip=None):
@@ -114,5 +114,5 @@
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
 
-class AttachInterfacesTestXML(AttachInterfacesTestJSON):
+class AttachInterfacesV3TestXML(AttachInterfacesV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_addresses.py b/tempest/api/compute/v3/servers/test_server_addresses.py
index 7ca8a52..82588b6 100644
--- a/tempest/api/compute/v3/servers/test_server_addresses.py
+++ b/tempest/api/compute/v3/servers/test_server_addresses.py
@@ -20,12 +20,12 @@
 from tempest.test import attr
 
 
-class ServerAddressesTest(base.BaseV2ComputeTest):
+class ServerAddressesV3Test(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServerAddressesTest, cls).setUpClass()
+        super(ServerAddressesV3Test, cls).setUpClass()
         cls.client = cls.servers_client
 
         resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
@@ -80,5 +80,5 @@
                 self.assertTrue(any([a for a in addr if a == address]))
 
 
-class ServerAddressesTestXML(ServerAddressesTest):
+class ServerAddressesV3TestXML(ServerAddressesV3Test):
     _interface = 'xml'
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index c6639c2..e7cb806 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -18,6 +18,7 @@
 
 from tempest.api.identity.base import DataGenerator
 from tempest import clients
+from tempest.common import custom_matchers
 from tempest.common import isolated_creds
 from tempest import exceptions
 import tempest.test
@@ -120,3 +121,12 @@
                 container_client.delete_container(cont)
             except exceptions.NotFound:
                 pass
+
+    def assertHeaders(self, resp, target, method):
+        """
+        Common method to check the existence and the format of common response
+        headers
+        """
+        self.assertThat(resp, custom_matchers.ExistsAllResponseHeaders(
+                        target, method))
+        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 615b179..12c823b 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -46,6 +46,7 @@
         params = {'format': 'json'}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
 
         self.assertIsNotNone(container_list)
         container_names = [c['name'] for c in container_list]
@@ -59,6 +60,8 @@
             params = {'limit': limit}
             resp, container_list = \
                 self.account_client.list_account_containers(params=params)
+            self.assertHeaders(resp, 'Account', 'GET')
+
             self.assertEqual(len(container_list), limit)
 
     @attr(type='smoke')
@@ -70,10 +73,15 @@
         params = {'marker': self.containers[-1]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
+
         self.assertEqual(len(container_list), 0)
+
         params = {'marker': self.containers[self.containers_count / 2]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
+
         self.assertEqual(len(container_list), self.containers_count / 2 - 1)
 
     @attr(type='smoke')
@@ -85,10 +93,13 @@
         params = {'end_marker': self.containers[0]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
         self.assertEqual(len(container_list), 0)
+
         params = {'end_marker': self.containers[self.containers_count / 2]}
         resp, container_list = \
             self.account_client.list_account_containers(params=params)
+        self.assertHeaders(resp, 'Account', 'GET')
         self.assertEqual(len(container_list), self.containers_count / 2)
 
     @attr(type='smoke')
@@ -101,6 +112,8 @@
                       'limit': limit}
             resp, container_list = \
                 self.account_client.list_account_containers(params=params)
+            self.assertHeaders(resp, 'Account', 'GET')
+
             self.assertTrue(len(container_list) <= limit, str(container_list))
 
     @attr(type='smoke')
@@ -108,9 +121,7 @@
         # list all account metadata
         resp, metadata = self.account_client.list_account_metadata()
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
-        self.assertIn('x-account-object-count', resp)
-        self.assertIn('x-account-container-count', resp)
-        self.assertIn('x-account-bytes-used', resp)
+        self.assertHeaders(resp, 'Account', 'HEAD')
 
     @attr(type='smoke')
     def test_create_and_delete_account_metadata(self):
@@ -120,8 +131,11 @@
         resp, _ = self.account_client.create_account_metadata(
             metadata={header: data})
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
+        self.assertHeaders(resp, 'Account', 'HEAD')
+
         self.assertIn('x-account-meta-' + header, resp)
         self.assertEqual(resp['x-account-meta-' + header], data)
 
@@ -129,8 +143,10 @@
         resp, _ = \
             self.account_client.delete_account_metadata(metadata=[header])
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Account', 'POST')
 
         resp, _ = self.account_client.list_account_metadata()
+        self.assertHeaders(resp, 'Account', 'HEAD')
         self.assertNotIn('x-account-meta-' + header, resp)
 
     @attr(type=['negative', 'gate'])
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 4fae953..04de072 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -38,6 +38,7 @@
         resp, body = self.container_client.create_container(container_name)
         self.containers.append(container_name)
         self.assertIn(resp['status'], ('202', '201'))
+        self.assertHeaders(resp, 'Container', 'PUT')
 
     @attr(type='smoke')
     def test_delete_container(self):
@@ -48,6 +49,8 @@
         # delete container
         resp, _ = self.container_client.delete_container(container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'DELETE')
+
         self.containers.remove(container_name)
 
     @attr(type='smoke')
@@ -76,6 +79,8 @@
             self.container_client.\
             list_container_contents(container_name, params=params)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'GET')
+
         self.assertIsNotNone(object_list)
 
         object_names = [obj['name'] for obj in object_list]
@@ -97,11 +102,14 @@
             self.container_client.update_container_metadata(container_name,
                                                             metadata=metadata)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
 
         # list container metadata
         resp, _ = self.container_client.list_container_metadata(
             container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'HEAD')
+
         self.assertIn('x-container-meta-name', resp)
         self.assertIn('x-container-meta-description', resp)
         self.assertEqual(resp['x-container-meta-name'], 'Pictures')
@@ -112,9 +120,12 @@
             container_name,
             metadata=metadata.keys())
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'POST')
 
         # check if the metadata are no longer there
         resp, _ = self.container_client.list_container_metadata(container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'HEAD')
+
         self.assertNotIn('x-container-meta-name', resp)
         self.assertNotIn('x-container-meta-description', resp)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 7626af1..c7fdd0e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -16,8 +16,10 @@
 #    under the License.
 
 import hashlib
+import re
 
 from tempest.api.object_storage import base
+from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
 from tempest.test import attr
 from tempest.test import HTTP_SUCCESS
@@ -60,6 +62,7 @@
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
 
     @attr(type='smoke')
     def test_delete_object(self):
@@ -72,6 +75,7 @@
         resp, _ = self.object_client.delete_object(self.container_name,
                                                    object_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'DELETE')
 
     @attr(type='smoke')
     def test_object_metadata(self):
@@ -89,11 +93,14 @@
         resp, _ = self.object_client.update_object_metadata(
             self.container_name, object_name, orig_metadata)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'POST')
 
         # get object metadata
         resp, resp_metadata = self.object_client.list_object_metadata(
             self.container_name, object_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'HEAD')
+
         actual_meta_key = 'x-object-meta-' + meta_key
         self.assertIn(actual_meta_key, resp)
         self.assertEqual(resp[actual_meta_key], meta_value)
@@ -111,6 +118,8 @@
         resp, body = self.object_client.get_object(self.container_name,
                                                    object_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'GET')
+
         self.assertEqual(body, data)
 
     @attr(type='smoke')
@@ -133,6 +142,8 @@
         resp, _ = self.object_client.copy_object_in_same_container(
             self.container_name, src_object_name, dst_object_name)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
         # check data
         resp, body = self.object_client.get_object(self.container_name,
                                                    dst_object_name)
@@ -156,6 +167,8 @@
         resp, _ = self.object_client.copy_object_in_same_container(
             self.container_name, object_name, object_name, metadata)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
         # check the content type
         resp, _ = self.object_client.list_object_metadata(self.container_name,
                                                           object_name)
@@ -180,6 +193,17 @@
                                                         src_object_name,
                                                         dst_object_name)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'COPY')
+
+        self.assertIn('last-modified', resp)
+        self.assertIn('x-copied-from', resp)
+        self.assertIn('x-copied-from-last-modified', resp)
+        self.assertNotEqual(len(resp['last-modified']), 0)
+        self.assertEqual(
+            resp['x-copied-from'],
+            self.container_name + "/" + src_object_name)
+        self.assertNotEqual(len(resp['x-copied-from-last-modified']), 0)
+
         # check data
         resp, body = self.object_client.get_object(self.container_name,
                                                    dst_object_name)
@@ -210,11 +234,15 @@
                                                             object_name,
                                                             orig_metadata)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'POST')
+
         # copy object from source container to destination container
         resp, _ = self.object_client.copy_object_across_containers(
             src_container_name, object_name, dst_container_name,
             object_name)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
         # check if object is present in destination container
         resp, body = self.object_client.get_object(dst_container_name,
                                                    object_name)
@@ -238,13 +266,34 @@
         # creating a manifest file
         metadata = {'X-Object-Manifest': '%s/%s/'
                     % (self.container_name, object_name)}
-        self.object_client.create_object(self.container_name,
-                                         object_name, data='')
+        resp, _ = self.object_client.create_object(self.container_name,
+                                                   object_name, data='')
+        self.assertHeaders(resp, 'Object', 'PUT')
+
         resp, _ = self.object_client.update_object_metadata(
             self.container_name, object_name, metadata, metadata_prefix='')
+        self.assertHeaders(resp, 'Object', 'POST')
+
         resp, _ = self.object_client.list_object_metadata(
             self.container_name, object_name)
+
+        # Check only the existence of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.ExistsAllResponseHeaders(
+                        'Object', 'HEAD'))
         self.assertIn('x-object-manifest', resp)
+
+        # Etag value of a large object is enclosed in double-quotations.
+        # This is a special case, therefore the formats of response headers
+        # are checked without a custom matcher.
+        self.assertTrue(resp['etag'].startswith('\"'))
+        self.assertTrue(resp['etag'].endswith('\"'))
+        self.assertTrue(resp['etag'].strip('\"').isalnum())
+        self.assertTrue(re.match("^\d+\.?\d*\Z", resp['x-timestamp']))
+        self.assertNotEqual(len(resp['content-type']), 0)
+        self.assertTrue(re.match("^tx[0-9a-f]*-[0-9a-f]*$",
+                                 resp['x-trans-id']))
+        self.assertNotEqual(len(resp['date']), 0)
+        self.assertEqual(resp['accept-ranges'], 'bytes')
         self.assertEqual(resp['x-object-manifest'],
                          '%s/%s/' % (self.container_name, object_name))
 
@@ -270,12 +319,23 @@
         resp, _ = self.object_client.get(url, headers=headers)
         self.assertEqual(resp['status'], '304')
 
+        # When the file is not downloaded from Swift server, response does
+        # not contain 'X-Timestamp' header. This is the special case, therefore
+        # the existence of response headers is checked without custom matcher.
+        self.assertIn('content-type', resp)
+        self.assertIn('x-trans-id', resp)
+        self.assertIn('date', resp)
+        self.assertIn('accept-ranges', resp)
+        # Check only the format of common headers with custom matcher
+        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+
         # local copy is different, download
         local_data = "something different"
         md5 = hashlib.md5(local_data).hexdigest()
         headers = {'If-None-Match': md5}
         resp, body = self.object_client.get(url, headers=headers)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Object', 'GET')
 
 
 class PublicObjectTest(base.BaseObjectTest):
@@ -298,6 +358,8 @@
         resp_meta, body = self.container_client.update_container_metadata(
             self.container_name, metadata=cont_headers, metadata_prefix='')
         self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp_meta, 'Container', 'POST')
+
         # create object
         object_name = data_utils.rand_name(name='Object')
         data = data_utils.arbitrary_string(size=len(object_name),
@@ -305,17 +367,22 @@
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
 
         # list container metadata
         resp_meta, _ = self.container_client.list_container_metadata(
             self.container_name)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp_meta, 'Container', 'HEAD')
+
         self.assertIn('x-container-read', resp_meta)
         self.assertEqual(resp_meta['x-container-read'], '.r:*,.rlistings')
 
         # trying to get object with empty headers as it is public readable
         resp, body = self.custom_object_client.get_object(
             self.container_name, object_name, metadata={})
+        self.assertHeaders(resp, 'Object', 'GET')
+
         self.assertEqual(body, data)
 
     @attr(type='smoke')
@@ -327,6 +394,7 @@
             self.container_name, metadata=cont_headers,
             metadata_prefix='')
         self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp_meta, 'Container', 'POST')
 
         # create object
         object_name = data_utils.rand_name(name='Object')
@@ -335,11 +403,14 @@
         resp, _ = self.object_client.create_object(self.container_name,
                                                    object_name, data)
         self.assertEqual(resp['status'], '201')
+        self.assertHeaders(resp, 'Object', 'PUT')
 
         # list container metadata
         resp, _ = self.container_client.list_container_metadata(
             self.container_name)
         self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertHeaders(resp, 'Container', 'HEAD')
+
         self.assertIn('x-container-read', resp)
         self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
 
@@ -350,4 +421,6 @@
         resp, body = self.custom_object_client.get_object(
             self.container_name, object_name,
             metadata=headers)
+        self.assertHeaders(resp, 'Object', 'GET')
+
         self.assertEqual(body, data)
diff --git a/tempest/clients.py b/tempest/clients.py
index b2399c7..0f54fc0 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -54,6 +54,8 @@
     AvailabilityZoneV3ClientJSON
 from tempest.services.compute.v3.json.extensions_client import \
     ExtensionsV3ClientJSON
+from tempest.services.compute.v3.json.interfaces_client import \
+    InterfacesV3ClientJSON
 from tempest.services.compute.v3.json.servers_client import \
     ServersV3ClientJSON
 from tempest.services.compute.v3.json.services_client import \
@@ -62,6 +64,8 @@
     AvailabilityZoneV3ClientXML
 from tempest.services.compute.v3.xml.extensions_client import \
     ExtensionsV3ClientXML
+from tempest.services.compute.v3.xml.interfaces_client import \
+    InterfacesV3ClientXML
 from tempest.services.compute.v3.xml.servers_client import ServersV3ClientXML
 from tempest.services.compute.v3.xml.services_client import \
     ServicesV3ClientXML
@@ -210,6 +214,7 @@
             self.token_client = TokenClientXML(self.config)
             self.security_groups_client = SecurityGroupsClientXML(
                 *client_args)
+            self.interfaces_v3_client = InterfacesV3ClientXML(*client_args)
             self.interfaces_client = InterfacesClientXML(*client_args)
             self.endpoints_client = EndPointClientXML(*client_args)
             self.fixed_ips_client = FixedIPsClientXML(*client_args)
@@ -256,6 +261,7 @@
             self.token_client = TokenClientJSON(self.config)
             self.security_groups_client = SecurityGroupsClientJSON(
                 *client_args)
+            self.interfaces_v3_client = InterfacesV3ClientJSON(*client_args)
             self.interfaces_client = InterfacesClientJSON(*client_args)
             self.endpoints_client = EndPointClientJSON(*client_args)
             self.fixed_ips_client = FixedIPsClientJSON(*client_args)
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
new file mode 100644
index 0000000..307d5db
--- /dev/null
+++ b/tempest/common/custom_matchers.py
@@ -0,0 +1,154 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NTT Corporation
+#
+#    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 re
+
+
+class ExistsAllResponseHeaders(object):
+    """
+    Specific matcher to check the existence of Swift's response headers
+
+    This matcher checks the existence of common headers for each HTTP method
+    or the target, which means account, container or object.
+    When checking the existence of 'specific' headers such as
+    X-Account-Meta-* or X-Object-Manifest for example, those headers must be
+    checked in each test code.
+    """
+
+    def __init__(self, target, method):
+        """
+        param: target Account/Container/Object
+        param: method PUT/GET/HEAD/DELETE/COPY/POST
+        """
+        self.target = target
+        self.method = method
+
+    def match(self, actual):
+        """
+        param: actual HTTP response headers
+        """
+        # Check common headers for all HTTP methods
+        if 'content-length' not in actual:
+            return NonExistentHeader('content-length')
+        if 'content-type' not in actual:
+            return NonExistentHeader('content-type')
+        if 'x-trans-id' not in actual:
+            return NonExistentHeader('x-trans-id')
+        if 'date' not in actual:
+            return NonExistentHeader('date')
+
+        # Check headers for a specific method or target
+        if self.method == 'GET' or self.method == 'HEAD':
+            if 'x-timestamp' not in actual:
+                return NonExistentHeader('x-timestamp')
+            if 'accept-ranges' not in actual:
+                return NonExistentHeader('accept-ranges')
+            if self.target == 'Account':
+                if 'x-account-bytes-used' not in actual:
+                    return NonExistentHeader('x-account-bytes-used')
+                if 'x-account-container-count' not in actual:
+                    return NonExistentHeader('x-account-container-count')
+                if 'x-account-object-count' not in actual:
+                    return NonExistentHeader('x-account-object-count')
+            elif self.target == 'Container':
+                if 'x-container-bytes-used' not in actual:
+                    return NonExistentHeader('x-container-bytes-used')
+                if 'x-container-object-count' not in actual:
+                    return NonExistentHeader('x-container-object-count')
+            elif self.target == 'Object':
+                if 'etag' not in actual:
+                    return NonExistentHeader('etag')
+        elif self.method == 'PUT' or self.method == 'COPY':
+            if self.target == 'Object':
+                if 'etag' not in actual:
+                    return NonExistentHeader('etag')
+
+        return None
+
+
+class NonExistentHeader(object):
+    """
+    Informs an error message for end users in the case of missing a
+    certain header in Swift's responses
+    """
+
+    def __init__(self, header):
+        self.header = header
+
+    def describe(self):
+        return "%s header does not exist" % self.header
+
+    def get_details(self):
+        return {}
+
+
+class AreAllWellFormatted(object):
+    """
+    Specific matcher to check the correctness of formats of values of Swift's
+    response headers
+
+    This matcher checks the format of values of response headers.
+    When checking the format of values of 'specific' headers such as
+    X-Account-Meta-* or X-Object-Manifest for example, those values must be
+    checked in each test code.
+    """
+
+    def match(self, actual):
+        for key, value in actual.iteritems():
+            if key == 'content-length' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'x-timestamp' and not re.match("^\d+\.?\d*\Z", value):
+                return InvalidFormat(key, value)
+            elif key == 'x-account-bytes-used' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'x-account-container-count' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'x-account-object-count' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'x-container-bytes-used' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'x-container-object-count' and not value.isdigit():
+                return InvalidFormat(key, value)
+            elif key == 'content-type' and not value:
+                return InvalidFormat(key, value)
+            elif key == 'x-trans-id' and \
+                not re.match("^tx[0-9a-f]*-[0-9a-f]*$", value):
+                return InvalidFormat(key, value)
+            elif key == 'date' and not value:
+                return InvalidFormat(key, value)
+            elif key == 'accept-ranges' and not value == 'bytes':
+                return InvalidFormat(key, value)
+            elif key == 'etag' and not value.isalnum():
+                return InvalidFormat(key, value)
+
+        return None
+
+
+class InvalidFormat(object):
+    """
+    Informs an error message for end users if a format of a certain header
+    is invalid
+    """
+
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+
+    def describe(self):
+        return "InvalidFormat (%s, %s)" % (self.key, self.value)
+
+    def get_details(self):
+        return {}
diff --git a/tempest/common/generate_sample_tempest.py b/tempest/common/generate_sample_tempest.py
index 3811088..545703b 100644
--- a/tempest/common/generate_sample_tempest.py
+++ b/tempest/common/generate_sample_tempest.py
@@ -21,6 +21,15 @@
 from tempest import config
 from tempest.openstack.common.config import generator
 
+# NOTE(mtreinish): This hack is needed because of how oslo config is used in
+# tempest. Tempest is run from inside a test runner and so we can't rely on the
+# global CONF object being fully populated when we run a test. (test runners
+# don't init every file for running a test) So to get around that we manually
+# load the config file in tempest for each test class to ensure that every
+# config option is set. However, the tool expects the CONF object to be fully
+# populated when it inits all the files in the project. This just works around
+# the issue by manually loading the config file (which may or may not exist)
+# which will populate all the options before running the generator.
 
 config.TempestConfig()
 generator.generate(sys.argv[1:])
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index 06e6476..7fb0fa9 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -20,21 +20,22 @@
 from tempest import exceptions
 
 
-class InterfacesClientJSON(RestClient):
+class InterfacesV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(InterfacesClientJSON, self).__init__(config, username, password,
-                                                   auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(InterfacesV3ClientJSON, self).__init__(config, username,
+                                                     password, auth_url,
+                                                     tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def list_interfaces(self, server):
-        resp, body = self.get('servers/%s/os-interface' % server)
+        resp, body = self.get('servers/%s/os-attach-interfaces' % server)
         body = json.loads(body)
-        return resp, body['interfaceAttachments']
+        return resp, body['interface_attachments']
 
     def create_interface(self, server, port_id=None, network_id=None,
                          fixed_ip=None):
-        post_body = dict(interfaceAttachment=dict())
+        post_body = dict(interface_attachment=dict())
         if port_id:
             post_body['port_id'] = port_id
         if network_id:
@@ -42,20 +43,22 @@
         if fixed_ip:
             post_body['fixed_ips'] = [dict(ip_address=fixed_ip)]
         post_body = json.dumps(post_body)
-        resp, body = self.post('servers/%s/os-interface' % server,
+        resp, body = self.post('servers/%s/os-attach-interfaces' % server,
                                headers=self.headers,
                                body=post_body)
         body = json.loads(body)
-        return resp, body['interfaceAttachment']
+        return resp, body['interface_attachment']
 
     def show_interface(self, server, port_id):
-        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
+        resp, body =\
+            self.get('servers/%s/os-attach-interfaces/%s' % (server, port_id))
         body = json.loads(body)
-        return resp, body['interfaceAttachment']
+        return resp, body['interface_attachment']
 
     def delete_interface(self, server, port_id):
-        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
-                                                                 port_id))
+        resp, body =\
+            self.delete('servers/%s/os-attach-interfaces/%s' % (server,
+                                                                port_id))
         return resp, body
 
     def wait_for_interface_status(self, server, port_id, status):
diff --git a/tempest/services/compute/v3/json/tenant_usages_client.py b/tempest/services/compute/v3/json/tenant_usages_client.py
new file mode 100644
index 0000000..4dd6964
--- /dev/null
+++ b/tempest/services/compute/v3/json/tenant_usages_client.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+import urllib
+
+from tempest.common.rest_client import RestClient
+
+
+class TenantUsagesClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(TenantUsagesClientJSON, self).__init__(
+            config, username, password, auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_tenant_usages(self, params=None):
+        url = 'os-simple-tenant-usage'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['tenant_usages'][0]
+
+    def get_tenant_usage(self, tenant_id, params=None):
+        url = 'os-simple-tenant-usage/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['tenant_usage']
diff --git a/tempest/services/compute/v3/xml/interfaces_client.py b/tempest/services/compute/v3/xml/interfaces_client.py
index a84e0bd..870c130 100644
--- a/tempest/services/compute/v3/xml/interfaces_client.py
+++ b/tempest/services/compute/v3/xml/interfaces_client.py
@@ -25,12 +25,12 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class InterfacesClientXML(RestClientXML):
+class InterfacesV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(InterfacesClientXML, self).__init__(config, username, password,
-                                                  auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        super(InterfacesV3ClientXML, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+        self.service = self.config.compute.catalog_v3_type
 
     def _process_xml_interface(self, node):
         iface = xml_to_json(node)
@@ -40,7 +40,8 @@
         return iface
 
     def list_interfaces(self, server):
-        resp, body = self.get('servers/%s/os-interface' % server, self.headers)
+        resp, body = self.get('servers/%s/os-attach-interfaces' % server,
+                              self.headers)
         node = etree.fromstring(body)
         interfaces = [self._process_xml_interface(x)
                       for x in node.getchildren()]
@@ -49,7 +50,7 @@
     def create_interface(self, server, port_id=None, network_id=None,
                          fixed_ip=None):
         doc = Document()
-        iface = Element('interfaceAttachment')
+        iface = Element('interface_attachment')
         if port_id:
             _port_id = Element('port_id')
             _port_id.append(Text(port_id))
@@ -67,21 +68,23 @@
             _fixed_ips.append(_fixed_ip)
             iface.append(_fixed_ips)
         doc.append(iface)
-        resp, body = self.post('servers/%s/os-interface' % server,
+        resp, body = self.post('servers/%s/os-attach-interfaces' % server,
                                headers=self.headers,
                                body=str(doc))
         body = self._process_xml_interface(etree.fromstring(body))
         return resp, body
 
     def show_interface(self, server, port_id):
-        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id),
-                              self.headers)
+        resp, body =\
+            self.get('servers/%s/os-attach-interfaces/%s' % (server, port_id),
+                     self.headers)
         body = self._process_xml_interface(etree.fromstring(body))
         return resp, body
 
     def delete_interface(self, server, port_id):
-        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
-                                                                 port_id))
+        resp, body =\
+            self.delete('servers/%s/os-attach-interfaces/%s' % (server,
+                                                                port_id))
         return resp, body
 
     def wait_for_interface_status(self, server, port_id, status):
diff --git a/tempest/services/compute/v3/xml/tenant_usages_client.py b/tempest/services/compute/v3/xml/tenant_usages_client.py
new file mode 100644
index 0000000..cb92324
--- /dev/null
+++ b/tempest/services/compute/v3/xml/tenant_usages_client.py
@@ -0,0 +1,54 @@
+# 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 urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class TenantUsagesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(TenantUsagesClientXML, self).__init__(config, username,
+                                                    password, auth_url,
+                                                    tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _parse_array(self, node):
+        json = xml_to_json(node)
+        return json
+
+    def list_tenant_usages(self, params=None):
+        url = 'os-simple-tenant-usage'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        tenant_usage = self._parse_array(etree.fromstring(body))
+        return resp, tenant_usage['tenant_usage']
+
+    def get_tenant_usage(self, tenant_id, params=None):
+        url = 'os-simple-tenant-usage/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        tenant_usage = self._parse_array(etree.fromstring(body))
+        return resp, tenant_usage
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 2ad4f70..68ffced 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -26,6 +26,10 @@
 import yaml
 
 
+is_neutron = os.environ.get('DEVSTACK_GATE_NEUTRON', "0") == "1"
+dump_all_errors = is_neutron
+
+
 def process_files(file_specs, url_specs, whitelists):
     regexp = re.compile(r"^.*(ERROR|CRITICAL).*\[.*\-.*\]")
     had_errors = False
@@ -48,6 +52,7 @@
 
 def scan_content(name, content, regexp, whitelist):
     had_errors = False
+    print_log_name = True
     for line in content:
         if not line.startswith("Stderr:") and regexp.match(line):
             whitelisted = False
@@ -57,10 +62,12 @@
                 if re.match(pat, line):
                     whitelisted = True
                     break
-            if not whitelisted:
-                if not had_errors:
+            if not whitelisted or dump_all_errors:
+                if not print_log_name:
                     print("Log File: %s" % name)
-                had_errors = True
+                    print_log_name = False
+                if not whitelisted:
+                    had_errors = True
                 print(line)
     return had_errors
 
@@ -115,6 +122,9 @@
             whitelists = loaded
     if process_files(files_to_process, urls_to_process, whitelists):
         print("Logs have errors")
+        if is_neutron:
+            print("Currently not failing neutron builds with errors")
+            return 0
         # Return non-zero to start failing builds
         return 0
     else:
diff --git a/tools/config/check_uptodate.sh b/tools/config/check_uptodate.sh
index 9882ab6..45c8629 100755
--- a/tools/config/check_uptodate.sh
+++ b/tools/config/check_uptodate.sh
@@ -5,6 +5,6 @@
 if ! diff $TEMPDIR/$CFGFILE etc/$CFGFILE
 then
     echo "E: tempest.conf.sample is not up to date, please run:"
-    echo "MODULEPATH=tempest.common.generate_sample_tempest tools/config/generate_sample.sh"
+    echo "tools/generate_sample.sh"
     exit 42
 fi
diff --git a/tools/generate_sample.sh b/tools/generate_sample.sh
new file mode 100755
index 0000000..9b312c9
--- /dev/null
+++ b/tools/generate_sample.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+MODULEPATH=tempest.common.generate_sample_tempest tools/config/generate_sample.sh $@