Merge "Tests to verify Nova VM Rescue operations"
diff --git a/cli/__init__.py b/cli/__init__.py
index cea0b62..2548f24 100644
--- a/cli/__init__.py
+++ b/cli/__init__.py
@@ -16,8 +16,13 @@
 #    under the License.
 
 import logging
+import shlex
+import subprocess
 
-from tempest.openstack.common import cfg
+from oslo.config import cfg
+
+import tempest.test
+
 
 LOG = logging.getLogger(__name__)
 
@@ -34,3 +39,45 @@
 cli_group = cfg.OptGroup(name='cli', title="cli Configuration Options")
 CONF.register_group(cli_group)
 CONF.register_opts(cli_opts, group=cli_group)
+
+
+class ClientTestBase(tempest.test.BaseTestCase):
+    @classmethod
+    def setUpClass(cls):
+        if not CONF.cli.enabled:
+            msg = "cli testing disabled"
+            raise cls.skipException(msg)
+        cls.identity = cls.config.identity
+        super(ClientTestBase, cls).setUpClass()
+
+    def __init__(self, *args, **kwargs):
+        super(ClientTestBase, self).__init__(*args, **kwargs)
+
+    def nova(self, action, flags='', params='', admin=True, fail_ok=False):
+        """Executes nova command for the given action."""
+        return self.cmd_with_auth(
+            'nova', action, flags, params, admin, fail_ok)
+
+    def cmd_with_auth(self, cmd, action, flags='', params='',
+                      admin=True, fail_ok=False):
+        """Executes given command with auth attributes appended."""
+        #TODO(jogo) make admin=False work
+        creds = ('--os-username %s --os-tenant-name %s --os-password %s '
+                 '--os-auth-url %s ' % (self.identity.admin_username,
+                 self.identity.admin_tenant_name, self.identity.admin_password,
+                 self.identity.uri))
+        flags = creds + ' ' + flags
+        return self.cmd(cmd, action, flags, params, fail_ok)
+
+    def cmd(self, cmd, action, flags='', params='', fail_ok=False):
+        """Executes specified command for the given action."""
+        cmd = ' '.join([CONF.cli.cli_dir + cmd,
+                        flags, action, params])
+        LOG.info("running: '%s'" % cmd)
+        cmd = shlex.split(cmd)
+        try:
+            result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+        except subprocess.CalledProcessError, e:
+            LOG.error("command output:\n%s" % e.output)
+            raise
+        return result
diff --git a/cli/simple_read_only/test_compute.py b/cli/simple_read_only/test_compute.py
index 073fde1..bcdd2c5 100644
--- a/cli/simple_read_only/test_compute.py
+++ b/cli/simple_read_only/test_compute.py
@@ -16,15 +16,13 @@
 #    under the License.
 
 import logging
-import shlex
 import subprocess
 
+from oslo.config import cfg
 import testtools
 
 import cli
-
 from tempest import config
-from tempest.openstack.common import cfg
 
 
 CONF = cfg.CONF
@@ -33,7 +31,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class SimpleReadOnlyNovaCLientTest(testtools.TestCase):
+class SimpleReadOnlyNovaClientTest(cli.ClientTestBase):
 
     """
     This is a first pass at a simple read only python-novaclient test. This
@@ -47,33 +45,6 @@
 
     """
 
-    @classmethod
-    def setUpClass(cls):
-        if not CONF.cli.enabled:
-            msg = "cli testing disabled"
-            raise cls.skipException(msg)
-        cls.identity = config.TempestConfig().identity
-        super(SimpleReadOnlyNovaCLientTest, cls).setUpClass()
-
-    #NOTE(jogo): This should eventually be moved into a base class
-    def nova(self, action, flags='', params='', admin=True, fail_ok=False):
-        """Executes nova command for the given action."""
-        #TODO(jogo) make admin=False work
-        creds = ('--os-username %s --os-tenant-name %s --os-password %s '
-                 '--os-auth-url %s ' % (self.identity.admin_username,
-                 self.identity.admin_tenant_name, self.identity.admin_password,
-                 self.identity.uri))
-        flags = creds + ' ' + flags
-        cmd = ' '.join([CONF.cli.cli_dir + 'nova', flags, action, params])
-        LOG.info("running: '%s'" % cmd)
-        cmd = shlex.split(cmd)
-        try:
-            result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
-        except subprocess.CalledProcessError, e:
-            LOG.error("command output:\n%s" % e.output)
-            raise
-        return result
-
     def test_admin_fake_action(self):
         self.assertRaises(subprocess.CalledProcessError,
                           self.nova,
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9dca609..02bfdcb 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -88,6 +88,9 @@
 # Number of seconds to wait to authenticate to an instance
 ssh_timeout = 300
 
+# Number of seconds to wait for output from ssh channel
+ssh_channel_timeout = 60
+
 # The type of endpoint for a Compute API service. Unless you have a
 # custom Keystone service catalog implementation, you probably want to leave
 # this value as "compute"
@@ -190,6 +193,9 @@
 # for each tenant to have their own router.
 public_router_id = {$PUBLIC_ROUTER_ID}
 
+# Whether or not quantum is expected to be available
+quantum_available = false
+
 [volume]
 # This section contains the configuration options used when executing tests
 # against the OpenStack Block Storage API service
@@ -238,20 +244,20 @@
 
 #Image materials for S3 upload
 # ALL content of the specified directory will be uploaded to S3
-s3_materials_path = /opt/stack/devstack/files/images/s3-materials/cirros-0.3.0
+s3_materials_path = /opt/stack/devstack/files/images/s3-materials/cirros-0.3.1
 
 # The manifest.xml files, must be in the s3_materials_path directory
 # Subdirectories not allowed!
 # The filenames will be used as a Keys in the S3 Buckets
 
 #ARI Ramdisk manifest. Must be in the above s3_materials_path
-ari_manifest = cirros-0.3.0-x86_64-initrd.manifest.xml
+ari_manifest = cirros-0.3.1-x86_64-initrd.manifest.xml
 
 #AMI Machine Image manifest. Must be in the above s3_materials_path
-ami_manifest = cirros-0.3.0-x86_64-blank.img.manifest.xml
+ami_manifest = cirros-0.3.1-x86_64-blank.img.manifest.xml
 
 #AKI Kernel Image manifest, Must be in the above s3_materials_path
-aki_manifest = cirros-0.3.0-x86_64-vmlinuz.manifest.xml
+aki_manifest = cirros-0.3.1-x86_64-vmlinuz.manifest.xml
 
 #Instance type
 instance_type = m1.tiny
diff --git a/openstack-common.conf b/openstack-common.conf
index 0c9e43e..501328c 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
 [DEFAULT]
 
 # The list of modules to copy from openstack-common
-modules=setup,cfg,iniparser,install_venv_common
+modules=setup,install_venv_common
 
 # The base module to hold the copy of openstack.common
 base=tempest
diff --git a/tempest/clients.py b/tempest/clients.py
index ef07d9c..b3b5906 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -52,7 +52,8 @@
 from tempest.services.identity.json.identity_client import TokenClientJSON
 from tempest.services.identity.xml.identity_client import IdentityClientXML
 from tempest.services.identity.xml.identity_client import TokenClientXML
-from tempest.services.image.json.image_client import ImageClientJSON
+from tempest.services.image.v1.json.image_client import ImageClientJSON
+from tempest.services.image.v2.json.image_client import ImageClientV2JSON
 from tempest.services.network.json.network_client import NetworkClient
 from tempest.services.object_storage.account_client import AccountClient
 from tempest.services.object_storage.account_client import \
@@ -69,6 +70,10 @@
     VolumeTypesClientXML
 from tempest.services.volume.xml.snapshots_client import SnapshotsClientXML
 from tempest.services.volume.xml.volumes_client import VolumesClientXML
+from tempest.services.compute.json.interfaces_client import \
+    InterfacesClientJSON
+from tempest.services.compute.xml.interfaces_client import \
+    InterfacesClientXML
 
 LOG = logging.getLogger(__name__)
 
@@ -147,6 +152,11 @@
     "xml": SecurityGroupsClientXML,
 }
 
+INTERFACES_CLIENT = {
+    "json": InterfacesClientJSON,
+    "xml": InterfacesClientXML,
+}
+
 
 class Manager(object):
 
@@ -208,6 +218,7 @@
             self.token_client = TOKEN_CLIENT[interface](self.config)
             self.security_groups_client = \
                 SECURITY_GROUPS_CLIENT[interface](*client_args)
+            self.interfaces_client = INTERFACES_CLIENT[interface](*client_args)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
@@ -215,6 +226,7 @@
         self.hosts_client = HostsClientJSON(*client_args)
         self.account_client = AccountClient(*client_args)
         self.image_client = ImageClientJSON(*client_args)
+        self.image_client_v2 = ImageClientV2JSON(*client_args)
         self.container_client = ContainerClient(*client_args)
         self.object_client = ObjectClient(*client_args)
         self.ec2api_client = botoclients.APIClientEC2(*client_args)
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 36a9abd..0902239 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -18,6 +18,7 @@
 import copy
 import hashlib
 import httplib
+import json
 import logging
 import posixpath
 import re
@@ -26,10 +27,6 @@
 import struct
 import urlparse
 
-try:
-    import json
-except ImportError:
-    import simplejson as json
 
 # Python 2.5 compat fix
 if not hasattr(urlparse, 'parse_qsl'):
@@ -129,11 +126,11 @@
             resp = conn.getresponse()
         except socket.gaierror as e:
             message = "Error finding address for %(url)s: %(e)s" % locals()
-            raise exc.EndpointNotFound
+            raise exc.EndpointNotFound(message)
         except (socket.error, socket.timeout) as e:
             endpoint = self.endpoint
             message = "Error communicating with %(endpoint)s %(e)s" % locals()
-            raise exc.TimeoutException
+            raise exc.TimeoutException(message)
 
         body_iter = ResponseBodyIterator(resp)
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 170a137..5ce3be6 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -177,8 +177,8 @@
     def post(self, url, body, headers):
         return self.request('POST', url, headers, body)
 
-    def get(self, url, headers=None, wait=None):
-        return self.request('GET', url, headers, wait=wait)
+    def get(self, url, headers=None):
+        return self.request('GET', url, headers)
 
     def delete(self, url, headers=None):
         return self.request('DELETE', url, headers)
@@ -257,7 +257,7 @@
             self.LOG.warning("status >= 400 response with empty body")
 
     def request(self, method, url,
-                headers=None, body=None, depth=0, wait=None):
+                headers=None, body=None, depth=0):
         """A simple HTTP request interface."""
 
         if (self.token is None) or (self.base_url is None):
@@ -276,13 +276,12 @@
         self._log_response(resp, resp_body)
         self.response_checker(method, url, headers, body, resp, resp_body)
 
-        self._error_checker(method, url, headers, body, resp, resp_body, depth,
-                            wait)
+        self._error_checker(method, url, headers, body, resp, resp_body, depth)
 
         return resp, resp_body
 
     def _error_checker(self, method, url,
-                       headers, body, resp, resp_body, depth=0, wait=None):
+                       headers, body, resp, resp_body, depth=0):
 
         # NOTE(mtreinish): Check for httplib response from glance_http. The
         # object can't be used here because importing httplib breaks httplib2.
@@ -336,7 +335,12 @@
                 resp_body = self._parse_resp(resp_body)
             #Checking whether Absolute/Rate limit
             return self.check_over_limit(resp_body, method, url, headers, body,
-                                         depth, wait)
+                                         depth)
+
+        if resp.status == 422:
+            if parse_resp:
+                resp_body = self._parse_resp(resp_body)
+            raise exceptions.UnprocessableEntity(resp_body)
 
         if resp.status in (500, 501):
             message = resp_body
@@ -362,11 +366,11 @@
             raise exceptions.RestClientException(str(resp.status))
 
     def check_over_limit(self, resp_body, method, url,
-                         headers, body, depth, wait):
+                         headers, body, depth):
         self.is_absolute_limit(resp_body['overLimit'])
         return self.is_rate_limit_retry_max_recursion_depth(
             resp_body['overLimit'], method, url, headers,
-            body, depth, wait)
+            body, depth)
 
     def is_absolute_limit(self, resp_body):
         if 'exceeded' in resp_body['message']:
@@ -375,15 +379,14 @@
             return
 
     def is_rate_limit_retry_max_recursion_depth(self, resp_body, method,
-                                                url, headers, body, depth,
-                                                wait):
+                                                url, headers, body, depth):
         if 'retryAfter' in resp_body:
             if depth < MAX_RECURSION_DEPTH:
                 delay = resp_body['retryAfter']
                 time.sleep(int(delay))
                 return self.request(method, url, headers=headers,
                                     body=body,
-                                    depth=depth + 1, wait=wait)
+                                    depth=depth + 1)
             else:
                 raise exceptions.RateLimitExceeded(
                     message=resp_body['overLimitFault']['message'],
@@ -417,8 +420,8 @@
         return xml_to_json(etree.fromstring(body))
 
     def check_over_limit(self, resp_body, method, url,
-                         headers, body, depth, wait):
+                         headers, body, depth):
         self.is_absolute_limit(resp_body)
         return self.is_rate_limit_retry_max_recursion_depth(
             resp_body, method, url, headers,
-            body, depth, wait)
+            body, depth)
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 151060f..be6fe27 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -106,6 +106,7 @@
         ssh = self._get_ssh_connection()
         transport = ssh.get_transport()
         channel = transport.open_session()
+        channel.fileno()  # Register event pipe
         channel.exec_command(cmd)
         channel.shutdown_write()
         out_data = []
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index b501df4..fd5d3d0 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -15,6 +15,7 @@
         ssh_timeout = TempestConfig().compute.ssh_timeout
         network = TempestConfig().compute.network_for_ssh
         ip_version = TempestConfig().compute.ip_version_for_ssh
+        ssh_channel_timeout = TempestConfig().compute.ssh_channel_timeout
         if isinstance(server, basestring):
             ip_address = server
         else:
@@ -27,7 +28,8 @@
                 raise ServerUnreachable()
 
         self.ssh_client = Client(ip_address, username, password, ssh_timeout,
-                                 pkey=pkey)
+                                 pkey=pkey,
+                                 channel_timeout=ssh_channel_timeout)
         if not self.ssh_client.test_connection_auth():
             raise SSHTimeout()
 
diff --git a/tempest/config.py b/tempest/config.py
index c0e25c7..3a4a8c9 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -19,8 +19,9 @@
 import os
 import sys
 
+from oslo.config import cfg
+
 from tempest.common.utils.misc import singleton
-from tempest.openstack.common import cfg
 
 LOG = logging.getLogger(__name__)
 
@@ -149,8 +150,12 @@
                help="User name used to authenticate to an instance."),
     cfg.IntOpt('ssh_timeout',
                default=300,
-               help="Timeout in seconds to wait for authentcation to "
+               help="Timeout in seconds to wait for authentication to "
                     "succeed."),
+    cfg.IntOpt('ssh_channel_timeout',
+               default=60,
+               help="Timeout in seconds to wait for output from ssh "
+                    "channel."),
     cfg.StrOpt('network_for_ssh',
                default='public',
                help="Network used for SSH connections."),
@@ -276,6 +281,9 @@
                default="",
                help="Id of the public router that provides external "
                     "connectivity"),
+    cfg.BoolOpt('quantum_available',
+                default=False,
+                help="Whether or not quantum is expected to be available"),
 ]
 
 
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 577aa13..235a2e7 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -94,6 +94,10 @@
     message = "Bad request"
 
 
+class UnprocessableEntity(RestClientException):
+    message = "Unprocessable entity"
+
+
 class AuthenticationFailure(RestClientException):
     message = ("Authentication with user %(user)s and password "
                "%(password)s failed")
