Merge "Convert these tests of ServerBasicOps into one test."
diff --git a/.gitignore b/.gitignore
index b4dca86..c154603 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+AUTHORS
+ChangeLog
 *.pyc
 etc/tempest.conf
 include/swift_objects/swift_small
@@ -6,7 +8,7 @@
 *.log
 *.swp
 *.swo
-*.egg-info
+*.egg*
 .tox
 .venv
 dist
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..9a22c71
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,8 @@
+Ravikumar Venkatesan <ravikumar.venkatesan@hp.com> ravikumar-venkatesan <ravikumar.venkatesan@hp.com>
+Ravikumar Venkatesan <ravikumar.venkatesan@hp.com> ravikumar venkatesan <ravikumar.venkatesan@hp.com>
+Rohit Karajgi <rohit.karajgi@nttdata.com> Rohit Karajgi <rohit.karajgi@vertex.co.in>
+Jay Pipes <jaypipes@gmail.com> Jay Pipes <jpipes@librebox.gateway.2wire.net>
+<brian.waldon@rackspace.com> <bcwaldon@gmail.com>
+Daryl Walleck <daryl.walleck@rackspace.com> dwalleck <daryl.walleck@rackspace.com>
+<jeblair@hp.com> <corvus@inaugust.com>
+<jeblair@hp.com> <james.blair@rackspace.com>
diff --git a/run_tests.sh b/run_tests.sh
index 0df8a99..6b7ebec 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -10,6 +10,7 @@
   echo "  -f, --force              Force a clean re-build of the virtual environment. Useful when dependencies have been added."
   echo "  -s, --smoke              Only run smoke tests"
   echo "  -w, --whitebox           Only run whitebox tests"
+  echo "  -c, --nova-coverage      Enable Nova coverage collection"
   echo "  -p, --pep8               Just run pep8"
   echo "  -h, --help               Print this usage message"
   echo "  -d, --debug              Debug this script -- set -o xtrace"
@@ -25,6 +26,7 @@
     -s|--no-site-packages) no_site_packages=1;;
     -f|--force) force=1;;
     -d|--debug) set -o xtrace;;
+    -c|--nova-coverage) let nova_coverage=1;;
     -p|--pep8) let just_pep8=1;;
     -s|--smoke) noseargs="$noseargs --attr=type=smoke";;
     -w|--whitebox) noseargs="$noseargs --attr=type=whitebox";;
@@ -42,7 +44,7 @@
 no_site_packages=0
 force=0
 wrapper=""
-
+nova_coverage=0
 
 export NOSE_WITH_OPENSTACK=1
 export NOSE_OPENSTACK_COLOR=1
@@ -75,11 +77,22 @@
   echo "Running pep8 ..."
   srcfiles="`find tempest -type f -name "*.py"`"
   srcfiles+=" `find tools -type f -name "*.py"`"
+  srcfiles+=" `find stress -type f -name "*.py"`"
   srcfiles+=" setup.py"
 
-  ignore='--ignore=N4,E121,E122,E125,E126'
+  ignore='--ignore=E121,E122,E125,E126'
 
-  ${wrapper} python tools/hacking.py ${ignore} ${srcfiles}
+    ${wrapper} python tools/hacking.py ${ignore} ${srcfiles}
+}
+
+function run_coverage_start {
+  echo "Starting nova-coverage"
+  ${wrapper} python tools/tempest_coverage.py -c start
+}
+
+function run_coverage_report {
+  echo "Generating nova-coverage report"
+  ${wrapper} python tools/tempest_coverage.py -c report
 }
 
 NOSETESTS="nosetests $noseargs"
@@ -115,7 +128,15 @@
     exit
 fi
 
-run_tests || exit
+if [ $nova_coverage -eq 1 ]; then
+    run_coverage_start
+fi
+
+run_tests
+
+if [ $nova_coverage -eq 1 ]; then
+    run_coverage_report
+fi
 
 if [ -z "$noseargs" ]; then
   run_pep8
diff --git a/stress/config.py b/stress/config.py
index 64091cd..ca86ce5 100755
--- a/stress/config.py
+++ b/stress/config.py
@@ -39,15 +39,15 @@
 
     @property
     def nova_logdir(self):
-        """Directory containing log files on the compute nodes"""
+        """Directory containing log files on the compute nodes."""
         return self.get("nova_logdir", None)
 
     @property
     def controller(self):
-        """Controller host"""
+        """Controller host."""
         return self.get("controller", None)
 
     @property
     def max_instances(self):
-        """Maximum number of instances to create during test"""
+        """Maximum number of instances to create during test."""
         return self.get("max_instances", 16)
diff --git a/stress/pending_action.py b/stress/pending_action.py
index 635d2cc..abfa74d 100644
--- a/stress/pending_action.py
+++ b/stress/pending_action.py
@@ -46,7 +46,7 @@
         return False
 
     def check_timeout(self):
