Merge "close http connections"
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 69e15f7..5f31084 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -288,7 +288,7 @@
                 r, flavors = self.client.list_flavors_with_detail(params)
                 self.assertEqual(r.status, 200)
                 flavor = _flavor_lookup(flavors, flavor_name)
-                self.assertNotEqual(flavor, None)
+                self.assertIsNotNone(flavor)
 
         _test_string_variations(['f', 'false', 'no', '0'],
                                 flavor_name_not_public)
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 893d9e0..25df6e6 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -225,7 +225,7 @@
             resp, output = self.servers_client.get_console_output(
                 self.server_id, 10)
             self.assertEqual(200, resp.status)
-            self.assertNotEqual(output, None)
+            self.assertIsNotNone(output)
             lines = len(output.split('\n'))
             self.assertEqual(lines, 10)
         self.wait_for(get_output)
@@ -249,7 +249,7 @@
         resp, output = self.servers_client.get_console_output(self.server_id,
                                                               10)
         self.assertEqual(200, resp.status)
-        self.assertNotEqual(output, None)
+        self.assertIsNotNone(output)
         lines = len(output.split('\n'))
         self.assertEqual(lines, 10)
 
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 2a5be8c..e5ea30e 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -45,7 +45,7 @@
         # for a given server_id
         resp, output = self.client.list_virtual_interfaces(self.server_id)
         self.assertEqual(200, resp.status)
-        self.assertNotEqual(output, None)
+        self.assertIsNotNone(output)
         virt_ifaces = output
         self.assertNotEqual(0, len(virt_ifaces['virtual_interfaces']),
                             'Expected virtual interfaces, got 0 interfaces.')
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 8cdcee1..4cfeb45 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from testtools.matchers._basic import Contains
+from testtools.matchers import Contains
 
 from tempest.api.identity import base
 from tempest.common.utils.data_utils import rand_name
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index bc050dc..65fe1ac 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -75,22 +75,27 @@
             cls.data.test_password,
             cls.data.test_tenant)
 