diff --git a/tempest/openstack/common/cfg.py b/tempest/openstack/common/cfg.py
deleted file mode 100644
index cae4ecc..0000000
--- a/tempest/openstack/common/cfg.py
+++ /dev/null
@@ -1,1749 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 Red Hat, Inc.
-#
-#    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.
-
-r"""
-Configuration options which may be set on the command line or in config files.
-
-The schema for each option is defined using the Opt sub-classes, e.g.:
-
-::
-
-    common_opts = [
-        cfg.StrOpt('bind_host',
-                   default='0.0.0.0',
-                   help='IP address to listen on'),
-        cfg.IntOpt('bind_port',
-                   default=9292,
-                   help='Port number to listen on')
-    ]
-
-Options can be strings, integers, floats, booleans, lists or 'multi strings'::
-
-    enabled_apis_opt = cfg.ListOpt('enabled_apis',
-                                   default=['ec2', 'osapi_compute'],
-                                   help='List of APIs to enable by default')
-
-    DEFAULT_EXTENSIONS = [
-        'nova.api.openstack.compute.contrib.standard_extensions'
-    ]
-    osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension',
-                                                  default=DEFAULT_EXTENSIONS)
-
-Option schemas are registered with the config manager at runtime, but before
-the option is referenced::
-
-    class ExtensionManager(object):
-
-        enabled_apis_opt = cfg.ListOpt(...)
-
-        def __init__(self, conf):
-            self.conf = conf
-            self.conf.register_opt(enabled_apis_opt)
-            ...
-
-        def _load_extensions(self):
-            for ext_factory in self.conf.osapi_compute_extension:
-                ....
-
-A common usage pattern is for each option schema to be defined in the module or
-class which uses the option::
-
-    opts = ...
-
-    def add_common_opts(conf):
-        conf.register_opts(opts)
-
-    def get_bind_host(conf):
-        return conf.bind_host
-
-    def get_bind_port(conf):
-        return conf.bind_port
-
-An option may optionally be made available via the command line. Such options
-must registered with the config manager before the command line is parsed (for
-the purposes of --help and CLI arg validation)::
-
-    cli_opts = [
-        cfg.BoolOpt('verbose',
-                    short='v',
-                    default=False,
-                    help='Print more verbose output'),
-        cfg.BoolOpt('debug',
-                    short='d',
-                    default=False,
-                    help='Print debugging output'),
-    ]
-
-    def add_common_opts(conf):
-        conf.register_cli_opts(cli_opts)
-
-The config manager has two CLI options defined by default, --config-file
-and --config-dir::
-
-    class ConfigOpts(object):
-
-        def __call__(self, ...):
-
-            opts = [
-                MultiStrOpt('config-file',
-                        ...),
-                StrOpt('config-dir',
-                       ...),
-            ]
-
-            self.register_cli_opts(opts)
-
-Option values are parsed from any supplied config files using
-openstack.common.iniparser. If none are specified, a default set is used
-e.g. glance-api.conf and glance-common.conf::
-
-    glance-api.conf:
-      [DEFAULT]
-      bind_port = 9292
-
-    glance-common.conf:
-      [DEFAULT]
-      bind_host = 0.0.0.0
-
-Option values in config files override those on the command line. Config files
-are parsed in order, with values in later files overriding those in earlier
-files.
-
-The parsing of CLI args and config files is initiated by invoking the config
-manager e.g.::
-
-    conf = ConfigOpts()
-    conf.register_opt(BoolOpt('verbose', ...))
-    conf(sys.argv[1:])
-    if conf.verbose:
-        ...
-
-Options can be registered as belonging to a group::
-
-    rabbit_group = cfg.OptGroup(name='rabbit',
-                                title='RabbitMQ options')
-
-    rabbit_host_opt = cfg.StrOpt('host',
-                                 default='localhost',
-                                 help='IP/hostname to listen on'),
-    rabbit_port_opt = cfg.IntOpt('port',
-                                 default=5672,
-                                 help='Port number to listen on')
-
-    def register_rabbit_opts(conf):
-        conf.register_group(rabbit_group)
-        # options can be registered under a group in either of these ways:
-        conf.register_opt(rabbit_host_opt, group=rabbit_group)
-        conf.register_opt(rabbit_port_opt, group='rabbit')
-
-If it no group attributes are required other than the group name, the group
-need not be explicitly registered e.g.
-
-    def register_rabbit_opts(conf):
-        # The group will automatically be created, equivalent calling::
-        #   conf.register_group(OptGroup(name='rabbit'))
-        conf.register_opt(rabbit_port_opt, group='rabbit')
-
-If no group is specified, options belong to the 'DEFAULT' section of config
-files::
-
-    glance-api.conf:
-      [DEFAULT]
-      bind_port = 9292
-      ...
-
-      [rabbit]
-      host = localhost
-      port = 5672
-      use_ssl = False
-      userid = guest
-      password = guest
-      virtual_host = /
-
-Command-line options in a group are automatically prefixed with the
-group name::
-
-    --rabbit-host localhost --rabbit-port 9999
-
-Option values in the default group are referenced as attributes/properties on
-the config manager; groups are also attributes on the config manager, with
-attributes for each of the options associated with the group::
-
-    server.start(app, conf.bind_port, conf.bind_host, conf)
-
-    self.connection = kombu.connection.BrokerConnection(
-        hostname=conf.rabbit.host,
-        port=conf.rabbit.port,
-        ...)
-
-Option values may reference other values using PEP 292 string substitution::
-
-    opts = [
-        cfg.StrOpt('state_path',
-                   default=os.path.join(os.path.dirname(__file__), '../'),
-                   help='Top-level directory for maintaining nova state'),
-        cfg.StrOpt('sqlite_db',
-                   default='nova.sqlite',
-                   help='file name for sqlite'),
-        cfg.StrOpt('sql_connection',
-                   default='sqlite:///$state_path/$sqlite_db',
-                   help='connection string for sql database'),
-    ]
-
-Note that interpolation can be avoided by using '$$'.
-
-Options may be declared as required so that an error is raised if the user
-does not supply a value for the option.
-
-Options may be declared as secret so that their values are not leaked into
-log files::
-
-     opts = [
-        cfg.StrOpt('s3_store_access_key', secret=True),
-        cfg.StrOpt('s3_store_secret_key', secret=True),
-        ...
-     ]
-
-This module also contains a global instance of the ConfigOpts class
-in order to support a common usage pattern in OpenStack::
-
-    from tempest.openstack.common import cfg
-
-    opts = [
-        cfg.StrOpt('bind_host', default='0.0.0.0'),
-        cfg.IntOpt('bind_port', default=9292),
-    ]
-
-    CONF = cfg.CONF
-    CONF.register_opts(opts)
-
-    def start(server, app):
-        server.start(app, CONF.bind_port, CONF.bind_host)
-
-Positional command line arguments are supported via a 'positional' Opt
-constructor argument::
-
-    >>> conf = ConfigOpts()
-    >>> conf.register_cli_opt(MultiStrOpt('bar', positional=True))
-    True
-    >>> conf(['a', 'b'])
-    >>> conf.bar
-    ['a', 'b']
-
-It is also possible to use argparse "sub-parsers" to parse additional
-command line arguments using the SubCommandOpt class:
-
-    >>> def add_parsers(subparsers):
-    ...     list_action = subparsers.add_parser('list')
-    ...     list_action.add_argument('id')
-    ...
-    >>> conf = ConfigOpts()
-    >>> conf.register_cli_opt(SubCommandOpt('action', handler=add_parsers))
-    True
-    >>> conf(args=['list', '10'])
-    >>> conf.action.name, conf.action.id
-    ('list', '10')
-
-"""
-
-import argparse
-import collections
-import copy
-import functools
-import glob
-import os
-import string
-import sys
-
-from tempest.openstack.common import iniparser
-
-
-class Error(Exception):
-    """Base class for cfg exceptions."""
-
-    def __init__(self, msg=None):
-        self.msg = msg
-
-    def __str__(self):
-        return self.msg
-
-
-class ArgsAlreadyParsedError(Error):
-    """Raised if a CLI opt is registered after parsing."""
-
-    def __str__(self):
-        ret = "arguments already parsed"
-        if self.msg:
-            ret += ": " + self.msg
-        return ret
-
-
-class NoSuchOptError(Error, AttributeError):
-    """Raised if an opt which doesn't exist is referenced."""
-
-    def __init__(self, opt_name, group=None):
-        self.opt_name = opt_name
-        self.group = group
-
-    def __str__(self):
-        if self.group is None:
-            return "no such option: %s" % self.opt_name
-        else:
-            return "no such option in group %s: %s" % (self.group.name,
-                                                       self.opt_name)
-
-
-class NoSuchGroupError(Error):
-    """Raised if a group which doesn't exist is referenced."""
-
-    def __init__(self, group_name):
-        self.group_name = group_name
-
-    def __str__(self):
-        return "no such group: %s" % self.group_name
-
-
-class DuplicateOptError(Error):
-    """Raised if multiple opts with the same name are registered."""
-
-    def __init__(self, opt_name):
-        self.opt_name = opt_name
-
-    def __str__(self):
-        return "duplicate option: %s" % self.opt_name
-
-
-class RequiredOptError(Error):
-    """Raised if an option is required but no value is supplied by the user."""
-
-    def __init__(self, opt_name, group=None):
-        self.opt_name = opt_name
-        self.group = group
-
-    def __str__(self):
-        if self.group is None:
-            return "value required for option: %s" % self.opt_name
-        else:
-            return "value required for option: %s.%s" % (self.group.name,
-                                                         self.opt_name)
-
-
-class TemplateSubstitutionError(Error):
-    """Raised if an error occurs substituting a variable in an opt value."""
-
-    def __str__(self):
-        return "template substitution error: %s" % self.msg
-
-
-class ConfigFilesNotFoundError(Error):
-    """Raised if one or more config files are not found."""
-
-    def __init__(self, config_files):
-        self.config_files = config_files
-
-    def __str__(self):
-        return ('Failed to read some config files: %s' %
-                string.join(self.config_files, ','))
-
-
-class ConfigFileParseError(Error):
-    """Raised if there is an error parsing a config file."""
-
-    def __init__(self, config_file, msg):
-        self.config_file = config_file
-        self.msg = msg
-
-    def __str__(self):
-        return 'Failed to parse %s: %s' % (self.config_file, self.msg)
-
-
-class ConfigFileValueError(Error):
-    """Raised if a config file value does not match its opt type."""
-    pass
-
-
-def _fixpath(p):
-    """Apply tilde expansion and absolutization to a path."""
-    return os.path.abspath(os.path.expanduser(p))
-
-
-def _get_config_dirs(project=None):
-    """Return a list of directors where config files may be located.
-
-    :param project: an optional project name
-
-    If a project is specified, following directories are returned::
-
-      ~/.${project}/
-      ~/
-      /etc/${project}/
-      /etc/
-
-    Otherwise, these directories::
-
-      ~/
-      /etc/
-    """
-    cfg_dirs = [
-        _fixpath(os.path.join('~', '.' + project)) if project else None,
-        _fixpath('~'),
-        os.path.join('/etc', project) if project else None,
-        '/etc'
-    ]
-
-    return filter(bool, cfg_dirs)
-
-
-def _search_dirs(dirs, basename, extension=""):
-    """Search a list of directories for a given filename.
-
-    Iterator over the supplied directories, returning the first file
-    found with the supplied name and extension.
-
-    :param dirs: a list of directories
-    :param basename: the filename, e.g. 'glance-api'
-    :param extension: the file extension, e.g. '.conf'
-    :returns: the path to a matching file, or None
-    """
-    for d in dirs:
-        path = os.path.join(d, '%s%s' % (basename, extension))
-        if os.path.exists(path):
-            return path
-
-
-def find_config_files(project=None, prog=None, extension='.conf'):
-    """Return a list of default configuration files.
-
-    :param project: an optional project name
-    :param prog: the program name, defaulting to the basename of sys.argv[0]
-    :param extension: the type of the config file
-
-    We default to two config files: [${project}.conf, ${prog}.conf]
-
-    And we look for those config files in the following directories::
-
-      ~/.${project}/
-      ~/
-      /etc/${project}/
-      /etc/
-
-    We return an absolute path for (at most) one of each the default config
-    files, for the topmost directory it exists in.
-
-    For example, if project=foo, prog=bar and /etc/foo/foo.conf, /etc/bar.conf
-    and ~/.foo/bar.conf all exist, then we return ['/etc/foo/foo.conf',
-    '~/.foo/bar.conf']
-
-    If no project name is supplied, we only look for ${prog.conf}.
-    """
-    if prog is None:
-        prog = os.path.basename(sys.argv[0])
-
-    cfg_dirs = _get_config_dirs(project)
-
-    config_files = []
-    if project:
-        config_files.append(_search_dirs(cfg_dirs, project, extension))
-    config_files.append(_search_dirs(cfg_dirs, prog, extension))
-
-    return filter(bool, config_files)
-
-
-def _is_opt_registered(opts, opt):
-    """Check whether an opt with the same name is already registered.
-
-    The same opt may be registered multiple times, with only the first
-    registration having any effect. However, it is an error to attempt
-    to register a different opt with the same name.
-
-    :param opts: the set of opts already registered
-    :param opt: the opt to be registered
-    :returns: True if the opt was previously registered, False otherwise
-    :raises: DuplicateOptError if a naming conflict is detected
-    """
-    if opt.dest in opts:
-        if opts[opt.dest]['opt'] != opt:
-            raise DuplicateOptError(opt.name)
-        return True
-    else:
-        return False
-
-
-def set_defaults(opts, **kwargs):
-    for opt in opts:
-        if opt.dest in kwargs:
-            opt.default = kwargs[opt.dest]
-            break
-
-
-class Opt(object):
-
-    """Base class for all configuration options.
-
-    An Opt object has no public methods, but has a number of public string
-    properties:
-
-      name:
-        the name of the option, which may include hyphens
-      dest:
-        the (hyphen-less) ConfigOpts property which contains the option value
-      short:
-        a single character CLI option name
-      default:
-        the default value of the option
-      positional:
-        True if the option is a positional CLI argument
-      metavar:
-        the name shown as the argument to a CLI option in --help output
-      help:
-        an string explaining how the options value is used
-    """
-    multi = False
-
-    def __init__(self, name, dest=None, short=None, default=None,
-                 positional=False, metavar=None, help=None,
-                 secret=False, required=False, deprecated_name=None):
-        """Construct an Opt object.
-
-        The only required parameter is the option's name. However, it is
-        common to also supply a default and help string for all options.
-
-        :param name: the option's name
-        :param dest: the name of the corresponding ConfigOpts property
-        :param short: a single character CLI option name
-        :param default: the default value of the option
-        :param positional: True if the option is a positional CLI argument
-        :param metavar: the option argument to show in --help
-        :param help: an explanation of how the option is used
-        :param secret: true iff the value should be obfuscated in log output
-        :param required: true iff a value must be supplied for this option
-        :param deprecated_name: deprecated name option.  Acts like an alias
-        """
-        self.name = name
-        if dest is None:
-            self.dest = self.name.replace('-', '_')
-        else:
-            self.dest = dest
-        self.short = short
-        self.default = default
-        self.positional = positional
-        self.metavar = metavar
-        self.help = help
-        self.secret = secret
-        self.required = required
-        if deprecated_name is not None:
-            self.deprecated_name = deprecated_name.replace('-', '_')
-        else:
-            self.deprecated_name = None
-
-    def __ne__(self, another):
-        return vars(self) != vars(another)
-
-    def _get_from_config_parser(self, cparser, section):
-        """Retrieves the option value from a MultiConfigParser object.
-
-        This is the method ConfigOpts uses to look up the option value from
-        config files. Most opt types override this method in order to perform
-        type appropriate conversion of the returned value.
-
-        :param cparser: a ConfigParser object
-        :param section: a section name
-        """
-        return self._cparser_get_with_deprecated(cparser, section)
-
-    def _cparser_get_with_deprecated(self, cparser, section):
-        """If cannot find option as dest try deprecated_name alias."""
-        if self.deprecated_name is not None:
-            return cparser.get(section, [self.dest, self.deprecated_name])
-        return cparser.get(section, [self.dest])
-
-    def _add_to_cli(self, parser, group=None):
-        """Makes the option available in the command line interface.
-
-        This is the method ConfigOpts uses to add the opt to the CLI interface
-        as appropriate for the opt type. Some opt types may extend this method,
-        others may just extend the helper methods it uses.
-
-        :param parser: the CLI option parser
-        :param group: an optional OptGroup object
-        """
-        container = self._get_argparse_container(parser, group)
-        kwargs = self._get_argparse_kwargs(group)
-        prefix = self._get_argparse_prefix('', group)
-        self._add_to_argparse(container, self.name, self.short, kwargs, prefix,
-                              self.positional, self.deprecated_name)
-
-    def _add_to_argparse(self, container, name, short, kwargs, prefix='',
-                         positional=False, deprecated_name=None):
-        """Add an option to an argparse parser or group.
-
-        :param container: an argparse._ArgumentGroup object
-        :param name: the opt name
-        :param short: the short opt name
-        :param kwargs: the keyword arguments for add_argument()
-        :param prefix: an optional prefix to prepend to the opt name
-        :param position: whether the optional is a positional CLI argument
-        :raises: DuplicateOptError if a naming confict is detected
-        """
-        def hyphen(arg):
-            return arg if not positional else ''
-
-        args = [hyphen('--') + prefix + name]
-        if short:
-            args.append(hyphen('-') + short)
-        if deprecated_name:
-            args.append(hyphen('--') + prefix + deprecated_name)
-
-        try:
-            container.add_argument(*args, **kwargs)
-        except argparse.ArgumentError as e:
-            raise DuplicateOptError(e)
-
-    def _get_argparse_container(self, parser, group):
-        """Returns an argparse._ArgumentGroup.
-
-        :param parser: an argparse.ArgumentParser
-        :param group: an (optional) OptGroup object
-        :returns: an argparse._ArgumentGroup if group is given, else parser
-        """
-        if group is not None:
-            return group._get_argparse_group(parser)
-        else:
-            return parser
-
-    def _get_argparse_kwargs(self, group, **kwargs):
-        """Build a dict of keyword arguments for argparse's add_argument().
-
-        Most opt types extend this method to customize the behaviour of the
-        options added to argparse.
-
-        :param group: an optional group
-        :param kwargs: optional keyword arguments to add to
-        :returns: a dict of keyword arguments
-        """
-        if not self.positional:
-            dest = self.dest
-            if group is not None:
-                dest = group.name + '_' + dest
-            kwargs['dest'] = dest
-        else:
-            kwargs['nargs'] = '?'
-        kwargs.update({'default': None,
-                       'metavar': self.metavar,
-                       'help': self.help, })
-        return kwargs
-
-    def _get_argparse_prefix(self, prefix, group):
-        """Build a prefix for the CLI option name, if required.
-
-        CLI options in a group are prefixed with the group's name in order
-        to avoid conflicts between similarly named options in different
-        groups.
-
-        :param prefix: an existing prefix to append to (e.g. 'no' or '')
-        :param group: an optional OptGroup object
-        :returns: a CLI option prefix including the group name, if appropriate
-        """
-        if group is not None:
-            return group.name + '-' + prefix
-        else:
-            return prefix
-
-
-class StrOpt(Opt):
-    """
-    String opts do not have their values transformed and are returned as
-    str objects.
-    """
-    pass
-
-
-class BoolOpt(Opt):
-
-    """
-    Bool opts are set to True or False on the command line using --optname or
-    --noopttname respectively.
-
-    In config files, boolean values are case insensitive and can be set using
-    1/0, yes/no, true/false or on/off.
-    """
-
-    _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
-                       '0': False, 'no': False, 'false': False, 'off': False}
-
-    def __init__(self, *args, **kwargs):
-        if 'positional' in kwargs:
-            raise ValueError('positional boolean args not supported')
-        super(BoolOpt, self).__init__(*args, **kwargs)
-
-    def _get_from_config_parser(self, cparser, section):
-        """Retrieve the opt value as a boolean from ConfigParser."""
-        def convert_bool(v):
-            value = self._boolean_states.get(v.lower())
-            if value is None:
-                raise ValueError('Unexpected boolean value %r' % v)
-
-            return value
-
-        return [convert_bool(v) for v in
-                self._cparser_get_with_deprecated(cparser, section)]
-
-    def _add_to_cli(self, parser, group=None):
-        """Extends the base class method to add the --nooptname option."""
-        super(BoolOpt, self)._add_to_cli(parser, group)
-        self._add_inverse_to_argparse(parser, group)
-
-    def _add_inverse_to_argparse(self, parser, group):
-        """Add the --nooptname option to the option parser."""
-        container = self._get_argparse_container(parser, group)
-        kwargs = self._get_argparse_kwargs(group, action='store_false')
-        prefix = self._get_argparse_prefix('no', group)
-        kwargs["help"] = "The inverse of --" + self.name
-        self._add_to_argparse(container, self.name, None, kwargs, prefix,
-                              self.positional, self.deprecated_name)
-
-    def _get_argparse_kwargs(self, group, action='store_true', **kwargs):
-        """Extends the base argparse keyword dict for boolean options."""
-
-        kwargs = super(BoolOpt, self)._get_argparse_kwargs(group, **kwargs)
-
-        # metavar has no effect for BoolOpt
-        if 'metavar' in kwargs:
-            del kwargs['metavar']
-
-        if action != 'store_true':
-            action = 'store_false'
-
-        kwargs['action'] = action
-
-        return kwargs
-
-
-class IntOpt(Opt):
-
-    """Int opt values are converted to integers using the int() builtin."""
-
-    def _get_from_config_parser(self, cparser, section):
-        """Retrieve the opt value as a integer from ConfigParser."""
-        return [int(v) for v in self._cparser_get_with_deprecated(cparser,
-                section)]
-
-    def _get_argparse_kwargs(self, group, **kwargs):
-        """Extends the base argparse keyword dict for integer options."""
-        return super(IntOpt,
-                     self)._get_argparse_kwargs(group, type=int, **kwargs)
-
-
-class FloatOpt(Opt):
-
-    """Float opt values are converted to floats using the float() builtin."""
-
-    def _get_from_config_parser(self, cparser, section):
-        """Retrieve the opt value as a float from ConfigParser."""
-        return [float(v) for v in
-                self._cparser_get_with_deprecated(cparser, section)]
-
-    def _get_argparse_kwargs(self, group, **kwargs):
-        """Extends the base argparse keyword dict for float options."""
-        return super(FloatOpt, self)._get_argparse_kwargs(group,
-                                                          type=float, **kwargs)
-
-
-class ListOpt(Opt):
-
-    """
-    List opt values are simple string values separated by commas. The opt value
-    is a list containing these strings.
-    """
-
-    class _StoreListAction(argparse.Action):
-        """
-        An argparse action for parsing an option value into a list.
-        """
-        def __call__(self, parser, namespace, values, option_string=None):
-            if values is not None:
-                values = [a.strip() for a in values.split(',')]
-            setattr(namespace, self.dest, values)
-
-    def _get_from_config_parser(self, cparser, section):
-        """Retrieve the opt value as a list from ConfigParser."""
-        return [[a.strip() for a in v.split(',')] for v in
-                self._cparser_get_with_deprecated(cparser, section)]
-
-    def _get_argparse_kwargs(self, group, **kwargs):
-        """Extends the base argparse keyword dict for list options."""
-        return Opt._get_argparse_kwargs(self,
-                                        group,
-                                        action=ListOpt._StoreListAction,
-                                        **kwargs)
-
-
-class MultiStrOpt(Opt):
-
-    """
-    Multistr opt values are string opts which may be specified multiple times.
-    The opt value is a list containing all the string values specified.
-    """
-    multi = True
-
-    def _get_argparse_kwargs(self, group, **kwargs):
-        """Extends the base argparse keyword dict for multi str options."""
-        kwargs = super(MultiStrOpt, self)._get_argparse_kwargs(group)
-        if not self.positional:
-            kwargs['action'] = 'append'
-        else:
-            kwargs['nargs'] = '*'
-        return kwargs
-
-    def _cparser_get_with_deprecated(self, cparser, section):
-        """If cannot find option as dest try deprecated_name alias."""
-        if self.deprecated_name is not None:
-            return cparser.get(section, [self.dest, self.deprecated_name],
-                               multi=True)
-        return cparser.get(section, [self.dest], multi=True)
-
-
-class SubCommandOpt(Opt):
-
-    """
-    Sub-command options allow argparse sub-parsers to be used to parse
-    additional command line arguments.
-
-    The handler argument to the SubCommandOpt contructor is a callable
-    which is supplied an argparse subparsers object. Use this handler
-    callable to add sub-parsers.
-
-    The opt value is SubCommandAttr object with the name of the chosen
-    sub-parser stored in the 'name' attribute and the values of other
-    sub-parser arguments available as additional attributes.
-    """
-
-    def __init__(self, name, dest=None, handler=None,
-                 title=None, description=None, help=None):
-        """Construct an sub-command parsing option.
-
-        This behaves similarly to other Opt sub-classes but adds a
-        'handler' argument. The handler is a callable which is supplied
-        an subparsers object when invoked. The add_parser() method on
-        this subparsers object can be used to register parsers for
-        sub-commands.
-
-        :param name: the option's name
-        :param dest: the name of the corresponding ConfigOpts property
-        :param title: title of the sub-commands group in help output
-        :param description: description of the group in help output
-        :param help: a help string giving an overview of available sub-commands
-        """
-        super(SubCommandOpt, self).__init__(name, dest=dest, help=help)
-        self.handler = handler
-        self.title = title
-        self.description = description
-
-    def _add_to_cli(self, parser, group=None):
-        """Add argparse sub-parsers and invoke the handler method."""
-        dest = self.dest
-        if group is not None:
-            dest = group.name + '_' + dest
-
-        subparsers = parser.add_subparsers(dest=dest,
-                                           title=self.title,
-                                           description=self.description,
-                                           help=self.help)
-
-        if self.handler is not None:
-            self.handler(subparsers)
-
-
-class OptGroup(object):
-
-    """
-    Represents a group of opts.
-
-    CLI opts in the group are automatically prefixed with the group name.
-
-    Each group corresponds to a section in config files.
-
-    An OptGroup object has no public methods, but has a number of public string
-    properties:
-
-      name:
-        the name of the group
-      title:
-        the group title as displayed in --help
-      help:
-        the group description as displayed in --help
-    """
-
-    def __init__(self, name, title=None, help=None):
-        """Constructs an OptGroup object.
-
-        :param name: the group name
-        :param title: the group title for --help
-        :param help: the group description for --help
-        """
-        self.name = name
-        if title is None:
-            self.title = "%s options" % title
-        else:
-            self.title = title
-        self.help = help
-
-        self._opts = {}  # dict of dicts of (opt:, override:, default:)
-        self._argparse_group = None
-
-    def _register_opt(self, opt, cli=False):
-        """Add an opt to this group.
-
-        :param opt: an Opt object
-        :param cli: whether this is a CLI option
-        :returns: False if previously registered, True otherwise
-        :raises: DuplicateOptError if a naming conflict is detected
-        """
-        if _is_opt_registered(self._opts, opt):
-            return False
-
-        self._opts[opt.dest] = {'opt': opt, 'cli': cli}
-
-        return True
-
-    def _unregister_opt(self, opt):
-        """Remove an opt from this group.
-
-        :param opt: an Opt object
-        """
-        if opt.dest in self._opts:
-            del self._opts[opt.dest]
-
-    def _get_argparse_group(self, parser):
-        if self._argparse_group is None:
-            """Build an argparse._ArgumentGroup for this group."""
-            self._argparse_group = parser.add_argument_group(self.title,
-                                                             self.help)
-        return self._argparse_group
-
-    def _clear(self):
-        """Clear this group's option parsing state."""
-        self._argparse_group = None
-
-
-class ParseError(iniparser.ParseError):
-    def __init__(self, msg, lineno, line, filename):
-        super(ParseError, self).__init__(msg, lineno, line)
-        self.filename = filename
-
-    def __str__(self):
-        return 'at %s:%d, %s: %r' % (self.filename, self.lineno,
-                                     self.msg, self.line)
-
-
-class ConfigParser(iniparser.BaseParser):
-    def __init__(self, filename, sections):
-        super(ConfigParser, self).__init__()
-        self.filename = filename
-        self.sections = sections
-        self.section = None
-
-    def parse(self):
-        with open(self.filename) as f:
-            return super(ConfigParser, self).parse(f)
-
-    def new_section(self, section):
-        self.section = section
-        self.sections.setdefault(self.section, {})
-
-    def assignment(self, key, value):
-        if not self.section:
-            raise self.error_no_section()
-
-        self.sections[self.section].setdefault(key, [])
-        self.sections[self.section][key].append('\n'.join(value))
-
-    def parse_exc(self, msg, lineno, line=None):
-        return ParseError(msg, lineno, line, self.filename)
-
-    def error_no_section(self):
-        return self.parse_exc('Section must be started before assignment',
-                              self.lineno)
-
-
-class MultiConfigParser(object):
-    def __init__(self):
-        self.parsed = []
-
-    def read(self, config_files):
-        read_ok = []
-
-        for filename in config_files:
-            sections = {}
-            parser = ConfigParser(filename, sections)
-
-            try:
-                parser.parse()
-            except IOError:
-                continue
-            self.parsed.insert(0, sections)
-            read_ok.append(filename)
-
-        return read_ok
-
-    def get(self, section, names, multi=False):
-        rvalue = []
-        for sections in self.parsed:
-            if section not in sections:
-                continue
-            for name in names:
-                if name in sections[section]:
-                    if multi:
-                        rvalue = sections[section][name] + rvalue
-                    else:
-                        return sections[section][name]
-        if multi and rvalue != []:
-            return rvalue
-        raise KeyError
-
-
-class ConfigOpts(collections.Mapping):
-
-    """
-    Config options which may be set on the command line or in config files.
-
-    ConfigOpts is a configuration option manager with APIs for registering
-    option schemas, grouping options, parsing option values and retrieving
-    the values of options.
-    """
-
-    def __init__(self):
-        """Construct a ConfigOpts object."""
-        self._opts = {}  # dict of dicts of (opt:, override:, default:)
-        self._groups = {}
-
-        self._args = None
-
-        self._oparser = None
-        self._cparser = None
-        self._cli_values = {}
-        self.__cache = {}
-        self._config_opts = []
-
-    def _pre_setup(self, project, prog, version, usage, default_config_files):
-        """Initialize a ConfigCliParser object for option parsing."""
-
-        if prog is None:
-            prog = os.path.basename(sys.argv[0])
-
-        if default_config_files is None:
-            default_config_files = find_config_files(project, prog)
-
-        self._oparser = argparse.ArgumentParser(prog=prog, usage=usage)
-        self._oparser.add_argument('--version',
-                                   action='version',
-                                   version=version)
-
-        return prog, default_config_files
-
-    def _setup(self, project, prog, version, usage, default_config_files):
-        """Initialize a ConfigOpts object for option parsing."""
-
-        self._config_opts = [
-            MultiStrOpt('config-file',
-                        default=default_config_files,
-                        metavar='PATH',
-                        help='Path to a config file to use. Multiple config '
-                             'files can be specified, with values in later '
-                             'files taking precedence. The default files '
-                             ' used are: %s' % (default_config_files, )),
-            StrOpt('config-dir',
-                   metavar='DIR',
-                   help='Path to a config directory to pull *.conf '
-                        'files from. This file set is sorted, so as to '
-                        'provide a predictable parse order if individual '
-                        'options are over-ridden. The set is parsed after '
-                        'the file(s), if any, specified via --config-file, '
-                        'hence over-ridden options in the directory take '
-                        'precedence.'),
-        ]
-        self.register_cli_opts(self._config_opts)
-
-        self.project = project
-        self.prog = prog
-        self.version = version
-        self.usage = usage
-        self.default_config_files = default_config_files
-
-    def __clear_cache(f):
-        @functools.wraps(f)
-        def __inner(self, *args, **kwargs):
-            if kwargs.pop('clear_cache', True):
-                self.__cache.clear()
-            return f(self, *args, **kwargs)
-
-        return __inner
-
-    def __call__(self,
-                 args=None,
-                 project=None,
-                 prog=None,
-                 version=None,
-                 usage=None,
-                 default_config_files=None):
-        """Parse command line arguments and config files.
-
-        Calling a ConfigOpts object causes the supplied command line arguments
-        and config files to be parsed, causing opt values to be made available
-        as attributes of the object.
-
-        The object may be called multiple times, each time causing the previous
-        set of values to be overwritten.
-
-        Automatically registers the --config-file option with either a supplied
-        list of default config files, or a list from find_config_files().
-
-        If the --config-dir option is set, any *.conf files from this
-        directory are pulled in, after all the file(s) specified by the
-        --config-file option.
-
-        :param args: command line arguments (defaults to sys.argv[1:])
-        :param project: the toplevel project name, used to locate config files
-        :param prog: the name of the program (defaults to sys.argv[0] basename)
-        :param version: the program version (for --version)
-        :param usage: a usage string (%prog will be expanded)
-        :param default_config_files: config files to use by default
-        :returns: the list of arguments left over after parsing options
-        :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
-                 RequiredOptError, DuplicateOptError
-        """
-
-        self.clear()
-
-        prog, default_config_files = self._pre_setup(project,
-                                                     prog,
-                                                     version,
-                                                     usage,
-                                                     default_config_files)
-
-        self._setup(project, prog, version, usage, default_config_files)
-
-        self._cli_values = self._parse_cli_opts(args)
-
-        self._parse_config_files()
-
-        self._check_required_opts()
-
-    def __getattr__(self, name):
-        """Look up an option value and perform string substitution.
-
-        :param name: the opt name (or 'dest', more precisely)
-        :returns: the option value (after string subsititution) or a GroupAttr
-        :raises: NoSuchOptError,ConfigFileValueError,TemplateSubstitutionError
-        """
-        return self._get(name)
-
-    def __getitem__(self, key):
-        """Look up an option value and perform string substitution."""
-        return self.__getattr__(key)
-
-    def __contains__(self, key):
-        """Return True if key is the name of a registered opt or group."""
-        return key in self._opts or key in self._groups
-
-    def __iter__(self):
-        """Iterate over all registered opt and group names."""
-        for key in self._opts.keys() + self._groups.keys():
-            yield key
-
-    def __len__(self):
-        """Return the number of options and option groups."""
-        return len(self._opts) + len(self._groups)
-
-    def reset(self):
-        """Clear the object state and unset overrides and defaults."""
-        self._unset_defaults_and_overrides()
-        self.clear()
-
-    @__clear_cache
-    def clear(self):
-        """Clear the state of the object to before it was called.
-
-        Any subparsers added using the add_cli_subparsers() will also be
-        removed as a side-effect of this method.
-        """
-        self._args = None
-        self._cli_values.clear()
-        self._oparser = argparse.ArgumentParser()
-        self._cparser = None
-        self.unregister_opts(self._config_opts)
-        for group in self._groups.values():
-            group._clear()
-
-    @__clear_cache
-    def register_opt(self, opt, group=None, cli=False):
-        """Register an option schema.
-
-        Registering an option schema makes any option value which is previously
-        or subsequently parsed from the command line or config files available
-        as an attribute of this object.
-
-        :param opt: an instance of an Opt sub-class
-        :param cli: whether this is a CLI option
-        :param group: an optional OptGroup object or group name
-        :return: False if the opt was already register, True otherwise
-        :raises: DuplicateOptError
-        """
-        if group is not None:
-            group = self._get_group(group, autocreate=True)
-            return group._register_opt(opt, cli)
-
-        if _is_opt_registered(self._opts, opt):
-            return False
-
-        self._opts[opt.dest] = {'opt': opt, 'cli': cli}
-
-        return True
-
-    @__clear_cache
-    def register_opts(self, opts, group=None):
-        """Register multiple option schemas at once."""
-        for opt in opts:
-            self.register_opt(opt, group, clear_cache=False)
-
-    @__clear_cache
-    def register_cli_opt(self, opt, group=None):
-        """Register a CLI option schema.
-
-        CLI option schemas must be registered before the command line and
-        config files are parsed. This is to ensure that all CLI options are
-        show in --help and option validation works as expected.
-
-        :param opt: an instance of an Opt sub-class
-        :param group: an optional OptGroup object or group name
-        :return: False if the opt was already register, True otherwise
-        :raises: DuplicateOptError, ArgsAlreadyParsedError
-        """
-        if self._args is not None:
-            raise ArgsAlreadyParsedError("cannot register CLI option")
-
-        return self.register_opt(opt, group, cli=True, clear_cache=False)
-
-    @__clear_cache
-    def register_cli_opts(self, opts, group=None):
-        """Register multiple CLI option schemas at once."""
-        for opt in opts:
-            self.register_cli_opt(opt, group, clear_cache=False)
-
-    def register_group(self, group):
-        """Register an option group.
-
-        An option group must be registered before options can be registered
-        with the group.
-
-        :param group: an OptGroup object
-        """
-        if group.name in self._groups:
-            return
-
-        self._groups[group.name] = copy.copy(group)
-
-    @__clear_cache
-    def unregister_opt(self, opt, group=None):
-        """Unregister an option.
-
-        :param opt: an Opt object
-        :param group: an optional OptGroup object or group name
-        :raises: ArgsAlreadyParsedError, NoSuchGroupError
-        """
-        if self._args is not None:
-            raise ArgsAlreadyParsedError("reset before unregistering options")
-
-        if group is not None:
-            self._get_group(group)._unregister_opt(opt)
-        elif opt.dest in self._opts:
-            del self._opts[opt.dest]
-
-    @__clear_cache
-    def unregister_opts(self, opts, group=None):
-        """Unregister multiple CLI option schemas at once."""
-        for opt in opts:
-            self.unregister_opt(opt, group, clear_cache=False)
-
-    def import_opt(self, name, module_str, group=None):
-        """Import an option definition from a module.
-
-        Import a module and check that a given option is registered.
-
-        This is intended for use with global configuration objects
-        like cfg.CONF where modules commonly register options with
-        CONF at module load time. If one module requires an option
-        defined by another module it can use this method to explicitly
-        declare the dependency.
-
-        :param name: the name/dest of the opt
-        :param module_str: the name of a module to import
-        :param group: an option OptGroup object or group name
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        __import__(module_str)
-        self._get_opt_info(name, group)
-
-    def import_group(self, group, module_str):
-        """Import an option group from a module.
-
-        Import a module and check that a given option group is registered.
-
-        This is intended for use with global configuration objects
-        like cfg.CONF where modules commonly register options with
-        CONF at module load time. If one module requires an option group
-        defined by another module it can use this method to explicitly
-        declare the dependency.
-
-        :param group: an option OptGroup object or group name
-        :param module_str: the name of a module to import
-        :raises: ImportError, NoSuchGroupError
-        """
-        __import__(module_str)
-        self._get_group(group)
-
-    @__clear_cache
-    def set_override(self, name, override, group=None):
-        """Override an opt value.
-
-        Override the command line, config file and default values of a
-        given option.
-
-        :param name: the name/dest of the opt
-        :param override: the override value
-        :param group: an option OptGroup object or group name
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        opt_info = self._get_opt_info(name, group)
-        opt_info['override'] = override
-
-    @__clear_cache
-    def set_default(self, name, default, group=None):
-        """Override an opt's default value.
-
-        Override the default value of given option. A command line or
-        config file value will still take precedence over this default.
-
-        :param name: the name/dest of the opt
-        :param default: the default value
-        :param group: an option OptGroup object or group name
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        opt_info = self._get_opt_info(name, group)
-        opt_info['default'] = default
-
-    @__clear_cache
-    def clear_override(self, name, group=None):
-        """Clear an override an opt value.
-
-        Clear a previously set override of the command line, config file
-        and default values of a given option.
-
-        :param name: the name/dest of the opt
-        :param group: an option OptGroup object or group name
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        opt_info = self._get_opt_info(name, group)
-        opt_info.pop('override', None)
-
-    @__clear_cache
-    def clear_default(self, name, group=None):
-        """Clear an override an opt's default value.
-
-        Clear a previously set override of the default value of given option.
-
-        :param name: the name/dest of the opt
-        :param group: an option OptGroup object or group name
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        opt_info = self._get_opt_info(name, group)
-        opt_info.pop('default', None)
-
-    def _all_opt_infos(self):
-        """A generator function for iteration opt infos."""
-        for info in self._opts.values():
-            yield info, None
-        for group in self._groups.values():
-            for info in group._opts.values():
-                yield info, group
-
-    def _all_cli_opts(self):
-        """A generator function for iterating CLI opts."""
-        for info, group in self._all_opt_infos():
-            if info['cli']:
-                yield info['opt'], group
-
-    def _unset_defaults_and_overrides(self):
-        """Unset any default or override on all options."""
-        for info, group in self._all_opt_infos():
-            info.pop('default', None)
-            info.pop('override', None)
-
-    def find_file(self, name):
-        """Locate a file located alongside the config files.
-
-        Search for a file with the supplied basename in the directories
-        which we have already loaded config files from and other known
-        configuration directories.
-
-        The directory, if any, supplied by the config_dir option is
-        searched first. Then the config_file option is iterated over
-        and each of the base directories of the config_files values
-        are searched. Failing both of these, the standard directories
-        searched by the module level find_config_files() function is
-        used. The first matching file is returned.
-
-        :param basename: the filename, e.g. 'policy.json'
-        :returns: the path to a matching file, or None
-        """
-        dirs = []
-        if self.config_dir:
-            dirs.append(_fixpath(self.config_dir))
-
-        for cf in reversed(self.config_file):
-            dirs.append(os.path.dirname(_fixpath(cf)))
-
-        dirs.extend(_get_config_dirs(self.project))
-
-        return _search_dirs(dirs, name)
-
-    def log_opt_values(self, logger, lvl):
-        """Log the value of all registered opts.
-
-        It's often useful for an app to log its configuration to a log file at
-        startup for debugging. This method dumps to the entire config state to
-        the supplied logger at a given log level.
-
-        :param logger: a logging.Logger object
-        :param lvl: the log level (e.g. logging.DEBUG) arg to logger.log()
-        """
-        logger.log(lvl, "*" * 80)
-        logger.log(lvl, "Configuration options gathered from:")
-        logger.log(lvl, "command line args: %s", self._args)
-        logger.log(lvl, "config files: %s", self.config_file)
-        logger.log(lvl, "=" * 80)
-
-        def _sanitize(opt, value):
-            """Obfuscate values of options declared secret."""
-            return value if not opt.secret else '*' * len(str(value))
-
-        for opt_name in sorted(self._opts):
-            opt = self._get_opt_info(opt_name)['opt']
-            logger.log(lvl, "%-30s = %s", opt_name,
-                       _sanitize(opt, getattr(self, opt_name)))
-
-        for group_name in self._groups:
-            group_attr = self.GroupAttr(self, self._get_group(group_name))
-            for opt_name in sorted(self._groups[group_name]._opts):
-                opt = self._get_opt_info(opt_name, group_name)['opt']
-                logger.log(lvl, "%-30s = %s",
-                           "%s.%s" % (group_name, opt_name),
-                           _sanitize(opt, getattr(group_attr, opt_name)))
-
-        logger.log(lvl, "*" * 80)
-
-    def print_usage(self, file=None):
-        """Print the usage message for the current program."""
-        self._oparser.print_usage(file)
-
-    def print_help(self, file=None):
-        """Print the help message for the current program."""
-        self._oparser.print_help(file)
-
-    def _get(self, name, group=None):
-        if isinstance(group, OptGroup):
-            key = (group.name, name)
-        else:
-            key = (group, name)
-        try:
-            return self.__cache[key]
-        except KeyError:
-            value = self._substitute(self._do_get(name, group))
-            self.__cache[key] = value
-            return value
-
-    def _do_get(self, name, group=None):
-        """Look up an option value.
-
-        :param name: the opt name (or 'dest', more precisely)
-        :param group: an OptGroup
-        :returns: the option value, or a GroupAttr object
-        :raises: NoSuchOptError, NoSuchGroupError, ConfigFileValueError,
-                 TemplateSubstitutionError
-        """
-        if group is None and name in self._groups:
-            return self.GroupAttr(self, self._get_group(name))
-
-        info = self._get_opt_info(name, group)
-        opt = info['opt']
-
-        if isinstance(opt, SubCommandOpt):
-            return self.SubCommandAttr(self, group, opt.dest)
-
-        if 'override' in info:
-            return info['override']
-
-        values = []
-        if self._cparser is not None:
-            section = group.name if group is not None else 'DEFAULT'
-            try:
-                value = opt._get_from_config_parser(self._cparser, section)
-            except KeyError:
-                pass
-            except ValueError as ve:
-                raise ConfigFileValueError(str(ve))
-            else:
-                if not opt.multi:
-                    # No need to continue since the last value wins
-                    return value[-1]
-                values.extend(value)
-
-        name = name if group is None else group.name + '_' + name
-        value = self._cli_values.get(name)
-        if value is not None:
-            if not opt.multi:
-                return value
-
-            # argparse ignores default=None for nargs='*'
-            if opt.positional and not value:
-                value = opt.default
-
-            return value + values
-
-        if values:
-            return values
-
-        if 'default' in info:
-            return info['default']
-
-        return opt.default
-
-    def _substitute(self, value):
-        """Perform string template substitution.
-
-        Substitute any template variables (e.g. $foo, ${bar}) in the supplied
-        string value(s) with opt values.
-
-        :param value: the string value, or list of string values
-        :returns: the substituted string(s)
-        """
-        if isinstance(value, list):
-            return [self._substitute(i) for i in value]
-        elif isinstance(value, str):
-            tmpl = string.Template(value)
-            return tmpl.safe_substitute(self.StrSubWrapper(self))
-        else:
-            return value
-
-    def _get_group(self, group_or_name, autocreate=False):
-        """Looks up a OptGroup object.
-
-        Helper function to return an OptGroup given a parameter which can
-        either be the group's name or an OptGroup object.
-
-        The OptGroup object returned is from the internal dict of OptGroup
-        objects, which will be a copy of any OptGroup object that users of
-        the API have access to.
-
-        :param group_or_name: the group's name or the OptGroup object itself
-        :param autocreate: whether to auto-create the group if it's not found
-        :raises: NoSuchGroupError
-        """
-        group = group_or_name if isinstance(group_or_name, OptGroup) else None
-        group_name = group.name if group else group_or_name
-
-        if group_name not in self._groups:
-            if group is not None or not autocreate:
-                raise NoSuchGroupError(group_name)
-
-            self.register_group(OptGroup(name=group_name))
-
-        return self._groups[group_name]
-
-    def _get_opt_info(self, opt_name, group=None):
-        """Return the (opt, override, default) dict for an opt.
-
-        :param opt_name: an opt name/dest
-        :param group: an optional group name or OptGroup object
-        :raises: NoSuchOptError, NoSuchGroupError
-        """
-        if group is None:
-            opts = self._opts
-        else:
-            group = self._get_group(group)
-            opts = group._opts
-
-        if opt_name not in opts:
-            raise NoSuchOptError(opt_name, group)
-
-        return opts[opt_name]
-
-    def _parse_config_files(self):
-        """Parse the config files from --config-file and --config-dir.
-
-        :raises: ConfigFilesNotFoundError, ConfigFileParseError
-        """
-        config_files = list(self.config_file)
-
-        if self.config_dir:
-            config_dir_glob = os.path.join(self.config_dir, '*.conf')
-            config_files += sorted(glob.glob(config_dir_glob))
-
-        config_files = [_fixpath(p) for p in config_files]
-
-        self._cparser = MultiConfigParser()
-
-        try:
-            read_ok = self._cparser.read(config_files)
-        except iniparser.ParseError as pe:
-            raise ConfigFileParseError(pe.filename, str(pe))
-
-        if read_ok != config_files:
-            not_read_ok = filter(lambda f: f not in read_ok, config_files)
-            raise ConfigFilesNotFoundError(not_read_ok)
-
-    def _check_required_opts(self):
-        """Check that all opts marked as required have values specified.
-
-        :raises: RequiredOptError
-        """
-        for info, group in self._all_opt_infos():
-            opt = info['opt']
-
-            if opt.required:
-                if 'default' in info or 'override' in info:
-                    continue
-
-                if self._get(opt.dest, group) is None:
-                    raise RequiredOptError(opt.name, group)
-
-    def _parse_cli_opts(self, args):
-        """Parse command line options.
-
-        Initializes the command line option parser and parses the supplied
-        command line arguments.
-
-        :param args: the command line arguments
-        :returns: a dict of parsed option values
-        :raises: SystemExit, DuplicateOptError
-
-        """
-        self._args = args
-
-        for opt, group in sorted(self._all_cli_opts()):
-            opt._add_to_cli(self._oparser, group)
-
-        return vars(self._oparser.parse_args(args))
-
-    class GroupAttr(collections.Mapping):
-
-        """
-        A helper class representing the option values of a group as a mapping
-        and attributes.
-        """
-
-        def __init__(self, conf, group):
-            """Construct a GroupAttr object.
-
-            :param conf: a ConfigOpts object
-            :param group: an OptGroup object
-            """
-            self._conf = conf
-            self._group = group
-
-        def __getattr__(self, name):
-            """Look up an option value and perform template substitution."""
-            return self._conf._get(name, self._group)
-
-        def __getitem__(self, key):
-            """Look up an option value and perform string substitution."""
-            return self.__getattr__(key)
-
-        def __contains__(self, key):
-            """Return True if key is the name of a registered opt or group."""
-            return key in self._group._opts
-
-        def __iter__(self):
-            """Iterate over all registered opt and group names."""
-            for key in self._group._opts.keys():
-                yield key
-
-        def __len__(self):
-            """Return the number of options and option groups."""
-            return len(self._group._opts)
-
-    class SubCommandAttr(object):
-
-        """
-        A helper class representing the name and arguments of an argparse
-        sub-parser.
-        """
-
-        def __init__(self, conf, group, dest):
-            """Construct a SubCommandAttr object.
-
-            :param conf: a ConfigOpts object
-            :param group: an OptGroup object
-            :param dest: the name of the sub-parser
-            """
-            self._conf = conf
-            self._group = group
-            self._dest = dest
-
-        def __getattr__(self, name):
-            """Look up a sub-parser name or argument value."""
-            if name == 'name':
-                name = self._dest
-                if self._group is not None:
-                    name = self._group.name + '_' + name
-                return self._conf._cli_values[name]
-
-            if name in self._conf:
-                raise DuplicateOptError(name)
-
-            try:
-                return self._conf._cli_values[name]
-            except KeyError:
-                raise NoSuchOptError(name)
-
-    class StrSubWrapper(object):
-
-        """
-        A helper class exposing opt values as a dict for string substitution.
-        """
-
-        def __init__(self, conf):
-            """Construct a StrSubWrapper object.
-
-            :param conf: a ConfigOpts object
-            """
-            self.conf = conf
-
-        def __getitem__(self, key):
-            """Look up an opt value from the ConfigOpts object.
-
-            :param key: an opt name
-            :returns: an opt value
-            :raises: TemplateSubstitutionError if attribute is a group
-            """
-            value = getattr(self.conf, key)
-            if isinstance(value, self.conf.GroupAttr):
-                raise TemplateSubstitutionError(
-                    'substituting group %s not supported' % key)
-            return value
-
-
-CONF = ConfigOpts()
diff --git a/tempest/openstack/common/iniparser.py b/tempest/openstack/common/iniparser.py
deleted file mode 100644
index 9a8762a..0000000
--- a/tempest/openstack/common/iniparser.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack LLC.
-#
-#    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.
-
-
-class ParseError(Exception):
-    def __init__(self, message, lineno, line):
-        self.msg = message
-        self.line = line
-        self.lineno = lineno
-
-    def __str__(self):
-        return 'at line %d, %s: %r' % (self.lineno, self.msg, self.line)
-
-
-class BaseParser(object):
-    lineno = 0
-    parse_exc = ParseError
-
-    def _assignment(self, key, value):
-        self.assignment(key, value)
-        return None, []
-
-    def _get_section(self, line):
-        if line[-1] != ']':
-            return self.error_no_section_end_bracket(line)
-        if len(line) <= 2:
-            return self.error_no_section_name(line)
-
-        return line[1:-1]
-
-    def _split_key_value(self, line):
-        colon = line.find(':')
-        equal = line.find('=')
-        if colon < 0 and equal < 0:
-            return self.error_invalid_assignment(line)
-
-        if colon < 0 or (equal >= 0 and equal < colon):
-            key, value = line[:equal], line[equal + 1:]
-        else:
-            key, value = line[:colon], line[colon + 1:]
-
-        value = value.strip()
-        if ((value and value[0] == value[-1]) and
-                (value[0] == "\"" or value[0] == "'")):
-            value = value[1:-1]
-        return key.strip(), [value]
-
-    def parse(self, lineiter):
-        key = None
-        value = []
-
-        for line in lineiter:
-            self.lineno += 1
-
-            line = line.rstrip()
-            if not line:
-                # Blank line, ends multi-line values
-                if key:
-                    key, value = self._assignment(key, value)
-                continue
-            elif line[0] in (' ', '\t'):
-                # Continuation of previous assignment
-                if key is None:
-                    self.error_unexpected_continuation(line)
-                else:
-                    value.append(line.lstrip())
-                continue
-
-            if key:
-                # Flush previous assignment, if any
-                key, value = self._assignment(key, value)
-
-            if line[0] == '[':
-                # Section start
-                section = self._get_section(line)
-                if section:
-                    self.new_section(section)
-            elif line[0] in '#;':
-                self.comment(line[1:].lstrip())
-            else:
-                key, value = self._split_key_value(line)
-                if not key:
-                    return self.error_empty_key(line)
-
-        if key:
-            # Flush previous assignment, if any
-            self._assignment(key, value)
-
-    def assignment(self, key, value):
-        """Called when a full assignment is parsed."""
-        raise NotImplementedError()
-
-    def new_section(self, section):
-        """Called when a new section is started."""
-        raise NotImplementedError()
-
-    def comment(self, comment):
-        """Called when a comment is parsed."""
-        pass
-
-    def error_invalid_assignment(self, line):
-        raise self.parse_exc("No ':' or '=' found in assignment",
-                             self.lineno, line)
-
-    def error_empty_key(self, line):
-        raise self.parse_exc('Key cannot be empty', self.lineno, line)
-
-    def error_unexpected_continuation(self, line):
-        raise self.parse_exc('Unexpected continuation line',
-                             self.lineno, line)
-
-    def error_no_section_end_bracket(self, line):
-        raise self.parse_exc('Invalid section (must end with ])',
-                             self.lineno, line)
-
-    def error_no_section_name(self, line):
-        raise self.parse_exc('Empty section name', self.lineno, line)
diff --git a/tempest/openstack/common/setup.py b/tempest/openstack/common/setup.py
index 2fb9cf2..80a0ece 100644
--- a/tempest/openstack/common/setup.py
+++ b/tempest/openstack/common/setup.py
@@ -43,6 +43,11 @@
     return mapping
 
 
+def _parse_git_mailmap(git_dir, mailmap='.mailmap'):
+    mailmap = os.path.join(os.path.dirname(git_dir), mailmap)
+    return parse_mailmap(mailmap)
+
+
 def canonicalize_emails(changelog, mapping):
     """Takes in a string and an email alias mapping and replaces all
        instances of the aliases in the string with their real email.