-        """Check for timeouts of TestCase actions"""
+        """Check for timeouts of TestCase actions."""
         time_diff = time.time() - self._start_time
         if time_diff > self._timeout:
             self._logger.error('%s exceeded timeout of %d' %
@@ -76,7 +76,7 @@
         self._target = target_server
 
     def _check_for_status(self, state_string):
-        """Check to see if the machine has transitioned states"""
+        """Check to see if the machine has transitioned states."""
         t = time.time()  # for debugging
         target = self._target
         _resp, body = self._manager.servers_client.get_server(target['id'])
diff --git a/stress/test_case.py b/stress/test_case.py
index fe510d5..d04ace0 100644
--- a/stress/test_case.py
+++ b/stress/test_case.py
@@ -25,5 +25,5 @@
         self._logger = logging.getLogger(self.__class__.__name__)
 
     def run(self, nova_manager, state_obj, *pargs, **kargs):
-        """Nova API methods to call that would modify state of the cluster"""
+        """Nova API methods to call that would modify state of the cluster."""
         return
diff --git a/stress/test_floating_ips.py b/stress/test_floating_ips.py
index 9d89510..97e4382 100755
--- a/stress/test_floating_ips.py
+++ b/stress/test_floating_ips.py
@@ -61,7 +61,7 @@
 
 
 class VerifyChangeFloatingIp(pending_action.PendingAction):
-    """Verify that floating ip was changed"""
+    """Verify that floating ip was changed."""
     def __init__(self, manager, floating_ip, timeout, add=None):
         super(VerifyChangeFloatingIp, self).__init__(manager, timeout=timeout)
         self.floating_ip = floating_ip
diff --git a/stress/test_server_actions.py b/stress/test_server_actions.py
index 3b4e71c..a2032f0 100644
--- a/stress/test_server_actions.py
+++ b/stress/test_server_actions.py
@@ -27,7 +27,7 @@
 
 
 class TestRebootVM(test_case.StressTestCase):
-    """Reboot a server"""
+    """Reboot a server."""
 
     def run(self, manager, state, *pargs, **kwargs):
         """
@@ -132,7 +132,7 @@
 
 # This code needs to be tested against a cluster that supports resize.
 #class TestResizeVM(test_case.StressTestCase):
-#    """Resize a server (change flavors)"""
+#    """Resize a server (change flavors)."""
 #
 #    def run(self, manager, state, *pargs, **kwargs):
 #        """
@@ -193,7 +193,7 @@
 #                              timeout=_timeout)
 #
 #class VerifyResizeVM(pending_action.PendingServerAction):
-#    """Verify that resizing of a VM was successful"""
+#    """Verify that resizing of a VM was successful."""
 #    States = enum('VERIFY_RESIZE_CHECK', 'ACTIVE_CHECK')
 #
 #    def __init__(self, manager, state, created_server,
diff --git a/stress/test_servers.py b/stress/test_servers.py
index 9957cdb..e1cb4d1 100644
--- a/stress/test_servers.py
+++ b/stress/test_servers.py
@@ -96,7 +96,7 @@
 
 
 class VerifyCreateVM(pending_action.PendingServerAction):
-    """Verify that VM was built and is running"""
+    """Verify that VM was built and is running."""
     def __init__(self, manager,
                  state,
                  created_server,
@@ -175,7 +175,7 @@
 
 
 class VerifyKillActiveVM(pending_action.PendingServerAction):
-    """Verify that server was destroyed"""
+    """Verify that server was destroyed."""
 
     def retry(self):
         """
@@ -238,7 +238,7 @@
 
 
 class TestUpdateVMName(test_case.StressTestCase):
-    """Class to change the name of the active server"""
+    """Class to change the name of the active server."""
     def run(self, manager, state, *pargs, **kwargs):
         """
         Issue HTTP POST request to change the name of active server.
@@ -288,7 +288,7 @@
 
 
 class VerifyUpdateVMName(pending_action.PendingServerAction):
-    """Check that VM has new name"""
+    """Check that VM has new name."""
     def retry(self):
         """
         Check that VM has new name. Update local view of `state` to RUNNING.
diff --git a/stress/tests/floating_ips.py b/stress/tests/floating_ips.py
index d62e892..6a4452c 100755
--- a/stress/tests/floating_ips.py
+++ b/stress/tests/floating_ips.py
@@ -11,7 +11,7 @@
 #    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.
-"""Stress test that associates/disasssociates floating ips"""
+"""Stress test that associates/disasssociates floating ips."""
 
 from stress.basher import BasherAction
 from stress.driver import *
diff --git a/stress/tests/hard_reboots.py b/stress/tests/hard_reboots.py
index f35c6de..fe57be1 100644
--- a/stress/tests/hard_reboots.py
+++ b/stress/tests/hard_reboots.py
@@ -11,7 +11,7 @@
 #    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.
-"""Test that reboots random instances in a Nova cluster"""
+"""Test that reboots random instances in a Nova cluster."""
 
 
 from stress.test_servers import *
diff --git a/stress/utils/util.py b/stress/utils/util.py
index f90796d..ec63b99 100644
--- a/stress/utils/util.py
+++ b/stress/utils/util.py
@@ -50,6 +50,6 @@
 
 
 def enum(*sequential, **named):
-    """Create auto-incremented enumerated types"""
+    """Create auto-incremented enumerated types."""
     enums = dict(zip(sequential, range(len(sequential))), **named)
     return type('Enum', (), enums)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index c96fc4f..287ef56 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -303,7 +303,7 @@
         return resp, resp_body
 
     def wait_for_resource_deletion(self, id):
-        """Waits for a resource to be deleted"""
+        """Waits for a resource to be deleted."""
         start_time = int(time.time())
         while True:
             if self.is_resource_deleted(id):
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 1b40ddc..151060f 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -48,7 +48,7 @@
         self.buf_size = 1024
 
     def _get_ssh_connection(self):
-        """Returns an ssh connection to the specified host"""
+        """Returns an ssh connection to the specified host."""
         _timeout = True
         ssh = paramiko.SSHClient()
         ssh.set_missing_host_key_policy(
@@ -79,7 +79,7 @@
         return (time.time() - timeout) > start_time
 
     def connect_until_closed(self):
-        """Connect to the server and wait until connection is lost"""
+        """Connect to the server and wait until connection is lost."""
         try:
             ssh = self._get_ssh_connection()
             _transport = ssh.get_transport()
@@ -137,7 +137,7 @@
         return ''.join(out_data)
 
     def test_connection_auth(self):
-        """ Returns true if ssh can connect to server"""
+        """Returns true if ssh can connect to server."""
         try:
             connection = self._get_ssh_connection()
             connection.close()
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index 951fb61..3a7661c 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -29,7 +29,7 @@
 
 def build_url(host, port, api_version=None, path=None,
               params=None, use_ssl=False):
-    """Build the request URL from given host, port, path and parameters"""
+    """Build the request URL from given host, port, path and parameters."""
 
     pattern = 'v\d\.\d'
     if re.match(pattern, path):
@@ -59,7 +59,7 @@
 
 
 def parse_image_id(image_ref):
-    """Return the image id from a given image ref"""
+    """Return the image id from a given image ref."""
     return  image_ref.rsplit('/')[-1]
 
 
diff --git a/tempest/config.py b/tempest/config.py
index 2b0eb70..8233dd5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -458,7 +458,7 @@
 
 # TODO(jaypipes): Move this to a common utils (not data_utils...)
 def singleton(cls):
-    """Simple wrapper for classes that should only have a single instance"""
+    """Simple wrapper for classes that should only have a single instance."""
     instances = {}
 
     def getinstance():
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index c9e2f95..178c2f2 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -118,7 +118,7 @@
 
 
 class SSHExecCommandFailed(TempestException):
-    ''' Raised when remotely executed command returns nonzero status.  '''
+    """Raised when remotely executed command returns nonzero status."""
     message = ("Command '%(command)s', exit status: %(exit_status)d, "
                "Error:\n%(strerror)s")
 
diff --git a/tempest/openstack/common/cfg.py b/tempest/openstack/common/cfg.py
index 25667a4..1bbfe6a 100644
--- a/tempest/openstack/common/cfg.py
+++ b/tempest/openstack/common/cfg.py
@@ -1417,7 +1417,7 @@
         logger.log(lvl, "=" * 80)
 
         def _sanitize(opt, value):
-            """Obfuscate values of options declared secret"""
+            """Obfuscate values of options declared secret."""
             return value if not opt.secret else '*' * len(str(value))
 
         for opt_name in sorted(self._opts):
diff --git a/tempest/openstack/common/iniparser.py b/tempest/openstack/common/iniparser.py
index 2412844..b5cb604 100644
--- a/tempest/openstack/common/iniparser.py
+++ b/tempest/openstack/common/iniparser.py
@@ -100,15 +100,15 @@
             self._assignment(key, value)
 
     def assignment(self, key, value):
-        """Called when a full assignment is parsed"""
+        """Called when a full assignment is parsed."""
         raise NotImplementedError()
 
     def new_section(self, section):
-        """Called when a new section is started"""
+        """Called when a new section is started."""
         raise NotImplementedError()
 
     def comment(self, comment):
-        """Called when a comment is parsed"""
+        """Called when a comment is parsed."""
         pass
 
     def error_invalid_assignment(self, line):
diff --git a/tempest/services/boto/__init__.py b/tempest/services/boto/__init__.py
index 58a2b37..1365435 100644
--- a/tempest/services/boto/__init__.py
+++ b/tempest/services/boto/__init__.py
@@ -35,8 +35,8 @@
                  auth_url=None, tenant_name=None,
                  *args, **kwargs):
 
-        self.connection_timeout = config.boto.http_socket_timeout
-        self.num_retries = config.boto.num_retries
+        self.connection_timeout = str(config.boto.http_socket_timeout)
+        self.num_retries = str(config.boto.num_retries)
         self.build_timeout = config.boto.build_timeout
         # We do not need the "path":  "/token" part
         if auth_url:
@@ -73,7 +73,7 @@
         boto.config.set("Boto", "num_retries", retries)
 
     def __getattr__(self, name):
-        """Automatically creates methods for the allowed methods set"""
+        """Automatically creates methods for the allowed methods set."""
         if name in self.ALLOWED_METHODS:
             def func(self, *args, **kwargs):
                 with closing(self.get_connection()) as conn:
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 1c8d4f3..bd339b2 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -51,20 +51,23 @@
         body = json.loads(body)
         return resp, body['flavor']
 
-    def create_flavor(self, name, ram, vcpus, disk, ephemeral, flavor_id,
-                      swap, rxtx):
-        """Creates a new flavor or instance type"""
+    def create_flavor(self, name, ram, vcpus, disk, flavor_id, **kwargs):
+        """Creates a new flavor or instance type."""
         post_body = {
             'name': name,
             'ram': ram,
             'vcpus': vcpus,
             'disk': disk,
-            'OS-FLV-EXT-DATA:ephemeral': ephemeral,
             'id': flavor_id,
-            'swap': swap,
-            'rxtx_factor': rxtx,
         }
-
+        if kwargs.get('ephemeral'):
+            post_body['OS-FLV-EXT-DATA:ephemeral'] = kwargs.get('ephemeral')
+        if kwargs.get('swap'):
+            post_body['swap'] = kwargs.get('swap')
+        if kwargs.get('rxtx'):
+            post_body['rxtx_factor'] = kwargs.get('rxtx')
+        if kwargs.get('is_public'):
+            post_body['os-flavor-access:is_public'] = kwargs.get('is_public')
         post_body = json.dumps({'flavor': post_body})
         resp, body = self.post('flavors', post_body, self.headers)
 
@@ -72,5 +75,15 @@
         return resp, body['flavor']
 
     def delete_flavor(self, flavor_id):
-        """Deletes the given flavor"""
+        """Deletes the given flavor."""
         return self.delete("flavors/%s" % str(flavor_id))
+
+    def is_resource_deleted(self, id):
+        #Did not use get_flavor_details(id) for verification as it gives
+        #200 ok even for deleted id. LP #981263
+        #we can remove the loop here and use get by ID when bug gets sortedout
+        resp, flavors = self.list_flavors_with_detail()
+        for flavor in flavors:
+            if flavor['id'] == id:
+                return False
+        return True
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 15882c7..d73b8a9 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -29,7 +29,7 @@
         self.service = self.config.compute.catalog_type
 
     def list_floating_ips(self, params=None):
-        """Returns a list of all floating IPs filtered by any parameters"""
+        """Returns a list of all floating IPs filtered by any parameters."""
         url = 'os-floating-ips'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -39,7 +39,7 @@
         return resp, body['floating_ips']
 
     def get_floating_ip_details(self, floating_ip_id):
-        """Get the details of a floating IP"""
+        """Get the details of a floating IP."""
         url = "os-floating-ips/%s" % str(floating_ip_id)
         resp, body = self.get(url)
         body = json.loads(body)
@@ -48,20 +48,20 @@
         return resp, body['floating_ip']
 
     def create_floating_ip(self):
-        """Allocate a floating IP to the project"""
+        """Allocate a floating IP to the project."""
         url = 'os-floating-ips'
         resp, body = self.post(url, None, None)
         body = json.loads(body)
         return resp, body['floating_ip']
 
     def delete_floating_ip(self, floating_ip_id):
-        """Deletes the provided floating IP from the project"""
+        """Deletes the provided floating IP from the project."""
         url = "os-floating-ips/%s" % str(floating_ip_id)
         resp, body = self.delete(url)
         return resp, body
 
     def associate_floating_ip_to_server(self, floating_ip, server_id):
-        """Associate the provided floating IP to a specific server"""
+        """Associate the provided floating IP to a specific server."""
         url = "servers/%s/action" % str(server_id)
         post_body = {
             'addFloatingIp': {
@@ -74,7 +74,7 @@
         return resp, body
 
     def disassociate_floating_ip_from_server(self, floating_ip, server_id):
-        """Disassociate the provided floating IP from a specific server"""
+        """Disassociate the provided floating IP from a specific server."""
         url = "servers/%s/action" % str(server_id)
         post_body = {
             'removeFloatingIp': {
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 517e230..dc3c524 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -11,7 +11,7 @@
         self.service = self.config.compute.catalog_type
 
     def list_hosts(self):
-        """Lists all hosts"""
+        """Lists all hosts."""
 
         url = 'os-hosts'
         resp, body = self.get(url)
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 452400a..376dafc 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -33,7 +33,7 @@
         self.build_timeout = self.config.compute.build_timeout
 
     def create_image(self, server_id, name, meta=None):
-        """Creates an image of the original server"""
+        """Creates an image of the original server."""
 
         post_body = {
             'createImage': {
@@ -50,7 +50,7 @@
         return resp, body
 
     def list_images(self, params=None):
-        """Returns a list of all images filtered by any parameters"""
+        """Returns a list of all images filtered by any parameters."""
         url = 'images'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -60,7 +60,7 @@
         return resp, body['images']
 
     def list_images_with_detail(self, params=None):
-        """Returns a detailed list of images filtered by any parameters"""
+        """Returns a detailed list of images filtered by any parameters."""
         url = 'images/detail'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -70,13 +70,13 @@
         return resp, body['images']
 
     def get_image(self, image_id):
-        """Returns the details of a single image"""
+        """Returns the details of a single image."""
         resp, body = self.get("images/%s" % str(image_id))
         body = json.loads(body)
         return resp, body['image']
 
     def delete_image(self, image_id):
-        """Deletes the provided image"""
+        """Deletes the provided image."""
         return self.delete("images/%s" % str(image_id))
 
     def wait_for_image_resp_code(self, image_id, code):
@@ -110,13 +110,13 @@
                 raise exceptions.TimeoutException
 
     def list_image_metadata(self, image_id):
-        """Lists all metadata items for an image"""
+        """Lists all metadata items for an image."""
         resp, body = self.get("images/%s/metadata" % str(image_id))
         body = json.loads(body)
         return resp, body['metadata']
 
     def set_image_metadata(self, image_id, meta):
-        """Sets the metadata for an image"""
+        """Sets the metadata for an image."""
         post_body = json.dumps({'metadata': meta})
         resp, body = self.put('images/%s/metadata' % str(image_id),
                               post_body, self.headers)
@@ -124,7 +124,7 @@
         return resp, body['metadata']
 
     def update_image_metadata(self, image_id, meta):
-        """Updates the metadata for an image"""
+        """Updates the metadata for an image."""
         post_body = json.dumps({'metadata': meta})
         resp, body = self.post('images/%s/metadata' % str(image_id),
                                post_body, self.headers)
@@ -132,13 +132,13 @@
         return resp, body['metadata']
 
     def get_image_metadata_item(self, image_id, key):
-        """Returns the value for a specific image metadata key"""
+        """Returns the value for a specific image metadata key."""
         resp, body = self.get("images/%s/metadata/%s" % (str(image_id), key))
         body = json.loads(body)
         return resp, body['meta']
 
     def set_image_metadata_item(self, image_id, key, meta):
-        """Sets the value for a specific image metadata key"""
+        """Sets the value for a specific image metadata key."""
         post_body = json.dumps({'meta': meta})
         resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
                               post_body, self.headers)
@@ -146,7 +146,7 @@
         return resp, body['meta']
 
     def delete_image_metadata_item(self, image_id, key):
-        """Deletes a single image metadata key/value pair"""
+        """Deletes a single image metadata key/value pair."""
         resp, body = self.delete("images/%s/metadata/%s" %
                                  (str(image_id), key))
         return resp, body
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 2cc417f..543b015 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -28,7 +28,7 @@
         self.service = self.config.compute.catalog_type
 
     def get_quota_set(self, tenant_id):
-        """List the quota set for a tenant"""
+        """List the quota set for a tenant."""
 
         url = 'os-quota-sets/%s' % str(tenant_id)
         resp, body = self.get(url)
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index f2586e5..95f2831 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -30,7 +30,7 @@
         self.service = self.config.compute.catalog_type
 
     def list_security_groups(self, params=None):
-        """List all security groups for a user"""
+        """List all security groups for a user."""
 
         url = 'os-security-groups'
         if params:
@@ -41,7 +41,7 @@
         return resp, body['security_groups']
 
     def get_security_group(self, security_group_id):
-        """Get the details of a Security Group"""
+        """Get the details of a Security Group."""
         url = "os-security-groups/%s" % str(security_group_id)
         resp, body = self.get(url)
         body = json.loads(body)
@@ -63,7 +63,7 @@
         return resp, body['security_group']
 
     def delete_security_group(self, security_group_id):
-        """Deletes the provided Security Group"""
+        """Deletes the provided Security Group."""
         return self.delete('os-security-groups/%s' % str(security_group_id))
 
     def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
@@ -93,5 +93,5 @@
         return resp, body['security_group_rule']
 
     def delete_security_group_rule(self, group_rule_id):
-        """Deletes the provided Security Group rule"""
+        """Deletes the provided Security Group rule."""
         return self.delete('os-security-group-rules/%s' % str(group_rule_id))
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 2e34ef8..b832af0 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -110,17 +110,17 @@
         return resp, body['server']
 
     def get_server(self, server_id):
-        """Returns the details of an existing server"""
+        """Returns the details of an existing server."""
         resp, body = self.get("servers/%s" % str(server_id))
         body = json.loads(body)
         return resp, body['server']
 
     def delete_server(self, server_id):
-        """Deletes the given server"""
+        """Deletes the given server."""
         return self.delete("servers/%s" % str(server_id))
 
     def list_servers(self, params=None):
-        """Lists all servers for a user"""
+        """Lists all servers for a user."""
 
         url = 'servers'
         if params:
@@ -131,7 +131,7 @@
         return resp, body
 
     def list_servers_with_detail(self, params=None):
-        """Lists all servers in detail for a user"""
+        """Lists all servers in detail for a user."""
 
         url = 'servers/detail'
         if params:
@@ -142,7 +142,7 @@
         return resp, body
 
     def wait_for_server_status(self, server_id, status):
-        """Waits for a server to reach a given status"""
+        """Waits for a server to reach a given status."""
         resp, body = self.get_server(server_id)
         server_status = body['status']
         start = int(time.time())
@@ -165,7 +165,7 @@
                 raise exceptions.TimeoutException(message)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
-        """Waits for server to reach termination"""
+        """Waits for server to reach termination."""
         start_time = int(time.time())
         while True:
             try:
@@ -183,20 +183,20 @@
             time.sleep(self.build_interval)
 
     def list_addresses(self, server_id):
-        """Lists all addresses for a server"""
+        """Lists all addresses for a server."""
         resp, body = self.get("servers/%s/ips" % str(server_id))
         body = json.loads(body)
         return resp, body['addresses']
 
     def list_addresses_by_network(self, server_id, network_id):
-        """Lists all addresses of a specific network type for a server"""
+        """Lists all addresses of a specific network type for a server."""
         resp, body = self.get("servers/%s/ips/%s" %
                               (str(server_id), network_id))
         body = json.loads(body)
         return resp, body
 
     def change_password(self, server_id, password):
-        """Changes the root password for the server"""
+        """Changes the root password for the server."""
         post_body = {
             'changePassword': {
                 'adminPass': password,
@@ -208,7 +208,7 @@
                          post_body, self.headers)
 
     def reboot(self, server_id, reboot_type):
-        """Reboots a server"""
+        """Reboots a server."""
         post_body = {
             'reboot': {
                 'type': reboot_type,
@@ -221,7 +221,7 @@
 
     def rebuild(self, server_id, image_ref, name=None, meta=None,
                 personality=None, adminPass=None, disk_config=None):
-        """Rebuilds a server with a new image"""
+        """Rebuilds a server with a new image."""
         post_body = {
                 'imageRef': image_ref,
         }
@@ -264,7 +264,7 @@
         return resp, body
 
     def confirm_resize(self, server_id):
-        """Confirms the flavor change for a server"""
+        """Confirms the flavor change for a server."""
         post_body = {
             'confirmResize': None,
         }
@@ -275,7 +275,7 @@
         return resp, body
 
     def revert_resize(self, server_id):
-        """Reverts a server back to its original flavor"""
+        """Reverts a server back to its original flavor."""
         post_body = {
             'revertResize': None,
         }
@@ -286,7 +286,7 @@
         return resp, body
 
     def create_image(self, server_id, image_name):
-        """Creates an image of the given server"""
+        """Creates an image of the given server."""
         post_body = {
             'createImage': {
                 'name': image_name,
@@ -345,7 +345,7 @@
                                post_body, self.headers)
 
     def attach_volume(self, server_id, volume_id, device='/dev/vdz'):
-        """Attaches a volume to a server instance"""
+        """Attaches a volume to a server instance."""
         post_body = json.dumps({
             'volumeAttachment': {
                 'volumeId': volume_id,
@@ -357,13 +357,13 @@
         return resp, body
 
     def detach_volume(self, server_id, volume_id):
-        """Detaches a volume from a server instance"""
+        """Detaches a volume from a server instance."""
         resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
                                  (server_id, volume_id))
         return resp, body
 
     def add_security_group(self, server_id, security_group_name):
-        """Adds a security group to the server"""
+        """Adds a security group to the server."""
         post_body = {
             'addSecurityGroup': {
                 'name': security_group_name
@@ -374,7 +374,7 @@
                          post_body, self.headers)
 
     def remove_security_group(self, server_id, security_group_name):
-        """Removes a security group from the server"""
+        """Removes a security group from the server."""
         post_body = {
             'removeSecurityGroup': {
                 'name': security_group_name
@@ -385,7 +385,7 @@
                          post_body, self.headers)
 
     def live_migrate_server(self, server_id, dest_host, use_block_migration):
-        """ This should be called with administrator privileges """
+        """This should be called with administrator privileges ."""
 
         migrate_params = {
             "disk_over_commit": False,
@@ -409,7 +409,7 @@
         return resp, body['servers']
 
     def migrate_server(self, server_id):
-        """Migrates a server to a new host"""
+        """Migrates a server to a new host."""
         post_body = {'migrate': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
@@ -417,7 +417,7 @@
         return resp, body
 
     def confirm_migration(self, server_id):
-        """Confirms the migration of a server"""
+        """Confirms the migration of a server."""
         post_body = {'confirmResize': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
@@ -425,63 +425,63 @@
         return resp, body
 
     def lock_server(self, server_id):
-        """Locks the given server"""
+        """Locks the given server."""
         post_body = {'lock': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def unlock_server(self, server_id):
-        """UNlocks the given server"""
+        """UNlocks the given server."""
         post_body = {'unlock': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def start_server(self, server_id):
-        """Starts the given server"""
+        """Starts the given server."""
         post_body = {'os-start': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def stop_server(self, server_id):
-        """Stops the given server"""
+        """Stops the given server."""
         post_body = {'os-stop': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def suspend_server(self, server_id):
-        """Suspends the provded server"""
+        """Suspends the provded server."""
         post_body = {'suspend': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def resume_server(self, server_id):
-        """Un-suspends the provded server"""
+        """Un-suspends the provded server."""
         post_body = {'resume': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def pause_server(self, server_id):
-        """Pauses the provded server"""
+        """Pauses the provded server."""
         post_body = {'pause': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def unpause_server(self, server_id):
-        """Un-pauses the provded server"""
+        """Un-pauses the provded server."""
         post_body = {'unpause': 'null'}
         post_body = json.dumps(post_body)
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body, self.headers)
 
     def reset_state(self, server_id, new_state='error'):
-        """Resets the state of a server to active/error"""
+        """Resets the state of a server to active/error."""
         post_body = {
             'os-resetState': {
                 'state': new_state
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index 6cf6c23..a5f6ec3 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -34,7 +34,7 @@
         self.build_timeout = self.config.volume.build_timeout
 
     def list_volumes(self, params=None):
-        """List all the volumes created"""
+        """List all the volumes created."""
         url = 'os-volumes'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -44,7 +44,7 @@
         return resp, body['volumes']
 
     def list_volumes_with_detail(self, params=None):
-        """List all the details of volumes"""
+        """List all the details of volumes."""
         url = 'os-volumes/detail'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -54,7 +54,7 @@
         return resp, body['volumes']
 
     def get_volume(self, volume_id, wait=None):
-        """Returns the details of a single volume"""
+        """Returns the details of a single volume."""
         url = "os-volumes/%s" % str(volume_id)
         resp, body = self.get(url, wait=wait)
         body = json.loads(body)
@@ -80,11 +80,11 @@
         return resp, body['volume']
 
     def delete_volume(self, volume_id):
-        """Deletes the Specified Volume"""
+        """Deletes the Specified Volume."""
         return self.delete("os-volumes/%s" % str(volume_id))
 
     def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status"""
+        """Waits for a Volume to reach a given status."""
         resp, body = self.get_volume(volume_id)
         volume_name = body['displayName']
         volume_status = body['status']
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index 1dc34a5..c011fe4 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -27,7 +27,9 @@
 
 
 XMLNS_OS_FLV_EXT_DATA = \
-        "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1"
+    "http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1"
+XMLNS_OS_FLV_ACCESS = \
+    "http://docs.openstack.org/compute/ext/flavor_access/api/v1.1"
 
 
 class FlavorsClientXML(RestClientXML):
@@ -84,26 +86,42 @@
         flavor = self._format_flavor(body)
         return resp, flavor
 
-    def create_flavor(self, name, ram, vcpus, disk, ephemeral, flavor_id,
-                      swap, rxtx):
-        """Creates a new flavor or instance type"""
+    def create_flavor(self, name, ram, vcpus, disk, flavor_id, **kwargs):
+        """Creates a new flavor or instance type."""
         flavor = Element("flavor",
                          xmlns=XMLNS_11,
                          ram=ram,
                          vcpus=vcpus,
                          disk=disk,
                          id=flavor_id,
-                         swap=swap,
-                         rxtx_factor=rxtx,
                          name=name)
+        if kwargs.get('rxtx'):
+            flavor.add_attr('rxtx_factor', kwargs.get('rxtx'))
+        if kwargs.get('swap'):
+            flavor.add_attr('swap', kwargs.get('swap'))
+        if kwargs.get('ephemeral'):
+            flavor.add_attr('OS-FLV-EXT-DATA:ephemeral',
+                            kwargs.get('ephemeral'))
+        if kwargs.get('is_public'):
+            flavor.add_attr('os-flavor-access:is_public',
+                            kwargs.get('is_public'))
         flavor.add_attr('xmlns:OS-FLV-EXT-DATA', XMLNS_OS_FLV_EXT_DATA)
-        flavor.add_attr('OS-FLV-EXT-DATA:ephemeral', ephemeral)
-
+        flavor.add_attr('xmlns:os-flavor-access', XMLNS_OS_FLV_ACCESS)
         resp, body = self.post('flavors', str(Document(flavor)), self.headers)
         body = xml_to_json(etree.fromstring(body))
         flavor = self._format_flavor(body)
         return resp, flavor
 
     def delete_flavor(self, flavor_id):
-        """Deletes the given flavor"""
+        """Deletes the given flavor."""
         return self.delete("flavors/%s" % str(flavor_id), self.headers)
+
+    def is_resource_deleted(self, id):
+        #Did not use get_flavor_details(id) for verification as it gives
+        #200 ok even for deleted id. LP #981263
+        #we can remove the loop here and use get by ID when bug gets sortedout
+        resp, flavors = self.list_flavors_with_detail()
+        for flavor in flavors:
+            if flavor['id'] == id:
+                return False
+        return True
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index a0059a8..74b4be2 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -42,7 +42,7 @@
         return json
 
     def list_floating_ips(self, params=None):
-        """Returns a list of all floating IPs filtered by any parameters"""
+        """Returns a list of all floating IPs filtered by any parameters."""
         url = 'os-floating-ips'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -52,7 +52,7 @@
         return resp, body
 
     def get_floating_ip_details(self, floating_ip_id):
-        """Get the details of a floating IP"""
+        """Get the details of a floating IP."""
         url = "os-floating-ips/%s" % str(floating_ip_id)
         resp, body = self.get(url, self.headers)
         body = self._parse_floating_ip(etree.fromstring(body))
@@ -61,20 +61,20 @@
         return resp, body
 
     def create_floating_ip(self):
-        """Allocate a floating IP to the project"""
+        """Allocate a floating IP to the project."""
         url = 'os-floating-ips'
         resp, body = self.post(url, None, self.headers)
         body = self._parse_floating_ip(etree.fromstring(body))
         return resp, body
 
     def delete_floating_ip(self, floating_ip_id):
-        """Deletes the provided floating IP from the project"""
+        """Deletes the provided floating IP from the project."""
         url = "os-floating-ips/%s" % str(floating_ip_id)
         resp, body = self.delete(url, self.headers)
         return resp, body
 
     def associate_floating_ip_to_server(self, floating_ip, server_id):
-        """Associate the provided floating IP to a specific server"""
+        """Associate the provided floating IP to a specific server."""
         url = "servers/%s/action" % str(server_id)
         doc = Document()
         server = Element("addFloatingIp")
@@ -84,7 +84,7 @@
         return resp, body
 
     def disassociate_floating_ip_from_server(self, floating_ip, server_id):
-        """Disassociate the provided floating IP from a specific server"""
+        """Disassociate the provided floating IP from a specific server."""
         url = "servers/%s/action" % str(server_id)
         doc = Document()
         server = Element("removeFloatingIp")
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index efed3fb..bde9e16 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -43,7 +43,7 @@
         return self._parse_links(node, json)
 
     def _parse_image(self, node):
-        """Parses detailed XML image information into dictionary"""
+        """Parses detailed XML image information into dictionary."""
         json = xml_to_json(node)
 
         self._parse_links(node, json)
@@ -61,7 +61,7 @@
         return json
 
     def _parse_links(self, node, json):
-        """Append multiple links under a list"""
+        """Append multiple links under a list."""
         # look for links
         if 'link' in json:
             # remove single link element
@@ -70,8 +70,15 @@
                              node.findall('{http://www.w3.org/2005/Atom}link')]
         return json
 
+    def _parse_images(self, xml):
+        json = {'images': []}
+        images = xml.getchildren()
+        for image in images:
+            json['images'].append(self._parse_image(image))
+        return json
+
     def create_image(self, server_id, name, meta=None):
-        """Creates an image of the original server"""
+        """Creates an image of the original server."""
         post_body = Element('createImage', name=name)
 
         if meta:
@@ -86,17 +93,17 @@
         return resp, body
 
     def list_images(self, params=None):
-        """Returns a list of all images filtered by any parameters"""
+        """Returns a list of all images filtered by any parameters."""
         url = 'images'
         if params:
             url += '?%s' % urllib.urlencode(params)
 
         resp, body = self.get(url, self.headers)
-        body = xml_to_json(etree.fromstring(body))
+        body = self._parse_images(etree.fromstring(body))
         return resp, body['images']
 
     def list_images_with_detail(self, params=None):
-        """Returns a detailed list of images filtered by any parameters"""
+        """Returns a detailed list of images filtered by any parameters."""
         url = 'images/detail'
         if params:
             param_list = urllib.urlencode(params)
@@ -104,17 +111,17 @@
             url = "images/detail?" + param_list
 
         resp, body = self.get(url, self.headers)
-        body = xml_to_json(etree.fromstring(body))
+        body = self._parse_images(etree.fromstring(body))
         return resp, body['images']
 
     def get_image(self, image_id):
-        """Returns the details of a single image"""
+        """Returns the details of a single image."""
         resp, body = self.get("images/%s" % str(image_id), self.headers)
         body = self._parse_image(etree.fromstring(body))
         return resp, body
 
     def delete_image(self, image_id):
-        """Deletes the provided image"""
+        """Deletes the provided image."""
         return self.delete("images/%s" % str(image_id), self.headers)
 
     def wait_for_image_resp_code(self, image_id, code):
@@ -147,14 +154,14 @@
                 raise exceptions.TimeoutException
 
     def list_image_metadata(self, image_id):
-        """Lists all metadata items for an image"""
+        """Lists all metadata items for an image."""
         resp, body = self.get("images/%s/metadata" % str(image_id),
                               self.headers)
         body = xml_to_json(etree.fromstring(body))
         return resp, body['metadata']
 
     def set_image_metadata(self, image_id, meta):
-        """Sets the metadata for an image"""
+        """Sets the metadata for an image."""
         post_body = json.dumps({'metadata': meta})
         resp, body = self.put('images/%s/metadata' % str(image_id),
                               post_body, self.headers)
@@ -162,7 +169,7 @@
         return resp, body['metadata']
 
     def update_image_metadata(self, image_id, meta):
-        """Updates the metadata for an image"""
+        """Updates the metadata for an image."""
         post_body = Element('metadata', meta)
         for k, v in meta:
             metadata = Element('meta', key=k)
@@ -176,14 +183,14 @@
         return resp, body['metadata']
 
     def get_image_metadata_item(self, image_id, key):
-        """Returns the value for a specific image metadata key"""
+        """Returns the value for a specific image metadata key."""
         resp, body = self.get("images/%s/metadata/%s.xml" %
                               (str(image_id), key), self.headers)
         body = xml_to_json(etree.fromstring(body))
         return resp, body['meta']
 
     def set_image_metadata_item(self, image_id, key, meta):
-        """Sets the value for a specific image metadata key"""
+        """Sets the value for a specific image metadata key."""
         post_body = json.dumps({'meta': meta})
         resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
                               post_body, self.headers)
