Merge "Bump oslo.config version"
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 1c72eba..3c76069 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -93,6 +93,14 @@
         self.assertRaises(exceptions.NotFound,
                           self.client.get_floating_ip_details, non_exist_id)
 
+    @attr(type='gate')
+    def test_list_floating_ip_pools(self):
+        # Positive test:Should return the list of floating IP Pools
+        resp, floating_ip_pools = self.client.list_floating_ip_pools()
+        self.assertEqual(200, resp.status)
+        self.assertNotEqual(0, len(floating_ip_pools),
+                            "Expected floating IP Pools. Got zero.")
+
 
 class FloatingIPDetailsTestXML(FloatingIPDetailsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index aec1d49..0d5a8fa 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -300,6 +300,26 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
+    @attr(type='gate')
+    def test_lock_unlock_server(self):
+        # Lock the server,try server stop(exceptions throw),unlock it and retry
+        resp, server = self.servers_client.lock_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        resp, server = self.servers_client.get_server(self.server_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(server['status'], 'ACTIVE')
+        # Locked server is not allowed to be stopped by non-admin user
+        self.assertRaises(exceptions.BadRequest,
+                          self.servers_client.stop, self.server_id)
+        resp, server = self.servers_client.unlock_server(self.server_id)
+        self.assertEqual(202, resp.status)
+        resp, server = self.servers_client.stop(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'SHUTOFF')
+        resp, server = self.servers_client.start(self.server_id)
+        self.assertEqual(202, resp.status)
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+
 
 class ServerActionsTestXML(ServerActionsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index fcbde50..e9c395e 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -18,6 +18,8 @@
 
 from tempest.api.image import base
 from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
 from tempest.test import attr
 
 
@@ -83,3 +85,25 @@
         self.assertEqual(200, resp.status)
         members = body['members']
         self.assertEqual(0, len(members))
+
+    @attr(type=['negative', 'gate'])
+    def test_add_member_with_non_existing_image(self):
+        # Add member with non existing image.
+        non_exist_image = rand_name('image_')
+        self.assertRaises(exceptions.NotFound, self.client.add_member,
+                          self.tenants[0], non_exist_image)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_member_with_non_existing_image(self):
+        # Delete member with non existing image.
+        non_exist_image = rand_name('image_')
+        self.assertRaises(exceptions.NotFound, self.client.delete_member,
+                          self.tenants[0], non_exist_image)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_member_with_non_existing_tenant(self):
+        # Delete member with non existing tenant.
+        image_id = self._create_image()
+        non_exist_tenant = rand_name('tenant_')
+        self.assertRaises(exceptions.NotFound, self.client.delete_member,
+                          non_exist_tenant, image_id)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 7c10138..407c3ec 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -16,7 +16,6 @@
 #    under the License.
 
 import hashlib
-import time
 
 from tempest.api.object_storage import base
 from tempest.common.utils.data_utils import arbitrary_string
@@ -225,44 +224,6 @@
         self.assertEqual(resp[actual_meta_key], meta_value)
 
     @attr(type='gate')
-    def test_get_object_using_temp_url(self):
-        # access object using temporary URL within expiration time
-
-        try:
-            # update account metadata
-            # flag to check if account metadata got updated
-            flag = False
-            key = 'Meta'
-            metadata = {'Temp-URL-Key': key}
-            resp, _ = self.account_client.create_account_metadata(
-                metadata=metadata)
-            self.assertIn(int(resp['status']), HTTP_SUCCESS)
-            flag = True
-            resp, _ = self.account_client.list_account_metadata()
-            self.assertIn('x-account-meta-temp-url-key', resp)
-            self.assertEqual(resp['x-account-meta-temp-url-key'], key)
-
-            # create object
-            object_name = rand_name(name='ObjectTemp')
-            data = arbitrary_string(size=len(object_name),
-                                    base_text=object_name)
-            self.object_client.create_object(self.container_name,
-                                             object_name, data)
-            expires = int(time.time() + 10)
-
-            # trying to get object using temp url with in expiry time
-            _, body = self.object_client.get_object_using_temp_url(
-                self.container_name, object_name,
-                expires, key)
-            self.assertEqual(body, data)
-        finally:
-            if flag:
-                resp, _ = self.account_client.delete_account_metadata(
-                    metadata=metadata)
-                resp, _ = self.account_client.list_account_metadata()
-                self.assertNotIn('x-account-meta-temp-url-key', resp)
-
-    @attr(type='gate')
     def test_object_upload_in_segments(self):
         # create object
         object_name = rand_name(name='LObject')
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
new file mode 100644
index 0000000..0fd5499
--- /dev/null
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -0,0 +1,157 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
+#
+# Author: Joe H. Rahme <joe.hakim.rahme@enovance.com>
+#
+# 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 hashlib
+import hmac
+import time
+import urlparse
+
+from tempest.api.object_storage import base
+from tempest.common.utils.data_utils import arbitrary_string
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+from tempest.test import HTTP_SUCCESS
+
+
+class ObjectTempUrlTest(base.BaseObjectTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(ObjectTempUrlTest, cls).setUpClass()
+        cls.container_name = rand_name(name='TestContainer')
+        cls.container_client.create_container(cls.container_name)
+        cls.containers = [cls.container_name]
+
+        # update account metadata
+        cls.key = 'Meta'
+        cls.metadata = {'Temp-URL-Key': cls.key}
+        cls.account_client.create_account_metadata(metadata=cls.metadata)
+        cls.account_client_metadata, _ = \
+            cls.account_client.list_account_metadata()
+
+    @classmethod
+    def tearDownClass(cls):
+        resp, _ = cls.account_client.delete_account_metadata(
+            metadata=cls.metadata)
+        resp, _ = cls.account_client.list_account_metadata()
+
+        cls.delete_containers(cls.containers)
+        # delete the user setup created
+        cls.data.teardown_all()
+        super(ObjectTempUrlTest, cls).tearDownClass()
+
+    def setUp(self):
+        super(ObjectTempUrlTest, self).setUp()
+        # make sure the metadata has been set
+        self.assertIn('x-account-meta-temp-url-key',
+                      self.account_client_metadata)
+
+        self.assertEqual(
+            self.account_client_metadata['x-account-meta-temp-url-key'],
+            self.key)
+
+        # create object
+        self.object_name = rand_name(name='ObjectTemp')
+        self.data = arbitrary_string(size=len(self.object_name),
+                                     base_text=self.object_name)
+        self.object_client.create_object(self.container_name,
+                                         self.object_name, self.data)
+
+    def get_temp_url(self, container, object_name, method, expires,
+                     key):
+        """Create the temporary URL."""
+
+        path = "%s/%s/%s" % (
+            urlparse.urlparse(self.object_client.base_url).path,
+            container, object_name)
+
+        hmac_body = '%s\n%s\n%s' % (method, expires, path)
+        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
+
+        url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
+                                                             object_name,
+                                                             sig, expires)
+
+        return url
+
+    @attr(type='gate')
+    def test_get_object_using_temp_url(self):
+        EXPIRATION_TIME = 10000  # high to ensure the test finishes.
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        # get a temp URL for the created object
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        # trying to get object using temp url within expiry time
+        _, body = self.object_client.get_object_using_temp_url(url)
+
+        self.assertEqual(body, self.data)
+
+        # Testing a HEAD on this Temp URL
+        resp, body = self.object_client.head(url)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+    @attr(type='gate')
+    def test_put_object_using_temp_url(self):
+        # make sure the metadata has been set
+        new_data = arbitrary_string(size=len(self.object_name),
+                                    base_text=rand_name(name="random"))
+
+        EXPIRATION_TIME = 10000  # high to ensure the test finishes.
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "PUT",
+                                expires, self.key)
+
+        # trying to put random data in the object using temp url
+        resp, body = self.object_client.put_object_using_temp_url(
+            url, new_data)
+
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+        # Testing a HEAD on this Temp URL
+        resp, body = self.object_client.head(url)
+        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+
+        # Validate that the content of the object has been modified
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        _, body = self.object_client.get_object_using_temp_url(url)
+        self.assertEqual(body, new_data)
+
+    @attr(type=['gate', 'negative'])
+    def test_get_object_after_expiration_time(self):
+        EXPIRATION_TIME = 1
+        expires = int(time.time() + EXPIRATION_TIME)
+
+        # get a temp URL for the created object
+        url = self.get_temp_url(self.container_name,
+                                self.object_name, "GET",
+                                expires, self.key)
+
+        # temp URL is valid for 1 seconds, let's wait 3
+        time.sleep(EXPIRATION_TIME + 2)
+
+        self.assertRaises(exceptions.Unauthorized,
+                          self.object_client.get_object_using_temp_url,
+                          url)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 2fc4eb9..93dec71 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -150,7 +150,6 @@
             rand_name_root += suffix
         tenant_name = rand_name_root + "-tenant"
         tenant_desc = tenant_name + "-desc"
-        rand_name_root = rand_name(self.name)
         tenant = self._create_tenant(name=tenant_name,
                                      description=tenant_desc)
         if suffix:
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index a867a12..4b5127a 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -80,6 +80,21 @@
         self.http_obj = http.ClosingHttp(
             disable_ssl_certificate_validation=dscv)
 
+    def __str__(self):
+        STRING_LIMIT = 80
+        str_format = ("config:%s, user:%s, password:%s, "
+                      "auth_url:%s, tenant_name:%s, auth_version:%s, "
+                      "service:%s, base_url:%s, region:%s, "
+                      "endpoint_url:%s, build_interval:%s, build_timeout:%s"
+                      "\ntoken:%s..., \nheaders:%s...")
+        return str_format % (self.config, self.user, self.password,
+                             self.auth_url, self.tenant_name,
+                             self.auth_version, self.service,
+                             self.base_url, self.region, self.endpoint_url,
+                             self.build_interval, self.build_timeout,
+                             str(self.token)[0:STRING_LIMIT],
+                             str(self.headers)[0:STRING_LIMIT])
+
     def _set_auth(self):
         """
         Sets the token and base_url used in requests based on the strategy type
@@ -448,8 +463,8 @@
         # NOTE(mtreinish): This is for compatibility with Glance and swift
         # APIs. These are the return content types that Glance api v1
         # (and occasionally swift) are using.
-        TXT_ENC = ['text/plain; charset=UTF-8', 'text/html; charset=UTF-8',
-                   'text/plain; charset=utf-8']
+        TXT_ENC = ['text/plain', 'text/plain; charset=UTF-8',
+                   'text/html; charset=UTF-8', 'text/plain; charset=utf-8']
         XML_ENC = ['application/xml', 'application/xml; charset=UTF-8']
 
         if ctype in JSON_ENC or ctype in XML_ENC:
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 769c538..8349263 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -94,3 +94,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def list_floating_ip_pools(self, params=None):
+        """Returns a list of all floating IP Pools."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['floating_ip_pools']
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index 278cc88..2fffaa2 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -107,3 +107,13 @@
         except exceptions.NotFound:
             return True
         return False
+
+    def list_floating_ip_pools(self, params=None):
+        """Returns a list of all floating IP Pools."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index bf72bdc..9b688aa 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -193,6 +193,14 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
+    def lock_server(self, server_id, **kwargs):
+        """Locks the given server."""
+        return self.action(server_id, 'lock', None, **kwargs)
+
+    def unlock_server(self, server_id, **kwargs):
+        """Unlocks the given server."""
+        return self.action(server_id, 'unlock', None, **kwargs)
+
     def suspend_server(self, server_id, **kwargs):
         """Suspends the provided server."""
         return self.action(server_id, 'suspend', None, **kwargs)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index d338d45..2fee042 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -15,10 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import hashlib
-import hmac
-import urlparse
-
 from tempest.common import http
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
@@ -125,29 +121,20 @@
         resp, body = self.copy(url, headers=headers)
         return resp, body
 
-    def get_object_using_temp_url(self, container, object_name, expires, key):
-        """Retrieve object's data using temporary URL."""
-
-        self._set_auth()
-        method = 'GET'
-        path = "%s/%s/%s" % (urlparse.urlparse(self.base_url).path, container,
-                             object_name)
-        hmac_body = '%s\n%s\n%s' % (method, expires, path)
-        sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest()
-
-        url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container,
-                                                             object_name,
-                                                             sig, expires)
-
-        resp, body = self.get(url)
-        return resp, body
-
     def create_object_segments(self, container, object_name, segment, data):
         """Creates object segments."""
         url = "{0}/{1}/{2}".format(container, object_name, segment)
         resp, body = self.put(url, data, self.headers)
         return resp, body
 
+    def get_object_using_temp_url(self, url):
+        """Retrieve object's data using temp URL."""
+        return self.get(url)
+
+    def put_object_using_temp_url(self, url, data):
+        """Put data in an object using temp URL."""
+        return self.put(url, data, None)
+
 
 class ObjectClientCustomizedHeader(RestClient):