@@ -117,9 +122,9 @@
         output = subprocess.Popen(["/bin/sh", "-c", cmd],
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
+    out = output.communicate()
     if output.returncode and throw_on_error:
         raise Exception("%s returned %d" % cmd, output.returncode)
-    out = output.communicate()
     if len(out) == 0:
         return None
     if len(out[0].strip()) == 0:
@@ -127,14 +132,26 @@
     return out[0].strip()
 
 
+def _get_git_directory():
+    parent_dir = os.path.dirname(__file__)
+    while True:
+        git_dir = os.path.join(parent_dir, '.git')
+        if os.path.exists(git_dir):
+            return git_dir
+        parent_dir, child = os.path.split(parent_dir)
+        if not child:   # reached to root dir
+            return None
+
+
 def write_git_changelog():
     """Write a changelog based on the git changelog."""
     new_changelog = 'ChangeLog'
+    git_dir = _get_git_directory()
     if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
-        if os.path.isdir('.git'):
-            git_log_cmd = 'git log --stat'
+        if git_dir:
+            git_log_cmd = 'git --git-dir=%s log --stat' % git_dir
             changelog = _run_shell_command(git_log_cmd)
-            mailmap = parse_mailmap()
+            mailmap = _parse_git_mailmap(git_dir)
             with open(new_changelog, "w") as changelog_file:
                 changelog_file.write(canonicalize_emails(changelog, mailmap))
     else:
@@ -146,13 +163,15 @@
     jenkins_email = 'jenkins@review.(openstack|stackforge).org'
     old_authors = 'AUTHORS.in'
     new_authors = 'AUTHORS'
+    git_dir = _get_git_directory()
     if not os.getenv('SKIP_GENERATE_AUTHORS'):
-        if os.path.isdir('.git'):
+        if git_dir:
             # don't include jenkins email address in AUTHORS file
-            git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
+            git_log_cmd = ("git --git-dir=" + git_dir +
+                           " log --format='%aN <%aE>' | sort -u | "
                            "egrep -v '" + jenkins_email + "'")
             changelog = _run_shell_command(git_log_cmd)
-            mailmap = parse_mailmap()
+            mailmap = _parse_git_mailmap(git_dir)
             with open(new_authors, 'w') as new_authors_fh:
                 new_authors_fh.write(canonicalize_emails(changelog, mailmap))
                 if os.path.exists(old_authors):
@@ -258,40 +277,44 @@
     return cmdclass
 
 
-def _get_revno():
+def _get_revno(git_dir):
     """Return the number of commits since the most recent tag.
 
     We use git-describe to find this out, but if there are no
     tags then we fall back to counting commits since the beginning
     of time.
     """
-    describe = _run_shell_command("git describe --always")
+    describe = _run_shell_command(
+        "git --git-dir=%s describe --always" % git_dir)
     if "-" in describe:
         return describe.rsplit("-", 2)[-2]
 
     # no tags found
-    revlist = _run_shell_command("git rev-list --abbrev-commit HEAD")
+    revlist = _run_shell_command(
+        "git --git-dir=%s rev-list --abbrev-commit HEAD" % git_dir)
     return len(revlist.splitlines())
 
 
 def _get_version_from_git(pre_version):
     """Return a version which is equal to the tag that's on the current
     revision if there is one, or tag plus number of additional revisions
-    if the current revision has no tag.
-    """
+    if the current revision has no tag."""
 
-    if os.path.isdir('.git'):
+    git_dir = _get_git_directory()
+    if git_dir:
         if pre_version:
             try:
                 return _run_shell_command(
-                    "git describe --exact-match",
+                    "git --git-dir=" + git_dir + " describe --exact-match",
                     throw_on_error=True).replace('-', '.')
             except Exception:
-                sha = _run_shell_command("git log -n1 --pretty=format:%h")
-                return "%s.a%s.g%s" % (pre_version, _get_revno(), sha)
+                sha = _run_shell_command(
+                    "git --git-dir=" + git_dir + " log -n1 --pretty=format:%h")
+                return "%s.a%s.g%s" % (pre_version, _get_revno(git_dir), sha)
         else:
             return _run_shell_command(
-                "git describe --always").replace('-', '.')
+                "git --git-dir=" + git_dir + " describe --always").replace(
+                    '-', '.')
     return None
 
 
diff --git a/tempest/services/botoclients.py b/tempest/services/botoclients.py
index 143257a..0870c96 100644
--- a/tempest/services/botoclients.py
+++ b/tempest/services/botoclients.py
@@ -17,7 +17,6 @@
 
 import ConfigParser
 import contextlib
-import re
 import types
 import urlparse
 
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
new file mode 100644
index 0000000..468a5c2
--- /dev/null
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -0,0 +1,57 @@
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class InterfacesClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(InterfacesClientJSON, self).__init__(config, username, password,
+                                                   auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_interfaces(self, server):
+        resp, body = self.get('servers/%s/os-interface' % server)
+        body = json.loads(body)
+        return resp, body['interfaceAttachments']
+
+    def create_interface(self, server, port_id=None, network_id=None,
+                         fixed_ip=None):
+        post_body = dict(interfaceAttachment=dict())
+        if port_id:
+            post_body['port_id'] = port_id
+        if network_id:
+            post_body['net_id'] = network_id
+        if fixed_ip:
+            post_body['fixed_ips'] = [dict(ip_address=fixed_ip)]
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/os-interface' % server,
+                               headers=self.headers,
+                               body=post_body)
+        body = json.loads(body)
+        return resp, body['interfaceAttachment']
+
+    def show_interface(self, server, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
+        body = json.loads(body)
+        return resp, body['interfaceAttachment']
+
+    def delete_interface(self, server, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
+                                                                 port_id))
+        return resp, body
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index e4271d9..d12b97b 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -53,10 +53,10 @@
         body = json.loads(body)
         return resp, body['volumes']
 
-    def get_volume(self, volume_id, wait=None):
+    def get_volume(self, volume_id):
         """Returns the details of a single volume."""
         url = "os-volumes/%s" % str(volume_id)
-        resp, body = self.get(url, wait=wait)
+        resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['volume']
 
diff --git a/tempest/services/compute/xml/interfaces_client.py b/tempest/services/compute/xml/interfaces_client.py
new file mode 100644
index 0000000..4a692a1
--- /dev/null
+++ b/tempest/services/compute/xml/interfaces_client.py
@@ -0,0 +1,82 @@
+# Copyright 2013 IBM Corp.
+# 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.
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class InterfacesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(InterfacesClientXML, self).__init__(config, username, password,
+                                                  auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _process_xml_interface(self, node):
+        iface = xml_to_json(node)
+        # NOTE(danms): if multiple addresses per interface is ever required,
+        # xml_to_json will need to be fixed or replaced in this case
+        iface['fixed_ips'] = [dict(iface['fixed_ips']['fixed_ip'].items())]
+        return iface
+
+    def list_interfaces(self, server):
+        resp, body = self.get('servers/%s/os-interface' % server, self.headers)
+        node = etree.fromstring(body)
+        interfaces = [self._process_xml_interface(x)
+                      for x in node.getchildren()]
+        return resp, interfaces
+
+    def create_interface(self, server, port_id=None, network_id=None,
+                         fixed_ip=None):
+        doc = Document()
+        iface = Element('interfaceAttachment')
+        if port_id:
+            _port_id = Element('port_id')
+            _port_id.append(Text(port_id))
+            iface.append(_port_id)
+        if network_id:
+            _network_id = Element('net_id')
+            _network_id.append(Text(network_id))
+            iface.append(_network_id)
+        if fixed_ip:
+            _fixed_ips = Element('fixed_ips')
+            _fixed_ip = Element('fixed_ip')
+            _ip_address = Element('ip_address')
+            _ip_address.append(Text(fixed_ip))
+            _fixed_ip.append(_ip_address)
+            _fixed_ips.append(_fixed_ip)
+            iface.append(_fixed_ips)
+        doc.append(iface)
+        resp, body = self.post('servers/%s/os-interface' % server,
+                               headers=self.headers,
+                               body=str(doc))
+        body = self._process_xml_interface(etree.fromstring(body))
+        return resp, body
+
+    def show_interface(self, server, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id),
+                              self.headers)
+        body = self._process_xml_interface(etree.fromstring(body))
+        return resp, body
+
+    def delete_interface(self, server, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
+                                                                 port_id))
+        return resp, body
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index b0e4ce8..655a345 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -183,13 +183,13 @@
         server = Element("server")
         doc.append(server)
 
-        if name:
+        if name is not None:
             server.add_attr("name", name)
-        if accessIPv4:
+        if accessIPv4 is not None:
             server.add_attr("accessIPv4", accessIPv4)
-        if accessIPv6:
+        if accessIPv6 is not None:
             server.add_attr("accessIPv6", accessIPv6)
-        if meta:
+        if meta is not None:
             metadata = Element("metadata")
             server.append(metadata)
             for k, v in meta:
@@ -229,10 +229,26 @@
                          flavorRef=flavor_ref,
                          name=name)
 
-        for attr in ["adminPass", "accessIPv4", "accessIPv6", "key_name"]:
+        for attr in ["adminPass", "accessIPv4", "accessIPv6", "key_name",
+                     "user_data", "availability_zone"]:
             if attr in kwargs:
                 server.add_attr(attr, kwargs[attr])
 
+        if 'security_groups' in kwargs:
+            secgroups = Element("security_groups")
+            server.append(secgroups)
+            for secgroup in kwargs['security_groups']:
+                s = Element("security_group", name=secgroup['name'])
+                secgroups.append(s)
+
+        if 'networks' in kwargs:
+            networks = Element("networks")
+            server.append(networks)
+            for network in kwargs['networks']:
+                s = Element("network", uuid=network['uuid'],
+                            fixed_ip=network['fixed_ip'])
+                networks.append(s)
+
         if 'meta' in kwargs:
             metadata = Element("metadata")
             server.append(metadata)
@@ -306,7 +322,8 @@
         resp, body = self.get("servers/%s/ips" % str(server_id), self.headers)
 
         networks = {}
-        for child in etree.fromstring(body.getchildren()):
+        xml_list = etree.fromstring(body)
+        for child in xml_list.getchildren():
             network = self._parse_network(child)
             networks.update(**network)
 
@@ -384,6 +401,58 @@
     def remove_security_group(self, server_id, name):
         return self.action(server_id, 'removeSecurityGroup', None, name=name)
 
+    def list_server_metadata(self, server_id):
+        resp, body = self.get("servers/%s/metadata" % str(server_id),
+                              self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def set_server_metadata(self, server_id, meta):
+        doc = Document()
+        metadata = Element("metadata")
+        doc.append(metadata)
+        for k, v in meta.items():
+            meta_element = Element("meta", key=k)
+            meta_element.append(Text(v))
+            metadata.append(meta_element)
+        resp, body = self.put('servers/%s/metadata' % str(server_id),
+                              str(doc), self.headers)
+        return resp, xml_to_json(etree.fromstring(body))
+
+    def update_server_metadata(self, server_id, meta):
+        doc = Document()
+        metadata = Element("metadata")
+        doc.append(metadata)
+        for k, v in meta.items():
+            meta_element = Element("meta", key=k)
+            meta_element.append(Text(v))
+            metadata.append(meta_element)
+        resp, body = self.post("/servers/%s/metadata" % str(server_id),
+                               str(doc), headers=self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def get_server_metadata_item(self, server_id, key):
+        resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key),
+                              headers=self.headers)
+        return resp, dict([(etree.fromstring(body).attrib['key'],
+                            xml_to_json(etree.fromstring(body)))])
+
+    def set_server_metadata_item(self, server_id, key, meta):
+        doc = Document()
+        for k, v in meta.items():
+            meta_element = Element("meta", key=k)
+            meta_element.append(Text(v))
+            doc.append(meta_element)
+        resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
+                              str(doc), self.headers)
+        return resp, xml_to_json(etree.fromstring(body))
+
+    def delete_server_metadata_item(self, server_id, key):
+        resp, body = self.delete("servers/%s/metadata/%s" %
+                                 (str(server_id), key))
+        return resp, body
+
     def get_console_output(self, server_id, length):
         return self.action(server_id, 'os-getConsoleOutput', 'output',
                            length=length)
diff --git a/tempest/services/compute/xml/volumes_extensions_client.py b/tempest/services/compute/xml/volumes_extensions_client.py
index 69b9bac..06cfcfb 100644
--- a/tempest/services/compute/xml/volumes_extensions_client.py
+++ b/tempest/services/compute/xml/volumes_extensions_client.py
@@ -81,10 +81,10 @@
             volumes += [self._parse_volume(vol) for vol in list(body)]
         return resp, volumes
 
-    def get_volume(self, volume_id, wait=None):
+    def get_volume(self, volume_id):
         """Returns the details of a single volume."""
         url = "os-volumes/%s" % str(volume_id)
-        resp, body = self.get(url, self.headers, wait=wait)
+        resp, body = self.get(url, self.headers)
         body = etree.fromstring(body)
         return resp, self._parse_volume(body)
 
diff --git a/tempest/services/image/json/__init__.py b/tempest/services/image/v1/__init__.py
similarity index 100%
copy from tempest/services/image/json/__init__.py
copy to tempest/services/image/v1/__init__.py
diff --git a/tempest/services/image/json/__init__.py b/tempest/services/image/v1/json/__init__.py
similarity index 100%
rename from tempest/services/image/json/__init__.py
rename to tempest/services/image/v1/json/__init__.py
diff --git a/tempest/services/image/json/image_client.py b/tempest/services/image/v1/json/image_client.py
similarity index 77%
rename from tempest/services/image/json/image_client.py
rename to tempest/services/image/v1/json/image_client.py
index e9276aa..45e93e2 100644
--- a/tempest/services/image/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -19,13 +19,11 @@
 import errno
 import json
 import os
-import time
 import urllib
 
 from tempest.common import glance_http
 from tempest.common.rest_client import RestClient
 from tempest import exceptions
-from tempest import manager
 
 
 class ImageClientJSON(RestClient):
@@ -97,11 +95,11 @@
             return None
 
     def _get_http(self):
-        temp_manager = manager.DefaultClientManager()
-        keystone = temp_manager._get_identity_client()
-        token = keystone.auth_token
-        endpoint = keystone.service_catalog.url_for(service_type='image',
-                                                    endpoint_type='publicURL')
+        token, endpoint = self.keystone_auth(self.user,
+                                             self.password,
+                                             self.auth_url,
+                                             self.service,
+                                             self.tenant_name)
         dscv = self.config.identity.disable_ssl_certificate_validation
         return glance_http.HTTPClient(endpoint=endpoint, token=token,
                                       insecure=dscv)
@@ -170,31 +168,69 @@
 
     def delete_image(self, image_id):
         url = 'v1/images/%s' % image_id
-        try:
-            self.delete(url)
-        except exceptions.Unauthorized:
-            url = '/' + url
-            self.http.raw_request('DELETE', url)
+        self.delete(url)
 
-    def image_list(self, params=None):
+    def image_list(self, **kwargs):
         url = 'v1/images'
 
-        if params:
-            url += '?%s' % urllib.urlencode(params)
+        if len(kwargs) > 0:
+            url += '?%s' % urllib.urlencode(kwargs)
 
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['images']
 
-    def get_image(self, image_id, wait=None):
+    def image_list_detail(self, **kwargs):
+        url = 'v1/images/detail'
+
+        if len(kwargs) > 0:
+            url += '?%s' % urllib.urlencode(kwargs)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body['images']
+
+    def get_image(self, image_id):
         url = 'v1/images/%s' % image_id
-        resp, __ = self.get(url, wait=wait)
+        resp, __ = self.get(url)
         body = self._image_meta_from_headers(resp)
         return resp, body
 
     def is_resource_deleted(self, id):
         try:
-            self.get_image(id, wait=True)
+            self.get_image(id)
         except exceptions.NotFound:
             return True
         return False
+
+    def get_image_membership(self, image_id):
+        url = 'v1/images/%s/members' % image_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body
+
+    def get_shared_images(self, member_id):
+        url = 'v1/shared-images/%s' % member_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body
+
+    def add_member(self, member_id, image_id, can_share=False):
+        url = 'v1/images/%s/members/%s' % (image_id, member_id)
+        body = None
+        if can_share:
+            body = json.dumps({'member': {'can_share': True}})
+        resp, __ = self.put(url, body, self.headers)
+        return resp
+
+    def delete_member(self, member_id, image_id):
+        url = 'v1/images/%s/members/%s' % (image_id, member_id)
+        resp, __ = self.delete(url)
+        return resp
+
+    def replace_membership_list(self, image_id, member_list):
+        url = 'v1/images/%s/members' % image_id
+        body = json.dumps({'membership': member_list})
+        resp, data = self.put(url, body, self.headers)
+        data = json.loads(data)
+        return resp, data
diff --git a/tempest/services/image/json/__init__.py b/tempest/services/image/v2/__init__.py
similarity index 100%
copy from tempest/services/image/json/__init__.py
copy to tempest/services/image/v2/__init__.py
diff --git a/tempest/services/image/json/__init__.py b/tempest/services/image/v2/json/__init__.py
similarity index 100%
copy from tempest/services/image/json/__init__.py
copy to tempest/services/image/v2/json/__init__.py
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
new file mode 100644
index 0000000..bcae79b
--- /dev/null
+++ b/tempest/services/image/v2/json/image_client.py
@@ -0,0 +1,123 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 IBM
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+import urllib
+
+import jsonschema
+
+from tempest.common import glance_http
+from tempest.common import rest_client
+from tempest import exceptions
+
+
+class ImageClientV2JSON(rest_client.RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ImageClientV2JSON, self).__init__(config, username, password,
+                                                auth_url, tenant_name)
+        self.service = self.config.images.catalog_type
+        self.http = self._get_http()
+
+    def _get_http(self):
+        token, endpoint = self.keystone_auth(self.user, self.password,
+                                             self.auth_url, self.service,
+                                             self.tenant_name)
+        dscv = self.config.identity.disable_ssl_certificate_validation
+        return glance_http.HTTPClient(endpoint=endpoint, token=token,
+                                      insecure=dscv)
+
+    def get_images_schema(self):
+        url = 'v2/schemas/images'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body
+
+    def get_image_schema(self):
+        url = 'v2/schemas/image'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body
+
+    def _validate_schema(self, body, type='image'):
+        if type == 'image':
+            resp, schema = self.get_image_schema()
+        elif type == 'images':
+            resp, schema = self.get_images_schema()
+        else:
+            raise ValueError("%s is not a valid schema type" % type)
+
+        jsonschema.validate(body, schema)
+
+    def create_image(self, name, container_format, disk_format, is_public=True,
+                     properties=None):
+        params = {
+            "name": name,
+            "container_format": container_format,
+            "disk_format": disk_format,
+        }
+        if is_public:
+            params["visibility"] = "public"
+        else:
+            params["visibility"] = "private"
+
+        data = json.dumps(params)
+        self._validate_schema(data)
+
+        resp, body = self.post('v2/images', data, self.headers)
+        body = json.loads(body)
+        return resp, body
+
+    def delete_image(self, image_id):
+        url = 'v2/images/%s' % image_id
+        self.delete(url)
+
+    def image_list(self, params=None):
+        url = 'v2/images'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self._validate_schema(body, type='images')
+        return resp, body['images']
+
+    def get_image_metadata(self, image_id):
+        url = 'v2/images/%s' % image_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return resp, body
+
+    def is_resource_deleted(self, id):
+        try:
+            self.get_image_metadata(id)
+        except exceptions.NotFound:
+            return True
+        return False
+
+    def store_image(self, image_id, data):
+        url = 'v2/images/%s/file' % image_id
+        headers = {'Content-Type': 'application/octet-stream'}
+        resp, body = self.http.raw_request('PUT', url, headers=headers,
+                                           body=data)
+        return resp, body
+
+    def get_image_file(self, image_id):
+        url = 'v2/images/%s/file' % image_id
+        resp, body = self.get(url)
+        return resp, body
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index fec273c..a71a287 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -103,7 +103,7 @@
         self.service = self.config.object_storage.catalog_type
         self.format = 'json'
 
-    def request(self, method, url, headers=None, body=None, wait=None):
+    def request(self, method, url, headers=None, body=None):
         """A simple HTTP request interface."""
         self.http_obj = httplib2.Http()
         if headers is None:
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 7b5efff..93477fa 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -97,7 +97,6 @@
         #TODO(dwalleck):  Rewite using json format to avoid newlines at end of
         #obj names. Set limit to API limit - 1 (max returned items = 9999)
         limit = 9999
-        marker = None
         if params is not None:
             if 'limit' in params:
                 limit = params['limit']
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index ac1859a..9626b6b 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -156,7 +156,7 @@
         self.service = self.config.object_storage.catalog_type
         self.format = 'json'
 
-    def request(self, method, url, headers=None, body=None, wait=None):
+    def request(self, method, url, headers=None, body=None):
         """A simple HTTP request interface."""
         dscv = self.config.identity.disable_ssl_certificate_validation
         self.http_obj = httplib2.Http(disable_ssl_certificate_validation=dscv)
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index ff1556f..6b0befd 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -56,10 +56,10 @@
         body = json.loads(body)
         return resp, body['volumes']
 
-    def get_volume(self, volume_id, wait=None):
+    def get_volume(self, volume_id):
         """Returns the details of a single volume."""
         url = "volumes/%s" % str(volume_id)
-        resp, body = self.get(url, wait=wait)
+        resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['volume']
 
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 5041869..4c15256 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -84,10 +84,10 @@
             volumes += [self._parse_volume(vol) for vol in list(body)]
         return resp, volumes
 
-    def get_volume(self, volume_id, wait=None):
+    def get_volume(self, volume_id):
         """Returns the details of a single volume."""
         url = "volumes/%s" % str(volume_id)
-        resp, body = self.get(url, self.headers, wait=wait)
+        resp, body = self.get(url, self.headers)
         body = etree.fromstring(body)
         return resp, self._parse_volume(body)
 
diff --git a/tempest/test.py b/tempest/test.py
index 90793ac..e0639b6 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -58,12 +58,11 @@
 
 
 class TestCase(BaseTestCase):
-
-    """
-    Base test case class for all Tempest tests
+    """Base test case class for all Tempest tests
 
     Contains basic setup and convenience methods
     """
+
     manager_class = None
 
     @classmethod
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 5625841..cee8843 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -17,16 +17,21 @@
 
 from contextlib import closing
 import logging
+import os
 import re
+import urlparse
 
 import boto
 from boto import ec2
 from boto import exception
 from boto import s3
+import keystoneclient.exceptions
 
+import tempest.clients
+from tempest.common.utils.file_utils import have_effective_read_access
+import tempest.config
 from tempest import exceptions
 import tempest.test
-import tempest.tests.boto
 from tempest.tests.boto.utils.wait import re_search_wait
 from tempest.tests.boto.utils.wait import state_wait
 from tempest.tests.boto.utils.wait import wait_exception
@@ -34,6 +39,71 @@
 LOG = logging.getLogger(__name__)
 
 
+def decision_maker():
+    A_I_IMAGES_READY = True  # ari,ami,aki
+    S3_CAN_CONNECT_ERROR = None
+    EC2_CAN_CONNECT_ERROR = None
+    secret_matcher = re.compile("[A-Za-z0-9+/]{32,}")  # 40 in other system
+    id_matcher = re.compile("[A-Za-z0-9]{20,}")
+
+    def all_read(*args):
+        return all(map(have_effective_read_access, args))
+
+    config = tempest.config.TempestConfig()
+    materials_path = config.boto.s3_materials_path
+    ami_path = materials_path + os.sep + config.boto.ami_manifest
+    aki_path = materials_path + os.sep + config.boto.aki_manifest
+    ari_path = materials_path + os.sep + config.boto.ari_manifest
+
+    A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
+    boto_logger = logging.getLogger('boto')
+    level = boto_logger.level
+    boto_logger.setLevel(logging.CRITICAL)  # suppress logging for these
+
+    def _cred_sub_check(connection_data):
+        if not id_matcher.match(connection_data["aws_access_key_id"]):
+            raise Exception("Invalid AWS access Key")
+        if not secret_matcher.match(connection_data["aws_secret_access_key"]):
+            raise Exception("Invalid AWS secret Key")
+        raise Exception("Unknown (Authentication?) Error")
+    openstack = tempest.clients.Manager()
+    try:
+        if urlparse.urlparse(config.boto.ec2_url).hostname is None:
+            raise Exception("Failed to get hostname from the ec2_url")
+        ec2client = openstack.ec2api_client
+        try:
+            ec2client.get_all_regions()
+        except exception.BotoServerError as exc:
+                if exc.error_code is None:
+                    raise Exception("EC2 target does not looks EC2 service")
+                _cred_sub_check(ec2client.connection_data)
+
+    except keystoneclient.exceptions.Unauthorized:
+        EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\
+                                " faild to get them even by keystoneclient"
+    except Exception as exc:
+        EC2_CAN_CONNECT_ERROR = str(exc)
+
+    try:
+        if urlparse.urlparse(config.boto.s3_url).hostname is None:
+            raise Exception("Failed to get hostname from the s3_url")
+        s3client = openstack.s3_client
+        try:
+            s3client.get_bucket("^INVALID*#()@INVALID.")
+        except exception.BotoServerError as exc:
+            if exc.status == 403:
+                _cred_sub_check(s3client.connection_data)
+    except Exception as exc:
+        S3_CAN_CONNECT_ERROR = str(exc)
+    except keystoneclient.exceptions.Unauthorized:
+        S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
+                               " faild to get them even by keystoneclient"
+    boto_logger.setLevel(level)
+    return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
+            'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
+            'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
+
+
 class BotoExceptionMatcher(object):
     STATUS_RE = r'[45]\d\d'
     CODE_RE = '.*'  # regexp makes sense in group match
@@ -121,7 +191,7 @@
 class BotoTestCase(tempest.test.BaseTestCase):
     """Recommended to use as base class for boto related test."""
 
-    conclusion = tempest.tests.boto.generic_setup_package()
+    conclusion = decision_maker()
 
     @classmethod
     def setUpClass(cls):
@@ -130,13 +200,13 @@
         cls._resource_trash_bin = {}
         cls._sequence = -1
         if (hasattr(cls, "EC2") and
-            tempest.tests.boto.EC2_CAN_CONNECT_ERROR is not None):
+            cls.conclusion['EC2_CAN_CONNECT_ERROR'] is not None):
             raise cls.skipException("EC2 " + cls.__name__ + ": " +
-                                    tempest.tests.boto.EC2_CAN_CONNECT_ERROR)
+                                    cls.conclusion['EC2_CAN_CONNECT_ERROR'])
         if (hasattr(cls, "S3") and
-            tempest.tests.boto.S3_CAN_CONNECT_ERROR is not None):
+            cls.conclusion['S3_CAN_CONNECT_ERROR'] is not None):
             raise cls.skipException("S3 " + cls.__name__ + ": " +
-                                    tempest.tests.boto.S3_CAN_CONNECT_ERROR)
+                                    cls.conclusion['S3_CAN_CONNECT_ERROR'])
 
     @classmethod
     def addResourceCleanUp(cls, function, *args, **kwargs):
