Merge "Remove trailing whitespaces in regular file"
diff --git a/README.rst b/README.rst
index 001404b..11ed58f 100644
--- a/README.rst
+++ b/README.rst
@@ -10,7 +10,7 @@
 
 To run Tempest, you first need to create a configuration file that
 will tell Tempest where to find the various OpenStack services and
-other testing behaviour switches.
+other testing behavior switches.
 
 The easiest way to create a configuration file is to copy the sample
 one in the ``etc/`` directory ::
@@ -19,7 +19,9 @@
     $> cp etc/tempest.conf.sample etc/tempest.conf
 
 After that, open up the ``etc/tempest.conf`` file and edit the
-variables to fit your test environment.
+configuration variables to match valid data in your environment.
+This includes your Keystone endpoint, a valid user and credentials,
+and reference data to be used in testing.
 
 .. note::
 
@@ -31,7 +33,40 @@
     devstack uploaded and set the image_ref value in the [environment]
     section in the tempest.conf to that image UUID.
 
-After setting up your configuration file, you can execute the set of
-Tempest tests by using ``nosetests`` ::
+    In addition, the ``tempest/tools/conf_from_devstack`` script can also be
+    used to generate a tempest.conf based on your localrc file.
+
+Tempest is not tied to any single test runner, but Nose been the most commonly
+used tool. After setting up your configuration file, you can execute
+the set of Tempest tests by using ``nosetests`` ::
 
     $> nosetests tempest
+
+Configuration
+-------------
+
+At present, there are three sections to be configured: nova, environment,
+and image. The nova section includes information about your Keystone endpoint,
+as well as valid credentials for a user. It also contains logical timeouts
+for certain actions. The environment section contains reference data to be
+used when testing the Compute portion of OpenStack, as well as feature flags
+for tests that may or may not work based on your hypervisor or current
+environment. Lastly, the image section contains credentials and endpoints for
+the Glance image service.
+
+Common Issues
+-------------
+
+Tempest was originally designed to primarily run against a full OpenStack
+deployment. Due to that focus, some issues may occur when running Tempest
+against devstack.
+
+Running Tempest, especially in parallel, against a devstack instance may
+cause requests to be rate limited, which will cause unexpected failures.
+Given the number of requests Tempest can make against a cluster, rate limiting
+should be disabled for all test accounts.
+
+Additionally, devstack only provides a single image which Nova can use.
+For the moment, the best solution is to provide the same image uuid for
+both image_ref and image_ref_alt. Tempest will skip tests as needed if it
+detects that both images are the same.
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 1530313..7dd3a86 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -2,9 +2,14 @@
 import httplib2
 import logging
 import sys
+import time
 from tempest import exceptions
 
 
+# redrive rate limited calls at most twice
+MAX_RECURSION_DEPTH = 2
+
+
 class RestClient(object):
 
     def __init__(self, config, user, key, auth_url, service, tenant_name=None):
@@ -107,7 +112,7 @@
         self.log.error('Response Headers: ' + str(resp))
         self.log.error('Response Body: ' + str(resp_body))
 
-    def request(self, method, url, headers=None, body=None):
+    def request(self, method, url, headers=None, body=None, depth=0):
         """A simple HTTP request interface."""
 
         self.http_obj = httplib2.Http()
@@ -138,6 +143,10 @@
             self._log(req_url, body, resp, resp_body)
             if 'overLimit' in resp_body:
                 raise exceptions.OverLimit(resp_body['overLimit']['message'])
+            elif depth < MAX_RECURSION_DEPTH:
+                delay = resp['Retry-After'] if 'Retry-After' in resp else 60
+                time.sleep(int(delay))
+                return self.request(method, url, headers, body, depth + 1)
             else:
                 raise exceptions.RateLimitExceeded(
                     message=resp_body['overLimitFault']['message'],
diff --git a/tempest/config.py b/tempest/config.py
index 2995447..b100530 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -56,7 +56,7 @@
     @property
     def use_ssl(self):
         """Specifies if we are using https."""
-        return bool(self.get("use_ssl", False))
+        return self.get("use_ssl", 'false') != 'false'
 
     @property
     def username(self):
diff --git a/tempest/services/nova/json/keypairs_client.py b/tempest/services/nova/json/keypairs_client.py
index 59057ab..9c912ef 100644
--- a/tempest/services/nova/json/keypairs_client.py
+++ b/tempest/services/nova/json/keypairs_client.py
@@ -14,7 +14,7 @@
                         'Accept': 'application/json'}
 
     def list_keypairs(self):
-        resp, body = self.client.get("/os-keypairs")
+        resp, body = self.client.get("os-keypairs")
         body = json.loads(body)
         #Each returned keypair is embedded within an unnecessary 'keypair'
         #element which is a deviation from other resources like floating-ips,
@@ -24,7 +24,7 @@
         return resp, body['keypairs']
 
     def get_keypair(self, key_name):
-        resp, body = self.client.get("/os-keypairs/%s" % str(key_name))
+        resp, body = self.client.get("os-keypairs/%s" % str(key_name))
         body = json.loads(body)
         return resp, body['keypair']
 
@@ -33,10 +33,10 @@
         if pub_key:
             post_body['keypair']['public_key'] = pub_key
         post_body = json.dumps(post_body)
-        resp, body = self.client.post("/os-keypairs",
+        resp, body = self.client.post("os-keypairs",
                                 headers=self.headers, body=post_body)
         body = json.loads(body)
         return resp, body['keypair']
 
     def delete_keypair(self, key_name):
-        return self.client.delete("/os-keypairs/%s" % str(key_name))
+        return self.client.delete("os-keypairs/%s" % str(key_name))
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index 605bd46..e0a26de 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -146,10 +146,13 @@
             if server_status == 'ERROR':
                 raise exceptions.BuildErrorException(server_id=server_id)
 
-            if int(time.time()) - start >= self.build_timeout:
+            timed_out = int(time.time()) - start >= self.build_timeout
+
+            if server_status != status and timed_out:
                 message = 'Server %s failed to reach %s status within the \
                 required time (%s s).' % (server_id, status,
                                           self.build_timeout)
+                message += ' Current status: %s.' % server_status
                 raise exceptions.TimeoutException(message)
 
     def list_addresses(self, server_id):
diff --git a/tempest/tests/test_server_actions.py b/tempest/tests/test_server_actions.py
index f91aa81..46e7ed5 100644
--- a/tempest/tests/test_server_actions.py
+++ b/tempest/tests/test_server_actions.py
@@ -75,13 +75,15 @@
 
         #Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
-        self.assertEqual(self.image_ref_alt, rebuilt_server['image']['id'])
+        rebuilt_image_id = rebuilt_server['image']['id']
+        self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
         self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
 
         #Verify the server properties after the rebuild completes
         self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
         resp, server = self.client.get_server(rebuilt_server['id'])
-        self.assertEqual(self.image_ref_alt, rebuilt_server['image']['id'])
+        rebuilt_image_id = rebuilt_server['image']['id']
+        self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
         self.assertEqual(new_name, rebuilt_server['name'])
 
     @attr(type='smoke')