@@ -191,7 +198,7 @@
         return resp, body['meta']
 
     def delete_image_metadata_item(self, image_id, key):
-        """Deletes a single image metadata key/value pair"""
+        """Deletes a single image metadata key/value pair."""
         resp, body = self.delete("images/%s/metadata/%s" % (str(image_id), key,
                                  self.headers))
         return resp, body
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index bfd6c7a..ac70f1b 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -44,7 +44,7 @@
         return json
 
     def list_security_groups(self, params=None):
-        """List all security groups for a user"""
+        """List all security groups for a user."""
 
         url = 'os-security-groups'
         if params:
@@ -55,7 +55,7 @@
         return resp, body
 
     def get_security_group(self, security_group_id):
-        """Get the details of a Security Group"""
+        """Get the details of a Security Group."""
         url = "os-security-groups/%s" % str(security_group_id)
         resp, body = self.get(url, self.headers)
         body = self._parse_body(etree.fromstring(body))
@@ -78,7 +78,7 @@
         return resp, body
 
     def delete_security_group(self, security_group_id):
-        """Deletes the provided Security Group"""
+        """Deletes the provided Security Group."""
         return self.delete('os-security-groups/%s' %
                            str(security_group_id), self.headers)
 
@@ -125,6 +125,6 @@
         return resp, body
 
     def delete_security_group_rule(self, group_rule_id):
-        """Deletes the provided Security Group rule"""
+        """Deletes the provided Security Group rule."""
         return self.delete('os-security-group-rules/%s' %
                            str(group_rule_id), self.headers)
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index b33335d..4a84646 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -33,6 +33,69 @@
 LOG = logging.getLogger(__name__)
 
 
+def _translate_ip_xml_json(ip):
+    """
+    Convert the address version to int.
+    """
+    ip = dict(ip)
+    version = ip.get('version')
+    if version:
+        ip['version'] = int(version)
+    return ip
+
+
+def _translate_network_xml_to_json(network):
+    return [_translate_ip_xml_json(ip.attrib)
+            for ip in network.findall('{%s}ip' % XMLNS_11)]
+
+
+def _translate_addresses_xml_to_json(xml_addresses):
+    return dict((network.attrib['id'], _translate_network_xml_to_json(network))
+                for network in xml_addresses.findall('{%s}network' % XMLNS_11))
+
+
+def _translate_server_xml_to_json(xml_dom):
+    """ Convert server XML to server JSON.
+
+    The addresses collection does not convert well by the dumb xml_to_json.
+    This method does some pre and post-processing to deal with that.
+
+    Translate XML addresses subtree to JSON.
+
+    Having xml_doc similar to
+    <api:server  xmlns:api="http://docs.openstack.org/compute/api/v1.1">
+        <api:addresses>
+            <api:network id="foo_novanetwork">
+                <api:ip version="4" addr="192.168.0.4"/>
+            </api:network>
+            <api:network id="bar_novanetwork">
+                <api:ip version="4" addr="10.1.0.4"/>
+                <api:ip version="6" addr="2001:0:0:1:2:3:4:5"/>
+            </api:network>
+        </api:addresses>
+    </api:server>
+
+    the _translate_server_xml_to_json(etree.fromstring(xml_doc)) should produce
+    something like
+
+    {'addresses': {'bar_novanetwork': [{'addr': '10.1.0.4', 'version': 4},
+                                       {'addr': '2001:0:0:1:2:3:4:5',
+                                        'version': 6}],
+                   'foo_novanetwork': [{'addr': '192.168.0.4', 'version': 4}]}}
+    """
+    nsmap = {'api': XMLNS_11}
+    addresses = xml_dom.xpath('/api:server/api:addresses', namespaces=nsmap)
+    if addresses:
+        if len(addresses) > 1:
+            raise ValueError('Expected only single `addresses` element.')
+        json_addresses = _translate_addresses_xml_to_json(addresses[0])
+        json = xml_to_json(xml_dom)
+        json['addresses'] = json_addresses
+    else:
+        json = xml_to_json(xml_dom)
+    return json
+
+
 class ServersClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
@@ -41,7 +104,7 @@
         self.service = self.config.compute.catalog_type
 
     def _parse_key_value(self, node):
-        """Parse <foo key='key'>value</foo> data into {'key': 'value'}"""
+        """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
         data = {}
         for node in node.getchildren():
             data[node.get('key')] = node.text
@@ -54,7 +117,8 @@
             json['links'].append(xml_to_json(linknode))
 
     def _parse_server(self, body):
-        json = xml_to_json(body)
+        json = _translate_server_xml_to_json(body)
+
         if 'metadata' in json and json['metadata']:
             # NOTE(danms): if there was metadata, we need to re-parse
             # that as a special type
@@ -65,17 +129,16 @@
         for sub in ['image', 'flavor']:
             if sub in json and 'link' in json[sub]:
                 self._parse_links(body, json[sub])
-
         return json
 
     def get_server(self, server_id):
-        """Returns the details of an existing server"""
+        """Returns the details of an existing server."""
         resp, body = self.get("servers/%s" % str(server_id), self.headers)
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
     def delete_server(self, server_id):
-        """Deletes the given server"""
+        """Deletes the given server."""
         return self.delete("servers/%s" % str(server_id))
 
     def _parse_array(self, node):
@@ -179,7 +242,7 @@
         return resp, server
 
     def wait_for_server_status(self, server_id, status):
-        """Waits for a server to reach a given status"""
+        """Waits for a server to reach a given status."""
         resp, body = self.get_server(server_id)
         server_status = body['status']
         start = int(time.time())
@@ -202,7 +265,7 @@
                 raise exceptions.TimeoutException(message)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
-        """Waits for server to reach termination"""
+        """Waits for server to reach termination."""
         start_time = int(time.time())
         while True:
             try:
@@ -227,7 +290,7 @@
         return {node.get('id'): addrs}
 
     def list_addresses(self, server_id):
-        """Lists all addresses for a server"""
+        """Lists all addresses for a server."""
         resp, body = self.get("servers/%s/ips" % str(server_id), self.headers)
 
         networks = {}
@@ -238,7 +301,7 @@
         return resp, networks
 
     def list_addresses_by_network(self, server_id, network_id):
-        """Lists all addresses of a specific network type for a server"""
+        """Lists all addresses of a specific network type for a server."""
         resp, body = self.get("servers/%s/ips/%s" % (str(server_id),
                                                      network_id),
                               self.headers)
diff --git a/tempest/services/compute/xml/volumes_extensions_client.py b/tempest/services/compute/xml/volumes_extensions_client.py
index 1e8c738..60ef398 100644
--- a/tempest/services/compute/xml/volumes_extensions_client.py
+++ b/tempest/services/compute/xml/volumes_extensions_client.py
@@ -54,7 +54,7 @@
         return vol
 
     def list_volumes(self, params=None):
-        """List all the volumes created"""
+        """List all the volumes created."""
         url = 'os-volumes'
 
         if params:
@@ -68,7 +68,7 @@
         return resp, volumes
 
     def list_volumes_with_detail(self, params=None):
-        """List all the details of volumes"""
+        """List all the details of volumes."""
         url = 'os-volumes/detail'
 
         if params:
@@ -82,7 +82,7 @@
         return resp, volumes
 
     def get_volume(self, volume_id, wait=None):
-        """Returns the details of a single volume"""
+        """Returns the details of a single volume."""
         url = "os-volumes/%s" % str(volume_id)
         resp, body = self.get(url, self.headers, wait=wait)
         body = etree.fromstring(body)
@@ -116,11 +116,11 @@
         return resp, body
 
     def delete_volume(self, volume_id):
-        """Deletes the Specified Volume"""
+        """Deletes the Specified Volume."""
         return self.delete("os-volumes/%s" % str(volume_id))
 
     def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status"""
+        """Waits for a Volume to reach a given status."""
         resp, body = self.get_volume(volume_id)
         volume_name = body['displayName']
         volume_status = body['status']
diff --git a/tempest/services/identity/json/admin_client.py b/tempest/services/identity/json/admin_client.py
index 7ea33b5..c4e6c95 100644
--- a/tempest/services/identity/json/admin_client.py
+++ b/tempest/services/identity/json/admin_client.py
@@ -25,7 +25,7 @@
         return self._has_admin_extensions
 
     def create_role(self, name):
-        """Create a role"""
+        """Create a role."""
         post_body = {
             'name': name,
         }
@@ -52,19 +52,19 @@
         return resp, body['tenant']
 
     def delete_role(self, role_id):
-        """Delete a role"""
+        """Delete a role."""
         resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
         return resp, body
 
     def list_user_roles(self, tenant_id, user_id):
-        """Returns a list of roles assigned to a user for a tenant"""
+        """Returns a list of roles assigned to a user for a tenant."""
         url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['roles']
 
     def assign_user_role(self, tenant_id, user_id, role_id):
-        """Add roles to a user on a tenant"""
+        """Add roles to a user on a tenant."""
         post_body = json.dumps({})
         resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                               (tenant_id, user_id, role_id), post_body,
@@ -73,29 +73,29 @@
         return resp, body['role']
 
     def remove_user_role(self, tenant_id, user_id, role_id):
-        """Removes a role assignment for a user on a tenant"""
+        """Removes a role assignment for a user on a tenant."""
         return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                            (tenant_id, user_id, role_id))
 
     def delete_tenant(self, tenant_id):
-        """Delete a tenant"""
+        """Delete a tenant."""
         resp, body = self.delete('tenants/%s' % str(tenant_id))
         return resp, body
 
     def get_tenant(self, tenant_id):
-        """Get tenant details"""
+        """Get tenant details."""
         resp, body = self.get('tenants/%s' % str(tenant_id))
         body = json.loads(body)
         return resp, body['tenant']
 
     def list_roles(self):
-        """Returns roles"""
+        """Returns roles."""
         resp, body = self.get('OS-KSADM/roles')
         body = json.loads(body)
         return resp, body['roles']
 
     def list_tenants(self):
-        """Returns tenants"""
+        """Returns tenants."""
         resp, body = self.get('tenants')
         body = json.loads(body)
         return resp, body['tenants']
@@ -108,7 +108,7 @@
         raise exceptions.NotFound('No such tenant')
 
     def update_tenant(self, tenant_id, **kwargs):
-        """Updates a tenant"""
+        """Updates a tenant."""
         resp, body = self.get_tenant(tenant_id)
         name = kwargs.get('name', body['name'])
         desc = kwargs.get('description', body['description'])
@@ -126,7 +126,7 @@
         return resp, body['tenant']
 
     def create_user(self, name, password, tenant_id, email):