diff --git a/tempest/tests/boto/__init__.py b/tempest/tests/boto/__init__.py
index dd224d6..e69de29 100644
--- a/tempest/tests/boto/__init__.py
+++ b/tempest/tests/boto/__init__.py
@@ -1,94 +0,0 @@
-# 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 logging
-import os
-import re
-import urlparse
-
-import boto.exception
-import keystoneclient.exceptions
-
-import tempest.clients
-from tempest.common.utils.file_utils import have_effective_read_access
-import tempest.config
-
-A_I_IMAGES_READY = True  # ari,ami,aki
-S3_CAN_CONNECT_ERROR = None
-EC2_CAN_CONNECT_ERROR = None
-
-
-def generic_setup_package():
-    global A_I_IMAGES_READY
-    global S3_CAN_CONNECT_ERROR
-    global EC2_CAN_CONNECT_ERROR
-    secret_matcher = re.compile("[A-Za-z0-9+/]{32,}")  # 40 in other system
-    id_matcher = re.compile("[A-Za-z0-9]{20,}")
-
-    def all_read(*args):
-        return all(map(have_effective_read_access, args))
-
-    config = tempest.config.TempestConfig()
-    materials_path = config.boto.s3_materials_path
-    ami_path = materials_path + os.sep + config.boto.ami_manifest
-    aki_path = materials_path + os.sep + config.boto.aki_manifest
-    ari_path = materials_path + os.sep + config.boto.ari_manifest
-
-    A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
-    boto_logger = logging.getLogger('boto')
-    level = boto_logger.level
-    boto_logger.setLevel(logging.CRITICAL)  # suppress logging for these
-
-    def _cred_sub_check(connection_data):
-        if not id_matcher.match(connection_data["aws_access_key_id"]):
-            raise Exception("Invalid AWS access Key")
-        if not secret_matcher.match(connection_data["aws_secret_access_key"]):
-            raise Exception("Invalid AWS secret Key")
-        raise Exception("Unknown (Authentication?) Error")
-    openstack = tempest.clients.Manager()
-    try:
-        if urlparse.urlparse(config.boto.ec2_url).hostname is None:
-            raise Exception("Failed to get hostname from the ec2_url")
-        ec2client = openstack.ec2api_client
-        try:
-            ec2client.get_all_regions()
-        except boto.exception.BotoServerError as exc:
-                if exc.error_code is None:
-                    raise Exception("EC2 target does not looks EC2 service")
-                _cred_sub_check(ec2client.connection_data)
-
-    except keystoneclient.exceptions.Unauthorized:
-        EC2_CAN_CONNECT_ERROR = "AWS credentials not set," +\
-                                " faild to get them even by keystoneclient"
-    except Exception as exc:
-        EC2_CAN_CONNECT_ERROR = str(exc)
-
-    try:
-        if urlparse.urlparse(config.boto.s3_url).hostname is None:
-            raise Exception("Failed to get hostname from the s3_url")
-        s3client = openstack.s3_client
-        try:
-            s3client.get_bucket("^INVALID*#()@INVALID.")
-        except boto.exception.BotoServerError as exc:
-            if exc.status == 403:
-                _cred_sub_check(s3client.connection_data)
-    except Exception as exc:
-        S3_CAN_CONNECT_ERROR = str(exc)
-    except keystoneclient.exceptions.Unauthorized:
-        S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
-                               " faild to get them even by keystoneclient"
-    boto_logger.setLevel(level)
diff --git a/tempest/tests/boto/test_ec2_instance_run.py b/tempest/tests/boto/test_ec2_instance_run.py
index 6b61c11..3293dea 100644
--- a/tempest/tests/boto/test_ec2_instance_run.py
+++ b/tempest/tests/boto/test_ec2_instance_run.py
@@ -17,15 +17,15 @@
 
 import logging
 
