Merge "Modify hacking flake8 extension"
diff --git a/tempest/README.rst b/tempest/README.rst
index 63f3ad0..d506bc6 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -38,8 +38,6 @@
 projects themselves, possibly as functional tests in their unit test
 frameworks.
 
-TODO: The bulk of tempest/tests should move to tempest/api
-
 
 cli
 ------------
@@ -50,8 +48,6 @@
 instantiate. Tempest seems like a logical place for this, as it
 prereqs having a running OpenStack cloud.
 
-TODO: the top level cli directory moves to tempest/cli
-
 
 scenario
 ------------
@@ -62,9 +58,6 @@
 
 Scenario tests can and should use the OpenStack python clients.
 
-TODO: tests/network/test_network_basic_ops.py,
-tests/compute/servers/*_ops.py should move to tempest/scenario (others)
-
 
 stress
 -----------
@@ -85,8 +78,6 @@
 but those should be kept seperate from the normal OpenStack
 validation.
 
-TODO: tempest/tests/boto should become tempest/3rdparty/boto
-
 
 whitebox
 ----------
@@ -94,5 +85,3 @@
 Whitebox tests are tests which require access to the database of the
 target OpenStack machine to verify internal state after opperations
 are made. White box tests are allowed to use the python clients.
-
-TODO: collect out whitebox tests to this location.
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
new file mode 100644
index 0000000..9c4c4b9
--- /dev/null
+++ b/tempest/api/compute/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.BaseComputeAdminTest):
+
+    _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_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('positive')
+    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('positive')
+    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('positive')
+    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('negative')
+    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('negative')
+    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('negative')
+    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/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index c05a6d1..c013ae4 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -13,8 +13,10 @@
 #    under the License.
 
 import logging
+import testtools
 
 from tempest.api.volume import base
+from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
 
 LOG = logging.getLogger(__name__)
@@ -37,27 +39,39 @@
     def tearDownClass(cls):
         super(VolumesSnapshotTest, cls).tearDownClass()
 
-    @attr(type=['smoke'])
-    def test_snapshot_create_get_delete(self):
-        # Create a snapshot, get some of the details and then deletes it
-        resp, snapshot = self.snapshots_client.create_snapshot(
-            self.volume_origin['id'])
-        self.assertEqual(200, resp.status)
-        self.snapshots_client.wait_for_snapshot_status(snapshot['id'],
-                                                       'available')
-        errmsg = "Referred volume origin ID mismatch"
-        self.assertEqual(self.volume_origin['id'],
-                         snapshot['volume_id'],
-                         errmsg)
-        self.snapshots_client.delete_snapshot(snapshot['id'])
-        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+    @attr(type='gate')
+    def test_snapshot_create_get_list_delete(self):
+        # Create a snapshot
+        s_name = rand_name('snap')
+        snapshot = self.create_snapshot(self.volume_origin['id'],
+                                        display_name=s_name)
 
-    @attr(type=['smoke'])
+        # Get the snap and check for some of its details
+        resp, snap_get = self.snapshots_client.get_snapshot(snapshot['id'])
+        self.assertEqual(200, resp.status)
+        self.assertEqual(self.volume_origin['id'],
+                         snap_get['volume_id'],
+                         "Referred volume origin mismatch")
+
+        # Compare also with the output from the list action
+        tracking_data = (snapshot['id'], snapshot['display_name'])
+        resp, snaps_list = self.snapshots_client.list_snapshots()
+        self.assertEqual(200, resp.status)
+        snaps_data = [(f['id'], f['display_name']) for f in snaps_list]
+        self.assertIn(tracking_data, snaps_data)
+
+        # Delete the snapshot
+        self.snapshots_client.delete_snapshot(snapshot['id'])
+        self.assertEqual(200, resp.status)
+        self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+        self.snapshots.remove(snapshot)
+
+    @attr(type='gate')
     def test_volume_from_snapshot(self):
         # Create a temporary snap using wrapper method from base, then
         # create a snap based volume, check resp code and deletes it
         snapshot = self.create_snapshot(self.volume_origin['id'])
-        # NOTE: size is required also when passing snapshot_id
+        # NOTE(gfidente): size is required also when passing snapshot_id
         resp, volume = self.volumes_client.create_volume(
             size=1,
             snapshot_id=snapshot['id'])
@@ -68,5 +82,6 @@
         self.clear_snapshots()
 
 
+@testtools.skip("Until Bug #1177610 is resolved.")
 class VolumesSnapshotTestXML(VolumesSnapshotTest):
     _interface = "xml"
diff --git a/tempest/clients.py b/tempest/clients.py
index 037a1c4..f284f16 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -41,6 +41,8 @@
     SecurityGroupsClientJSON
 from tempest.services.compute.json.servers_client import ServersClientJSON
 from tempest.services.compute.json.services_client import ServicesClientJSON
+from tempest.services.compute.json.tenant_usages_client import \
+    TenantUsagesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
 from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
@@ -61,6 +63,8 @@
     import SecurityGroupsClientXML
 from tempest.services.compute.xml.servers_client import ServersClientXML
 from tempest.services.compute.xml.services_client import ServicesClientXML
+from tempest.services.compute.xml.tenant_usages_client import \
+    TenantUsagesClientXML
 from tempest.services.compute.xml.volumes_extensions_client import \
     VolumesExtensionsClientXML
 from tempest.services.identity.json.identity_client import IdentityClientJSON
@@ -216,6 +220,11 @@
     "xml": ServicesClientXML,
 }
 
+TENANT_USAGES_CLIENT = {
+    "json": TenantUsagesClientJSON,
+    "xml": TenantUsagesClientXML,
+}
+
 
 class Manager(object):
 
@@ -287,6 +296,8 @@
             self.service_client = SERVICE_CLIENT[interface](*client_args)
             self.aggregates_client = AGGREGATES_CLIENT[interface](*client_args)
             self.services_client = SERVICES_CLIENT[interface](*client_args)
+            self.tenant_usages_client = \
+                TENANT_USAGES_CLIENT[interface](*client_args)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
new file mode 100644
index 0000000..4dd6964
--- /dev/null
+++ b/tempest/services/compute/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/xml/tenant_usages_client.py b/tempest/services/compute/xml/tenant_usages_client.py
new file mode 100644
index 0000000..cb92324
--- /dev/null
+++ b/tempest/services/compute/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