-        headers = {"X-Auth-Token": cls.reselleradmin_token,
+    def setUp(self):
+        super(AccountQuotasTest, self).setUp()
+
+        # Set a quota of 20 bytes on the user's account before each test
+        headers = {"X-Auth-Token": self.reselleradmin_token,
                    "X-Account-Meta-Quota-Bytes": "20"}
 
-        cls.os.custom_account_client.request("POST", "", headers, "")
+        self.os.custom_account_client.request("POST", "", headers, "")
+
+    def tearDown(self):
+        # remove the quota from the container
+        headers = {"X-Auth-Token": self.reselleradmin_token,
+                   "X-Remove-Account-Meta-Quota-Bytes": "x"}
+
+        self.os.custom_account_client.request("POST", "", headers, "")
+        super(AccountQuotasTest, self).tearDown()
 
     @classmethod
     def tearDownClass(cls):
         cls.delete_containers([cls.container_name])
         cls.data.teardown_all()
-
-        # remove the quota from the container
-        headers = {"X-Auth-Token": cls.reselleradmin_token,
-                   "X-Remove-Account-Meta-Quota-Bytes": "x"}
-
-        cls.os.custom_account_client.request("POST", "", headers, "")
-
         super(AccountQuotasTest, cls).tearDownClass()
 
     @testtools.skipIf(not accounts_quotas_available,
@@ -113,3 +118,45 @@
         self.assertRaises(exceptions.OverLimit,
                           self.object_client.create_object,
                           self.container_name, object_name, data)
+
+    @testtools.skipIf(not accounts_quotas_available,
+                      "Account Quotas middleware not available")
+    @attr(type=["smoke"])
+    def test_admin_modify_quota(self):
+        """Test that the ResellerAdmin is able to modify and remove the quota
+        on a user's account.
+
+        Using the custom_account client, the test modifies the quota
+        successively to:
+
+        * "25": a random value different from the initial quota value.
+        * ""  : an empty value, equivalent to the removal of the quota.
+        * "20": set the quota to its initial value.
+        """
+        for quota in ("25", "", "20"):
+
+            headers = {"X-Auth-Token": self.reselleradmin_token,
+                       "X-Account-Meta-Quota-Bytes": quota}
+
+            resp, _ = self.os.custom_account_client.request("POST", "",
+                                                            headers, "")
+
+            self.assertEqual(resp["status"], "204")
+
+    @testtools.skipIf(not accounts_quotas_available,
+                      "Account Quotas middleware not available")
+    @attr(type=["negative", "smoke"])
+    def test_user_modify_quota(self):
+        """Test that a user is not able to modify or remove a quota on
+        its account.
+        """
+
+        # Not able to remove quota
+        self.assertRaises(exceptions.Unauthorized,
+                          self.account_client.create_account_metadata,
+                          {"Quota-Bytes": ""})
+
+        # Not able to modify quota
+        self.assertRaises(exceptions.Unauthorized,
+                          self.account_client.create_account_metadata,
+                          {"Quota-Bytes": "100"})
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index dad7eca..e93d9bc 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -34,6 +34,7 @@
 from tempest.common import isolated_creds
 from tempest.common import ssh
 from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
 import tempest.manager
 from tempest.openstack.common import log as logging
 import tempest.test
@@ -382,6 +383,18 @@
         self.set_resource(name, keypair)
         return keypair
 
+    def get_remote_client(self, server_or_ip, username=None, private_key=None):
+        if isinstance(server_or_ip, basestring):
+            ip = server_or_ip
+        else:
+            network_name_for_ssh = self.config.compute.network_for_ssh
+            ip = server_or_ip.networks[network_name_for_ssh][0]
+        if username is None:
+            username = self.config.scenario.ssh_user
+        if private_key is None:
+            private_key = self.keypair.private_key
+        return RemoteClient(ip, username, pkey=private_key)
+
 
 class NetworkScenarioTest(OfficialClientTest):
     """
@@ -561,6 +574,12 @@
     """
 
     @classmethod
+    def setUpClass(cls):
+        super(OrchestrationScenarioTest, cls).setUpClass()
+        if not cls.config.service_available.heat:
+            raise cls.skipException("Heat support is required")
+
+    @classmethod
     def credentials(cls):
         username = cls.config.identity.admin_username
         password = cls.config.identity.admin_password
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 25735e9..5cddde2 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 
@@ -132,10 +131,7 @@
         self.server.add_floating_ip(self.floating_ip)
 
     def ssh_to_server(self):
-        username = self.config.scenario.ssh_user
-        self.linux_client = RemoteClient(self.floating_ip.ip,
-                                         username,
-                                         pkey=self.keypair.private_key)
+        self.linux_client = self.get_remote_client(self.floating_ip.ip)
 
     def check_partitions(self):
         partitions = self.linux_client.get_partitions()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 1e090af..c55e2a3 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 
@@ -54,16 +53,7 @@
         self.keypair = self.create_keypair()
 
     def _ssh_to_server(self, server_or_ip):
-        if isinstance(server_or_ip, basestring):
-            ip = server_or_ip
-        else:
-            network_name_for_ssh = self.config.compute.network_for_ssh
-            ip = server_or_ip.networks[network_name_for_ssh][0]
-        username = self.config.scenario.ssh_user
-        linux_client = RemoteClient(ip,
-                                    username,
-                                    pkey=self.keypair.private_key)
-
+        linux_client = self.get_remote_client(server_or_ip)
         return linux_client.ssh_client
 
     def _write_timestamp(self, server_or_ip):
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 8864b2f..c74b88d 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -21,7 +21,6 @@
 import testtools
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.common.utils.linux.remote_client import RemoteClient
 from tempest import exceptions
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
@@ -81,20 +80,8 @@
     def _add_floating_ip(self, server, floating_ip):
         server.add_floating_ip(floating_ip)
 
-    def _remote_client_to_server(self, server_or_ip):
-        if isinstance(server_or_ip, basestring):
-            ip = server_or_ip
-        else:
-            network_name_for_ssh = self.config.compute.network_for_ssh
-            ip = server_or_ip.networks[network_name_for_ssh][0]
-        username = self.config.scenario.ssh_user
-        linux_client = RemoteClient(ip,
-                                    username,
-                                    pkey=self.keypair.private_key)
-        return linux_client
-
     def _ssh_to_server(self, server_or_ip):
-        linux_client = self._remote_client_to_server(server_or_ip)
+        linux_client = self.get_remote_client(server_or_ip)
         return linux_client.ssh_client
 
     def _create_image(self, server):
@@ -148,7 +135,7 @@
         self._wait_for_volume_status(volume, 'available')
 
     def _wait_for_volume_availible_on_the_system(self, server_or_ip):
-        ssh = self._remote_client_to_server(server_or_ip)
+        ssh = self.get_remote_client(server_or_ip)
         conf = self.config
 
         def _func():
diff --git a/tempest/test.py b/tempest/test.py
index 7787790..68cedf0 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -97,7 +97,7 @@
 
 def validate_tearDownClass():
     if at_exit_set:
-        raise RuntimeError("tearDownClass does not calls the super's"
+        raise RuntimeError("tearDownClass does not calls the super's "
                            "tearDownClass in these classes: "
                            + str(at_exit_set))
 
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index 1ed6961..c244808 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -61,7 +61,7 @@
     """
     Return the skip tuples in a test file
     """
-    BUG_RE = re.compile(r'.*skip\(.*bug:*\s*\#*(\d+)', re.IGNORECASE)
+    BUG_RE = re.compile(r'.*skip.*bug:*\s*\#*(\d+)', re.IGNORECASE)
     DEF_RE = re.compile(r'.*def (\w+)\(')
     bug_found = False
     results = []
diff --git a/tox.ini b/tox.ini
index 471fecb..ea27b92 100644
--- a/tox.ini
+++ b/tox.ini
@@ -27,6 +27,13 @@
 commands =
   sh tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
 
+[testenv:heat-slow]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+# The regex below is used to select heat api/scenario tests tagged as slow.
+commands =
+  sh tools/pretty_tox_serial.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
+
 [testenv:py26-full]
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}