-from boto.exception import EC2ResponseError
+from boto import exception
 import testtools
 
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
 from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest import exceptions
 from tempest.test import attr
 from tempest.testboto import BotoTestCase
-import tempest.tests.boto
 from tempest.tests.boto.utils.s3 import s3_upload_dir
 from tempest.tests.boto.utils.wait import re_search_wait
 from tempest.tests.boto.utils.wait import state_wait
@@ -39,7 +39,7 @@
     @classmethod
     def setUpClass(cls):
         super(InstanceRunTest, cls).setUpClass()
-        if not tempest.tests.boto.A_I_IMAGES_READY:
+        if not cls.conclusion['A_I_IMAGES_READY']:
             raise cls.skipException("".join(("EC2 ", cls.__name__,
                                     ": requires ami/aki/ari manifest")))
         cls.os = clients.Manager()
@@ -86,7 +86,8 @@
             if state != "available":
                 for _image in cls.images.itervalues():
                     cls.ec2_client.deregister_image(_image["image_id"])
-                raise EC2RegisterImageException(image_id=image["image_id"])
+                raise exceptions.EC2RegisterImageException(image_id=
+                                                           image["image_id"])
 
     @attr(type='smoke')
     def test_run_stop_terminate_instance(self):
@@ -129,7 +130,7 @@
             instance.update(validate=True)
         except ValueError:
             pass
-        except EC2ResponseError as exc:
+        except exception.EC2ResponseError as exc:
             if self.ec2_error_code.\
                 client.InvalidInstanceID.NotFound.match(exc):
                 pass
@@ -150,16 +151,18 @@
                                                                group_desc)
         self.addResourceCleanUp(self.destroy_security_group_wait,
                                 security_group)
-        self.ec2_client.authorize_security_group(sec_group_name,
-                                                 ip_protocol="icmp",
-                                                 cidr_ip="0.0.0.0/0",
-                                                 from_port=-1,
-                                                 to_port=-1)
-        self.ec2_client.authorize_security_group(sec_group_name,
-                                                 ip_protocol="tcp",
-                                                 cidr_ip="0.0.0.0/0",
-                                                 from_port=22,
-                                                 to_port=22)
+        self.assertTrue(self.ec2_client.authorize_security_group(
+                sec_group_name,
+                ip_protocol="icmp",
+                cidr_ip="0.0.0.0/0",
+                from_port=-1,
+                to_port=-1))
+        self.assertTrue(self.ec2_client.authorize_security_group(
+                sec_group_name,
+                ip_protocol="tcp",
+                cidr_ip="0.0.0.0/0",
+                from_port=22,
+                to_port=22))
         reservation = image_ami.run(kernel_id=self.images["aki"]["image_id"],
                                     ramdisk_id=self.images["ari"]["image_id"],
                                     instance_type=self.instance_type,
@@ -176,7 +179,7 @@
 
         address = self.ec2_client.allocate_address()
         rcuk_a = self.addResourceCleanUp(address.delete)
-        address.associate(instance.id)
+        self.assertTrue(address.associate(instance.id))
 
         rcuk_da = self.addResourceCleanUp(address.disassociate)
         #TODO(afazekas): ping test. dependecy/permission ?
diff --git a/tempest/tests/boto/test_s3_ec2_images.py b/tempest/tests/boto/test_s3_ec2_images.py
index 1088b00..4068aba 100644
--- a/tempest/tests/boto/test_s3_ec2_images.py
+++ b/tempest/tests/boto/test_s3_ec2_images.py
@@ -23,7 +23,6 @@
 from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
 from tempest.testboto import BotoTestCase
-import tempest.tests.boto
 from tempest.tests.boto.utils.s3 import s3_upload_dir
 from tempest.tests.boto.utils.wait import state_wait
 
@@ -34,7 +33,7 @@
     @classmethod
     def setUpClass(cls):
         super(S3ImagesTest, cls).setUpClass()
-        if not tempest.tests.boto.A_I_IMAGES_READY:
+        if not cls.conclusion['A_I_IMAGES_READY']:
             raise cls.skipException("".join(("EC2 ", cls.__name__,
                                     ": requires ami/aki/ari manifest")))
         cls.os = clients.Manager()
diff --git a/tempest/tests/compute/admin/test_quotas.py b/tempest/tests/compute/admin/test_quotas.py
index d63f72f..7430a7c 100644
--- a/tempest/tests/compute/admin/test_quotas.py
+++ b/tempest/tests/compute/admin/test_quotas.py
@@ -152,6 +152,19 @@
 
 #TODO(afazekas): Add test that tried to update the quota_set as a regular user
 
+    @attr(type='negative')
+    def test_create_server_when_instances_quota_is_full(self):
+        #Once instances quota limit is reached, disallow server creation
+        resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
+        default_instances_quota = quota_set['instances']
+        instances_quota = 0  # Set quota to zero to disallow server creation
+
+        self.adm_client.update_quota_set(self.demo_tenant_id,
+                                         instances=instances_quota)
+        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+                        instances=default_instances_quota)
+        self.assertRaises(exceptions.OverLimit, self.create_server)
+
 
 class QuotasAdminTestXML(QuotasAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 94fff13..3b2026e 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -59,6 +59,7 @@
         cls.limits_client = os.limits_client
         cls.volumes_extensions_client = os.volumes_extensions_client
         cls.volumes_client = os.volumes_client
+        cls.interfaces_client = os.interfaces_client
         cls.build_interval = cls.config.compute.build_interval
         cls.build_timeout = cls.config.compute.build_timeout
         cls.ssh_user = cls.config.compute.ssh_user
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 0f63016..0ff81e1 100644
--- a/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/tests/compute/floating_ips/test_floating_ips_actions.py
@@ -114,42 +114,26 @@
         # 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)
-        except Exception:
-            pass
-        else:
-            self.fail('Should not be able to delete a nonexistent floating IP')
+        # Deleting the non existent floating IP
+        self.assertRaises(exceptions.NotFound, self.client.delete_floating_ip,
+                          self.non_exist_id)
 
     @attr(type='negative')
     def test_associate_nonexistant_floating_ip(self):
         # Negative test:Association of a non existent floating IP
         # to specific server should fail
-        #Associating non existent floating IP
-        try:
-            resp, body = \
-            self.client.associate_floating_ip_to_server("0.0.0.0",
-                                                        self.server_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to associate'
-                      ' a nonexistent floating IP')
+        # Associating non existent floating IP
+        self.assertRaises(exceptions.NotFound,
+                          self.client.associate_floating_ip_to_server,
+                          "0.0.0.0", self.server_id)
 
     @attr(type='negative')
     def test_dissociate_nonexistant_floating_ip(self):
         # Negative test:Dissociation of a non existent floating IP should fail
-        #Dissociating non existent floating IP
-        try:
-            resp, body = \
-            self.client.disassociate_floating_ip_from_server("0.0.0.0",
-                                                             self.server_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to dissociate'
-                      ' a nonexistent floating IP')
+        # Dissociating non existent floating IP
+        self.assertRaises(exceptions.NotFound,
+                          self.client.disassociate_floating_ip_from_server,
+                          "0.0.0.0", self.server_id)
 
     @attr(type='positive')
     def test_associate_already_associated_floating_ip(self):
@@ -171,38 +155,25 @@
         self.client.associate_floating_ip_to_server(self.floating_ip,
                                                     self.new_server_id)
 
-        #Make sure no longer associated with old server
-        try:
-            self.client.disassociate_floating_ip_from_server(
-                self.floating_ip,
-                self.server_id)
-        except (exceptions.NotFound, exceptions.BadRequest):
-            pass
-        else:
-            self.fail('The floating IP should be associated to the second '
-                      'server')
+        self.addCleanup(self.servers_client.delete_server, self.new_server_id)
         if (resp['status'] is not None):
-            #Dissociation of the floating IP associated in this method
-            resp, _ = \
-            self.client.disassociate_floating_ip_from_server(
-                self.floating_ip,
-                self.new_server_id)
-        #Deletion of server created in this method
-        resp, body = self.servers_client.delete_server(self.new_server_id)
+            self.addCleanup(self.client.disassociate_floating_ip_from_server,
+                            self.floating_ip,
+                            self.new_server_id)
+
+        # Make sure no longer associated with old server
+        self.assertRaises((exceptions.NotFound,
+                           exceptions.UnprocessableEntity),
+                          self.client.disassociate_floating_ip_from_server,
+                          self.floating_ip, self.server_id)
 
     @attr(type='negative')
     def test_associate_ip_to_server_without_passing_floating_ip(self):
         # Negative test:Association of empty floating IP to specific server
         # should raise NotFound exception
-        try:
-            resp, body =\
-            self.client.associate_floating_ip_to_server('',
-                                                        self.server_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Association of floating IP to specific server'
-                      ' with out passing floating IP  should raise BadRequest')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.associate_floating_ip_to_server,
+                          '', self.server_id)
 
 
 class FloatingIPsTestXML(FloatingIPsTestJSON):