-        """Create a user"""
+        """Create a user."""
         post_body = {
             'name': name,
             'password': password,
@@ -139,18 +139,18 @@
         return resp, body['user']
 
     def delete_user(self, user_id):
-        """Delete a user"""
+        """Delete a user."""
         resp, body = self.delete("users/%s" % user_id)
         return resp, body
 
     def get_users(self):
-        """Get the list of users"""
+        """Get the list of users."""
         resp, body = self.get("users")
         body = json.loads(body)
         return resp, body['users']
 
     def enable_disable_user(self, user_id, enabled):
-        """Enables or disables a user"""
+        """Enables or disables a user."""
         put_body = {
                 'enabled': enabled
         }
@@ -161,12 +161,12 @@
         return resp, body
 
     def delete_token(self, token_id):
-        """Delete a token"""
+        """Delete a token."""
         resp, body = self.delete("tokens/%s" % token_id)
         return resp, body
 
     def list_users_for_tenant(self, tenant_id):
-        """List users for a Tenant"""
+        """List users for a Tenant."""
         resp, body = self.get('/tenants/%s/users' % tenant_id)
         body = json.loads(body)
         return resp, body['users']
@@ -179,7 +179,7 @@
         raise exceptions.NotFound('No such user')
 
     def create_service(self, name, type, **kwargs):
-        """Create a service"""
+        """Create a service."""
         post_body = {
             'name': name,
             'type': type,
@@ -191,14 +191,14 @@
         return resp, body['OS-KSADM:service']
 
     def get_service(self, service_id):
-        """Get Service"""
+        """Get Service."""
         url = '/OS-KSADM/services/%s' % service_id
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['OS-KSADM:service']
 
     def delete_service(self, service_id):
-        """Delete Service"""
+        """Delete Service."""
         url = '/OS-KSADM/services/%s' % service_id
         return self.delete(url)
 
diff --git a/tempest/services/identity/xml/admin_client.py b/tempest/services/identity/xml/admin_client.py
index 3a947b6..60897e9 100644
--- a/tempest/services/identity/xml/admin_client.py
+++ b/tempest/services/identity/xml/admin_client.py
@@ -63,7 +63,7 @@
         return self._has_admin_extensions
 
     def create_role(self, name):
-        """Create a role"""
+        """Create a role."""
         create_role = Element("role", xmlns=XMLNS, name=name)
         resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
                                self.headers)
@@ -89,55 +89,62 @@
         return resp, body
 
     def delete_role(self, role_id):
-        """Delete a role"""
+        """Delete a role."""
         resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
                                  self.headers)
         return resp, body
 
     def list_user_roles(self, tenant_id, user_id):
-        """Returns a list of roles assigned to a user for a tenant"""
+        """Returns a list of roles assigned to a user for a tenant."""
         url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
         resp, body = self.get(url, self.headers)
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
     def assign_user_role(self, tenant_id, user_id, role_id):
-        """Add roles to a user on a tenant"""
+        """Add roles to a user on a tenant."""
         resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                               (tenant_id, user_id, role_id), '', self.headers)
         body = self._parse_body(etree.fromstring(body))
         return resp, body
 
     def remove_user_role(self, tenant_id, user_id, role_id):
-        """Removes a role assignment for a user on a tenant"""
+        """Removes a role assignment for a user on a tenant."""
         return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
                            (tenant_id, user_id, role_id), self.headers)
 
     def delete_tenant(self, tenant_id):
-        """Delete a tenant"""
+        """Delete a tenant."""
         resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
         return resp, body
 
     def get_tenant(self, tenant_id):
-        """Get tenant details"""
+        """Get tenant details."""
         resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
         body = self._parse_body(etree.fromstring(body))
         return resp, body
 
     def list_roles(self):
-        """Returns roles"""
+        """Returns roles."""
         resp, body = self.get('OS-KSADM/roles', self.headers)
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
     def list_tenants(self):
-        """Returns tenants"""
+        """Returns tenants."""
         resp, body = self.get('tenants', self.headers)
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
+    def get_tenant_by_name(self, tenant_name):
+        resp, tenants = self.list_tenants()
+        for tenant in tenants:
+            if tenant['name'] == tenant_name:
+                return tenant
+        raise exceptions.NotFound('No such tenant')
+
     def update_tenant(self, tenant_id, **kwargs):
-        """Updates a tenant"""
+        """Updates a tenant."""
         resp, body = self.get_tenant(tenant_id)
         name = kwargs.get('name', body['name'])
         desc = kwargs.get('description', body['description'])
@@ -156,7 +163,7 @@
         return resp, body
 
     def create_user(self, name, password, tenant_id, email):