diff --git a/tempest/tests/compute/floating_ips/test_list_floating_ips.py b/tempest/tests/compute/floating_ips/test_list_floating_ips.py
index 11b9465..b795909 100644
--- a/tempest/tests/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/tests/compute/floating_ips/test_list_floating_ips.py
@@ -90,14 +90,8 @@
             non_exist_id = rand_name('999')
             if non_exist_id not in floating_ip_id:
                 break
-        try:
-            resp, body = \
-            self.client.get_floating_ip_details(non_exist_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to GET the details from a'
-                      'nonexistant floating IP')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.get_floating_ip_details, non_exist_id)
 
 
 class FloatingIPDetailsTestXML(FloatingIPDetailsTestJSON):
diff --git a/tempest/tests/compute/images/test_image_metadata.py b/tempest/tests/compute/images/test_image_metadata.py
index fa69395..311ee8e 100644
--- a/tempest/tests/compute/images/test_image_metadata.py
+++ b/tempest/tests/compute/images/test_image_metadata.py
@@ -111,68 +111,41 @@
     def test_list_nonexistant_image_metadata(self):
         # Negative test: List on nonexistant image
         # metadata should not happen
-        try:
-            resp, resp_metadata = self.client.list_image_metadata(999)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('List on nonexistant image metadata should'
-                      'not happen')
+        self.assertRaises(exceptions.NotFound, self.client.list_image_metadata,
+                          999)
 
     @attr(type='negative')
     def test_update_nonexistant_image_metadata(self):
         # Negative test:An update should not happen for a nonexistant image
         meta = {'key1': 'alt1', 'key2': 'alt2'}
-        try:
-            resp, metadata = self.client.update_image_metadata(999, meta)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('An update shouldnt happen for nonexistant image')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.update_image_metadata, 999, meta)
 
     @attr(type='negative')
     def test_get_nonexistant_image_metadata_item(self):
         # Negative test: Get on nonexistant image should not happen
-        try:
-            resp, metadata = self.client.get_image_metadata_item(999, 'key2')
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Get on nonexistant image should not happen')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.get_image_metadata_item, 999, 'key2')
 
     @attr(type='negative')
     def test_set_nonexistant_image_metadata(self):
         # Negative test: Metadata should not be set to a nonexistant image
         meta = {'key1': 'alt1', 'key2': 'alt2'}
-        try:
-            resp, meta = self.client.set_image_metadata(999, meta)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Metadata should not be set to a nonexistant image')
+        self.assertRaises(exceptions.NotFound, self.client.set_image_metadata,
+                          999, meta)
 
     @attr(type='negative')
     def test_set_nonexistant_image_metadata_item(self):
         # Negative test: Metadata item should not be set to a
         # nonexistant image
         meta = {'key1': 'alt'}
-        try:
-            resp, body = self.client.set_image_metadata_item(999, 'key1', meta)
-            resp, metadata = self.client.list_image_metadata(999)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Metadata item should not be set to a nonexistant image')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.set_image_metadata_item, 999, 'key1',
+                          meta)
 
     @attr(type='negative')
     def test_delete_nonexistant_image_metadata_item(self):
         # Negative test: Shouldnt be able to delete metadata
-                          # item from nonexistant image
-        try:
-            resp, body = self.client.delete_image_metadata_item(999, 'key1')
-            resp, metadata = self.client.list_image_metadata(999)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to delete metadata item from a'
-                      'nonexistant image')
+        # item from nonexistant image
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_image_metadata_item, 999, 'key1')
diff --git a/tempest/tests/compute/images/test_images.py b/tempest/tests/compute/images/test_images.py
index 1e90202..a61cef6 100644
--- a/tempest/tests/compute/images/test_images.py
+++ b/tempest/tests/compute/images/test_images.py
@@ -133,40 +133,24 @@
     @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:
-            snapshot_name = rand_name('test-snap-')
-            test_uuid = ('a' * 35)
-            self.assertRaises(exceptions.NotFound, self.client.create_image,
-                              test_uuid, snapshot_name)
-        except Exception:
-            self.fail("Should return 404 Not Found if server uuid is 35"
-                      " characters or less")
+        snapshot_name = rand_name('test-snap-')
+        test_uuid = ('a' * 35)
+        self.assertRaises(exceptions.NotFound, self.client.create_image,
+                          test_uuid, snapshot_name)
 
     @attr(type='negative')
     def test_create_image_specify_uuid_37_characters_or_more(self):
         # Return an error if Image ID passed is 37 characters or more
-        try:
-            snapshot_name = rand_name('test-snap-')
-            test_uuid = ('a' * 37)
-            self.assertRaises(exceptions.NotFound, self.client.create_image,
-                              test_uuid, snapshot_name)
-        except Exception:
-            self.fail("Should return 404 Not Found if server uuid is 37"
-                      " characters or more")
+        snapshot_name = rand_name('test-snap-')
+        test_uuid = ('a' * 37)
+        self.assertRaises(exceptions.NotFound, self.client.create_image,
+                          test_uuid, snapshot_name)
 
     @attr(type='negative')
     def test_delete_image_with_invalid_image_id(self):
         # An image should not be deleted with invalid image id
-        try:
-            # Delete an image with invalid image id
-            resp, _ = self.client.delete_image('!@$%^&*()')
-
-        except exceptions.NotFound:
-            pass
-
-        else:
-            self.fail("DELETE image request should rasie NotFound exception "
-                      "when requested with invalid image")
+        self.assertRaises(exceptions.NotFound, self.client.delete_image,
+                          '!@$%^&*()')
 
     @attr(type='negative')
     def test_delete_non_existent_image(self):
@@ -179,44 +163,25 @@
     @attr(type='negative')
     def test_delete_image_blank_id(self):
         # Return an error while trying to delete an image with blank Id
-
-        try:
-            self.assertRaises(exceptions.NotFound, self.client.delete_image,
-                              '')
-        except Exception:
-            self.fail("Did not return HTTP 404 NotFound for blank image id")
+        self.assertRaises(exceptions.NotFound, self.client.delete_image, '')
 
     @attr(type='negative')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
-
         image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
-        try:
-            self.assertRaises(exceptions.NotFound, self.client.delete_image,
-                              image_id)
-        except Exception:
-            self.fail("Did not return HTTP 404 NotFound for non hex image")
+        self.assertRaises(exceptions.NotFound, self.client.delete_image,
+                          image_id)
 
     @attr(type='negative')
     def test_delete_image_negative_image_id(self):
         # Return an error while trying to delete an image with negative id
-
-        try:
-            self.assertRaises(exceptions.NotFound, self.client.delete_image,
-                              -1)
-        except Exception:
-            self.fail("Did not return HTTP 404 NotFound for negative image id")
+        self.assertRaises(exceptions.NotFound, self.client.delete_image, -1)
 
     @attr(type='negative')
     def test_delete_image_id_is_over_35_character_limit(self):
         # Return an error while trying to delete image with id over limit
-
-        try:
-            self.assertRaises(exceptions.NotFound, self.client.delete_image,
-                              '11a22b9-120q-5555-cc11-00ab112223gj-3fac')
-        except Exception:
-            self.fail("Did not return HTTP 404 NotFound for image id that "
-                      "exceeds 35 character ID length limit")
+        self.assertRaises(exceptions.NotFound, self.client.delete_image,
+                          '11a22b9-120q-5555-cc11-00ab112223gj-3fac')
 
 
 class ImagesTestXML(ImagesTestJSON):
diff --git a/tempest/tests/compute/images/test_images_oneserver.py b/tempest/tests/compute/images/test_images_oneserver.py
index 6d3f043..d89b6dd 100644
--- a/tempest/tests/compute/images/test_images_oneserver.py
+++ b/tempest/tests/compute/images/test_images_oneserver.py
@@ -61,40 +61,28 @@
     @testtools.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")
+        snapshot_name = rand_name('\xef\xbb\xbf')
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_image, self.server['id'],
+                          snapshot_name)
 
     @attr(type='negative')
     @testtools.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")
+        snapshot_name = rand_name('test-snap-')
+        meta = {'': ''}
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          self.server['id'], snapshot_name, meta)
 
     @attr(type='negative')
     @testtools.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")
+        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)
 
     @attr(type='negative')
     @testtools.skipUnless(compute.MULTI_USER,
@@ -151,38 +139,28 @@
     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 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")
+        # Create second snapshot
+        alt_snapshot_name = rand_name('test-snap-')
+        self.assertRaises(exceptions.Duplicate, self.client.create_image,
+                          self.server['id'], alt_snapshot_name)
+        self.client.wait_for_image_status(image_id, 'ACTIVE')
 
     @attr(type='negative')
     @testtools.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")
+        snapshot_name = rand_name('a' * 260)
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          self.server['id'], snapshot_name)
 
     @attr(type='negative')
     def test_delete_image_that_is_not_yet_active(self):
diff --git a/tempest/tests/compute/images/test_list_image_filters.py b/tempest/tests/compute/images/test_list_image_filters.py
index 5038a65..472f7fb 100644
--- a/tempest/tests/compute/images/test_list_image_filters.py
+++ b/tempest/tests/compute/images/test_list_image_filters.py
@@ -225,9 +225,4 @@
     @attr(type='negative')
     def test_get_nonexistant_image(self):
         # Negative test: GET on non existant image should fail
-        try:
-            resp, image = self.client.get_image(999)
-        except Exception:
-            pass
-        else:
-            self.fail('GET on non existant image should fail')
+        self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
diff --git a/tempest/tests/compute/keypairs/test_keypairs.py b/tempest/tests/compute/keypairs/test_keypairs.py
index aefc5ff..b48b439 100644
--- a/tempest/tests/compute/keypairs/test_keypairs.py
+++ b/tempest/tests/compute/keypairs/test_keypairs.py
@@ -134,48 +134,32 @@
         # Keypair should not be created with a non RSA public key
         k_name = rand_name('keypair-')
         pub_key = "ssh-rsa JUNK nova@ubuntu"
-        try:
-            resp, _ = self.client.create_keypair(k_name, pub_key)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Expected BadRequest for invalid public key')
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_keypair, k_name, pub_key)
 
     @attr(type='negative')
     @testtools.skip("Skipped until the Bug #1086980 is resolved")
     def test_keypair_delete_nonexistant_key(self):
         # Non-existant key deletion should throw a proper error
         k_name = rand_name("keypair-non-existant-")
-        try:
-            resp, _ = self.client.delete_keypair(k_name)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('nonexistent key')
+        self.assertRaises(exceptions.NotFound, self.client.delete_keypair,
+                          k_name)
 
     @attr(type='negative')
     def test_create_keypair_with_empty_public_key(self):
         # Keypair should not be created with an empty public key
         k_name = rand_name("keypair-")
         pub_key = ' '
-        try:
-            resp, _ = self.client.create_keypair(k_name, pub_key)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Expected BadRequest for empty public key')
+        self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+                          k_name, pub_key)
 
     @attr(type='negative')
     def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
         # Keypair should not be created when public key bits are too long
         k_name = rand_name("keypair-")
         pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
-        try:
-            resp, _ = self.client.create_keypair(k_name, pub_key)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Expected BadRequest for too long public key')
+        self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+                          k_name, pub_key)
 
     @attr(type='negative')
     def test_create_keypair_with_duplicate_name(self):
@@ -184,47 +168,30 @@
         resp, _ = self.client.create_keypair(k_name)
         self.assertEqual(200, resp.status)
         #Now try the same keyname to ceate another key
-        try:
-            resp, _ = self.client.create_keypair(k_name)
-            #Expect a HTTP 409 Conflict Error
-        except exceptions.Duplicate:
-            pass
-        else:
-            self.fail('duplicate name')
+        self.assertRaises(exceptions.Duplicate, self.client.create_keypair,
+                          k_name)
         resp, _ = self.client.delete_keypair(k_name)
         self.assertEqual(202, resp.status)
 
     @attr(type='negative')
     def test_create_keypair_with_empty_name_string(self):
         # Keypairs with name being an empty string should not be created
-        try:
-            resp, _ = self.client.create_keypair('')
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('empty string')
+        self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+                          '')
 
     @attr(type='negative')
     def test_create_keypair_with_long_keynames(self):
         # Keypairs with name longer than 255 chars should not be created
         k_name = 'keypair-'.ljust(260, '0')
-        try:
-            resp, _ = self.client.create_keypair(k_name)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('too long')
+        self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+                          k_name)
 
     @attr(type='negative')
     def test_create_keypair_invalid_name(self):
         # Keypairs with name being an invalid name should not be created
         k_name = 'key_/.\@:'
-        try:
-            resp, _ = self.client.create_keypair(k_name)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('invalid name')
+        self.assertRaises(exceptions.BadRequest, self.client.create_keypair,
+                          k_name)
 
 
 class KeyPairsTestXML(KeyPairsTestJSON):
diff --git a/tempest/tests/compute/limits/test_absolute_limits.py b/tempest/tests/compute/limits/test_absolute_limits.py
index 129339c..2b31680 100644
--- a/tempest/tests/compute/limits/test_absolute_limits.py
+++ b/tempest/tests/compute/limits/test_absolute_limits.py
@@ -15,8 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.tests.compute import base
 
 
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 5063fd3..dc85f4b 100644
--- a/tempest/tests/compute/security_groups/test_security_group_rules.py
+++ b/tempest/tests/compute/security_groups/test_security_group_rules.py
@@ -37,18 +37,19 @@
             #Creating a Security Group to add rules to it
             s_name = rand_name('securitygroup-')
             s_description = rand_name('description-')
-            resp, securitygroup =\
-            self.client.create_security_group(s_name, s_description)
+            resp, securitygroup = \
+                self.client.create_security_group(s_name, s_description)
             securitygroup_id = securitygroup['id']
             #Adding rules to the created Security Group
             parent_group_id = securitygroup['id']
             ip_protocol = 'tcp'
             from_port = 22
             to_port = 22
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol, from_port,
-                                                   to_port)
+            resp, rule = \
+                self.client.create_security_group_rule(parent_group_id,
+                                                       ip_protocol,
+                                                       from_port,
+                                                       to_port)
             self.assertEqual(200, resp.status)
         finally:
             #Deleting the Security Group rule, created in this method
@@ -70,14 +71,14 @@
             #Creating a Security Group to add rules to it
             s_name = rand_name('securitygroup-')
             s_description = rand_name('description-')
-            resp, securitygroup =\
-            self.client.create_security_group(s_name, s_description)
+            resp, securitygroup = \
+                self.client.create_security_group(s_name, s_description)
             secgroup1 = securitygroup['id']
             #Creating a Security Group so as to assign group_id to the rule
             s_name2 = rand_name('securitygroup-')
             s_description2 = rand_name('description-')
-            resp, securitygroup =\
-            self.client.create_security_group(s_name2, s_description2)
+            resp, securitygroup = \
+                self.client.create_security_group(s_name2, s_description2)
             secgroup2 = securitygroup['id']
             #Adding rules to the created Security Group with optional arguments
             parent_group_id = secgroup1
@@ -86,12 +87,13 @@
             to_port = 22
             cidr = '10.2.3.124/24'
             group_id = secgroup2
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port,
-                                                   cidr=cidr,
-                                                   group_id=group_id)
+            resp, rule = \
+                self.client.create_security_group_rule(parent_group_id,
+                                                       ip_protocol,
+                                                       from_port,
+                                                       to_port,
+                                                       cidr=cidr,
+                                                       group_id=group_id)
             rule_id = rule['id']
             self.assertEqual(200, resp.status)
         finally:
@@ -112,18 +114,19 @@
             #Creating a Security Group to add rule to it
             s_name = rand_name('securitygroup-')
             s_description = rand_name('description-')
-            resp, securitygroup =\
-            self.client.create_security_group(s_name, s_description)
+            resp, securitygroup = \
+                self.client.create_security_group(s_name, s_description)
             securitygroup_id = securitygroup['id']
             #Adding rules to the created Security Group
             parent_group_id = securitygroup['id']
             ip_protocol = 'tcp'
             from_port = 22
             to_port = 22
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port)
+            resp, rule = \
+                self.client.create_security_group_rule(parent_group_id,
+                                                       ip_protocol,
+                                                       from_port,
+                                                       to_port)
         finally:
             #Deleting the Security Group rule, created in this method
             group_rule_id = rule['id']
@@ -135,21 +138,14 @@
     def test_security_group_rules_create_with_invalid_id(self):
         # Negative test: Creation of Security Group rule should FAIL
         # with invalid Parent group id
-        #Adding rules to the invalid Security Group id
+        # Adding rules to the invalid Security Group id
         parent_group_id = rand_name('999')
         ip_protocol = 'tcp'
         from_port = 22
         to_port = 22
-        try:
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Security Group rule should not be created '
-                      'with invalid parent group id')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
 
     @attr(type='negative')
     def test_security_group_rules_create_with_invalid_ip_protocol(self):
@@ -165,18 +161,11 @@
         ip_protocol = rand_name('999')
         from_port = 22
         to_port = 22
-        try:
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group rule should not be created '
-                      'with invalid ip_protocol')
-        #Deleting the Security Group created in this method
-        resp, _ = self.client.delete_security_group(securitygroup['id'])
+
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
 
     @attr(type='negative')
     def test_security_group_rules_create_with_invalid_from_port(self):
@@ -192,18 +181,10 @@
         ip_protocol = 'tcp'
         from_port = rand_name('999')
         to_port = 22
-        try:
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group rule should not be created'
-                      'with invalid from_port')
-        #Deleting the Security Group created in this method
-        resp, _ = self.client.delete_security_group(securitygroup['id'])
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
 
     @attr(type='negative')
     def test_security_group_rules_create_with_invalid_to_port(self):
@@ -219,30 +200,37 @@
         ip_protocol = 'tcp'
         from_port = 22
         to_port = rand_name('999')
-        try:
-            resp, rule =\
-            self.client.create_security_group_rule(parent_group_id,
-                                                   ip_protocol,
-                                                   from_port, to_port)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group rule should not be created'
-                      'with invalid from_port')
-        #Deleting the Security Group created in this method
-        resp, _ = self.client.delete_security_group(securitygroup['id'])
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          parent_group_id, ip_protocol, from_port, to_port)
+
+    @attr(type='negative')
+    def test_security_group_rules_create_with_invalid_port_range(self):
+        # Negative test: Creation of Security Group rule should FAIL
+        # with invalid port range.
+        # Creating a Security Group to add rule to it.
+        s_name = rand_name('securitygroup-')
+        s_description = rand_name('description-')
+        resp, securitygroup = self.client.create_security_group(s_name,
+                                                                s_description)
+        # Adding a rule to the created Security Group
+        secgroup_id = securitygroup['id']
+        ip_protocol = 'tcp'
+        from_port = 22
+        to_port = 21
+        self.addCleanup(self.client.delete_security_group, securitygroup['id'])
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group_rule,
+                          secgroup_id, ip_protocol, from_port, to_port)
 
     @attr(type='negative')
     def test_security_group_rules_delete_with_invalid_id(self):
         # Negative test: Deletion of Security Group rule should be FAIL
         # with invalid rule id
-        try:
-            self.client.delete_security_group_rule(rand_name('999'))
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Security Group Rule should not be deleted '
-                      'with nonexistant rule id')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_security_group_rule,
+                          rand_name('999'))
 
 
 class SecurityGroupRulesTestXML(SecurityGroupRulesTestJSON):
diff --git a/tempest/tests/compute/security_groups/test_security_groups.py b/tempest/tests/compute/security_groups/test_security_groups.py
index c086280..70a01a0 100644
--- a/tempest/tests/compute/security_groups/test_security_groups.py
+++ b/tempest/tests/compute/security_groups/test_security_groups.py
@@ -38,8 +38,8 @@
             for i in range(3):
                 s_name = rand_name('securitygroup-')
                 s_description = rand_name('description-')
-                resp, securitygroup =\
-                self.client.create_security_group(s_name, s_description)
+                resp, securitygroup = \
+                    self.client.create_security_group(s_name, s_description)
                 self.assertEqual(200, resp.status)
                 security_group_list.append(securitygroup)
             #Fetch all Security Groups and verify the list
@@ -47,8 +47,8 @@
             resp, fetched_list = self.client.list_security_groups()
             self.assertEqual(200, resp.status)
             #Now check if all the created Security Groups are in fetched list
-            missing_sgs =\
-            [sg for sg in security_group_list if sg not in fetched_list]
+            missing_sgs = \
+                [sg for sg in security_group_list if sg not in fetched_list]
             self.assertFalse(missing_sgs,
                              "Failed to find Security Group %s in fetched "
                              "list" % ', '.join(m_group['name']
@@ -56,8 +56,8 @@
         finally:
             #Delete all the Security Groups created in this method
             for securitygroup in security_group_list:
-                resp, _ =\
-                self.client.delete_security_group(securitygroup['id'])
+                resp, _ = \
+                    self.client.delete_security_group(securitygroup['id'])
                 self.assertEqual(202, resp.status)
 
     @attr(type='positive')
@@ -67,7 +67,7 @@
             s_name = rand_name('securitygroup-')
             s_description = rand_name('description-')
             resp, securitygroup = \
-            self.client.create_security_group(s_name, s_description)
+                self.client.create_security_group(s_name, s_description)
             self.assertEqual(200, resp.status)
             self.assertTrue('id' in securitygroup)
             securitygroup_id = securitygroup['id']
@@ -88,12 +88,12 @@
         try:
             s_name = rand_name('securitygroup-')
             s_description = rand_name('description-')
-            resp, securitygroup =\
-            self.client.create_security_group(s_name, s_description)
+            resp, securitygroup = \
+                self.client.create_security_group(s_name, s_description)
             self.assertEqual(200, resp.status)
             #Now fetch the created Security Group by its 'id'
-            resp, fetched_group =\
-            self.client.get_security_group(securitygroup['id'])
+            resp, fetched_group = \
+                self.client.get_security_group(securitygroup['id'])
             self.assertEqual(200, resp.status)
             self.assertEqual(securitygroup, fetched_group,
                              "The fetched Security Group is different "
@@ -116,100 +116,74 @@
             non_exist_id = rand_name('999')
             if non_exist_id not in security_group_id:
                 break
-        try:
-            resp, body = \
-            self.client.get_security_group(non_exist_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to GET the details from a '
-                      'nonexistant Security Group')
+        self.assertRaises(exceptions.NotFound, self.client.get_security_group,
+                          non_exist_id)
 
     @attr(type='negative')
     def test_security_group_create_with_invalid_group_name(self):
         # Negative test: Security Group should not be created with group name
         # as an empty string/with white spaces/chars more than 255
         s_description = rand_name('description-')
-        #Create Security Group with empty string as group name
-        try:
-            resp, _ = self.client.create_security_group("", s_description)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with EMPTY Name')
-        #Create Security Group with white space in group name
-        try:
-            resp, _ = self.client.create_security_group(" ", s_description)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with WHITE SPACE in Name')
-        #Create Security Group with group name longer than 255 chars
+        # Create Security Group with empty string as group name
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, "", s_description)
+        # Create Security Group with white space in group name
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, " ",
+                          s_description)
+        # Create Security Group with group name longer than 255 chars
         s_name = 'securitygroup-'.ljust(260, '0')
-        try:
-            resp, _ = self.client.create_security_group(s_name, s_description)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with more than 255 chars in Name')
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, s_name,
+                          s_description)
 
     @attr(type='negative')
     def test_security_group_create_with_invalid_group_description(self):
         # Negative test:Security Group should not be created with description
         # as an empty string/with white spaces/chars more than 255
         s_name = rand_name('securitygroup-')
-        #Create Security Group with empty string as description
-        try:
-            resp, _ = self.client.create_security_group(s_name, "")
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with EMPTY Description')
-        #Create Security Group with white space in description
-        try:
-            resp, _ = self.client.create_security_group(s_name, " ")
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with WHITE SPACE in Description')
-        #Create Security Group with group description longer than 255 chars
+        # Create Security Group with empty string as description
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, s_name, "")
+        # Create Security Group with white space in description
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, s_name, " ")
+        # Create Security Group with group description longer than 255 chars
         s_description = 'description-'.ljust(260, '0')
-        try:
-            resp, _ = self.client.create_security_group(s_name, s_description)
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Security Group should not be created '
-                      'with more than 255 chars in Description')
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, s_name,
+                          s_description)
 
     @attr(type='negative')
     def test_security_group_create_with_duplicate_name(self):
         # Negative test:Security Group with duplicate name should not
         # be created
-        try:
-            s_name = rand_name('securitygroup-')
-            s_description = rand_name('description-')
-            resp, security_group =\
+        s_name = rand_name('securitygroup-')
+        s_description = rand_name('description-')
+        resp, security_group =\
             self.client.create_security_group(s_name, s_description)
-            self.assertEqual(200, resp.status)
-            #Now try the Security Group with the same 'Name'
-            try:
-                resp, _ =\
-                self.client.create_security_group(s_name, s_description)
-            except exceptions.BadRequest:
-                pass
-            else:
-                self.fail('Security Group should not be created '
-                          'with duplicate Group Name')
-        finally:
-            #Delete the Security Group created in this method
-            resp, _ = self.client.delete_security_group(security_group['id'])
-            self.assertEqual(202, resp.status)
+        self.assertEqual(200, resp.status)
+
+        self.addCleanup(self.client.delete_security_group,
+                        security_group['id'])
+        # Now try the Security Group with the same 'Name'
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_security_group, s_name,
+                          s_description)
+
+    @attr(type='negative')
+    def test_delete_the_default_security_group(self):
+        # Negative test:Deletion of the "default" Security Group should Fail
+        default_security_group_id = None
+        resp, body = self.client.list_security_groups()
+        for i in range(len(body)):
+            if body[i]['name'] == 'default':
+                default_security_group_id = body[i]['id']
+                break
+        #Deleting the "default" Security Group
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.delete_security_group,
+                          default_security_group_id)
 
     @attr(type='negative')
     def test_delete_nonexistant_security_group(self):
@@ -223,25 +197,15 @@
             non_exist_id = rand_name('999')
             if non_exist_id not in security_group_id:
                 break
-        try:
-            resp, body = self.client.delete_security_group(non_exist_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to delete a nonexistant '
-                      'Security Group')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_security_group, non_exist_id)
 
     @attr(type='negative')
     def test_delete_security_group_without_passing_id(self):
         # Negative test:Deletion of a Security Group with out passing ID
         # should Fail
-        try:
-            resp, body = self.client.delete_security_group('')
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to delete a Security Group'
-                      'with out passing ID')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_security_group, '')
 
     def test_server_security_groups(self):
         # Checks that security groups may be added and linked to a server