-        """Create a user"""
+        """Create a user."""
         create_user = Element("user",
                               xmlns=XMLNS,
                               name=name,
@@ -169,18 +176,18 @@
         return resp, body
 
     def delete_user(self, user_id):
-        """Delete a user"""
+        """Delete a user."""
         resp, body = self.delete("users/%s" % user_id, self.headers)
         return resp, body
 
     def get_users(self):
-        """Get the list of users"""
+        """Get the list of users."""
         resp, body = self.get("users", self.headers)
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
     def enable_disable_user(self, user_id, enabled):
-        """Enables or disables a user"""
+        """Enables or disables a user."""
         enable_user = Element("user", enabled=str(enabled).lower())
         resp, body = self.put('users/%s/enabled' % user_id,
                               str(Document(enable_user)), self.headers)
@@ -188,18 +195,25 @@
         return resp, body
 
     def delete_token(self, token_id):
-        """Delete a token"""
+        """Delete a token."""
         resp, body = self.delete("tokens/%s" % token_id, self.headers)
         return resp, body
 
     def list_users_for_tenant(self, tenant_id):
-        """List users for a Tenant"""
+        """List users for a Tenant."""
         resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
         body = self._parse_array(etree.fromstring(body))
         return resp, body
 
+    def get_user_by_username(self, tenant_id, username):
+        resp, users = self.list_users_for_tenant(tenant_id)
+        for user in users:
+            if user['name'] == username:
+                return user
+        raise exceptions.NotFound('No such user')
+
     def create_service(self, name, type, **kwargs):
-        """Create a service"""
+        """Create a service."""
         OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
         create_service = Element("service",
                                  xmlns=OS_KSADM,
@@ -213,14 +227,14 @@
         return resp, body
 
     def get_service(self, service_id):
-        """Get Service"""
+        """Get Service."""
         url = '/OS-KSADM/services/%s' % service_id
         resp, body = self.get(url, self.headers)
         body = self._parse_body(etree.fromstring(body))
         return resp, body
 
     def delete_service(self, service_id):
-        """Delete Service"""
+        """Delete Service."""
         url = '/OS-KSADM/services/%s' % service_id
         return self.delete(url, self.headers)
 
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 0ab5cd4..26f8329 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -40,7 +40,7 @@
 
     def create_account_metadata(self, metadata,
                                 metadata_prefix='X-Account-Meta-'):
-        """Creates an account metadata entry"""
+        """Creates an account metadata entry."""
         headers = {}
         for key in metadata:
             headers[metadata_prefix + key] = metadata[key]
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 6b5342a..7b5efff 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -48,14 +48,14 @@
         return resp, body
 
     def delete_container(self, container_name):
-        """Deletes the container (if it's empty)"""
+        """Deletes the container (if it's empty)."""
         url = str(container_name)
         resp, body = self.delete(url)
         return resp, body
 
     def update_container_metadata(self, container_name, metadata,
                                   metadata_prefix='X-Container-Meta-'):
-        """Updates arbitrary metadata on container"""
+        """Updates arbitrary metadata on container."""
         url = str(container_name)
         headers = {}
 
@@ -68,7 +68,7 @@
 
     def delete_container_metadata(self, container_name, metadata,
                                   metadata_prefix='X-Remove-Container-Meta-'):
-        """Deletes arbitrary metadata on container"""
+        """Deletes arbitrary metadata on container."""
         url = str(container_name)
         headers = {}
 
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 0047b50..c05c905 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -30,25 +30,25 @@
         self.service = self.config.object_storage.catalog_type
 
     def create_object(self, container, object_name, data):
-        """Create storage object"""
+        """Create storage object."""
 
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.put(url, data, self.headers)
         return resp, body
 
     def update_object(self, container, object_name, data):
-        """Upload data to replace current storage object"""
+        """Upload data to replace current storage object."""
         return create_object(container, object_name, data)
 
     def delete_object(self, container, object_name):
-        """Delete storage object"""
+        """Delete storage object."""
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.delete(url)
         return resp, body
 
     def update_object_metadata(self, container, object_name, metadata,
                                metadata_prefix='X-Object-Meta-'):
-        """Add, remove, or change X-Object-Meta metadata for storage object"""
+        """Add, remove, or change X-Object-Meta metadata for storage object."""
 
         headers = {}
         for key in metadata:
@@ -59,7 +59,7 @@
         return resp, body
 
     def list_object_metadata(self, container, object_name):
-        """List all storage object X-Object-Meta- metadata"""
+        """List all storage object X-Object-Meta- metadata."""
 
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.head(url)
@@ -74,7 +74,7 @@
 
     def copy_object_in_same_container(self, container, src_object_name,
                                       dest_object_name, metadata=None):
-        """Copy storage object's data to the new object using PUT"""
+        """Copy storage object's data to the new object using PUT."""
 
         url = "{0}/{1}".format(container, dest_object_name)
         headers = {}
@@ -91,7 +91,7 @@
     def copy_object_across_containers(self, src_container, src_object_name,
                                       dst_container, dst_object_name,
                                       metadata=None):
-        """Copy storage object's data to the new object using PUT"""
+        """Copy storage object's data to the new object using PUT."""
 
         url = "{0}/{1}".format(dst_container, dst_object_name)
         headers = {}
@@ -107,7 +107,7 @@
 
     def copy_object_2d_way(self, container, src_object_name, dest_object_name,
                            metadata=None):
-        """Copy storage object's data to the new object using COPY"""
+        """Copy storage object's data to the new object using COPY."""
 
         url = "{0}/{1}".format(container, src_object_name)
         headers = {}
@@ -161,7 +161,7 @@
         return resp, body
 
     def create_object(self, container, object_name, data, metadata=None):
-        """Create storage object"""
+        """Create storage object."""
 
         headers = {}
         if metadata:
@@ -173,7 +173,7 @@
         return resp, body
 
     def delete_object(self, container, object_name):
-        """Delete storage object"""
+        """Delete storage object."""
 
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.delete(url)
diff --git a/tempest/services/volume/json/admin/volume_types_client.py b/tempest/services/volume/json/admin/volume_types_client.py
index fc8897f..0cadcb5 100644
--- a/tempest/services/volume/json/admin/volume_types_client.py
+++ b/tempest/services/volume/json/admin/volume_types_client.py
@@ -35,7 +35,7 @@
         self.build_timeout = self.config.volume.build_timeout
 
     def list_volume_types(self, params=None):
-        """List all the volume_types created"""
+        """List all the volume_types created."""
         url = 'types'
         if params is not None:
             url += '?%s' % urllib.urlencode(params)
@@ -45,7 +45,7 @@
         return resp, body['volume_types']
 
     def get_volume_type(self, volume_id):
-        """Returns the details of a single volume_type"""
+        """Returns the details of a single volume_type."""
         url = "types/%s" % str(volume_id)
         resp, body = self.get(url)
         body = json.loads(body)
@@ -69,11 +69,11 @@
         return resp, body['volume_type']
 
     def delete_volume_type(self, volume_id):
-        """Deletes the Specified Volume_type"""
+        """Deletes the Specified Volume_type."""
         return self.delete("types/%s" % str(volume_id))
 
     def list_volume_types_extra_specs(self, vol_type_id, params=None):
-        """List all the volume_types extra specs created"""
+        """List all the volume_types extra specs created."""
         url = 'types/%s/extra_specs' % str(vol_type_id)
         if params is not None:
             url += '?%s' % urllib.urlencode(params)
@@ -83,7 +83,7 @@
         return resp, body['extra_specs']
 
     def get_volume_type_extra_specs(self, vol_type_id, extra_spec_name):
-        """Returns the details of a single volume_type extra spec"""
+        """Returns the details of a single volume_type extra spec."""
         url = "types/%s/extra_specs/%s" % (str(vol_type_id),
                                            str(extra_spec_name))
         resp, body = self.get(url)
@@ -103,7 +103,7 @@
         return resp, body['extra_specs']
 
     def delete_volume_type_extra_specs(self, vol_id, extra_spec_name):
-        """Deletes the Specified Volume_type extra spec"""
+        """Deletes the Specified Volume_type extra spec."""
         return self.delete("types/%s/extra_specs/%s" % ((str(vol_id)),
                                                         str(extra_spec_name)))
 
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 962fe72..cc5a115 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -37,7 +37,7 @@
         self.build_timeout = self.config.volume.build_timeout
 
     def list_volumes(self, params=None):
-        """List all the volumes created"""
+        """List all the volumes created."""
         url = 'volumes'
         if params:
                 url += '?%s' % urllib.urlencode(params)
@@ -47,7 +47,7 @@
         return resp, body['volumes']
 
     def list_volumes_with_detail(self, params=None):
-        """List the details of all volumes"""
+        """List the details of all volumes."""
         url = 'volumes/detail'
         if params:
                 url += '?%s' % urllib.urlencode(params)
@@ -57,7 +57,7 @@
         return resp, body['volumes']
 
     def get_volume(self, volume_id, wait=None):
-        """Returns the details of a single volume"""
+        """Returns the details of a single volume."""
         url = "volumes/%s" % str(volume_id)
         resp, body = self.get(url, wait=wait)
         body = json.loads(body)
@@ -85,11 +85,11 @@
         return resp, body['volume']
 
     def delete_volume(self, volume_id):
-        """Deletes the Specified Volume"""
+        """Deletes the Specified Volume."""
         return self.delete("volumes/%s" % str(volume_id))
 
     def attach_volume(self, volume_id, instance_uuid, mountpoint):
-        """Attaches a volume to a given instance on a given mountpoint"""
+        """Attaches a volume to a given instance on a given mountpoint."""
         post_body = {
             'instance_uuid': instance_uuid,
             'mountpoint': mountpoint,
@@ -100,7 +100,7 @@
         return resp, body
 
     def detach_volume(self, volume_id):
-        """Detaches a volume from an instance"""
+        """Detaches a volume from an instance."""
         post_body = {}
         post_body = json.dumps({'os-detach': post_body})
         url = 'volumes/%s/action' % (volume_id)
@@ -108,7 +108,7 @@
         return resp, body
 
     def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status"""
+        """Waits for a Volume to reach a given status."""
         resp, body = self.get_volume(volume_id)
         volume_name = body['display_name']
         volume_status = body['status']
diff --git a/tempest/services/volume/xml/admin/volume_types_client.py b/tempest/services/volume/xml/admin/volume_types_client.py
index 3da1af0..74d4631 100644
--- a/tempest/services/volume/xml/admin/volume_types_client.py
+++ b/tempest/services/volume/xml/admin/volume_types_client.py
@@ -67,7 +67,7 @@
             return extra_spec
 
     def list_volume_types(self, params=None):
-        """List all the volume_types created"""
+        """List all the volume_types created."""
         url = 'types'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -81,7 +81,7 @@
         return resp, volume_types
 
     def get_volume_type(self, type_id):
-        """Returns the details of a single volume_type"""
+        """Returns the details of a single volume_type."""
         url = "types/%s" % str(type_id)
         resp, body = self.get(url, self.headers)
         body = etree.fromstring(body)
@@ -114,11 +114,11 @@
         return resp, body
 
     def delete_volume_type(self, type_id):
-        """Deletes the Specified Volume_type"""
+        """Deletes the Specified Volume_type."""
         return self.delete("types/%s" % str(type_id))
 
     def list_volume_types_extra_specs(self, vol_type_id, params=None):
-        """List all the volume_types extra specs created"""
+        """List all the volume_types extra specs created."""
         url = 'types/%s/extra_specs' % str(vol_type_id)
 
         if params:
@@ -133,7 +133,7 @@
         return resp, extra_specs
 
     def get_volume_type_extra_specs(self, vol_type_id, extra_spec_name):
-        """Returns the details of a single volume_type extra spec"""
+        """Returns the details of a single volume_type extra spec."""
         url = "types/%s/extra_specs/%s" % (str(vol_type_id),
                                            str(extra_spec_name))
         resp, body = self.get(url, self.headers)
@@ -161,7 +161,7 @@
         return resp, body
 
     def delete_volume_type_extra_specs(self, vol_id, extra_spec_name):
-        """Deletes the Specified Volume_type extra spec"""
+        """Deletes the Specified Volume_type extra spec."""
         return self.delete("types/%s/extra_specs/%s" % ((str(vol_id)),
                                                         str(extra_spec_name)))
 
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 91d0fc7..b0104e0 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -56,7 +56,7 @@
             return vol
 
     def list_volumes(self, params=None):
-        """List all the volumes created"""
+        """List all the volumes created."""
         url = 'volumes'
 
         if params:
@@ -70,7 +70,7 @@
         return resp, volumes
 
     def list_volumes_with_detail(self, params=None):
-        """List all the details of volumes"""
+        """List all the details of volumes."""
         url = 'volumes/detail'
 
         if params:
@@ -84,7 +84,7 @@
         return resp, volumes
 
     def get_volume(self, volume_id, wait=None):
-        """Returns the details of a single volume"""
+        """Returns the details of a single volume."""
         url = "volumes/%s" % str(volume_id)
         resp, body = self.get(url, self.headers, wait=wait)
         body = etree.fromstring(body)
@@ -116,11 +116,11 @@
         return resp, body
 
     def delete_volume(self, volume_id):
-        """Deletes the Specified Volume"""
+        """Deletes the Specified Volume."""
         return self.delete("volumes/%s" % str(volume_id))
 
     def wait_for_volume_status(self, volume_id, status):
-        """Waits for a Volume to reach a given status"""
+        """Waits for a Volume to reach a given status."""
         resp, body = self.get_volume(volume_id)
         volume_name = body['displayName']
         volume_status = body['status']
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 3a0eb25..c38bf99 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -122,7 +122,7 @@
 
 
 class BotoTestCase(unittest.TestCase):
-    """Recommended to use as base class for boto related test"""
+    """Recommended to use as base class for boto related test."""
     @classmethod
     def setUpClass(cls):
         # The trash contains cleanup functions and paramaters in tuples
@@ -148,7 +148,7 @@
 
     @classmethod
     def cancelResourceCleanUp(cls, key):
-        """Cancel Clean up request"""
+        """Cancel Clean up request."""
         del cls._resource_trash_bin[key]
 
     #TODO(afazekas): Add "with" context handling
@@ -293,7 +293,7 @@
 
     @classmethod
     def destroy_bucket(cls, connection_data, bucket):
-        """Destroys the bucket and its content, just for teardown"""
+        """Destroys the bucket and its content, just for teardown."""
         exc_num = 0
         try:
             with closing(boto.connect_s3(**connection_data)) as conn:
@@ -316,7 +316,7 @@
 
     @classmethod
     def destroy_reservation(cls, reservation):
-        """Terminate instances in a reservation, just for teardown"""
+        """Terminate instances in a reservation, just for teardown."""
         exc_num = 0
 
         def _instance_state():
@@ -383,7 +383,7 @@
 
     @classmethod
     def destroy_snapshot_wait(cls, snapshot):
-        """delete snaphot, wait until not exists"""
+        """delete snaphot, wait until not exists."""
         snapshot.delete()
 
         def _update():
diff --git a/tempest/tests/boto/test_ec2_instance_run.py b/tempest/tests/boto/test_ec2_instance_run.py
index 95ef23c..331e54c 100644
--- a/tempest/tests/boto/test_ec2_instance_run.py
+++ b/tempest/tests/boto/test_ec2_instance_run.py
@@ -121,6 +121,7 @@
         self.cancelResourceCleanUp(rcuk)
 
     @attr(type='smoke')
+    @unittest.skip("Skipped until the Bug #1098112 is resolved")
     def test_run_terminate_instance(self):
         # EC2 run, terminate immediately
         image_ami = self.ec2_client.get_image(self.images["ami"]
diff --git a/tempest/tests/boto/utils/wait.py b/tempest/tests/boto/utils/wait.py
index 951b5bf..77fe037 100644
--- a/tempest/tests/boto/utils/wait.py
+++ b/tempest/tests/boto/utils/wait.py
@@ -62,7 +62,7 @@
 
 
 def re_search_wait(lfunction, regexp):
-    """Stops waiting on success"""
+    """Stops waiting on success."""
     start_time = time.time()
     while True:
         text = lfunction()
@@ -84,7 +84,7 @@
 
 
 def wait_no_exception(lfunction, exc_class=None, exc_matcher=None):
-    """Stops waiting on success"""
+    """Stops waiting on success."""
     start_time = time.time()
     if exc_matcher is not None:
         exc_class = BotoServerError
@@ -114,7 +114,7 @@
 
 #NOTE(afazekas): EC2/boto normally raise exception instead of empty list
 def wait_exception(lfunction):
-    """Returns with the exception or raises one"""
+    """Returns with the exception or raises one."""
     start_time = time.time()
     while True:
         try:
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
index b5ee13a..8172bd4 100644
--- a/tempest/tests/compute/admin/test_flavors.py
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -29,8 +29,8 @@
     Tests Flavors API Create and Delete that require admin privileges
     """
 
-    @staticmethod
-    def setUpClass(cls):
+    @classmethod
+    def setUpClass(self, cls):
         if not compute.FLAVOR_EXTRA_DATA_ENABLED:
             msg = "FlavorExtraData extension not enabled."
             raise nose.SkipTest(msg)
@@ -43,61 +43,77 @@
         cls.ephemeral = 10
         cls.new_flavor_id = 1234
         cls.swap = 1024
-        cls.rxtx = 1
+        cls.rxtx = 2
 
     @attr(type='positive')
     def test_create_flavor(self):
         # Create a flavor and ensure it is listed
         # This operation requires the user to have 'admin' role
-        #Create the flavor
-        resp, flavor = self.client.create_flavor(self.flavor_name,
-                                                 self.ram, self.vcpus,
-                                                 self.disk,
-                                                 self.ephemeral,
-                                                 self.new_flavor_id,
-                                                 self.swap, self.rxtx)
-        self.assertEqual(200, resp.status)
-        self.assertEqual(flavor['name'], self.flavor_name)
-        self.assertEqual(flavor['vcpus'], self.vcpus)
-        self.assertEqual(flavor['disk'], self.disk)
-        self.assertEqual(flavor['ram'], self.ram)
-        self.assertEqual(int(flavor['id']), self.new_flavor_id)
-        self.assertEqual(flavor['swap'], self.swap)
-        self.assertEqual(flavor['rxtx_factor'], self.rxtx)
-        self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'], self.ephemeral)
+        try:
+            #Create the flavor
+            resp, flavor = self.client.create_flavor(self.flavor_name,
+                                                     self.ram, self.vcpus,
+                                                     self.disk,
+                                                     self.new_flavor_id,
+                                                     ephemeral=self.ephemeral,
+                                                     swap=self.swap,
+                                                     rxtx=self.rxtx)
+            self.assertEqual(200, resp.status)
+            self.assertEqual(flavor['name'], self.flavor_name)
+            self.assertEqual(flavor['vcpus'], self.vcpus)
+            self.assertEqual(flavor['disk'], self.disk)
+            self.assertEqual(flavor['ram'], self.ram)
+            self.assertEqual(int(flavor['id']), self.new_flavor_id)
+            self.assertEqual(flavor['swap'], self.swap)
+            self.assertEqual(flavor['rxtx_factor'], self.rxtx)
+            self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
+                             self.ephemeral)
+            if self._interface == "xml":
+                XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
+                    "flavor_access/api/v2"
+                key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
+                self.assertEqual(flavor[key], "True")
+            if self._interface == "json":
+                self.assertEqual(flavor['os-flavor-access:is_public'], True)
 
-        #Verify flavor is retrieved
-        resp, flavor = self.client.get_flavor_details(self.new_flavor_id)
-        self.assertEqual(resp.status, 200)
-        self.assertEqual(flavor['name'], self.flavor_name)
+            #Verify flavor is retrieved
+            resp, flavor = self.client.get_flavor_details(self.new_flavor_id)
+            self.assertEqual(resp.status, 200)
+            self.assertEqual(flavor['name'], self.flavor_name)
 
-        #Delete the flavor
-        resp, body = self.client.delete_flavor(flavor['id'])
-        self.assertEqual(resp.status, 202)
+        finally:
+            #Delete the flavor
+            resp, body = self.client.delete_flavor(self.new_flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.client.wait_for_resource_deletion(self.new_flavor_id)
 
     @attr(type='positive')
     def test_create_flavor_verify_entry_in_list_details(self):
         # Create a flavor and ensure it's details are listed
         # This operation requires the user to have 'admin' role
-        #Create the flavor
-        resp, flavor = self.client.create_flavor(self.flavor_name,
-                                                 self.ram, self.vcpus,
-                                                 self.disk,
-                                                 self.ephemeral,
-                                                 self.new_flavor_id,
-                                                 self.swap, self.rxtx)
-        flag = False
-        #Verify flavor is retrieved
-        resp, flavors = self.client.list_flavors_with_detail()
-        self.assertEqual(resp.status, 200)
-        for flavor in flavors:
-            if flavor['name'] == self.flavor_name:
-                flag = True
-        self.assertTrue(flag)
+        try:
+            #Create the flavor
+            resp, flavor = self.client.create_flavor(self.flavor_name,
+                                                     self.ram, self.vcpus,
+                                                     self.disk,
+                                                     self.new_flavor_id,
+                                                     ephemeral=self.ephemeral,
+                                                     swap=self.swap,
+                                                     rxtx=self.rxtx)
+            flag = False
+            #Verify flavor is retrieved
+            resp, flavors = self.client.list_flavors_with_detail()
+            self.assertEqual(resp.status, 200)
+            for flavor in flavors:
+                if flavor['name'] == self.flavor_name:
+                    flag = True
+            self.assertTrue(flag)
 
-        #Delete the flavor
-        resp, body = self.client.delete_flavor(self.new_flavor_id)
-        self.assertEqual(resp.status, 202)
+        finally:
+            #Delete the flavor
+            resp, body = self.client.delete_flavor(self.new_flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.client.wait_for_resource_deletion(self.new_flavor_id)
 
     @attr(type='negative')
     def test_get_flavor_details_for_deleted_flavor(self):
@@ -106,9 +122,10 @@
         resp, flavor = self.client.create_flavor(self.flavor_name,
                                                  self.ram,
                                                  self.vcpus, self.disk,
-                                                 self.ephemeral,
                                                  self.new_flavor_id,
-                                                 self.swap, self.rxtx)
+                                                 ephemeral=self.ephemeral,
+                                                 swap=self.swap,
+                                                 rxtx=self.rxtx)
         self.assertEquals(200, resp.status)
 
         # Delete the flavor
@@ -129,20 +146,118 @@
                 flag = False
         self.assertTrue(flag)
 
+    def test_create_list_flavor_without_extra_data(self):
+        #Create a flavor and ensure it is listed
+        #This operation requires the user to have 'admin' role
+        try:
+            #Create the flavor
+            resp, flavor = self.client.create_flavor(self.flavor_name,
+                                                     self.ram, self.vcpus,
+                                                     self.disk,
+                                                     self.new_flavor_id)
+            self.assertEqual(200, resp.status)
+            self.assertEqual(flavor['name'], self.flavor_name)
+            self.assertEqual(flavor['ram'], self.ram)
+            self.assertEqual(flavor['vcpus'], self.vcpus)
+            self.assertEqual(flavor['disk'], self.disk)
+            self.assertEqual(int(flavor['id']), self.new_flavor_id)
+            self.assertEqual(flavor['swap'], '')
+            self.assertEqual(int(flavor['rxtx_factor']), 1)
+            self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
+            if self._interface == "xml":
+                XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
+                    "flavor_access/api/v2"
+                key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
+                self.assertEqual(flavor[key], "True")
+            if self._interface == "json":
+                self.assertEqual(flavor['os-flavor-access:is_public'], True)
+
+            #Verify flavor is retrieved
+            resp, flavor = self.client.get_flavor_details(self.new_flavor_id)
+            self.assertEqual(resp.status, 200)
+            self.assertEqual(flavor['name'], self.flavor_name)
+            #Check if flavor is present in list
+            resp, flavors = self.client.list_flavors_with_detail()
+            self.assertEqual(resp.status, 200)
+            for flavor in flavors:
+                if flavor['name'] == self.flavor_name:
+                    flag = True
+            self.assertTrue(flag)
+
+        finally:
+            #Delete the flavor
+            resp, body = self.client.delete_flavor(self.new_flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.client.wait_for_resource_deletion(self.new_flavor_id)
+
+    @attr(type='positive')
+    def test_flavor_not_public_verify_entry_not_in_list_details(self):
+        #Create a flavor with os-flavor-access:is_public false should not
+        #be present in list_details.
+        #This operation requires the user to have 'admin' role
+        try:
+            #Create the flavor
+            resp, flavor = self.client.create_flavor(self.flavor_name,
+                                                     self.ram, self.vcpus,
+                                                     self.disk,
+                                                     self.new_flavor_id,
+                                                     is_public="False")
+            flag = False
+            #Verify flavor is retrieved
+            resp, flavors = self.client.list_flavors_with_detail()
+            self.assertEqual(resp.status, 200)
+            for flavor in flavors:
+                if flavor['name'] == self.flavor_name:
+                    flag = True
+            self.assertFalse(flag)
+        finally:
+            #Delete the flavor
+            resp, body = self.client.delete_flavor(self.new_flavor_id)
+            self.assertEqual(resp.status, 202)
+
+    def test_list_public_flavor_with_other_user(self):
+        #Create a Flavor with public access.
+        #Try to List/Get flavor with another user
+        try:
+            #Create the flavor
+            resp, flavor = self.client.create_flavor(self.flavor_name,
+                                                     self.ram, self.vcpus,
+                                                     self.disk,
+                                                     self.new_flavor_id,
+                                                     is_public="True")
+            flag = False
+            self.new_client = self.flavors_client
+            #Verify flavor is retrieved with new user
+            resp, flavors = self.new_client.list_flavors_with_detail()
+            self.assertEqual(resp.status, 200)
+            for flavor in flavors:
+                if flavor['name'] == self.flavor_name:
+                    flag = True
+            self.assertTrue(flag)
+        finally:
+            #Delete the flavor
+            resp, body = self.client.delete_flavor(self.new_flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.client.wait_for_resource_deletion(self.new_flavor_id)
+
 
 class FlavorsAdminTestXML(base.BaseComputeAdminTestXML,
+                          base.BaseComputeTestXML,
                           FlavorsAdminTestBase):
 
     @classmethod
     def setUpClass(cls):
         super(FlavorsAdminTestXML, cls).setUpClass()
+        base.BaseComputeTestXML.setUpClass()
         FlavorsAdminTestBase.setUpClass(cls)
 
 
 class FlavorsAdminTestJSON(base.BaseComputeAdminTestJSON,
+                           base.BaseComputeTestJSON,
                            FlavorsAdminTestBase):
 
     @classmethod
     def setUpClass(cls):
         super(FlavorsAdminTestJSON, cls).setUpClass()
+        base.BaseComputeTestJSON.setUpClass()
         FlavorsAdminTestBase.setUpClass(cls)
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 4040705..8044d01 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -34,7 +34,7 @@
 
 class BaseCompTest(unittest.TestCase):
 
-    """Base test case class for all Compute API tests"""
+    """Base test case class for all Compute API tests."""
 
     @classmethod
     def setUpClass(cls):
@@ -179,7 +179,7 @@
 
     @classmethod
     def create_server(cls, image_id=None, flavor=None):
-        """Wrapper utility that returns a test server"""
+        """Wrapper utility that returns a test server."""
         server_name = rand_name(cls.__name__ + "-instance")
 
         if not flavor:
@@ -212,7 +212,7 @@
         return resp, server
 
     def wait_for(self, condition):
-        """Repeatedly calls condition() until a timeout"""
+        """Repeatedly calls condition() until a timeout."""
         start_time = int(time.time())
         while True:
             try:
@@ -246,7 +246,7 @@
 
 class BaseComputeAdminTest(unittest.TestCase):
 
-    """Base test case class for all Compute Admin API tests"""
+    """Base test case class for all Compute Admin API tests."""
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/tests/compute/floating_ips/test_floating_ips_actions.py b/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
index e5b4e0d..9a9914a 100644
--- a/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
@@ -111,11 +111,9 @@
 
     @attr(type='negative')
     def test_delete_nonexistant_floating_ip(self):
-        """
+        # Negative test:Deletion of a nonexistent floating IP
+        # from project should fail
 
-        Negative test:Deletion of a nonexistent floating IP
-        from project should fail
-        """
         #Deleting the non existent floating IP
         try:
             resp, body = self.client.delete_floating_ip(self.non_exist_id)
diff --git a/tempest/tests/compute/images/test_images.py b/tempest/tests/compute/images/test_images.py
index 46c8eee..2557f16 100644
--- a/tempest/tests/compute/images/test_images.py
+++ b/tempest/tests/compute/images/test_images.py
@@ -31,7 +31,7 @@
 class ImagesTestBase(object):
 
     def tearDown(self):
-        """Terminate test instances created after a test is executed"""
+        """Terminate test instances created after a test is executed."""
         for server in self.servers:
             resp, body = self.servers_client.delete_server(server['id'])
             if resp['status'] == '204':
@@ -42,39 +42,6 @@
             self.client.delete_image(image_id)
             self.image_ids.remove(image_id)
 
-    @attr(type='smoke')
-    @unittest.skipUnless(compute.CREATE_IMAGE_ENABLED,
-                         'Environment unable to create images.')
-    def test_create_delete_image(self):
-        # An image for the provided server should be created
-        server_name = rand_name('server')
-        resp, server = self.servers_client.create_server(server_name,
-                                                         self.image_ref,
-                                                         self.flavor_ref)
-        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
-
-        # Create a new image
-        name = rand_name('image')
-        meta = {'image_type': 'test'}
-        resp, body = self.client.create_image(server['id'], name, meta)
-        image_id = parse_image_id(resp['location'])
-        self.client.wait_for_image_resp_code(image_id, 200)
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
-
-        # Verify the image was created correctly
-        resp, image = self.client.get_image(image_id)
-        self.assertEqual(name, image['name'])
-        self.assertEqual('test', image['metadata']['image_type'])
-
-        # Verify minRAM and minDisk values are the same as the original image
-        resp, original_image = self.client.get_image(self.image_ref)
-        self.assertEqual(original_image['minRam'], image['minRam'])
-        self.assertEqual(original_image['minDisk'], image['minDisk'])
-
-        # Teardown
-        self.client.delete_image(image['id'])
-        self.servers_client.delete_server(server['id'])
-
     @attr(type='negative')
     def test_create_image_from_deleted_server(self):
         # An image should not be created if the server instance is removed
@@ -125,13 +92,13 @@
                           "with invalid server id")
 
     @attr(type='negative')
-    @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
-    def test_create_image_for_server_in_another_tenant(self):
-        # Creating image of another tenant's server should be return error
+    def test_create_image_when_server_is_terminating(self):
+        # Return an error when creating image of server that is terminating
         server = self.create_server()
+        self.servers_client.delete_server(server['id'])
 
         snapshot_name = rand_name('test-snap-')
-        self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
+        self.assertRaises(exceptions.Duplicate, self.client.create_image,
                           server['id'], snapshot_name)
 
     @attr(type='negative')
@@ -158,52 +125,6 @@
                           server['id'], snapshot_name)
 
     @attr(type='negative')
-    def test_create_image_when_server_is_terminating(self):
-        # Return an error when creating image of server that is terminating
-        server = self.create_server()
-        self.servers_client.delete_server(server['id'])
-
-        snapshot_name = rand_name('test-snap-')
-        self.assertRaises(exceptions.Duplicate, self.client.create_image,
-                          server['id'], snapshot_name)
-
-    @attr(type='negative')
-    def test_create_second_image_when_first_image_is_being_saved(self):
-        # Disallow creating another image when first image is being saved
-        server = self.create_server()
-
-        try:
-            # Create first snapshot
-            snapshot_name = rand_name('test-snap-')
-            resp, body = self.client.create_image(server['id'], snapshot_name)
-            image_id = parse_image_id(resp['location'])
-            self.image_ids.append(image_id)
-
-            # Create second snapshot
-            alt_snapshot_name = rand_name('test-snap-')
-            self.client.create_image(server['id'], alt_snapshot_name)
-        except exceptions.Duplicate:
-            pass
-
-        else:
-            self.fail("Should allow creating an image when another image of"
-                      "the server is still being saved")
-
-    @attr(type='negative')
-    @unittest.skip("Until Bug 1004564 is fixed")
-    def test_create_image_specify_name_over_256_chars(self):
-        # Return an error if snapshot name over 256 characters is passed
-        server = self.create_server()
-
-        try:
-            snapshot_name = rand_name('a' * 260)
-            self.assertRaises(exceptions.BadRequest, self.client.create_image,
-                              server['id'], snapshot_name)
-        except Exception:
-            self.fail("Should return 400 Bad Request if image name is over 256"
-                      " characters")
-
-    @attr(type='negative')
     def test_create_image_specify_uuid_35_characters_or_less(self):
         # Return an error if Image ID passed is 35 characters or less
         try:
@@ -228,51 +149,6 @@
                       " characters or more")
 
     @attr(type='negative')
-    @unittest.skip("Until Bug 1006725 is fixed")
-    def test_create_image_specify_multibyte_character_image_name(self):
-        # Return an error if the image name has multi-byte characters
-        server = self.create_server()
-
-        try:
-            snapshot_name = rand_name('\xef\xbb\xbf')
-            self.assertRaises(exceptions.BadRequest,
-                              self.client.create_image, server['id'],
-                              snapshot_name)
-        except Exception:
-            self.fail("Should return 400 Bad Request if multi byte characters"
-                      " are used for image name")
-
-    @attr(type='negative')
-    @unittest.skip("Until Bug 1005423 is fixed")
-    def test_create_image_specify_invalid_metadata(self):
-        # Return an error when creating image with invalid metadata
-        server = self.create_server()
-
-        try:
-            snapshot_name = rand_name('test-snap-')
-            meta = {'': ''}
-            self.assertRaises(exceptions.BadRequest, self.client.create_image,
-                              server['id'], snapshot_name, meta)
-
-        except Exception:
-            self.fail("Should raise 400 Bad Request if meta data is invalid")
-
-    @attr(type='negative')
-    @unittest.skip("Until Bug 1005423 is fixed")
-    def test_create_image_specify_metadata_over_limits(self):
-        # Return an error when creating image with meta data over 256 chars
-        server = self.create_server()
-
-        try:
-            snapshot_name = rand_name('test-snap-')
-            meta = {'a' * 260: 'b' * 260}
-            self.assertRaises(exceptions.OverLimit, self.client.create_image,
-                              server['id'], snapshot_name, meta)
-
-        except Exception:
-            self.fail("Should raise 413 Over Limit if meta data was too long")
-
-    @attr(type='negative')
     def test_delete_image_with_invalid_image_id(self):
         # An image should not be deleted with invalid image id
         try:
@@ -336,42 +212,6 @@
             self.fail("Did not return HTTP 404 NotFound for image id that "
                       "exceeds 35 character ID length limit")
 
-    @attr(type='negative')
-    @unittest.skipUnless(compute.MULTI_USER, 'Second user not configured')
-    def test_delete_image_of_another_tenant(self):
-        # Return an error while trying to delete another tenant's image
-
-        server = self.create_server()
-
-        snapshot_name = rand_name('test-snap-')
-        resp, body = self.client.create_image(server['id'], snapshot_name)
-        image_id = parse_image_id(resp['location'])
-        self.image_ids.append(image_id)
-        self.client.wait_for_image_resp_code(image_id, 200)
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
-
-        # Delete image
-        self.assertRaises(exceptions.NotFound,
-                          self.alt_client.delete_image, image_id)
-
-    @attr(type='negative')
-    def test_delete_image_that_is_not_yet_active(self):
-        # Return an error while trying to delete an active that is creating
-
-        server = self.create_server()
-
-        snapshot_name = rand_name('test-snap-')
-        resp, body = self.client.create_image(server['id'], snapshot_name)
-        image_id = parse_image_id(resp['location'])
-        self.image_ids.append(image_id)
-
-        # Do not wait, attempt to delete the image, ensure it's successful
-        resp, body = self.client.delete_image(image_id)
-        self.assertEqual('204', resp['status'])
-        self.image_ids.remove(image_id)
-
-        self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
-
 
 class ImagesTestJSON(base.BaseComputeTestJSON,
                      ImagesTestBase):
@@ -380,7 +220,6 @@
 
     @classmethod
     def setUpClass(cls):
-        raise nose.SkipTest("Until Bug 1046870 is fixed")
         super(ImagesTestJSON, cls).setUpClass()
         cls.client = cls.images_client
         cls.servers_client = cls.servers_client
@@ -407,7 +246,6 @@
 
     @classmethod
     def setUpClass(cls):
-        raise nose.SkipTest("Until Bug 1046870 is fixed")
         super(ImagesTestXML, cls).setUpClass()
         cls.client = cls.images_client
         cls.servers_client = cls.servers_client
diff --git a/tempest/tests/compute/images/test_images_oneserver.py b/tempest/tests/compute/images/test_images_oneserver.py
new file mode 100644
index 0000000..2841a21
--- /dev/null
+++ b/tempest/tests/compute/images/test_images_oneserver.py
@@ -0,0 +1,239 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# 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 nose
+from nose.plugins.attrib import attr
+import unittest2 as unittest
+
+from tempest import clients
+from tempest.common.utils.data_utils import parse_image_id
+from tempest.common.utils.data_utils import rand_name
+import tempest.config
+from tempest import exceptions
+from tempest.tests import compute
+from tempest.tests.compute import base
+
+
+class ImagesOneServerTestBase(object):
+    def tearDownClass(cls):
+        """Terminate test instances created after a test is executed."""
+        resp, body = self.servers_client.delete_server(cls.server['id'])
+        if resp['status'] == '204':
+            self.servers.remove(server)
+            self.servers_client.wait_for_server_termination(cls.server['id'])
+
+    def tearDown(self):
+        """Terminate test instances created after a test is executed."""
+        for image_id in self.image_ids:
+            self.client.delete_image(image_id)
+            self.image_ids.remove(image_id)
+
+    @attr(type='negative')
+    @unittest.skip("Until Bug 1006725 is fixed")
+    def test_create_image_specify_multibyte_character_image_name(self):
+        # Return an error if the image name has multi-byte characters
+        try:
+            snapshot_name = rand_name('\xef\xbb\xbf')
+            self.assertRaises(exceptions.BadRequest,
+                              self.client.create_image, self.server['id'],
+                              snapshot_name)
+        except Exception:
+            self.fail("Should return 400 Bad Request if multi byte characters"
+                      " are used for image name")
+
+    @attr(type='negative')
+    @unittest.skip("Until Bug 1005423 is fixed")
+    def test_create_image_specify_invalid_metadata(self):
+        # Return an error when creating image with invalid metadata
+        try:
+            snapshot_name = rand_name('test-snap-')
+            meta = {'': ''}
+            self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                              self.server['id'], snapshot_name, meta)
+
+        except Exception:
+            self.fail("Should raise 400 Bad Request if meta data is invalid")
+
+    @attr(type='negative')
+    @unittest.skip("Until Bug 1005423 is fixed")
+    def test_create_image_specify_metadata_over_limits(self):
+        # Return an error when creating image with meta data over 256 chars
+        try:
+            snapshot_name = rand_name('test-snap-')
+            meta = {'a' * 260: 'b' * 260}
+            self.assertRaises(exceptions.OverLimit, self.client.create_image,
+                              self.server['id'], snapshot_name, meta)
+
+        except Exception:
+            self.fail("Should raise 413 Over Limit if meta data was too long")
+
+    @attr(type='negative')
+    def test_delete_image_of_another_tenant(self):
+        # Return an error while trying to delete another tenant's image
+        self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
+        snapshot_name = rand_name('test-snap-')
+        resp, body = self.client.create_image(self.server['id'], snapshot_name)
+        image_id = parse_image_id(resp['location'])
+        self.image_ids.append(image_id)
+        self.client.wait_for_image_resp_code(image_id, 200)
+        self.client.wait_for_image_status(image_id, 'ACTIVE')
+
+        # Delete image
+        self.assertRaises(exceptions.NotFound,
+                          self.alt_client.delete_image, image_id)
+
+    @attr(type='smoke')
+    @unittest.skipUnless(compute.CREATE_IMAGE_ENABLED,
+                         'Environment unable to create images.')
+    def test_create_delete_image(self):
+
+        # Create a new image
+        name = rand_name('image')
+        meta = {'image_type': 'test'}
+        resp, body = self.client.create_image(self.server['id'], name, meta)
+        self.assertEqual(202, resp.status)
+        image_id = parse_image_id(resp['location'])
+        self.client.wait_for_image_resp_code(image_id, 200)
+        self.client.wait_for_image_status(image_id, 'ACTIVE')
+
+        # Verify the image was created correctly
+        resp, image = self.client.get_image(image_id)
+        self.assertEqual(name, image['name'])
+        self.assertEqual('test', image['metadata']['image_type'])
+
+        # Verify minRAM and minDisk values are the same as the original image
+        resp, original_image = self.client.get_image(self.image_ref)
+        self.assertEqual(original_image['minRam'], image['minRam'])
+        self.assertEqual(original_image['minDisk'], image['minDisk'])
+
+    @attr(type='negative')
+    def test_create_image_for_server_in_another_tenant(self):
+        # Creating image of another tenant's server should be return error
+
+        snapshot_name = rand_name('test-snap-')
+        self.assertRaises(exceptions.NotFound, self.alt_client.create_image,
+                          self.server['id'], snapshot_name)
+
+    @attr(type='negative')
+    def test_create_second_image_when_first_image_is_being_saved(self):
+        # Disallow creating another image when first image is being saved
+
+        try:
+            # Create first snapshot
+            snapshot_name = rand_name('test-snap-')
+            resp, body = self.client.create_image(self.server['id'],
+                                                  snapshot_name)
+            self.assertEqual(202, resp.status)
+            image_id = parse_image_id(resp['location'])
+            self.image_ids.append(image_id)
+
+            # Create second snapshot
+            alt_snapshot_name = rand_name('test-snap-')
+            self.client.create_image(self.server['id'],
+                                     alt_snapshot_name)
+        except exceptions.Duplicate:
+            self.client.wait_for_image_status(image_id, 'ACTIVE')
+
+        else:
+            self.fail("Should not allow creating an image when another image "
+                      "of the server is still being saved")
+
+    @attr(type='negative')
+    @unittest.skip("Until Bug 1004564 is fixed")
+    def test_create_image_specify_name_over_256_chars(self):
+        # Return an error if snapshot name over 256 characters is passed
+
+        try:
+            snapshot_name = rand_name('a' * 260)
+            self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                              self.server['id'], snapshot_name)
+        except Exception:
+            self.fail("Should return 400 Bad Request if image name is over 256"
+                      " characters")
+
+    @attr(type='negative')
+    def test_delete_image_that_is_not_yet_active(self):
+        # Return an error while trying to delete an image what is creating
+
+        snapshot_name = rand_name('test-snap-')
+        resp, body = self.client.create_image(self.server['id'], snapshot_name)
+        self.assertEqual(202, resp.status)
+        image_id = parse_image_id(resp['location'])
+        self.image_ids.append(image_id)
+
+        # Do not wait, attempt to delete the image, ensure it's successful
+        resp, body = self.client.delete_image(image_id)
+        self.assertEqual('204', resp['status'])
+        self.image_ids.remove(image_id)
+
+        self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
+
+
+class ImagesOneServerTestJSON(base.BaseComputeTestJSON,
+                              ImagesOneServerTestBase):
+
+    def tearDown(self):
+        ImagesOneServerTestBase.tearDown(self)
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesOneServerTestJSON, cls).setUpClass()
+        cls.client = cls.images_client
+        cls.servers_client = cls.servers_client
+        cls.server = cls.create_server()
+
+        cls.image_ids = []
+
+        if compute.MULTI_USER:
+            if cls.config.compute.allow_tenant_isolation:
+                creds = cls._get_isolated_creds()
+                username, tenant_name, password = creds
+                cls.alt_manager = clients.Manager(username=username,
+                                                  password=password,
+                                                  tenant_name=tenant_name)
+            else:
+                # Use the alt_XXX credentials in the config file
+                cls.alt_manager = clients.AltManager()
+            cls.alt_client = cls.alt_manager.images_client
+
+
+class ImagesOneServerTestXML(base.BaseComputeTestXML,
+                             ImagesOneServerTestBase):
+
+    def tearDown(self):
+        ImagesOneServerTestBase.tearDown(self)
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesOneServerTestXML, cls).setUpClass()
+        cls.client = cls.images_client
+        cls.servers_client = cls.servers_client
+        cls.server = cls.create_server()
+
+        cls.image_ids = []
+
+        if compute.MULTI_USER:
+            if cls.config.compute.allow_tenant_isolation:
+                creds = cls._get_isolated_creds()
+                username, tenant_name, password = creds
+                cls.alt_manager = clients.Manager(username=username,
+                                                  password=password,
+                                                  tenant_name=tenant_name)
+            else:
+                # Use the alt_XXX credentials in the config file
+                cls.alt_manager = clients.AltManager()
+            cls.alt_client = cls.alt_manager.images_client
diff --git a/tempest/tests/compute/images/test_images_whitebox.py b/tempest/tests/compute/images/test_images_whitebox.py
index f409970..2987534 100644
--- a/tempest/tests/compute/images/test_images_whitebox.py
+++ b/tempest/tests/compute/images/test_images_whitebox.py
@@ -36,7 +36,7 @@
 
     @classmethod
     def tearDownClass(cls):
-        """Terminate test instances created after a test is executed"""
+        """Terminate test instances created after a test is executed."""
 
         for server in cls.servers:
             cls.update_state(server['id'], "active", None)
@@ -51,7 +51,7 @@
 
     @classmethod
     def update_state(self, server_id, vm_state, task_state, deleted=False):
-        """Update states of an instance in database for validation"""
+        """Update states of an instance in database for validation."""
         if not task_state:
             task_state = "NULL"
 
@@ -64,7 +64,7 @@
         self.connection.execute(stmt, autocommit=True)
 
     def _test_create_image_409_base(self, vm_state, task_state, deleted=False):
-        """Base method for create image tests based on vm and task states"""
+        """Base method for create image tests based on vm and task states."""
         try:
             self.update_state(self.shared_server['id'], vm_state,
                               task_state, deleted)
diff --git a/tempest/tests/compute/security_groups/test_security_group_rules.py b/tempest/tests/compute/security_groups/test_security_group_rules.py
index fdf2892..805adf4 100644
--- a/tempest/tests/compute/security_groups/test_security_group_rules.py
+++ b/tempest/tests/compute/security_groups/test_security_group_rules.py
@@ -58,11 +58,10 @@
 
     @attr(type='positive')
     def test_security_group_rules_create_with_optional_arguments(self):
-        """
-        Positive test: Creation of Security Group rule
-        with optional arguments
-        should be successfull
-        """
+        # Positive test: Creation of Security Group rule
+        # with optional arguments
+        # should be successfull
+
         rule_id = None
         secgroup1 = None
         secgroup2 = None
diff --git a/tempest/tests/compute/servers/test_create_server.py b/tempest/tests/compute/servers/test_create_server.py
index 88ace2d..c5a54dc 100644
--- a/tempest/tests/compute/servers/test_create_server.py
+++ b/tempest/tests/compute/servers/test_create_server.py
@@ -43,7 +43,7 @@
         cls.accessIPv6canon = '::babe:dc0c:1602'
         cls.name = rand_name('server')
         file_contents = 'This is a test file.'