diff --git a/tempest/tests/compute/servers/test_attach_interfaces.py b/tempest/tests/compute/servers/test_attach_interfaces.py
new file mode 100644
index 0000000..47c0575
--- /dev/null
+++ b/tempest/tests/compute/servers/test_attach_interfaces.py
@@ -0,0 +1,112 @@
+# Copyright 2013 IBM Corp.
+# 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.
+
+from tempest import clients
+from tempest.tests.compute import base
+
+import time
+
+
+class AttachInterfacesTestJSON(base.BaseComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(AttachInterfacesTestJSON, cls).setUpClass()
+        os = clients.Manager()
+        if not os.config.network.quantum_available:
+            raise cls.skipException("Quantum is required")
+        cls.client = os.interfaces_client
+
+    def _check_interface(self, iface, port_id=None, network_id=None,
+                         fixed_ip=None):
+        self.assertIn('port_state', iface)
+        if port_id:
+            self.assertEqual(iface['port_id'], port_id)
+        if network_id:
+            self.assertEqual(iface['net_id'], network_id)
+        if fixed_ip:
+            self.assertEqual(iface['fixed_ips'][0]['ip_address'], fixed_ip)
+
+    def _create_server_get_interfaces(self):
+        server = self.create_server()
+        self.os.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        resp, ifs = self.client.list_interfaces(server['id'])
+        return server, ifs
+
+    def _test_create_interface(self, server):
+        resp, iface = self.client.create_interface(server['id'])
+        self._check_interface(iface)
+        return iface
+
+    def _test_create_interface_by_network_id(self, server, ifs):
+        network_id = ifs[0]['net_id']
+        resp, iface = self.client.create_interface(server['id'],
+                                                   network_id=network_id)
+        self._check_interface(iface, network_id=network_id)
+        return iface
+
+    def _test_show_interface(self, server, ifs):
+        iface = ifs[0]
+        resp, _iface = self.client.show_interface(server['id'],
+                                                  iface['port_id'])
+        self.assertEqual(iface, _iface)
+
+    def _test_delete_interface(self, server, ifs):
+        # NOTE(danms): delete not the first or last, but one in the middle
+        iface = ifs[1]
+        self.client.delete_interface(server['id'], iface['port_id'])
+        for i in range(0, 5):
+            _r, _ifs = self.client.list_interfaces(server['id'])
+            if len(ifs) != len(_ifs):
+                break
+            time.sleep(1)
+
+        self.assertEqual(len(_ifs), len(ifs) - 1)
+        for _iface in _ifs:
+            self.assertNotEqual(iface['port_id'], _iface['port_id'])
+        return _ifs
+
+    def _compare_iface_list(self, list1, list2):
+        # NOTE(danms): port_state will likely have changed, so just
+        # confirm the port_ids are the same at least
+        list1 = [x['port_id'] for x in list1]
+        list2 = [x['port_id'] for x in list2]
+
+        self.assertEqual(sorted(list1), sorted(list2))
+
+    def test_create_list_show_delete_interfaces(self):
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertTrue(interface_count > 0)
+        self._check_interface(ifs[0])
+
+        iface = self._test_create_interface(server)
+        ifs.append(iface)
+
+        iface = self._test_create_interface_by_network_id(server, ifs)
+        ifs.append(iface)
+
+        resp, _ifs = self.client.list_interfaces(server['id'])
+        self._compare_iface_list(ifs, _ifs)
+
+        self._test_show_interface(server, ifs)
+
+        _ifs = self._test_delete_interface(server, ifs)
+        self.assertEqual(len(ifs) - 1, len(_ifs))
+
+
+class AttachInterfacesTestXML(AttachInterfacesTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_create_server.py b/tempest/tests/compute/servers/test_create_server.py
index 3c8aeda..aaab9fa 100644
--- a/tempest/tests/compute/servers/test_create_server.py
+++ b/tempest/tests/compute/servers/test_create_server.py
@@ -46,24 +46,17 @@
         personality = [{'path': '/test.txt',
                        'contents': base64.b64encode(file_contents)}]
         cls.client = cls.servers_client
-        cli_resp = cls.client.create_server(cls.name,
-                                            cls.image_ref,
-                                            cls.flavor_ref,
-                                            meta=cls.meta,
-                                            accessIPv4=cls.accessIPv4,
-                                            accessIPv6=cls.accessIPv6,
-                                            personality=personality,
-                                            disk_config=cls.disk_config)
+        cli_resp = cls.create_server(name=cls.name,
+                                     meta=cls.meta,
+                                     accessIPv4=cls.accessIPv4,
+                                     accessIPv6=cls.accessIPv6,
+                                     personality=personality,
+                                     disk_config=cls.disk_config)
         cls.resp, cls.server_initial = cli_resp
         cls.password = cls.server_initial['adminPass']
         cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
         resp, cls.server = cls.client.get_server(cls.server_initial['id'])
 
-    @classmethod
-    def tearDownClass(cls):
-        cls.client.delete_server(cls.server_initial['id'])
-        super(ServersTestJSON, cls).tearDownClass()
-
     @attr(type='smoke')
     def test_create_server_response(self):
         # Check that the required fields are returned with values
diff --git a/tempest/tests/compute/servers/test_disk_config.py b/tempest/tests/compute/servers/test_disk_config.py
index 3a1ec20..2fbb876 100644
--- a/tempest/tests/compute/servers/test_disk_config.py
+++ b/tempest/tests/compute/servers/test_disk_config.py
@@ -17,7 +17,6 @@
 
 import testtools
 
-from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
 from tempest.tests import compute
 from tempest.tests.compute import base
diff --git a/tempest/tests/compute/servers/test_server_addresses.py b/tempest/tests/compute/servers/test_server_addresses.py
index b811d52..4807d1e 100644
--- a/tempest/tests/compute/servers/test_server_addresses.py
+++ b/tempest/tests/compute/servers/test_server_addresses.py
@@ -43,26 +43,15 @@
     @attr(type='negative', category='server-addresses')
     def test_list_server_addresses_invalid_server_id(self):
         # List addresses request should fail if server id not in system
-
-        try:
-            self.client.list_addresses('999')
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('The server rebuild for a non existing server should not'
-                      ' be allowed')
+        self.assertRaises(exceptions.NotFound, self.client.list_addresses,
+                          '999')
 
     @attr(type='negative', category='server-addresses')
     def test_list_server_addresses_by_network_neg(self):
         # List addresses by network should fail if network name not valid
-
-        try:
-            self.client.list_addresses_by_network(self.server['id'], 'invalid')
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('The server rebuild for a non existing server should not'
-                      ' be allowed')
+        self.assertRaises(exceptions.NotFound,
+                          self.client.list_addresses_by_network,
+                          self.server['id'], 'invalid')
 
     @attr(type='smoke', category='server-addresses')
     def test_list_server_addresses(self):
@@ -99,3 +88,7 @@
             addr = addr[addr_type]
             for address in addresses[addr_type]:
                 self.assertTrue(any([a for a in addr if a == address]))
+
+
+class ServerAddressesTestXML(ServerAddressesTest):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_server_metadata.py b/tempest/tests/compute/servers/test_server_metadata.py
index 4b17fa2..bc523de 100644
--- a/tempest/tests/compute/servers/test_server_metadata.py
+++ b/tempest/tests/compute/servers/test_server_metadata.py
@@ -15,28 +15,25 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 from tempest.tests.compute import base
 
 
-class ServerMetadataTest(base.BaseComputeTest):
+class ServerMetadataTestJSON(base.BaseComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServerMetadataTest, cls).setUpClass()
+        super(ServerMetadataTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
         cls.quotas = cls.quotas_client
         cls.admin_client = cls._get_identity_admin_client()
         resp, tenants = cls.admin_client.list_tenants()
         cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
                          cls.client.tenant_name][0]
-        #Create a server to be used for all read only tests
-        name = rand_name('server')
-        resp, server = cls.client.create_server(name, cls.image_ref,
-                                                cls.flavor_ref, meta={})
+        resp, server = cls.create_server(meta={})
+
         cls.server_id = server['id']
 
         #Wait for the server to become active
@@ -45,10 +42,10 @@
     @classmethod
     def tearDownClass(cls):
         cls.client.delete_server(cls.server_id)
-        super(ServerMetadataTest, cls).tearDownClass()
+        super(ServerMetadataTestJSON, cls).tearDownClass()
 
     def setUp(self):
-        super(ServerMetadataTest, self).setUp()
+        super(ServerMetadataTestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
         resp, _ = self.client.set_server_metadata(self.server_id, meta)
         self.assertEqual(resp.status, 200)
@@ -238,3 +235,7 @@
         self.assertRaises(exceptions.BadRequest,
                           self.client.set_server_metadata,
                           self.server_id, meta=meta)
+
+
+class ServerMetadataTestXML(ServerMetadataTestJSON):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/servers/test_server_personality.py b/tempest/tests/compute/servers/test_server_personality.py
index 0bafc2c..c529c43 100644
--- a/tempest/tests/compute/servers/test_server_personality.py
+++ b/tempest/tests/compute/servers/test_server_personality.py
@@ -17,7 +17,6 @@
 
 import base64
 
-from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 from tempest.tests.compute import base
diff --git a/tempest/tests/compute/servers/test_servers_negative.py b/tempest/tests/compute/servers/test_servers_negative.py
index 366b630..9013b36 100644
--- a/tempest/tests/compute/servers/test_servers_negative.py
+++ b/tempest/tests/compute/servers/test_servers_negative.py
@@ -29,7 +29,6 @@
 
     @classmethod
     def setUpClass(cls):
-        raise cls.skipException("Until Bug 1046870 is fixed")
         super(ServersNegativeTest, cls).setUpClass()
         cls.client = cls.servers_client
         cls.img_client = cls.images_client
@@ -115,6 +114,8 @@
     @attr(type='negative')
     def test_create_numeric_server_name(self):
         # Create a server with a numeric name
+        if self.__class__._interface == "xml":
+            raise self.skipException("Not testable in XML")
 
         server_name = 12345
         self.assertRaises(exceptions.BadRequest,
@@ -182,7 +183,7 @@
     def test_update_server_of_another_tenant(self):
         # Update name of a server that belongs to another tenant
 
-        server = self.create_server()
+        resp, server = self.create_server(wait_until='ACTIVE')
         new_name = server['id'] + '_new'
         self.assertRaises(exceptions.NotFound,
                           self.alt_client.update_server, server['id'],
@@ -192,7 +193,7 @@
     def test_update_server_name_length_exceeds_256(self):
         # Update name of server exceed the name length limit
 
-        server = self.create_server()
+        resp, server = self.create_server(wait_until='ACTIVE')
         new_name = 'a' * 256
         self.assertRaises(exceptions.BadRequest,
                           self.client.update_server,
@@ -210,7 +211,7 @@
     def test_delete_a_server_of_another_tenant(self):
         # Delete a server that belongs to another tenant
         try:
-            server = self.create_server()
+            resp, server = self.create_server(wait_until='ACTIVE')
             self.assertRaises(exceptions.NotFound,
                               self.alt_client.delete_server,
                               server['id'])
@@ -245,3 +246,7 @@
 
         self.assertRaises(exceptions.NotFound, self.client.get_server,
                           '999erra43')
+
+
+class ServersNegativeTestXML(ServersNegativeTest):
+    _interface = 'xml'
diff --git a/tempest/tests/compute/test_live_block_migration.py b/tempest/tests/compute/test_live_block_migration.py
index dcd6a78..abaaf85 100644
--- a/tempest/tests/compute/test_live_block_migration.py
+++ b/tempest/tests/compute/test_live_block_migration.py
@@ -86,7 +86,7 @@
             if 'ACTIVE' == self._get_server_status(server_id):
                 return server_id
         else:
-            server = self.create_server()
+            _, server = self.create_server(wait_until="ACTIVE")
             server_id = server['id']
             self.password = server['adminPass']
             self.password = 'password'
@@ -108,7 +108,6 @@
         self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
         self.assertEquals(target_host, self._get_host_for_server(server_id))
 
-    @testtools.skip('Until bug 1051881 is dealt with.')
     @testtools.skipIf(not live_migration_available,
                       'Block Live migration not available')
     def test_invalid_host_for_migration(self):
diff --git a/tempest/tests/compute/volumes/test_volumes_negative.py b/tempest/tests/compute/volumes/test_volumes_negative.py
index c0fa565..306b93b 100644
--- a/tempest/tests/compute/volumes/test_volumes_negative.py
+++ b/tempest/tests/compute/volumes/test_volumes_negative.py
@@ -39,18 +39,13 @@
             non_exist_id = rand_name('999')
             if non_exist_id not in volume_id_list:
                 break
-        #Trying to GET a non existant volume
-        try:
-            resp, body = self.client.get_volume(non_exist_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to GET the details from a '
-                      'nonexistant volume')
+        # Trying to GET a non existant volume
+        self.assertRaises(exceptions.NotFound, self.client.get_volume,
+                          non_exist_id)
 
     def test_volume_delete_nonexistant_volume_id(self):
         # Negative: Should not be able to delete nonexistant Volume
-        #Creating nonexistant volume id
+        # Creating nonexistant volume id
         volume_id_list = list()
         resp, body = self.client.list_volumes()
         for i in range(len(body)):
@@ -59,13 +54,9 @@
             non_exist_id = rand_name('999')
             if non_exist_id not in volume_id_list:
                 break
-        #Trying to DELETE a non existant volume
-        try:
-            resp, body = self.client.delete_volume(non_exist_id)
-        except exceptions.NotFound:
-            pass
-        else:
-            self.fail('Should not be able to DELETE a nonexistant volume')
+        # Trying to DELETE a non existant volume
+        self.assertRaises(exceptions.NotFound, self.client.delete_volume,
+                          non_exist_id)
 
     def test_create_volume_with_invalid_size(self):
         # Negative: Should not be able to create volume with invalid size
diff --git a/tempest/tests/identity/admin/test_roles.py b/tempest/tests/identity/admin/test_roles.py
index 53c5b0d..f71bed0 100644
--- a/tempest/tests/identity/admin/test_roles.py
+++ b/tempest/tests/identity/admin/test_roles.py
@@ -95,15 +95,9 @@
         role1_id = body.get('id')
         self.assertTrue('status' in resp)
         self.assertTrue(resp['status'].startswith('2'))
-
-        try:
-            resp, body = self.client.create_role(role_name)
-            # this should raise an exception
-            self.fail('Should not be able to create a duplicate role name.'
-                      ' %s' % role_name)
-        except exceptions.Duplicate:
-            pass
-        self.client.delete_role(role1_id)
+        self.addCleanup(self.client.delete_role, role1_id)
+        self.assertRaises(exceptions.Duplicate, self.client.create_role,
+                          role_name)
 
     def test_assign_user_role(self):
         # Assign a role to a user on a tenant
diff --git a/tempest/tests/identity/admin/test_tenants.py b/tempest/tests/identity/admin/test_tenants.py
index a3c9246..8155eb5 100644
--- a/tempest/tests/identity/admin/test_tenants.py
+++ b/tempest/tests/identity/admin/test_tenants.py
@@ -151,14 +151,10 @@
         self.data.tenants.append(tenant)
         tenant1_id = body.get('id')
 
-        try:
-            resp, body = self.client.create_tenant(tenant_name)
-            # this should have raised an exception
-            self.fail('Should not be able to create a duplicate tenant name')
-        except exceptions.Duplicate:
-            pass
-        self.client.delete_tenant(tenant1_id)
-        self.data.tenants.remove(tenant)
+        self.addCleanup(self.client.delete_tenant, tenant1_id)
+        self.addCleanup(self.data.tenants.remove, tenant)
+        self.assertRaises(exceptions.Duplicate, self.client.create_tenant,
+                          tenant_name)
 
     @attr(type='negative')
     def test_create_tenant_by_unauthorized_user(self):
diff --git a/tempest/tests/image/test_images.py b/tempest/tests/image/test_images.py
deleted file mode 100644
index 6ac852e..0000000
--- a/tempest/tests/image/test_images.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# 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 cStringIO as StringIO
-import random
-
-import tempest.test
-
-from tempest.test import attr
-
-
-from tempest import clients
-from tempest import exceptions
-
-
-class CreateRegisterImagesTest(tempest.test.BaseTestCase):
-
-    """
-    Here we test the registration and creation of images
-    """
-
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete(image_id)
-
-    @attr(type='negative')
-    def test_register_with_invalid_container_format(self):
-        # Negative tests for invalid data supplied to POST /images
-        try:
-            resp, body = self.client.create_image('test', 'wrong', 'vhd')
-        except exceptions.BadRequest:
-            pass
-        else:
-            self.fail('Invalid container format should not be accepted')
-
-    @attr(type='negative')
-    def test_register_with_invalid_disk_format(self):
-        try:
-            resp, body = self.client.create_image('test', 'bare', 'wrong')
-        except exceptions.BadRequest:
-                pass
-        else:
-            self.fail("Invalid disk format should not be accepted")
-
-    @attr(type='image')
-    def test_register_then_upload(self):
-        # Register, then upload an image
-        properties = {'prop1': 'val1'}
-        resp, body = self.client.create_image('New Name', 'bare', 'raw',
-                                              is_public=True,
-                                              properties=properties)
-        self.assertTrue('id' in body)
-        image_id = body.get('id')
-        self.created_images.append(image_id)
-        self.assertTrue('name' in body)
-        self.assertEqual('New Name', body.get('name'))
-        self.assertTrue('is_public' in body)
-        self.assertTrue(body.get('is_public'))
-        self.assertTrue('status' in body)
-        self.assertEqual('queued', body.get('status'))
-        self.assertTrue('properties' in body)
-        for key, val in properties.items():
-            self.assertEqual(val, body.get('properties')[key])
-
-        # Now try uploading an image file
-        image_file = StringIO.StringIO(('*' * 1024))
-        resp, body = self.client.update_image(image_id, data=image_file)
-        self.assertTrue('size' in body)
-        self.assertEqual(1024, body.get('size'))
-
-    @attr(type='image')
-    def test_register_remote_image(self):
-        # Register a new remote image
-        resp, body = self.client.create_image('New Remote Image', 'bare',
-                                              'raw', is_public=True,
-                                              location='http://example.com'
-                                                       '/someimage.iso')
-        self.assertTrue('id' in body)
-        image_id = body.get('id')
-        self.created_images.append(image_id)
-        self.assertTrue('name' in body)
-        self.assertEqual('New Remote Image', body.get('name'))
-        self.assertTrue('is_public' in body)
-        self.assertTrue(body.get('is_public'))
-        self.assertTrue('status' in body)
-        self.assertEqual('active', body.get('status'))
-
-
-class ListImagesTest(tempest.test.BaseTestCase):
-
-    """
-    Here we test the listing of image information
-    """
-
-    @classmethod
-    def setUpClass(cls):
-        cls.os = clients.Manager()
-        cls.client = cls.os.image_client
-        cls.created_images = []
-
-        # We add a few images here to test the listing functionality of
-        # the images API
-        for x in xrange(0, 10):
-            # We make even images remote and odd images standard
-            if x % 2 == 0:
-                cls.created_images.append(cls._create_remote_image(x))
-            else:
-                cls.created_images.append(cls._create_standard_image(x))
-
-    @classmethod
-    def tearDownClass(cls):
-        for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
-            cls.client.wait_for_resource_deletion(image_id)
-
-    @classmethod
-    def _create_remote_image(cls, x):
-        """
-        Create a new remote image and return the ID of the newly-registered
-        image
-        """
-        name = 'New Remote Image %s' % x
-        location = 'http://example.com/someimage_%s.iso' % x
-        resp, body = cls.client.create_image(name, 'bare', 'raw',
-                                             is_public=True,
-                                             location=location)
-        image_id = body['id']
-        return image_id
-
-    @classmethod
-    def _create_standard_image(cls, x):
-        """
-        Create a new standard image and return the ID of the newly-registered
-        image. Note that the size of the new image is a random number between
-        1024 and 4096
-        """
-        image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
-        name = 'New Standard Image %s' % x
-        resp, body = cls.client.create_image(name, 'bare', 'raw',
-                                             is_public=True, data=image_file)
-        image_id = body['id']
-        return image_id
-
-    @attr(type='image')
-    def test_index_no_params(self):
-        # Simple test to see all fixture images returned
-        resp, images_list = self.client.image_list()
-        self.assertEqual(resp['status'], '200')
-        image_list = map(lambda x: x['id'], images_list)
-        for image in self.created_images:
-            self.assertTrue(image in image_list)
diff --git a/tempest/services/image/json/__init__.py b/tempest/tests/image/v1/__init__.py
similarity index 100%
copy from tempest/services/image/json/__init__.py
copy to tempest/tests/image/v1/__init__.py
diff --git a/tempest/tests/image/v1/test_image_members.py b/tempest/tests/image/v1/test_image_members.py
new file mode 100644
index 0000000..30fa6c6
--- /dev/null
+++ b/tempest/tests/image/v1/test_image_members.py
@@ -0,0 +1,93 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+#    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 cStringIO as StringIO
+
+from tempest import clients
+import tempest.test
+
+
+class ImageMembersTests(tempest.test.BaseTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.client = cls.os.image_client
+        admin = clients.AdminManager(interface='json')
+        cls.admin_client = admin.identity_client
+        cls.created_images = []
+        cls.tenants = cls._get_tenants()
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            cls.client.delete_image(image_id)
+            cls.client.wait_for_resource_deletion(image_id)
+
+    @classmethod
+    def _get_tenants(cls):
+        resp, tenants = cls.admin_client.list_tenants()
+        tenants = map(lambda x: x['id'], tenants)
+        return tenants
+
+    def _create_image(self, name=None):
+        image_file = StringIO.StringIO('*' * 1024)
+        if name is not None:
+            name = 'New Standard Image with Members'
+        resp, image = self.client.create_image(name,
+                                               'bare', 'raw',
+                                               is_public=True, data=image_file)
+        self.assertEquals(201, resp.status)
+        image_id = image['id']
+        self.created_images.append(image_id)
+        return image_id
+
+    def test_add_image_member(self):
+        image = self._create_image()
+        resp = self.client.add_member(self.tenants[0], image)
+        self.assertEquals(204, resp.status)
+        resp, body = self.client.get_image_membership(image)
+        self.assertEquals(200, resp.status)
+        members = body['members']
+        members = map(lambda x: x['member_id'], members)
+        self.assertIn(self.tenants[0], members)
+
+    def test_get_shared_images(self):
+        image = self._create_image()
+        resp = self.client.add_member(self.tenants[0], image)
+        self.assertEquals(204, resp.status)
+        name = 'Shared Image'
+        share_image = self._create_image(name=name)
+        resp = self.client.add_member(self.tenants[0], share_image)
+        self.assertEquals(204, resp.status)
+        resp, body = self.client.get_shared_images(self.tenants[0])
+        self.assertEquals(200, resp.status)
+        images = body['shared_images']
+        images = map(lambda x: x['image_id'], images)
+        self.assertIn(share_image, images)
+        self.assertIn(image, images)
+
+    def test_remove_member(self):
+        name = 'Shared Image for Delete Test'
+        image_id = self._create_image(name=name)
+        resp = self.client.add_member(self.tenants[0], image_id)
+        self.assertEquals(204, resp.status)
+        resp = self.client.delete_member(self.tenants[0], image_id)
+        self.assertEquals(204, resp.status)
+        resp, body = self.client.get_image_membership(image_id)
+        self.assertEquals(200, resp.status)
+        members = body['members']
+        self.assertEquals(0, len(members))
diff --git a/tempest/tests/image/v1/test_images.py b/tempest/tests/image/v1/test_images.py
new file mode 100644
index 0000000..84bb650
--- /dev/null
+++ b/tempest/tests/image/v1/test_images.py
@@ -0,0 +1,244 @@
+# 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 cStringIO as StringIO
+
+from tempest import clients
+from tempest import exceptions
+import tempest.test
+from tempest.test import attr
+
+
+class CreateRegisterImagesTest(tempest.test.BaseTestCase):
+
+    """
+    Here we test the registration and creation of images
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.client = cls.os.image_client
+        cls.created_images = []
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            cls.client.delete(image_id)
+
+    @attr(type='negative')
+    def test_register_with_invalid_container_format(self):
+        # Negative tests for invalid data supplied to POST /images
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          'test', 'wrong', 'vhd')
+
+    @attr(type='negative')
+    def test_register_with_invalid_disk_format(self):
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          'test', 'bare', 'wrong')
+
+    @attr(type='image')
+    def test_register_then_upload(self):
+        # Register, then upload an image
+        properties = {'prop1': 'val1'}
+        resp, body = self.client.create_image('New Name', 'bare', 'raw',
+                                              is_public=True,
+                                              properties=properties)
+        self.assertTrue('id' in body)
+        image_id = body.get('id')
+        self.created_images.append(image_id)
+        self.assertTrue('name' in body)
+        self.assertEqual('New Name', body.get('name'))
+        self.assertTrue('is_public' in body)
+        self.assertTrue(body.get('is_public'))
+        self.assertTrue('status' in body)
+        self.assertEqual('queued', body.get('status'))
+        self.assertTrue('properties' in body)
+        for key, val in properties.items():
+            self.assertEqual(val, body.get('properties')[key])
+
+        # Now try uploading an image file
+        image_file = StringIO.StringIO(('*' * 1024))
+        resp, body = self.client.update_image(image_id, data=image_file)
+        self.assertTrue('size' in body)
+        self.assertEqual(1024, body.get('size'))
+
+    @attr(type='image')
+    def test_register_remote_image(self):
+        # Register a new remote image
+        resp, body = self.client.create_image('New Remote Image', 'bare',
+                                              'raw', is_public=True,
+                                              location='http://example.com'
+                                                       '/someimage.iso')
+        self.assertTrue('id' in body)
+        image_id = body.get('id')
+        self.created_images.append(image_id)
+        self.assertTrue('name' in body)
+        self.assertEqual('New Remote Image', body.get('name'))
+        self.assertTrue('is_public' in body)
+        self.assertTrue(body.get('is_public'))
+        self.assertTrue('status' in body)
+        self.assertEqual('active', body.get('status'))
+
+
+class ListImagesTest(tempest.test.BaseTestCase):
+
+    """
+    Here we test the listing of image information
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.client = cls.os.image_client
+        cls.created_images = []
+
+        # We add a few images here to test the listing functionality of
+        # the images API
+        img1 = cls._create_remote_image('one', 'bare', 'raw')
+        img2 = cls._create_remote_image('two', 'ami', 'ami')
+        img3 = cls._create_remote_image('dup', 'bare', 'raw')
+        img4 = cls._create_remote_image('dup', 'bare', 'raw')
+        img5 = cls._create_standard_image('1', 'ami', 'ami', 42)
+        img6 = cls._create_standard_image('2', 'ami', 'ami', 142)
+        img7 = cls._create_standard_image('33', 'bare', 'raw', 142)
+        img8 = cls._create_standard_image('33', 'bare', 'raw', 142)
+        cls.created_set = set(cls.created_images)
+        # 4x-4x remote image
+        cls.remote_set = set((img1, img2, img3, img4))
+        cls.standard_set = set((img5, img6, img7, img8))
+        # 5x bare, 3x ami
+        cls.bare_set = set((img1, img3, img4, img7, img8))
+        cls.ami_set = set((img2, img5, img6))
+        # 1x with size 42
+        cls.size42_set = set((img5,))
+        # 3x with size 142
+        cls.size142_set = set((img6, img7, img8))
+        # dup named
+        cls.dup_set = set((img3, img4))
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            cls.client.delete_image(image_id)
+            cls.client.wait_for_resource_deletion(image_id)
+
+    @classmethod
+    def _create_remote_image(cls, name, container_format, disk_format):
+        """
+        Create a new remote image and return the ID of the newly-registered
+        image
+        """
+        name = 'New Remote Image %s' % name
+        location = 'http://example.com/someimage_%s.iso' % name
+        resp, image = cls.client.create_image(name,
+                                              container_format, disk_format,
+                                              is_public=True,
+                                              location=location)
+        image_id = image['id']
+        cls.created_images.append(image_id)
+        return image_id
+
+    @classmethod
+    def _create_standard_image(cls, name, container_format,
+                               disk_format, size):
+        """
+        Create a new standard image and return the ID of the newly-registered
+        image. Note that the size of the new image is a random number between
+        1024 and 4096
+        """
+        image_file = StringIO.StringIO('*' * size)
+        name = 'New Standard Image %s' % name
+        resp, image = cls.client.create_image(name,
+                                              container_format, disk_format,
+                                              is_public=True, data=image_file)
+        image_id = image['id']
+        cls.created_images.append(image_id)
+        return image_id
+
+    @attr(type='image')
+    def test_index_no_params(self):
+        # Simple test to see all fixture images returned
+        resp, images_list = self.client.image_list()
+        self.assertEqual(resp['status'], '200')
+        image_list = map(lambda x: x['id'], images_list)
+        for image_id in self.created_images:
+            self.assertTrue(image_id in image_list)
+
+    @attr(type='image')
+    def test_index_disk_format(self):
+        resp, images_list = self.client.image_list(disk_format='ami')
+        self.assertEqual(resp['status'], '200')
+        for image in images_list:
+            self.assertEqual(image['disk_format'], 'ami')
+        result_set = set(map(lambda x: x['id'], images_list))
+        self.assertTrue(self.ami_set <= result_set)
+        self.assertFalse(self.created_set - self.ami_set <= result_set)
+
+    @attr(type='image')
+    def test_index_container_format(self):
+        resp, images_list = self.client.image_list(container_format='bare')
+        self.assertEqual(resp['status'], '200')
+        for image in images_list:
+            self.assertEqual(image['container_format'], 'bare')
+        result_set = set(map(lambda x: x['id'], images_list))
+        self.assertTrue(self.bare_set <= result_set)
+        self.assertFalse(self.created_set - self.bare_set <= result_set)
+
+    @attr(type='image')
+    def test_index_max_size(self):
+        resp, images_list = self.client.image_list(size_max=42)
+        self.assertEqual(resp['status'], '200')
+        for image in images_list:
+            self.assertTrue(image['size'] <= 42)
+        result_set = set(map(lambda x: x['id'], images_list))
+        self.assertTrue(self.size42_set <= result_set)
+        self.assertFalse(self.created_set - self.size42_set <= result_set)
+
+    @attr(type='image')
+    def test_index_min_size(self):
+        resp, images_list = self.client.image_list(size_min=142)
+        self.assertEqual(resp['status'], '200')
+        for image in images_list:
+            self.assertTrue(image['size'] >= 142)
+        result_set = set(map(lambda x: x['id'], images_list))
+        self.assertTrue(self.size142_set <= result_set)
+        self.assertFalse(self.size42_set <= result_set)
+
+    @attr(type='image')
+    def test_index_status_active_detail(self):
+        resp, images_list = self.client.image_list_detail(status='active',
+                                                          sort_key='size',
+                                                          sort_dir='desc')
+        self.assertEqual(resp['status'], '200')
+        top_size = images_list[0]['size']  # We have non-zero sized images
+        for image in images_list:
+            size = image['size']
+            self.assertTrue(size <= top_size)
+            top_size = size
+            self.assertEqual(image['status'], 'active')
+
+    @attr(type='image')
+    def test_index_name(self):
+        resp, images_list = self.client.image_list_detail(
+                                                name='New Remote Image dup')
+        self.assertEqual(resp['status'], '200')
+        result_set = set(map(lambda x: x['id'], images_list))
+        for image in images_list:
+            self.assertEqual(image['name'], 'New Remote Image dup')
+        self.assertTrue(self.dup_set <= result_set)
+        self.assertFalse(self.created_set - self.dup_set <= result_set)
diff --git a/tempest/services/image/json/__init__.py b/tempest/tests/image/v2/__init__.py
similarity index 100%
copy from tempest/services/image/json/__init__.py
copy to tempest/tests/image/v2/__init__.py
diff --git a/tempest/tests/image/v2/test_images.py b/tempest/tests/image/v2/test_images.py
new file mode 100644
index 0000000..9abf28d
--- /dev/null
+++ b/tempest/tests/image/v2/test_images.py
@@ -0,0 +1,126 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# All Rights Reserved.
+# Copyright 2013 IBM Corp
+#
+#    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 cStringIO as StringIO
+import random
+
+from tempest import clients
+from tempest import exceptions
+import tempest.test
+from tempest.test import attr
+
+
+class CreateRegisterImagesTest(tempest.test.BaseTestCase):
+
+    """
+    Here we test the registration and creation of images
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.client = cls.os.image_client_v2
+        cls.created_images = []
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            cls.client.delete(image_id)
+
+    @attr(type='negative')
+    def test_register_with_invalid_container_format(self):
+        # Negative tests for invalid data supplied to POST /images
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          'test', 'wrong', 'vhd')
+
+    @attr(type='negative')
+    def test_register_with_invalid_disk_format(self):
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          'test', 'bare', 'wrong')
+
+    @attr(type='image')
+    def test_register_then_upload(self):
+        # Register, then upload an image
+        resp, body = self.client.create_image('New Name', 'bare', 'raw',
+                                              is_public=True)
+        self.assertTrue('id' in body)
+        image_id = body.get('id')
+        self.created_images.append(image_id)
+        self.assertTrue('name' in body)
+        self.assertEqual('New Name', body.get('name'))
+        self.assertTrue('visibility' in body)
+        self.assertTrue(body.get('visibility') == 'public')
+        self.assertTrue('status' in body)
+        self.assertEqual('queued', body.get('status'))
+
+        # Now try uploading an image file
+        image_file = StringIO.StringIO(('*' * 1024))
+        resp, body = self.client.store_image(image_id, image_file)
+        self.assertEqual(resp.status, 204)
+        resp, body = self.client.get_image_metadata(image_id)
+        self.assertTrue('size' in body)
+        self.assertEqual(1024, body.get('size'))
+
+
+class ListImagesTest(tempest.test.BaseTestCase):
+
+    """
+    Here we test the listing of image information
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.os = clients.Manager()
+        cls.client = cls.os.image_client_v2
+        cls.created_images = []
+
+        # We add a few images here to test the listing functionality of
+        # the images API
+        for x in xrange(0, 10):
+            cls.created_images.append(cls._create_standard_image(x))
+
+    @classmethod
+    def tearDownClass(cls):
+        for image_id in cls.created_images:
+            cls.client.delete_image(image_id)
+            cls.client.wait_for_resource_deletion(image_id)
+
+    @classmethod
+    def _create_standard_image(cls, number):
+        """
+        Create a new standard image and return the ID of the newly-registered
+        image. Note that the size of the new image is a random number between
+        1024 and 4096
+        """
+        image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
+        name = 'New Standard Image %s' % number
+        resp, body = cls.client.create_image(name, 'bare', 'raw',
+                                             is_public=True)
+        image_id = body['id']
+        resp, body = cls.client.store_image(image_id, data=image_file)
+
+        return image_id
+
+    @attr(type='image')
+    def test_index_no_params(self):
+        # Simple test to see all fixture images returned
+        resp, images_list = self.client.image_list()
+        self.assertEqual(resp['status'], '200')
+        image_list = map(lambda x: x['id'], images_list)
+        for image in self.created_images:
+            self.assertTrue(image in image_list)
diff --git a/tempest/tests/network/base.py b/tempest/tests/network/base.py
index 4cc8b29..1b09513 100644
--- a/tempest/tests/network/base.py
+++ b/tempest/tests/network/base.py
@@ -18,7 +18,6 @@
 
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
 import tempest.test
 
 
@@ -27,15 +26,9 @@
     @classmethod
     def setUpClass(cls):
         os = clients.Manager()
-        client = os.network_client
 
-        # Validate that there is even an endpoint configured
-        # for networks, and mark the attr for skipping if not
-        try:
-            client.list_networks()
-        except exceptions.EndpointNotFound:
-            skip_msg = "No OpenStack Network API endpoint"
-            raise cls.skipException(skip_msg)
+        if not os.config.network.quantum_available:
+            raise cls.skipException("Quantum support is required")
 
     @classmethod
     def tearDownClass(cls):
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
index 8f91251..fd9076f 100644
--- a/tools/install_venv_common.py
+++ b/tools/install_venv_common.py
@@ -21,20 +21,12 @@
 Synced in from openstack-common
 """
 
+import argparse
 import os
 import subprocess
 import sys
 
 
-possible_topdir = os.getcwd()
-if os.path.exists(os.path.join(possible_topdir, "tempest",
-                               "__init__.py")):
-    sys.path.insert(0, possible_topdir)
-
-
-from tempest.openstack.common import cfg
-
-
 class InstallVenv(object):
 
     def __init__(self, root, venv, pip_requires, test_requires, py_version,
@@ -139,17 +131,12 @@
 
     def parse_args(self, argv):
         """Parses command-line arguments."""
-        cli_opts = [
-            cfg.BoolOpt('no-site-packages',
-                        default=False,
-                        short='n',
-                        help="Do not inherit packages from global Python"
-                             "install"),
-        ]
-        CLI = cfg.ConfigOpts()
-        CLI.register_cli_opts(cli_opts)
-        CLI(argv[1:])
-        return CLI
+        parser = argparse.ArgumentParser()
+        parser.add_argument('-n', '--no-site-packages',
+                            action='store_true',
+                            help="Do not inherit packages from global Python "
+                                 "install")
+        return parser.parse_args(argv[1:])
 
 
 class Distro(InstallVenv):
diff --git a/tools/pip-requires b/tools/pip-requires
index 5c45a49..ee21065 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -1,7 +1,7 @@
 anyjson
 nose
 httplib2>=0.7.0
-testtools
+testtools>=0.9.29
 lxml
 boto>=2.2.1
 paramiko
@@ -13,3 +13,5 @@
 testresources
 keyring
 testrepository
+
+http://tarballs.openstack.org/oslo-config/oslo.config-1.1.0b1.tar.gz#egg=oslo.config
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
index 267eafa..a46d0fb 100755
--- a/tools/tempest_coverage.py
+++ b/tools/tempest_coverage.py
@@ -20,9 +20,10 @@
 import shutil
 import sys
 
+from oslo.config import cfg
+
 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()
diff --git a/tox.ini b/tox.ini
index 1b18586..92ce6bc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,5 +19,4 @@
            python -m tools/tempest_coverage -c report --html
 
 [testenv:pep8]
-deps = pep8==1.3.3
 commands = python tools/hacking.py --ignore=E122,E125,E126 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack,*egg .