-        personality = [{'path': '/etc/test.txt',
+        personality = [{'path': '/test.txt',
                        'contents': base64.b64encode(file_contents)}]
         cls.client = cls.servers_client
         cli_resp = cls.client.create_server(cls.name,
diff --git a/tempest/tests/compute/servers/test_server_actions.py b/tempest/tests/compute/servers/test_server_actions.py
index f4e62b1..91f0674 100644
--- a/tempest/tests/compute/servers/test_server_actions.py
+++ b/tempest/tests/compute/servers/test_server_actions.py
@@ -44,7 +44,7 @@
         self.client.wait_for_server_status(self.server_id, 'ACTIVE')
 
     def tearDown(self):
-        self.client.delete_server(self.server_id)
+        self.clear_servers()
 
     @attr(type='smoke')
     @unittest.skipUnless(compute.CHANGE_PASSWORD_AVAILABLE,
@@ -67,11 +67,9 @@
         # The server should be power cycled
         if self.run_ssh:
             # Get the time the server was last rebooted,
-            # waiting for one minute as who doesn't have seconds precision
             resp, server = self.client.get_server(self.server_id)
             linux_client = RemoteClient(server, self.ssh_user, self.password)
             boot_time = linux_client.get_boot_time()
-            time.sleep(60)
 
         resp, body = self.client.reboot(self.server_id, 'HARD')
         self.assertEqual(202, resp.status)
@@ -89,11 +87,9 @@
         # The server should be signaled to reboot gracefully
         if self.run_ssh:
             # Get the time the server was last rebooted,
-            # waiting for one minute as who doesn't have seconds precision
             resp, server = self.client.get_server(self.server_id)
             linux_client = RemoteClient(server, self.ssh_user, self.password)
             boot_time = linux_client.get_boot_time()
-            time.sleep(60)
 
         resp, body = self.client.reboot(self.server_id, 'SOFT')
         self.assertEqual(202, resp.status)
diff --git a/tempest/tests/compute/servers/test_servers_whitebox.py b/tempest/tests/compute/servers/test_servers_whitebox.py
index 8aca745..3ff4df6 100644
--- a/tempest/tests/compute/servers/test_servers_whitebox.py
+++ b/tempest/tests/compute/servers/test_servers_whitebox.py
@@ -117,7 +117,7 @@
             self.connection.execute(stmt, autocommit=True)
 
     def update_state(self, server_id, vm_state, task_state, deleted=False):
-        """Update states of an instance in database for validation"""
+        """Update states of an instance in database for validation."""
         if not task_state:
             task_state = 'NULL'
 
diff --git a/tempest/tests/identity/admin/test_tenants.py b/tempest/tests/identity/admin/test_tenants.py
index 8fba7e3..578af4a 100644
--- a/tempest/tests/identity/admin/test_tenants.py
+++ b/tempest/tests/identity/admin/test_tenants.py
@@ -17,6 +17,7 @@
 
 import unittest2 as unittest
 
+from nose.plugins.attrib import attr
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.tests.identity import base
@@ -24,21 +25,6 @@
 
 class TenantsTestBase(object):
 
-    @staticmethod
-    def setUpClass(cls):
-        for _ in xrange(5):
-            resp, tenant = cls.client.create_tenant(rand_name('tenant-'))
-            cls.data.tenants.append(tenant)
-
-    def test_list_tenants(self):
-        # Return a list of all tenants
-        resp, body = self.client.list_tenants()
-        found = [tenant for tenant in body if tenant in self.data.tenants]
-        self.assertTrue(any(found), 'List did not return newly created '
-                        'tenants')
-        self.assertEqual(len(found), len(self.data.tenants))
-        self.assertTrue(resp['status'].startswith('2'))
-
     def test_list_tenants_by_unauthorized_user(self):
         # Non-admin user should not be able to list tenants
         self.assertRaises(exceptions.Unauthorized,
@@ -51,41 +37,50 @@
         self.assertRaises(exceptions.Unauthorized, self.client.list_tenants)
         self.client.clear_auth()
 
-    def test_tenant_delete(self):
+    def test_tenant_list_delete(self):
         # Create several tenants and delete them
         tenants = []
-        for _ in xrange(5):
-            resp, body = self.client.create_tenant(rand_name('tenant-new'))
-            tenants.append(body['id'])
-
+        for _ in xrange(3):
+            resp, tenant = self.client.create_tenant(rand_name('tenant-new'))
+            self.data.tenants.append(tenant)
+            tenants.append(tenant)
+        tenant_ids = map(lambda x: x['id'], tenants)
         resp, body = self.client.list_tenants()
-        found_1 = [tenant for tenant in body if tenant['id'] in tenants]
-        for tenant_id in tenants:
-            resp, body = self.client.delete_tenant(tenant_id)
+        self.assertTrue(resp['status'].startswith('2'))
+        found = [tenant for tenant in body if tenant['id'] in tenant_ids]
+        self.assertEqual(len(found), len(tenants), 'Tenants not created')
+
+        for tenant in tenants:
+            resp, body = self.client.delete_tenant(tenant['id'])
             self.assertTrue(resp['status'].startswith('2'))
+            self.data.tenants.remove(tenant)
 
         resp, body = self.client.list_tenants()
-        found_2 = [tenant for tenant in body if tenant['id'] in tenants]
-        self.assertTrue(any(found_1), 'Tenants not created')
-        self.assertFalse(any(found_2), 'Tenants failed to delete')
+        found = [tenant for tenant in body if tenant['id'] in tenant_ids]
+        self.assertFalse(any(found), 'Tenants failed to delete')
 
+    @attr(type='negative')
     def test_tenant_delete_by_unauthorized_user(self):
         # Non-admin user should not be able to delete a tenant
         tenant_name = rand_name('tenant-')
         resp, tenant = self.client.create_tenant(tenant_name)
+        self.data.tenants.append(tenant)
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.delete_tenant, tenant['id'])
 
+    @attr(type='negative')
     def test_tenant_delete_request_without_token(self):
         # Request to delete a tenant without a valid token should fail
         tenant_name = rand_name('tenant-')
         resp, tenant = self.client.create_tenant(tenant_name)
+        self.data.tenants.append(tenant)
         token = self.client.get_auth()
         self.client.delete_token(token)
         self.assertRaises(exceptions.Unauthorized, self.client.delete_tenant,
                           tenant['id'])
         self.client.clear_auth()
 
+    @attr(type='negative')
     def test_delete_non_existent_tenant(self):
         # Attempt to delete a non existent tenant should fail
         self.assertRaises(exceptions.NotFound, self.client.delete_tenant,
@@ -97,6 +92,8 @@
         tenant_desc = rand_name('desc-')
         resp, body = self.client.create_tenant(tenant_name,
                                                description=tenant_desc)
+        tenant = body
+        self.data.tenants.append(tenant)
         st1 = resp['status']
         tenant_id = body['id']
         desc1 = body['description']
@@ -108,11 +105,14 @@
         self.assertEqual(desc2, tenant_desc, 'Description does not appear'
                          'to be set')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_create_enabled(self):
         # Create a tenant that is enabled
         tenant_name = rand_name('tenant-')
         resp, body = self.client.create_tenant(tenant_name, enabled=True)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant_id = body['id']
         st1 = resp['status']
         en1 = body['enabled']
@@ -122,11 +122,14 @@
         en2 = body['enabled']
         self.assertTrue(en2, 'Enable should be True in lookup')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_create_not_enabled(self):
         # Create a tenant that is not enabled
         tenant_name = rand_name('tenant-')
         resp, body = self.client.create_tenant(tenant_name, enabled=False)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant_id = body['id']
         st1 = resp['status']
         en1 = body['enabled']
@@ -138,11 +141,15 @@
         self.assertEqual('false', str(en2).lower(),
                          'Enable should be False in lookup')
         self.client.delete_tenant(tenant_id)
+        self.data.tenants.remove(tenant)
 
+    @attr(type='negative')
     def test_tenant_create_duplicate(self):
         # Tenant names should be unique
         tenant_name = rand_name('tenant-dup-')
         resp, body = self.client.create_tenant(tenant_name)
+        tenant = body
+        self.data.tenants.append(tenant)
         tenant1_id = body.get('id')
 
         try:
@@ -151,15 +158,17 @@
             self.fail('Should not be able to create a duplicate tenant name')
         except exceptions.Duplicate:
             pass
-        if tenant1_id:
-            self.client.delete_tenant(tenant1_id)
+        self.client.delete_tenant(tenant1_id)
+        self.data.tenants.remove(tenant)
 
+    @attr(type='negative')
     def test_create_tenant_by_unauthorized_user(self):
         # Non-admin user should not be authorized to create a tenant
         tenant_name = rand_name('tenant-')
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.create_tenant, tenant_name)
 
+    @attr(type='negative')
     def test_create_tenant_request_without_token(self):
         # Create tenant request without a token should not be authorized
         tenant_name = rand_name('tenant-')
@@ -169,6 +178,7 @@
                           tenant_name)
         self.client.clear_auth()
 
+    @attr(type='negative')
     def test_create_tenant_with_empty_name(self):
         # Tenant name should not be empty
         self.assertRaises(exceptions.BadRequest, self.client.create_tenant,
@@ -184,6 +194,9 @@
         # Update name attribute of a tenant
         t_name1 = rand_name('tenant-')
         resp, body = self.client.create_tenant(t_name1)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_name = body['name']
 
@@ -202,12 +215,16 @@
         self.assertEqual(resp2_name, resp3_name)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_update_desc(self):
         # Update description attribute of a tenant
         t_name = rand_name('tenant-')
         t_desc = rand_name('desc-')
         resp, body = self.client.create_tenant(t_name, description=t_desc)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_desc = body['description']
 
@@ -226,12 +243,16 @@
         self.assertEqual(resp2_desc, resp3_desc)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
     def test_tenant_update_enable(self):
         # Update the enabled attribute of a tenant
         t_name = rand_name('tenant-')
         t_en = False
         resp, body = self.client.create_tenant(t_name, enabled=t_en)
+        tenant = body
+        self.data.tenants.append(tenant)
+
         t_id = body['id']
         resp1_en = body['enabled']
 
@@ -250,6 +271,7 @@
         self.assertEqual(resp2_en, resp3_en)
 
         self.client.delete_tenant(t_id)
+        self.data.tenants.remove(tenant)
 
 
 class TenantsTestJSON(base.BaseIdentityAdminTestJSON,
@@ -258,7 +280,6 @@
     @classmethod
     def setUpClass(cls):
         super(TenantsTestJSON, cls).setUpClass()
-        TenantsTestBase.setUpClass(cls)
 
 
 class TenantsTestXML(base.BaseIdentityAdminTestXML, TenantsTestBase):
@@ -266,4 +287,3 @@
     @classmethod
     def setUpClass(cls):
         super(TenantsTestXML, cls).setUpClass()
-        TenantsTestBase.setUpClass(cls)
diff --git a/tempest/tests/identity/base.py b/tempest/tests/identity/base.py
index 3867f0a..ce160da 100644
--- a/tempest/tests/identity/base.py
+++ b/tempest/tests/identity/base.py
@@ -95,7 +95,7 @@
             self.role_name = None
 
         def setup_test_user(self):
-            """Set up a test user"""
+            """Set up a test user."""
             self.setup_test_tenant()
             self.test_user = rand_name('test_user_')
             self.test_password = rand_name('pass_')
@@ -107,7 +107,7 @@
             self.users.append(self.user)
 
         def setup_test_tenant(self):
-            """Set up a test tenant"""
+            """Set up a test tenant."""
             self.test_tenant = rand_name('test_tenant_')
             self.test_description = rand_name('desc_')
             resp, self.tenant = self.client.create_tenant(
@@ -116,7 +116,7 @@
             self.tenants.append(self.tenant)
 
         def setup_test_role(self):
-            """Set up a test role"""
+            """Set up a test role."""
             self.test_role = rand_name('role')
             resp, self.role = self.client.create_role(self.test_role)
             self.roles.append(self.role)
diff --git a/tempest/tests/network/base.py b/tempest/tests/network/base.py
index f993441..90b351d 100644
--- a/tempest/tests/network/base.py
+++ b/tempest/tests/network/base.py
@@ -48,7 +48,7 @@
             cls.client.delete_network(network['id'])
 
     def create_network(self, network_name=None):
-        """Wrapper utility that returns a test network"""
+        """Wrapper utility that returns a test network."""
         network_name = network_name or rand_name('test-network')
 
         resp, body = self.client.create_network(network_name)
diff --git a/tempest/tests/utils.py b/tempest/tests/utils.py
index 8adaa51..571fc2a 100644
--- a/tempest/tests/utils.py
+++ b/tempest/tests/utils.py
@@ -15,7 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-"""Common utilities used in testing"""
+"""Common utilities used in testing."""
 
 import nose.plugins.skip
 
diff --git a/tempest/tests/volume/admin/base.py b/tempest/tests/volume/admin/base.py
index 420da42..81c7c78 100644
--- a/tempest/tests/volume/admin/base.py
+++ b/tempest/tests/volume/admin/base.py
@@ -27,7 +27,7 @@
 
 
 class BaseVolumeAdminTest(BaseVolumeTest):
-    """Base test case class for all Volume Admin API tests"""
+    """Base test case class for all Volume Admin API tests."""
     @classmethod
     def setUpClass(cls):
         super(BaseVolumeAdminTest, cls).setUpClass()
diff --git a/tempest/tests/volume/admin/test_volume_types.py b/tempest/tests/volume/admin/test_volume_types.py
index 8ebb78f..65c975a 100644
--- a/tempest/tests/volume/admin/test_volume_types.py
+++ b/tempest/tests/volume/admin/test_volume_types.py
@@ -126,7 +126,8 @@
         try:
             body = {}
             name = rand_name("volume-type-")
-            extra_specs = {"Spec1": "Val1", "Spec2": "Val2"}
+            extra_specs = {"storage_protocol": "iSCSI",
+                           "vendor_name": "Open Source"}
             resp, body = self.client.\
             create_volume_type(name, extra_specs=extra_specs)
             self.assertEqual(200, resp.status)
diff --git a/tempest/tests/volume/base.py b/tempest/tests/volume/base.py
index 549f541..8657db8 100644
--- a/tempest/tests/volume/base.py
+++ b/tempest/tests/volume/base.py
@@ -31,7 +31,7 @@
 
 class BaseVolumeTest(unittest.TestCase):
 
-    """Base test case class for all Cinder API tests"""
+    """Base test case class for all Cinder API tests."""
 
     @classmethod
     def setUpClass(cls):
@@ -122,7 +122,7 @@
         cls.clear_isolated_creds()
 
     def create_volume(self, size=1, metadata={}):
-        """Wrapper utility that returns a test volume"""
+        """Wrapper utility that returns a test volume."""
         display_name = rand_name(self.__class__.__name__ + "-volume")
         cli_resp = self.volumes_client.create_volume(size=size,
                                                      display_name=display_name,
@@ -133,7 +133,7 @@
         return volume
 
     def wait_for(self, condition):
-        """Repeatedly calls condition() until a timeout"""
+        """Repeatedly calls condition() until a timeout."""
         start_time = int(time.time())
         while True:
             try:
diff --git a/tempest/tests/volume/test_volumes_actions.py b/tempest/tests/volume/test_volumes_actions.py
index 7eddb67..155acb6 100644
--- a/tempest/tests/volume/test_volumes_actions.py
+++ b/tempest/tests/volume/test_volumes_actions.py
@@ -46,7 +46,10 @@
         super(VolumesActionsTest, cls).tearDownClass()
         # Delete the test instance and volume
         cls.client.delete_volume(cls.volume['id'])
+        cls.client.wait_for_resource_deletion(cls.volume['id'])
+
         cls.servers_client.delete_server(cls.server['id'])
+        cls.client.wait_for_resource_deletion(cls.server['id'])
 
     @attr(type='smoke')
     def test_attach_detach_volume_to_instance(self):
diff --git a/tempest/tests/volume/test_volumes_list.py b/tempest/tests/volume/test_volumes_list.py
index 26a85b7..2fc1353 100644
--- a/tempest/tests/volume/test_volumes_list.py
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -87,8 +87,9 @@
                     # because the backing file size of the volume group is
                     # too small. So, here, we clean up whatever we did manage
                     # to create and raise a SkipTest
-                    for volume in cls.volume_id_list:
-                        cls.client.delete_volume(volume)
+                    for volid in cls.volume_id_list:
+                        cls.client.delete_volume(volid)
+                        cls.client.wait_for_resource_deletion(volid)
                     msg = ("Failed to create ALL necessary volumes to run "
                            "test. This typically means that the backing file "
                            "size of the nova-volumes group is too small to "
@@ -99,9 +100,9 @@
     @classmethod
     def tearDownClass(cls):
         # Delete the created volumes
-        for volume in cls.volume_id_list:
-            resp, _ = cls.client.delete_volume(volume)
-            cls.client.wait_for_resource_deletion(volume)
+        for volid in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volid)
+            cls.client.wait_for_resource_deletion(volid)
         super(VolumeListTestXML, cls).tearDownClass()
 
 
@@ -133,8 +134,9 @@
                     # because the backing file size of the volume group is
                     # too small. So, here, we clean up whatever we did manage
                     # to create and raise a SkipTest
-                    for volume in cls.volume_id_list:
-                        cls.client.delete_volume(volume)
+                    for volid in cls.volume_id_list:
+                        cls.client.delete_volume(volid)
+                        cls.client.wait_for_resource_deletion(volid)
                     msg = ("Failed to create ALL necessary volumes to run "
                            "test. This typically means that the backing file "
                            "size of the nova-volumes group is too small to "
@@ -145,7 +147,7 @@
     @classmethod
     def tearDownClass(cls):
         # Delete the created volumes
-        for volume in cls.volume_id_list:
-            resp, _ = cls.client.delete_volume(volume)
-            cls.client.wait_for_resource_deletion(volume)
+        for volid in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volid)
+            cls.client.wait_for_resource_deletion(volid)
         super(VolumeListTestJSON, cls).tearDownClass()
diff --git a/tempest/whitebox.py b/tempest/whitebox.py
index 39cd666..d78b9e0 100644
--- a/tempest/whitebox.py
+++ b/tempest/whitebox.py
@@ -106,7 +106,7 @@
 
     @classmethod
     def create_server(cls, image_id=None):
-        """Wrapper utility that returns a test server"""
+        """Wrapper utility that returns a test server."""
         server_name = rand_name(cls.__name__ + "-instance")
         flavor = cls.flavor_ref
         if not image_id:
@@ -120,7 +120,7 @@
 
     @classmethod
     def get_db_handle_and_meta(cls, database='nova'):
-        """Return a connection handle and metadata of an OpenStack database"""
+        """Return a connection handle and metadata of an OpenStack database."""
         engine_args = {"echo": False,
                        "convert_unicode": True,
                        "pool_recycle": 3600
@@ -138,7 +138,7 @@
         return connection, meta
 
     def nova_manage(self, category, action, params):
-        """Executes nova-manage command for the given action"""
+        """Executes nova-manage command for the given action."""
 
         nova_manage_path = os.path.join(self.compute_bin_dir, 'nova-manage')
         cmd = ' '.join([nova_manage_path, category, action, params])
@@ -161,7 +161,7 @@
         return result
 
     def get_ssh_connection(self, host, username, password):
-        """Create an SSH connection object to a host"""
+        """Create an SSH connection object to a host."""
         ssh_timeout = self.config.compute.ssh_timeout
         ssh_client = Client(host, username, password, ssh_timeout)
         if not ssh_client.test_connection_auth():
diff --git a/tools/hacking.py b/tools/hacking.py
index 6e66005..617682d 100755
--- a/tools/hacking.py
+++ b/tools/hacking.py
@@ -36,13 +36,13 @@
 # Don't need this for testing
 logging.disable('LOG')
 
-#N1xx comments
-#N2xx except
-#N3xx imports
-#N4xx docstrings
-#N5xx dictionaries/lists
-#N6xx calling methods
-#N7xx localization
+#T1xx comments
+#T2xx except
+#T3xx imports
+#T4xx docstrings
+#T5xx dictionaries/lists
+#T6xx calling methods
+#T7xx localization
 #N8xx git commit messages
 
 IMPORT_EXCEPTIONS = ['sqlalchemy', 'migrate']
@@ -110,13 +110,13 @@
 
     tempest HACKING guide recommendation for TODO:
     Include your name with TODOs as in "#TODO(termie)"
-    N101
+    T101
     """
     pos = physical_line.find('TODO')
     pos1 = physical_line.find('TODO(')
     pos2 = physical_line.find('#')  # make sure it's a comment
     if (pos != pos1 and pos2 >= 0 and pos2 < pos):
-        return pos, "TEMPEST N101: Use TODO(NAME)"
+        return pos, "T101: Use TODO(NAME)"
 
 
 def tempest_except_format(logical_line):
@@ -124,10 +124,10 @@
 
     tempest HACKING guide recommends not using except:
     Do not write "except:", use "except Exception:" at the very least
-    N201
+    T201
     """
     if logical_line.startswith("except:"):
-        yield 6, "TEMPEST N201: no 'except:' at least use 'except Exception:'"
+        yield 6, "T201: no 'except:' at least use 'except Exception:'"
 
 
 def tempest_except_format_assert(logical_line):
@@ -135,10 +135,10 @@
 
     tempest HACKING guide recommends not using assertRaises(Exception...):
     Do not use overly broad Exception type
-    N202
+    T202
     """
     if logical_line.startswith("self.assertRaises(Exception"):
-        yield 1, "TEMPEST N202: assertRaises Exception too broad"
+        yield 1, "T202: assertRaises Exception too broad"
 
 
 def tempest_one_import_per_line(logical_line):
@@ -149,14 +149,14 @@
 
     Examples:
     BAD: from tempest.common.rest_client import RestClient, RestClientXML
-    N301
+    T301
     """
     pos = logical_line.find(',')
     parts = logical_line.split()
     if (pos > -1 and (parts[0] == "import" or
                       parts[0] == "from" and parts[2] == "import") and
         not is_import_exception(parts[1])):
-        yield pos, "TEMPEST N301: one import per line"
+        yield pos, "T301: one import per line"
 
 _missingImport = set([])
 
@@ -166,9 +166,9 @@
 
     tempest HACKING guide recommends importing only modules:
     Do not import objects, only modules
-    N302 import only modules
-    N303 Invalid Import
-    N304 Relative Import
+    T302 import only modules
+    T303 Invalid Import
+    T304 Relative Import
     """
     def importModuleCheck(mod, parent=None, added=False):
         """
@@ -193,12 +193,12 @@
                     if added:
                         sys.path.pop()
                         added = False
-                        return logical_line.find(mod), ("TEMPEST N304: No "
+                        return logical_line.find(mod), ("T304: No "
                                                         "relative  imports. "
                                                         "'%s' is a relative "
                                                         "import"
                                                         % logical_line)
-                    return logical_line.find(mod), ("TEMPEST N302: import only"
+                    return logical_line.find(mod), ("T302: import only"
                                                     " modules. '%s' does not "
                                                     "import a module"
                                                     % logical_line)
@@ -222,7 +222,7 @@
 
         except AttributeError:
             # Invalid import
-            return logical_line.find(mod), ("TEMPEST N303: Invalid import, "
+            return logical_line.find(mod), ("T303: Invalid import, "
                                             "AttributeError raised")
 
     # convert "from x import y" to " import x.y"
@@ -240,7 +240,7 @@
 
     # TODO(jogo) handle "from x import *"
 
-#TODO(jogo): import template: N305
+#TODO(jogo): import template: T305
 
 
 def tempest_import_alphabetical(logical_line, line_number, lines):
@@ -248,7 +248,7 @@
 
     Tempest HACKING guide recommendation for imports:
     imports in human alphabetical order
-    N306
+    T306
     """
     # handle import x
     # use .lower since capitalization shouldn't dictate order
@@ -260,7 +260,7 @@
     if (len(split_line) in length and len(split_previous) in length and
         split_line[0] == "import" and split_previous[0] == "import"):
         if split_line[1] < split_previous[1]:
-            yield (0, "TEMPEST N306: imports not in alphabetical order"
+            yield (0, "T306: imports not in alphabetical order"
                       " (%s, %s)"
                       % (split_previous[1], split_line[1]))
 
@@ -270,12 +270,13 @@
 
     tempest HACKING guide recommendation for docstring:
     Docstring should not start with space
-    N401
+    T401
     """
     pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE])  # start
-    if (pos != -1 and len(physical_line) > pos + 1):
+    end = max([physical_line[-4:-1] == i for i in DOCSTRING_TRIPLE])  # end
+    if (pos != -1 and end and len(physical_line) > pos + 4):
         if (physical_line[pos + 3] == ' '):
-            return (pos, "TEMPEST N401: one line docstring should not start"
+            return (pos, "T401: one line docstring should not start"
                          " with a space")
 
 
@@ -284,13 +285,13 @@
 
     tempest HACKING guide recommendation for one line docstring:
     A one line docstring looks like this and ends in a period.
-    N402
+    T402
     """
     pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE])  # start
     end = max([physical_line[-4:-1] == i for i in DOCSTRING_TRIPLE])  # end
     if (pos != -1 and end and len(physical_line) > pos + 4):
         if (physical_line[-5] != '.'):
-            return pos, "TEMPEST N402: one line docstring needs a period"
+            return pos, "T402: one line docstring needs a period"
 
 
 def tempest_docstring_multiline_end(physical_line):
@@ -298,12 +299,29 @@
 
     Tempest HACKING guide recommendation for docstring:
     Docstring should end on a new line
-    N403
+    T403
     """
     pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE])  # start
     if (pos != -1 and len(physical_line) == pos):
         if (physical_line[pos + 3] == ' '):
-            return (pos, "TEMPEST N403: multi line docstring end on new line")
+            return (pos, "T403: multi line docstring end on new line")
+
+
+def tempest_no_test_docstring(physical_line, previous_logical, filename):
+    """Check that test_ functions don't have docstrings
+
+    This ensure we get better results out of tempest, instead
+    of them being hidden behind generic descriptions of the
+    functions.
+
+    T404
+    """
+    if "tempest/test" in filename:
+        pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE])
+        if pos != -1:
+            if previous_logical.startswith("def test_"):
+                return (pos, "T404: test functions must "
+                        "not have doc strings")
 
 
 FORMAT_RE = re.compile("%(?:"
@@ -353,25 +371,25 @@
 
             if not format_string:
                 raise LocalizationError(start,
-                                        "TEMEPST N701: Empty localization "
+                                        "T701: Empty localization "
                                         "string")
             if token_type != tokenize.OP:
                 raise LocalizationError(start,
-                                        "TEMPEST N701: Invalid localization "
+                                        "T701: Invalid localization "
                                         "call")
             if text != ")":
                 if text == "%":
                     raise LocalizationError(start,
-                                            "TEMPEST N702: Formatting "
+                                            "T702: Formatting "
                                             "operation should be outside"
                                             " of localization method call")
                 elif text == "+":
                     raise LocalizationError(start,
-                                            "TEMPEST N702: Use bare string "
+                                            "T702: Use bare string "
                                             "concatenation instead of +")
                 else:
                     raise LocalizationError(start,
-                                            "TEMPEST N702: Argument to _ must"
+                                            "T702: Argument to _ must"
                                             " be just a string")
 
             format_specs = FORMAT_RE.findall(format_string)
@@ -380,16 +398,16 @@
             # not spec means %%, key means %(smth)s
             if len(positional_specs) > 1:
                 raise LocalizationError(start,
-                                        "TEMPEST N703: Multiple positional "
+                                        "T703: Multiple positional "
                                         "placeholders")
 
 
 def tempest_localization_strings(logical_line, tokens):
     """Check localization in line.
 
-    N701: bad localization call
-    N702: complex expression instead of string as argument to _()
-    N703: multiple positional placeholders
+    T701: bad localization call
+    T702: complex expression instead of string as argument to _()
+    T703: multiple positional placeholders
     """
 
     gen = check_i18n()
@@ -431,8 +449,8 @@
 
     tempest HACKING recommends not referencing a bug or blueprint
     in first line, it should provide an accurate description of the change
-    N801
-    N802 Title limited to 50 chars
+    T801
+    T802 Title limited to 50 chars
     """
     #Get title of most recent commit
 
@@ -453,12 +471,12 @@
     error = False
     #NOTE(jogo) if match regex but over 3 words, acceptable title
     if GIT_REGEX.search(title) is not None and len(title.split()) <= 3:
-        print ("N801: git commit title ('%s') should provide an accurate "
+        print ("T801: git commit title ('%s') should provide an accurate "
                "description of the change, not just a reference to a bug "
                "or blueprint" % title.strip())
         error = True
     if len(title.decode('utf-8')) > 72:
-        print ("N802: git commit title ('%s') should be under 50 chars"
+        print ("T802: git commit title ('%s') should be under 50 chars"
                % title.strip())
         error = True
     return error
@@ -468,8 +486,8 @@
     sys.path.append(os.getcwd())
     #Run once tests (not per line)
     once_error = once_git_check_commit_title()
-    #TEMPEST error codes start with an N
-    pep8.ERRORCODE_REGEX = re.compile(r'[EWN]\d{3}')
+    #TEMPEST error codes start with a T
+    pep8.ERRORCODE_REGEX = re.compile(r'[EWT]\d{3}')
     add_tempest()
     pep8.current_file = current_file
     pep8.readlines = readlines
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
old mode 100644
new mode 100755
index 9b12eb7..e890e92
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # vim: tabstop=4 shiftwidth=4 softtabstop=4
 
 # Copyright 2012 OpenStack, LLC
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
new file mode 100755
index 0000000..73dcfbc
--- /dev/null
+++ b/tools/tempest_coverage.py
@@ -0,0 +1,194 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 IBM
+#
+#    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 os
+import re
+import shutil
+import sys
+
+from tempest.common.rest_client import RestClient
+from tempest import config
+from tempest.openstack.common import cfg
+from tempest.tests.compute import base
+
+CONF = config.TempestConfig()
+
+
+class CoverageClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(CoverageClientJSON, self).__init__(config, username, password,
+                                                 auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def start_coverage(self):
+        post_body = {
+            'start': {},
+        }
+        post_body = json.dumps(post_body)
+        return self.post('os-coverage/action', post_body, self.headers)
+
+    def start_coverage_combine(self):
+        post_body = {
+            'start': {
+                'combine': True,
+            },
+        }
+        post_body = json.dumps(post_body)
+        return self.post('os-coverage/action', post_body, self.headers)
+
+    def stop_coverage(self):
+        post_body = {
+            'stop': {},
+        }
+        post_body = json.dumps(post_body)
+        resp, body = self.post('os-coverage/action', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def report_coverage_xml(self, file=None):
+        post_body = {
+            'report': {
+                'file': 'coverage.report',
+                'xml': True,
+            },
+        }
+        if file:
+            post_body['report']['file'] = file
+        post_body = json.dumps(post_body)
+        resp, body = self.post('os-coverage/action', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def report_coverage(self, file=None):
+        post_body = {
+            'report': {
+                'file': 'coverage.report',
+            },
+        }
+        if file:
+            post_body['report']['file'] = file
+        post_body = json.dumps(post_body)
+        resp, body = self.post('os-coverage/action', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def report_coverage_html(self, file=None):
+        post_body = {
+            'report': {
+                'file': 'coverage.report',
+                'html': True,
+            },
+        }
+        if file:
+            post_body['report']['file'] = file
+        post_body = json.dumps(post_body)
+        resp, body = self.post('os-coverage/action', post_body, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+
+def parse_opts(argv):
+    cli_opts = [
+        cfg.StrOpt('command',
+                   short='c',
+                   default='',
+                   help="This required argument is used to specify the "
+                        "coverage command to run. Only 'start', "
+                        "'stop', or 'report' are valid fields."),
+        cfg.StrOpt('filename',
+                   default='tempest-coverage',
+                   help="Specify a filename to be used for generated report "
+                        "files"),
+        cfg.BoolOpt('xml',
+                    default=False,
+                    help='Generate XML reports instead of text'),
+        cfg.BoolOpt('html',
+                    default=False,
+                    help='Generate HTML reports instead of text'),
+        cfg.BoolOpt('combine',
+                    default=False,
+                    help='Generate a single report for all services'),
+        cfg.StrOpt('output',
+                   short='o',
+                   default=None,
+                   help='Optional directory to copy generated coverage data or'
+                        ' reports into. This directory must not already exist '
+                        'it will be created')
+    ]
+    CLI = cfg.ConfigOpts()
+    CLI.register_cli_opts(cli_opts)
+    CLI(argv[1:])
+    return CLI
+
+
+def main(argv):
+    CLI = parse_opts(argv)
+    client_args = (CONF, CONF.compute_admin.username,
+                   CONF.compute_admin.password, CONF.identity.auth_url,
+                   CONF.compute_admin.tenant_name)
+    coverage_client = CoverageClientJSON(*client_args)
+
+    if CLI.command == 'start':
+        if CLI.combine:
+            coverage_client.start_coverage_combine()
+        else:
+            coverage_client.start_coverage()
+
+    elif CLI.command == 'stop':
+        resp, body = coverage_client.stop_coverage()
+        if not resp['status'] == '200':
+            print 'coverage stop failed with: %s:' % (resp['status'] + ': '
+                                                      + body)
+            exit(int(resp['status']))
+        path = body['path']
+        if CLI.output:
+            shutil.copytree(path, CLI.output)
+        else:
+            print "Data files located at: %s" % path
+
+    elif CLI.command == 'report':
+        if CLI.xml:
+            resp, body = coverage_client.report_coverage_xml(file=CLI.filename)
+        elif CLI.html:
+            resp, body = coverage_client.report_coverage_html(
+                                                            file=CLI.filename)
+        else:
+            resp, body = coverage_client.report_coverage(file=CLI.filename)
+        if not resp['status'] == '200':
+            print 'coverage report failed with: %s:' % (resp['status'] + ': '
+                                                        + body)
+            exit(int(resp['status']))
+        path = body['path']
+        if CLI.output:
+            if CLI.html:
+                shutil.copytree(path, CLI.output)
+            else:
+                path = os.path.dirname(path)
+                shutil.copytree(path, CLI.output)
+        else:
+            if not CLI.html:
+                path = os.path.dirname(path)
+            print 'Report files located at: %s' % path
+
+    else:
+        print 'Invalid command'
+        exit(1)
+
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/tox.ini b/tox.ini
index 2d8e627..1b18586 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,6 +13,11 @@
        -r{toxinidir}/tools/test-requires
 commands = nosetests {posargs}
 
+[testenv:coverage]
+commands = python -m tools/tempest_coverage -c start --combine
+           nosetests {posargs}
+           python -m tools/tempest_coverage -c report --html
+
 [testenv:pep8]
 deps = pep8==1.3.3
-commands = python tools/hacking.py --ignore=N4,E122,E125,E126 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack,*egg .
+commands = python tools/hacking.py --ignore=E122,E125,E126 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack,*egg .