Merge "Add a version API test for Nova v3 API"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f306d8e..bb9a68e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -95,6 +95,17 @@
 #syslog_log_facility=LOG_USER
 
 
+[baremetal]
+
+#
+# Options defined in tempest.config
+#
+
+# Catalog type of the baremetal provisioning service. (string
+# value)
+#catalog_type=baremetal
+
+
 [boto]
 
 #
@@ -416,7 +427,7 @@
 # (string value)
 #alt_password=<None>
 
-# Administrative Username to use forKeystone API requests.
+# Administrative Username to use for Keystone API requests.
 # (string value)
 #admin_username=admin
 
@@ -428,6 +439,17 @@
 #admin_password=pass
 
 
+[identity-feature-enabled]
+
+#
+# Options defined in tempest.config
+#
+
+# Does the identity service have delegation and impersonation
+# enabled (boolean value)
+#trust=true
+
+
 [image]
 
 #
@@ -460,6 +482,29 @@
 #api_v1=true
 
 
+[input-scenario]
+
+#
+# Options defined in tempest.config
+#
+
+# Matching images become parameters for scenario tests (string
+# value)
+#image_regex=^cirros-0.3.1-x86_64-uec$
+
+# Matching flavors become parameters for scenario tests
+# (string value)
+#flavor_regex=^m1.(micro|nano|tiny)$
+
+# SSH verification in tests is skippedfor matching images
+# (string value)
+#non_ssh_image_regex=^.*[Ww]in.*$
+
+# List of user mapped to regex to matching image names.
+# (string value)
+#ssh_user_regex=[["^.*[Cc]irros.*$", "root"]]
+
+
 [network]
 
 #
@@ -529,11 +574,11 @@
 # (string value)
 #region=
 
-# Number of seconds to time on waiting for a containerto
+# Number of seconds to time on waiting for a container to
 # container synchronization complete. (integer value)
 #container_sync_timeout=120
 
-# Number of seconds to wait while looping to check thestatus
+# Number of seconds to wait while looping to check the status
 # of a container to container synchronization (integer value)
 #container_sync_interval=5
 
@@ -548,21 +593,10 @@
 # Options defined in tempest.config
 #
 
-# Set to True if the Container Quota middleware is enabled
-# (boolean value)
-#container_quotas=true
-
-# Set to True if the Account Quota middleware is enabled
-# (boolean value)
-#accounts_quotas=true
-
-# Set to True if the Crossdomain middleware is enabled
-# (boolean value)
-#crossdomain=true
-
-# Set to True if the TempURL middleware is enabled (boolean
-# value)
-#tempurl=true
+# A list of the enabled optional discoverable apis. A single
+# entry, all, indicates that all of these features are
+# expected to be enabled (list value)
+#discoverable_apis=all
 
 
 [orchestration]
@@ -672,6 +706,10 @@
 # value)
 #savanna=false
 
+# Whether or not Ironic is expected to be available (boolean
+# value)
+#ironic=false
+
 
 [stress]
 
diff --git a/etc/whitelist.yaml b/etc/whitelist.yaml
index a8c5276..1c12b6c 100644
--- a/etc/whitelist.yaml
+++ b/etc/whitelist.yaml
@@ -39,6 +39,8 @@
       message: "Instance failed network setup after 1 attempt"
     - module: "nova.compute.manager"
       message: "Periodic sync_power_state task had an error"
+    - module: "nova.virt.driver"
+      message: "Info cache for instance .* could not be found"
 
 g-api:
     - module: "glance.store.sheepdog"
@@ -73,6 +75,10 @@
     - module: "ceilometer.compute.pollsters.disk"
       message: ".*"
 
+ceilometer-acentral:
+    - module: "ceilometer.central.manager"
+      message: "403 Forbidden"
+
 ceilometer-alarm-evaluator:
     - module: "ceilometer.alarm.service"
       message: "alarm evaluation cycle failed"
diff --git a/requirements.txt b/requirements.txt
index e070549..3b3e1fa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,6 +14,7 @@
 python-neutronclient>=2.3.0,<3
 python-cinderclient>=1.0.6
 python-heatclient>=0.2.3
+python-swiftclient>=1.5
 testresources>=0.2.4
 keyring>=1.6.1,<2.0
 testrepository>=0.0.17
@@ -21,3 +22,4 @@
 six>=1.4.1
 iso8601>=0.1.8
 fixtures>=0.3.14
+testscenarios>=0.4
diff --git a/run_tempest.sh b/run_tempest.sh
new file mode 100755
index 0000000..be9b38a
--- /dev/null
+++ b/run_tempest.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+
+function usage {
+  echo "Usage: $0 [OPTION]..."
+  echo "Run Tempest test suite"
+  echo ""
+  echo "  -V, --virtual-env        Always use virtualenv.  Install automatically if not present"
+  echo "  -N, --no-virtual-env     Don't use virtualenv.  Run tests in local environment"
+  echo "  -n, --no-site-packages   Isolate the virtualenv from the global Python environment"
+  echo "  -f, --force              Force a clean re-build of the virtual environment. Useful when dependencies have been added."
+  echo "  -u, --update             Update the virtual environment with any newer package versions"
+  echo "  -s, --smoke              Only run smoke tests"
+  echo "  -t, --serial             Run testr serially"
+  echo "  -C, --config             Config file location"
+  echo "  -h, --help               Print this usage message"
+  echo "  -d, --debug              Debug this script -- set -o xtrace"
+  echo "  -l, --logging            Enable logging"
+  echo "  -L, --logging-config     Logging config file location.  Default is etc/logging.conf"
+  echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
+}
+
+testrargs=""
+venv=.venv
+with_venv=tools/with_venv.sh
+serial=0
+always_venv=0
+never_venv=0
+no_site_packages=0
+force=0
+wrapper=""
+config_file=""
+update=0
+logging=0
+logging_config=etc/logging.conf
+
+if ! options=$(getopt -o VNnfusthdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,serial,help,debug,config:,logging,logging-config: -- "$@")
+then
+    # parse error
+    usage
+    exit 1
+fi
+
+eval set -- $options
+first_uu=yes
+while [ $# -gt 0 ]; do
+  case "$1" in
+    -h|--help) usage; exit;;
+    -V|--virtual-env) always_venv=1; never_venv=0;;
+    -N|--no-virtual-env) always_venv=0; never_venv=1;;
+    -n|--no-site-packages) no_site_packages=1;;
+    -f|--force) force=1;;
+    -u|--update) update=1;;
+    -d|--debug) set -o xtrace;;
+    -C|--config) config_file=$2; shift;;
+    -s|--smoke) testrargs+="smoke"; noseargs+="--attr=type=smoke";;
+    -t|--serial) serial=1;;
+    -l|--logging) logging=1;;
+    -L|--logging-config) logging_config=$2; shift;;
+    --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
+    *) testrargs="$testrargs $1"; noseargs+=" $1" ;;
+  esac
+  shift
+done
+
+if [ -n "$config_file" ]; then
+    config_file=`readlink -f "$config_file"`
+    export TEMPEST_CONFIG_DIR=`dirname "$config_file"`
+    export TEMPEST_CONFIG=`basename "$config_file"`
+fi
+
+if [ $logging -eq 1 ]; then
+    if [ ! -f "$logging_config" ]; then
+        echo "No such logging config file: $logging_config"
+        exit 1
+    fi
+    logging_config=`readlink -f "$logging_config"`
+    export TEMPEST_LOG_CONFIG_DIR=`dirname "$logging_config"`
+    export TEMPEST_LOG_CONFIG=`basename "$logging_config"`
+fi
+
+cd `dirname "$0"`
+
+if [ $no_site_packages -eq 1 ]; then
+  installvenvopts="--no-site-packages"
+fi
+
+function testr_init {
+  if [ ! -d .testrepository ]; then
+      ${wrapper} testr init
+  fi
+}
+
+function run_tests {
+  testr_init
+  ${wrapper} find . -type f -name "*.pyc" -delete
+  export OS_TEST_PATH=./tempest/test_discover
+  if [ $serial -eq 1 ]; then
+      ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
+  else
+      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
+  fi
+}
+
+function run_tests_nose {
+    export NOSE_WITH_OPENSTACK=1
+    export NOSE_OPENSTACK_COLOR=1
+    export NOSE_OPENSTACK_RED=15.00
+    export NOSE_OPENSTACK_YELLOW=3.00
+    export NOSE_OPENSTACK_SHOW_ELAPSED=1
+    export NOSE_OPENSTACK_STDOUT=1
+    export TEMPEST_PY26_NOSE_COMPAT=1
+    if [[ "x$noseargs" =~ "tempest" ]]; then
+        noseargs="$testrargs"
+    else
+        noseargs="$noseargs tempest"
+    fi
+    ${wrapper} nosetests $noseargs
+}
+
+if [ $never_venv -eq 0 ]
+then
+  # Remove the virtual environment if --force used
+  if [ $force -eq 1 ]; then
+    echo "Cleaning virtualenv..."
+    rm -rf ${venv}
+  fi
+  if [ $update -eq 1 ]; then
+      echo "Updating virtualenv..."
+      python tools/install_venv.py $installvenvopts
+  fi
+  if [ -e ${venv} ]; then
+    wrapper="${with_venv}"
+  else
+    if [ $always_venv -eq 1 ]; then
+      # Automatically install the virtualenv
+      python tools/install_venv.py $installvenvopts
+      wrapper="${with_venv}"
+    else
+      echo -e "No virtual environment found...create one? (Y/n) \c"
+      read use_ve
+      if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
+        # Install the virtualenv and run the test suite in it
+        python tools/install_venv.py $installvenvopts
+        wrapper=${with_venv}
+      fi
+    fi
+  fi
+fi
+
+py_version=`${wrapper} python --version 2>&1`
+if [[ $py_version =~ "2.6" ]] ; then
+    run_tests_nose
+else
+    run_tests
+fi
+retval=$?
+
+exit $retval
diff --git a/run_tests.sh b/run_tests.sh
index 3c9c051..3f00453 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -2,21 +2,18 @@
 
 function usage {
   echo "Usage: $0 [OPTION]..."
-  echo "Run Tempest test suite"
+  echo "Run Tempest unit tests"
   echo ""
   echo "  -V, --virtual-env        Always use virtualenv.  Install automatically if not present"
   echo "  -N, --no-virtual-env     Don't use virtualenv.  Run tests in local environment"
   echo "  -n, --no-site-packages   Isolate the virtualenv from the global Python environment"
   echo "  -f, --force              Force a clean re-build of the virtual environment. Useful when dependencies have been added."
   echo "  -u, --update             Update the virtual environment with any newer package versions"
-  echo "  -s, --smoke              Only run smoke tests"
   echo "  -t, --serial             Run testr serially"
-  echo "  -C, --config             Config file location"
   echo "  -p, --pep8               Just run pep8"
+  echo "  -c, --coverage           Generate coverage report"
   echo "  -h, --help               Print this usage message"
   echo "  -d, --debug              Debug this script -- set -o xtrace"
-  echo "  -l, --logging            Enable logging"
-  echo "  -L, --logging-config     Logging config file location.  Default is etc/logging.conf"
   echo "  -- [TESTROPTIONS]        After the first '--' you can pass arbitrary arguments to testr "
 }
 
@@ -29,13 +26,12 @@
 never_venv=0
 no_site_packages=0
 force=0
+coverage=0
 wrapper=""
 config_file=""
 update=0
-logging=0
-logging_config=etc/logging.conf
 
-if ! options=$(getopt -o VNnfustphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,serial,pep8,help,debug,config:,logging,logging-config: -- "$@")
+if ! options=$(getopt -o VNnfuctphd -l virtual-env,no-virtual-env,no-site-packages,force,update,serial,coverage,pep8,help,debug -- "$@")
 then
     # parse error
     usage
@@ -53,33 +49,15 @@
     -f|--force) force=1;;
     -u|--update) update=1;;
     -d|--debug) set -o xtrace;;
-    -C|--config) config_file=$2; shift;;
     -p|--pep8) let just_pep8=1;;
-    -s|--smoke) testrargs+="smoke"; noseargs+="--attr=type=smoke";;
+    -c|--coverage) coverage=1;;
     -t|--serial) serial=1;;
-    -l|--logging) logging=1;;
-    -L|--logging-config) logging_config=$2; shift;;
     --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
     *) testrargs="$testrargs $1"; noseargs+=" $1" ;;
   esac
   shift
 done
 
-if [ -n "$config_file" ]; then
-    config_file=`readlink -f "$config_file"`
-    export TEMPEST_CONFIG_DIR=`dirname "$config_file"`
-    export TEMPEST_CONFIG=`basename "$config_file"`
-fi
-
-if [ $logging -eq 1 ]; then
-    if [ ! -f "$logging_config" ]; then
-        echo "No such logging config file: $logging_config"
-        exit 1
-    fi
-    logging_config=`readlink -f "$logging_config"`
-    export TEMPEST_LOG_CONFIG_DIR=`dirname "$logging_config"`
-    export TEMPEST_LOG_CONFIG=`basename "$logging_config"`
-fi
 
 cd `dirname "$0"`
 
@@ -96,6 +74,7 @@
 function run_tests {
   testr_init
   ${wrapper} find . -type f -name "*.pyc" -delete
+  export OS_TEST_PATH=./tempest/tests
   if [ $serial -eq 1 ]; then
       ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
   else
@@ -103,22 +82,6 @@
   fi
 }
 
-function run_tests_nose {
-    export NOSE_WITH_OPENSTACK=1
-    export NOSE_OPENSTACK_COLOR=1
-    export NOSE_OPENSTACK_RED=15.00
-    export NOSE_OPENSTACK_YELLOW=3.00
-    export NOSE_OPENSTACK_SHOW_ELAPSED=1
-    export NOSE_OPENSTACK_STDOUT=1
-    export TEMPEST_PY26_NOSE_COMPAT=1
-    if [[ "x$noseargs" =~ "tempest" ]]; then
-        noseargs="$testrargs"
-    else
-        noseargs="$noseargs tempest"
-    fi
-    ${wrapper} nosetests $noseargs
-}
-
 function run_pep8 {
   echo "Running flake8 ..."
   if [ $never_venv -eq 1 ]; then
@@ -163,12 +126,11 @@
     exit
 fi
 
-py_version=`${wrapper} python --version 2>&1`
-if [[ $py_version =~ "2.6" ]] ; then
-    run_tests_nose
-else
-    run_tests
+if [ $coverage -eq 1 ]; then
+    $testrargs = "--coverage $testrargs"
 fi
+
+run_tests
 retval=$?
 
 if [ -z "$testrargs" ]; then
diff --git a/tempest/api/__init__.py b/tempest/api/__init__.py
index 0b3d2db..e69de29 100644
--- a/tempest/api/__init__.py
+++ b/tempest/api/__init__.py
@@ -1,16 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack Foundation
-# 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.
diff --git a/tempest/api/baremetal/__init__.py b/tempest/api/baremetal/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/baremetal/__init__.py
diff --git a/tempest/api/baremetal/base.py b/tempest/api/baremetal/base.py
new file mode 100644
index 0000000..3aad1b5
--- /dev/null
+++ b/tempest/api/baremetal/base.py
@@ -0,0 +1,171 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 functools
+
+from tempest import clients
+from tempest.common.utils import data_utils
+from tempest import exceptions as exc
+from tempest import test
+
+
+def creates(resource):
+    """Decorator that adds resources to the appropriate cleanup list."""
+
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(cls, *args, **kwargs):
+            result = f(cls, *args, **kwargs)
+            body = result[resource]
+
+            if 'uuid' in body:
+                cls.created_objects[resource].add(body['uuid'])
+
+            return result
+        return wrapper
+    return decorator
+
+
+class BaseBaremetalTest(test.BaseTestCase):
+    """Base class for Baremetal API tests."""
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseBaremetalTest, cls).setUpClass()
+
+        if not cls.config.service_available.ironic:
+            skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+        mgr = clients.AdminManager()
+        cls.client = mgr.baremetal_client
+
+        cls.created_objects = {'chassis': set(),
+                               'port': set(),
+                               'node': set()}
+
+    @classmethod
+    def tearDownClass(cls):
+        """Ensure that all created objects get destroyed."""
+
+        try:
+            for resource, uuids in cls.created_objects.iteritems():
+                delete_method = getattr(cls.client, 'delete_%s' % resource)
+                for u in uuids:
+                    delete_method(u, ignore_errors=exc.NotFound)
+        finally:
+            super(BaseBaremetalTest, cls).tearDownClass()
+
+    @classmethod
+    @creates('chassis')
+    def create_chassis(cls, description=None, expect_errors=False):
+        """
+        Wrapper utility for creating test chassis.
+
+        :param description: A description of the chassis. if not supplied,
+            a random value will be generated.
+        :return: Created chassis.
+
+        """
+        description = description or data_utils.rand_name('test-chassis-')
+        resp, body = cls.client.create_chassis(description=description)
+
+        return {'chassis': body, 'response': resp}
+
+    @classmethod
+    @creates('node')
+    def create_node(cls, chassis_id, cpu_arch='x86', cpu_num=8, storage=1024,
+                    memory=4096, driver='fake'):
+        """
+        Wrapper utility for creating test baremetal nodes.
+
+        :param cpu_arch: CPU architecture of the node. Default: x86.
+        :param cpu_num: Number of CPUs. Default: 8.
+        :param storage: Disk size. Default: 1024.
+        :param memory: Available RAM. Default: 4096.
+        :return: Created node.
+
+        """
+        resp, body = cls.client.create_node(chassis_id, cpu_arch=cpu_arch,
+                                            cpu_num=cpu_num, storage=storage,
+                                            memory=memory, driver=driver)
+
+        return {'node': body, 'response': resp}
+
+    @classmethod
+    @creates('port')
+    def create_port(cls, node_id, address=None):
+        """
+        Wrapper utility for creating test ports.
+
+        :param address: MAC address of the port. If not supplied, a random
+            value will be generated.
+        :return: Created port.
+
+        """
+        address = address or data_utils.rand_mac_address()
+        resp, body = cls.client.create_port(address=address, node_id=node_id)
+
+        return {'port': body, 'response': resp}
+
+    @classmethod
+    def delete_chassis(cls, chassis_id):
+        """
+        Deletes a chassis having the specified UUID.
+
+        :param uuid: The unique identifier of the chassis.
+        :return: Server response.
+
+        """
+
+        resp, body = cls.client.delete_chassis(chassis_id)
+
+        if chassis_id in cls.created_objects['chassis']:
+            cls.created_objects['chassis'].remove(chassis_id)
+
+        return resp
+
+    @classmethod
+    def delete_node(cls, node_id):
+        """
+        Deletes a node having the specified UUID.
+
+        :param uuid: The unique identifier of the node.
+        :return: Server response.
+
+        """
+
+        resp, body = cls.client.delete_node(node_id)
+
+        if node_id in cls.created_objects['node']:
+            cls.created_objects['node'].remove(node_id)
+
+        return resp
+
+    @classmethod
+    def delete_port(cls, port_id):
+        """
+        Deletes a port having the specified UUID.
+
+        :param uuid: The unique identifier of the port.
+        :return: Server response.
+
+        """
+
+        resp, body = cls.client.delete_port(port_id)
+
+        if port_id in cls.created_objects['port']:
+            cls.created_objects['port'].remove(port_id)
+
+        return resp
diff --git a/tempest/api/baremetal/test_api_discovery.py b/tempest/api/baremetal/test_api_discovery.py
new file mode 100644
index 0000000..32f3d50
--- /dev/null
+++ b/tempest/api/baremetal/test_api_discovery.py
@@ -0,0 +1,46 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.api.baremetal import base
+from tempest import test
+
+
+class TestApiDiscovery(base.BaseBaremetalTest):
+    """Tests for API discovery features."""
+
+    @test.attr(type='smoke')
+    def test_api_versions(self):
+        resp, descr = self.client.get_api_description()
+        expected_versions = ('v1',)
+
+        versions = [version['id'] for version in descr['versions']]
+
+        for v in expected_versions:
+            self.assertIn(v, versions)
+
+    @test.attr(type='smoke')
+    def test_default_version(self):
+        resp, descr = self.client.get_api_description()
+        default_version = descr['default_version']
+
+        self.assertEqual(default_version['id'], 'v1')
+
+    @test.attr(type='smoke')
+    def test_version_1_resources(self):
+        resp, descr = self.client.get_version_description(version='v1')
+        expected_resources = ('nodes', 'chassis',
+                              'ports', 'links', 'media_types')
+
+        for res in expected_resources:
+            self.assertIn(res, descr)
diff --git a/tempest/api/baremetal/test_chassis.py b/tempest/api/baremetal/test_chassis.py
new file mode 100644
index 0000000..35a93ca
--- /dev/null
+++ b/tempest/api/baremetal/test_chassis.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.api.baremetal import base
+from tempest.common.utils import data_utils
+from tempest import exceptions as exc
+from tempest import test
+
+
+class TestChassis(base.BaseBaremetalTest):
+    """Tests for chassis."""
+
+    @test.attr(type='smoke')
+    def test_create_chassis(self):
+        descr = data_utils.rand_name('test-chassis-')
+        ch = self.create_chassis(description=descr)['chassis']
+
+        self.assertEqual(ch['description'], descr)
+
+    @test.attr(type='smoke')
+    def test_create_chassis_unicode_description(self):
+        # Use a unicode string for testing:
+        # 'We ♡ OpenStack in Ukraine'
+        descr = u'В Україні ♡ OpenStack!'
+        ch = self.create_chassis(description=descr)['chassis']
+
+        self.assertEqual(ch['description'], descr)
+
+    @test.attr(type='smoke')
+    def test_show_chassis(self):
+        descr = data_utils.rand_name('test-chassis-')
+        uuid = self.create_chassis(description=descr)['chassis']['uuid']
+
+        resp, chassis = self.client.show_chassis(uuid)
+
+        self.assertEqual(chassis['uuid'], uuid)
+        self.assertEqual(chassis['description'], descr)
+
+    @test.attr(type="smoke")
+    def test_list_chassis(self):
+        created_ids = [self.create_chassis()['chassis']['uuid']
+                       for i in range(0, 5)]
+
+        resp, body = self.client.list_chassis()
+        loaded_ids = [ch['uuid'] for ch in body['chassis']]
+
+        for i in created_ids:
+            self.assertIn(i, loaded_ids)
+
+    @test.attr(type='smoke')
+    def test_delete_chassis(self):
+        uuid = self.create_chassis()['chassis']['uuid']
+
+        self.delete_chassis(uuid)
+
+        self.assertRaises(exc.NotFound, self.client.show_chassis, uuid)
+
+    @test.attr(type='smoke')
+    def test_update_chassis(self):
+        chassis_id = self.create_chassis()['chassis']['uuid']
+
+        new_description = data_utils.rand_name('new-description-')
+        self.client.update_chassis(chassis_id, description=new_description)
+
+        resp, chassis = self.client.show_chassis(chassis_id)
+        self.assertEqual(chassis['description'], new_description)
diff --git a/tempest/api/baremetal/test_nodes.py b/tempest/api/baremetal/test_nodes.py
new file mode 100644
index 0000000..f9b65ed
--- /dev/null
+++ b/tempest/api/baremetal/test_nodes.py
@@ -0,0 +1,97 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 six
+
+from tempest.api.baremetal import base
+from tempest import exceptions as exc
+from tempest import test
+
+
+class TestNodes(base.BaseBaremetalTest):
+    '''Tests for baremetal nodes.'''
+
+    def setUp(self):
+        super(TestNodes, self).setUp()
+
+        self.chassis = self.create_chassis()['chassis']
+
+    @test.attr(type='smoke')
+    def test_create_node(self):
+        params = {'cpu_arch': 'x86_64',
+                  'cpu_num': '12',
+                  'storage': '10240',
+                  'memory': '1024'}
+
+        node = self.create_node(self.chassis['uuid'], **params)['node']
+
+        for key in params:
+            self.assertEqual(node['properties'][key], params[key])
+
+    @test.attr(type='smoke')
+    def test_delete_node(self):
+        node = self.create_node(self.chassis['uuid'])['node']
+        node_id = node['uuid']
+
+        resp = self.delete_node(node_id)
+
+        self.assertEqual(resp['status'], '204')
+        self.assertRaises(exc.NotFound, self.client.show_node, node_id)
+
+    @test.attr(type='smoke')
+    def test_show_node(self):
+        params = {'cpu_arch': 'x86_64',
+                  'cpu_num': '4',
+                  'storage': '100',
+                  'memory': '512'}
+
+        created_node = self.create_node(self.chassis['uuid'], **params)['node']
+        resp, loaded_node = self.client.show_node(created_node['uuid'])
+
+        for key, val in created_node.iteritems():
+            if key not in ('created_at', 'updated_at'):
+                self.assertEqual(loaded_node[key], val)
+
+    @test.attr(type='smoke')
+    def test_list_nodes(self):
+        uuids = [self.create_node(self.chassis['uuid'])['node']['uuid']
+                 for i in range(0, 5)]
+
+        resp, body = self.client.list_nodes()
+        loaded_uuids = [n['uuid'] for n in body['nodes']]
+
+        for u in uuids:
+            self.assertIn(u, loaded_uuids)
+
+    @test.attr(type='smoke')
+    def test_update_node(self):
+        props = {'cpu_arch': 'x86_64',
+                 'cpu_num': '12',
+                 'storage': '10',
+                 'memory': '128'}
+
+        node = self.create_node(self.chassis['uuid'], **props)['node']
+        node_id = node['uuid']
+
+        new_props = {'cpu_arch': 'x86',
+                     'cpu_num': '1',
+                     'storage': '10000',
+                     'memory': '12300'}
+
+        self.client.update_node(node_id, properties=new_props)
+        resp, node = self.client.show_node(node_id)
+
+        for name, value in six.iteritems(new_props):
+            if name not in ('created_at', 'updated_at'):
+                self.assertEqual(node['properties'][name], value)
diff --git a/tempest/api/baremetal/test_ports.py b/tempest/api/baremetal/test_ports.py
new file mode 100644
index 0000000..8249705
--- /dev/null
+++ b/tempest/api/baremetal/test_ports.py
@@ -0,0 +1,85 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.api.baremetal import base
+from tempest.common.utils import data_utils
+from tempest import exceptions as exc
+from tempest import test
+
+
+class TestPorts(base.BaseBaremetalTest):
+    """Tests for ports."""
+
+    def setUp(self):
+        super(TestPorts, self).setUp()
+
+        chassis = self.create_chassis()['chassis']
+        self.node = self.create_node(chassis['uuid'])['node']
+
+    @test.attr(type='smoke')
+    def test_create_port(self):
+        node_id = self.node['uuid']
+        address = data_utils.rand_mac_address()
+
+        port = self.create_port(node_id=node_id, address=address)['port']
+
+        self.assertEqual(port['address'], address)
+        self.assertEqual(port['node_uuid'], node_id)
+
+    @test.attr(type='smoke')
+    def test_delete_port(self):
+        node_id = self.node['uuid']
+        port_id = self.create_port(node_id=node_id)['port']['uuid']
+
+        resp = self.delete_port(port_id)
+
+        self.assertEqual(resp['status'], '204')
+        self.assertRaises(exc.NotFound, self.client.show_port, port_id)
+
+    @test.attr(type='smoke')
+    def test_show_port(self):
+        node_id = self.node['uuid']
+        address = data_utils.rand_mac_address()
+
+        port_id = self.create_port(node_id=node_id,
+                                   address=address)['port']['uuid']
+
+        resp, port = self.client.show_port(port_id)
+
+        self.assertEqual(port['uuid'], port_id)
+        self.assertEqual(port['address'], address)
+
+    @test.attr(type='smoke')
+    def test_list_ports(self):
+        node_id = self.node['uuid']
+
+        uuids = [self.create_port(node_id=node_id)['port']['uuid']
+                 for i in range(0, 5)]
+
+        resp, body = self.client.list_ports()
+        loaded_uuids = [p['uuid'] for p in body['ports']]
+
+        for u in uuids:
+            self.assertIn(u, loaded_uuids)
+
+    @test.attr(type='smoke')
+    def test_update_port(self):
+        node_id = self.node['uuid']
+        port_id = self.create_port(node_id=node_id)['port']['uuid']
+
+        new_address = data_utils.rand_mac_address()
+        self.client.update_port(port_id, address=new_address)
+
+        resp, body = self.client.show_port(port_id)
+        self.assertEqual(body['address'], new_address)
diff --git a/tempest/api/baremetal/test_ports_negative.py b/tempest/api/baremetal/test_ports_negative.py
new file mode 100644
index 0000000..423313cb
--- /dev/null
+++ b/tempest/api/baremetal/test_ports_negative.py
@@ -0,0 +1,42 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.api.baremetal import base
+from tempest.common.utils import data_utils
+from tempest import exceptions as exc
+from tempest import test
+
+
+class TestPortsNegative(base.BaseBaremetalTest):
+    """Negative tests for ports."""
+
+    def setUp(self):
+        super(TestPortsNegative, self).setUp()
+
+        chassis = self.create_chassis()['chassis']
+        self.node = self.create_node(chassis['uuid'])['node']
+
+    @test.attr(type='negative')
+    def test_create_port_invalid_mac(self):
+        node_id = self.node['uuid']
+        address = 'not an uuid'
+
+        self.assertRaises(exc.BadRequest,
+                          self.create_port, node_id=node_id, address=address)
+
+    @test.attr(type='negative')
+    def test_create_port_wrong_node_id(self):
+        node_id = str(data_utils.rand_uuid())
+
+        self.assertRaises(exc.BadRequest, self.create_port, node_id=node_id)
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index dbf7967..a1afe0e 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -66,7 +66,9 @@
 
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.update_host,
-                          hostname)
+                          hostname,
+                          status='enable',
+                          maintenance_mode='enable')
 
     @test.skip_because(bug="1261964", interface="xml")
     @test.attr(type=['negative', 'gate'])
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 6fe3186..2160949 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -44,6 +44,7 @@
         cls.s2_name = data_utils.rand_name('server')
         resp, server = cls.create_test_server(name=cls.s2_name,
                                               wait_until='ACTIVE')
+        cls.s2_id = server['id']
 
     def _get_unused_flavor_id(self):
         flavor_id = data_utils.rand_int_id(start=1000)
@@ -64,6 +65,22 @@
         self.assertEqual([], servers)
 
     @attr(type='gate')
+    def test_list_servers_filter_by_error_status(self):
+        # Filter the list of servers by server error status
+        params = {'status': 'error'}
+        resp, server = self.client.reset_state(self.s1_id, state='error')
+        resp, body = self.non_admin_client.list_servers(params)
+        # Reset server's state to 'active'
+        resp, server = self.client.reset_state(self.s1_id, state='active')
+        # Verify server's state
+        resp, server = self.client.get_server(self.s1_id)
+        self.assertEqual(server['status'], 'ACTIVE')
+        servers = body['servers']
+        # Verify error server in list result
+        self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
+
+    @attr(type='gate')
     def test_list_servers_by_admin_with_all_tenants(self):
         # Listing servers by admin user with all tenants parameter
         # Here should be listed all servers
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 6ef43bc..f628053 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -130,12 +130,22 @@
             r, b = cls.servers_client.list_servers()
             servers = [s for s in b['servers'] if s['name'].startswith(name)]
 
-        cls.servers.extend(servers)
-
         if 'wait_until' in kwargs:
             for server in servers:
-                cls.servers_client.wait_for_server_status(
-                    server['id'], kwargs['wait_until'])
+                try:
+                    cls.servers_client.wait_for_server_status(
+                        server['id'], kwargs['wait_until'])
+                except Exception as ex:
+                    if ('preserve_server_on_error' not in kwargs
+                        or kwargs['preserve_server_on_error'] is False):
+                        for server in servers:
+                            try:
+                                cls.servers_client.delete_server(server['id'])
+                            except Exception:
+                                pass
+                    raise ex
+
+        cls.servers.extend(servers)
 
         return resp, body
 
@@ -159,6 +169,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # By default compute tests do not create network resources
+        cls.set_network_resources()
         super(BaseV2ComputeTest, cls).setUpClass()
         cls.servers_client = cls.os.servers_client
         cls.flavors_client = cls.os.flavors_client
@@ -250,6 +262,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # By default compute tests do not create network resources
+        cls.set_network_resources()
         super(BaseV3ComputeTest, cls).setUpClass()
         if not cls.config.compute_feature_enabled.api_v3:
             cls.tearDownClass()
@@ -264,6 +278,8 @@
         cls.extensions_client = cls.os.extensions_v3_client
         cls.availability_zone_client = cls.os.availability_zone_v3_client
         cls.interfaces_client = cls.os.interfaces_v3_client
+        cls.instance_usages_audit_log_client = \
+            cls.os.instance_usages_audit_log_v3_client
         cls.hypervisor_client = cls.os.hypervisor_v3_client
         cls.keypairs_client = cls.os.keypairs_v3_client
         cls.tenant_usages_client = cls.os.tenant_usages_v3_client
@@ -333,6 +349,8 @@
 
         cls.os_adm = os_adm
         cls.servers_admin_client = cls.os_adm.servers_v3_client
+        cls.instance_usages_audit_log_admin_client = \
+            cls.os_adm.instance_usages_audit_log_v3_client
         cls.services_admin_client = cls.os_adm.services_v3_client
         cls.availability_zone_admin_client = \
             cls.os_adm.availability_zone_v3_client
diff --git a/tempest/api/compute/floating_ips/base.py b/tempest/api/compute/floating_ips/base.py
new file mode 100644
index 0000000..e2c9b04
--- /dev/null
+++ b/tempest/api/compute/floating_ips/base.py
@@ -0,0 +1,28 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2014 OpenStack Foundation
+# 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.api.compute import base
+
+
+class BaseFloatingIPsTest(base.BaseV2ComputeTest):
+
+    @classmethod
+    def setUpClass(cls):
+        # Floating IP actions might need a full network configuration
+        cls.set_network_resources(network=True, subnet=True,
+                                  router=True, dhcp=True)
+        super(BaseFloatingIPsTest, cls).setUpClass()
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 32e7b39..cd5f4a8 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -15,13 +15,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api.compute import base
+from tempest.api.compute.floating_ips import base
 from tempest.common.utils.data_utils import rand_name
 from tempest import exceptions
 from tempest.test import attr
 
 
-class FloatingIPsTestJSON(base.BaseV2ComputeTest):
+class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
     _interface = 'json'
     server_id = None
     floating_ip = None
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 89315bb..674669c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -17,13 +17,13 @@
 
 import uuid
 
-from tempest.api.compute import base
+from tempest.api.compute.floating_ips import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.test import attr
 
 
-class FloatingIPsNegativeTestJSON(base.BaseV2ComputeTest):
+class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
     _interface = 'json'
     server_id = None
 
diff --git a/tempest/api/compute/security_groups/base.py b/tempest/api/compute/security_groups/base.py
new file mode 100644
index 0000000..66f2600
--- /dev/null
+++ b/tempest/api/compute/security_groups/base.py
@@ -0,0 +1,27 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+
+
+class BaseSecurityGroupsTest(base.BaseV2ComputeTest):
+
+    @classmethod
+    def setUpClass(cls):
+        # A network and a subnet will be created for these tests
+        cls.set_network_resources(network=True, subnet=True)
+        super(BaseSecurityGroupsTest, cls).setUpClass()
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 2ccc3a8..eddccc1 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -15,12 +15,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api.compute import base
+from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest.test import attr
 
 
-class SecurityGroupRulesTestJSON(base.BaseV2ComputeTest):
+class SecurityGroupRulesTestJSON(base.BaseSecurityGroupsTest):
     _interface = 'json'
 
     @classmethod
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index c0b202d..bec95ad 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -17,7 +17,7 @@
 
 import testtools
 
-from tempest.api.compute import base
+from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
@@ -27,7 +27,7 @@
 CONF = config.CONF
 
 
-class SecurityGroupRulesNegativeTestJSON(base.BaseV2ComputeTest):
+class SecurityGroupRulesNegativeTestJSON(base.BaseSecurityGroupsTest):
     _interface = 'json'
 
     @classmethod
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 4ae65be..ffd9fd2 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -15,13 +15,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api.compute import base
+from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest import test
 
 
-class SecurityGroupsTestJSON(base.BaseV2ComputeTest):
+class SecurityGroupsTestJSON(base.BaseSecurityGroupsTest):
     _interface = 'json'
 
     @classmethod
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 6a8e604..cf78089 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -17,7 +17,7 @@
 
 import testtools
 
-from tempest.api.compute import base
+from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
@@ -26,7 +26,7 @@
 CONF = config.CONF
 
 
-class SecurityGroupsNegativeTestJSON(base.BaseV2ComputeTest):
+class SecurityGroupsNegativeTestJSON(base.BaseSecurityGroupsTest):
     _interface = 'json'
 
     @classmethod
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index dbb7d75..738e4a8 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -26,6 +26,8 @@
     def setUpClass(cls):
         if not cls.config.service_available.neutron:
             raise cls.skipException("Neutron is required")
+        # This test class requires network and subnet
+        cls.set_network_resources(network=True, subnet=True)
         super(AttachInterfacesTestJSON, cls).setUpClass()
         cls.client = cls.os.interfaces_client
 
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 5d62e1b..e45161b 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -109,6 +109,104 @@
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
 
+class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
+    _interface = 'json'
+    run_ssh = CONF.compute.run_ssh
+    disk_config = 'AUTO'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServersWithSpecificFlavorTestJSON, cls).setUpClass()
+        cls.meta = {'hello': 'world'}
+        cls.accessIPv4 = '1.1.1.1'
+        cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
+        cls.name = data_utils.rand_name('server')
+        file_contents = 'This is a test file.'
+        personality = [{'path': '/test.txt',
+                       'contents': base64.b64encode(file_contents)}]
+        cls.client = cls.servers_client
+        cls.flavor_client = cls.os_adm.flavors_client
+        cli_resp = cls.create_test_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'])
+
+    @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+    @attr(type='gate')
+    def test_verify_created_server_ephemeral_disk(self):
+        # Verify that the ephemeral disk is created when creating server
+
+        def create_flavor_with_extra_specs(self):
+            flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
+            flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
+            ram = 64
+            vcpus = 1
+            disk = 0
+
+            # Create a flavor with extra specs
+            resp, flavor = (self.flavor_client.
+                            create_flavor(flavor_with_eph_disk_name,
+                                          ram, vcpus, disk,
+                                          flavor_with_eph_disk_id,
+                                          ephemeral=1))
+            self.addCleanup(self.flavor_clean_up, flavor['id'])
+            self.assertEqual(200, resp.status)
+
+            return flavor['id']
+
+        def create_flavor_without_extra_specs(self):
+            flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
+            flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
+
+            ram = 64
+            vcpus = 1
+            disk = 0
+
+            # Create a flavor without extra specs
+            resp, flavor = (self.flavor_client.
+                            create_flavor(flavor_no_eph_disk_name,
+                                          ram, vcpus, disk,
+                                          flavor_no_eph_disk_id))
+            self.addCleanup(self.flavor_clean_up, flavor['id'])
+            self.assertEqual(200, resp.status)
+
+            return flavor['id']
+
+        def flavor_clean_up(self, flavor_id):
+            resp, body = self.flavor_client.delete_flavor(flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.flavor_client.wait_for_resource_deletion(flavor_id)
+
+        flavor_with_eph_disk_id = self.create_flavor_with_extra_specs()
+        flavor_no_eph_disk_id = self.create_flavor_without_extra_specs()
+
+        admin_pass = self.image_ssh_password
+
+        resp, server_no_eph_disk = (self.
+                                    create_test_server(
+                                    wait_until='ACTIVE',
+                                    adminPass=admin_pass,
+                                    flavor=flavor_no_eph_disk_id))
+        resp, server_with_eph_disk = (self.create_test_server(
+                                      wait_until='ACTIVE',
+                                      adminPass=admin_pass,
+                                      flavor=flavor_with_eph_disk_id))
+        # Get partition number of server without extra specs.
+        linux_client = RemoteClient(server_no_eph_disk,
+                                    self.ssh_user, self.password)
+        partition_num = len(linux_client.get_partitions())
+
+        linux_client = RemoteClient(server_with_eph_disk,
+                                    self.ssh_user, self.password)
+        self.assertEqual(partition_num + 1, linux_client.get_partitions())
+
+
 class ServersTestManualDisk(ServersTestJSON):
     disk_config = 'MANUAL'
 
@@ -122,3 +220,7 @@
 
 class ServersTestXML(ServersTestJSON):
     _interface = 'xml'
+
+
+class ServersWithSpecificFlavorTestXML(ServersWithSpecificFlavorTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 5019003..5058211 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -16,8 +16,7 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 
 class InstanceActionsTestJSON(base.BaseV2ComputeTest):
@@ -31,7 +30,7 @@
         cls.request_id = resp['x-compute-request-id']
         cls.server_id = server['id']
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_instance_actions(self):
         # List actions of the provided server
         resp, body = self.client.reboot(self.server_id, 'HARD')
@@ -43,7 +42,7 @@
         self.assertTrue(any([i for i in body if i['action'] == 'create']))
         self.assertTrue(any([i for i in body if i['action'] == 'reboot']))
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_get_instance_action(self):
         # Get the action details of the provided server
         resp, body = self.client.get_instance_action(self.server_id,
@@ -52,18 +51,6 @@
         self.assertEqual(self.server_id, body['instance_uuid'])
         self.assertEqual('create', body['action'])
 
-    @attr(type=['negative', 'gate'])
-    def test_list_instance_actions_invalid_server(self):
-        # List actions of the invalid server id
-        self.assertRaises(exceptions.NotFound,
-                          self.client.list_instance_actions, 'server-999')
-
-    @attr(type=['negative', 'gate'])
-    def test_get_instance_action_invalid_request(self):
-        # Get the action details of the provided server with invalid request
-        self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
-                          self.server_id, '999')
-
 
 class InstanceActionsTestXML(InstanceActionsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
new file mode 100644
index 0000000..68f2dcb
--- /dev/null
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -0,0 +1,50 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 NEC Corporation
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class InstanceActionsNegativeTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(InstanceActionsNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.servers_client
+        resp, server = cls.create_test_server(wait_until='ACTIVE')
+        cls.server_id = server['id']
+
+    @test.attr(type=['negative', 'gate'])
+    def test_list_instance_actions_non_existent_server(self):
+        # List actions of the non-existent server id
+        non_existent_server_id = data_utils.rand_uuid()
+        self.assertRaises(exceptions.NotFound,
+                          self.client.list_instance_actions,
+                          non_existent_server_id)
+
+    @test.attr(type=['negative', 'gate'])
+    def test_get_instance_action_invalid_request(self):
+        # Get the action details of the provided server with invalid request
+        self.assertRaises(exceptions.NotFound, self.client.get_instance_action,
+                          self.server_id, '999')
+
+
+class InstanceActionsNegativeTestXML(InstanceActionsNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 3748e37..f3863b3 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -121,6 +121,23 @@
         self.assertIn(self.s3['id'], map(lambda x: x['id'], servers))
 
     @attr(type='gate')
+    def test_list_servers_filter_by_shutoff_status(self):
+        # Filter the list of servers by server shutoff status
+        params = {'status': 'shutoff'}
+        self.client.stop(self.s1['id'])
+        self.client.wait_for_server_status(self.s1['id'],
+                                           'SHUTOFF')
+        resp, body = self.client.list_servers(params)
+        self.client.start(self.s1['id'])
+        self.client.wait_for_server_status(self.s1['id'],
+                                           'ACTIVE')
+        servers = body['servers']
+
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s3['id'], map(lambda x: x['id'], servers))
+
+    @attr(type='gate')
     def test_list_servers_filter_by_limit(self):
         # Verify only the expected number of servers are returned
         params = {'limit': 1}
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d985d2b..e4af2e1 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -174,7 +174,7 @@
 
     def _detect_server_image_flavor(self, server_id):
         # Detects the current server image flavor ref.
-        resp, server = self.client.get_server(self.server_id)
+        resp, server = self.client.get_server(server_id)
         current_flavor = server['flavor']['id']
         new_flavor_ref = self.flavor_ref_alt \
             if current_flavor == self.flavor_ref else self.flavor_ref
@@ -287,6 +287,7 @@
         self.addCleanup(self.os.image_client.delete_image, image3_id)
         self.assertEqual(202, resp.status)
         # the first back up should be deleted
+        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
         self.os.image_client.wait_for_resource_deletion(image1_id)
         oldest_backup_exist = False
         resp, image_list = self.os.image_client.image_list_detail(
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 1e55afb..39d9580 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -24,6 +24,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # This test module might use a network and a subnet
+        cls.set_network_resources(network=True, subnet=True)
         super(ServerAddressesTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
 
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
index 30aa7d1..0343538 100644
--- a/tempest/api/compute/servers/test_server_addresses_negative.py
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -25,6 +25,7 @@
 
     @classmethod
     def setUpClass(cls):
+        cls.set_network_resources(network=True, subnet=True)
         super(ServerAddressesNegativeTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
 
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 1008670..5048967 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -70,7 +70,7 @@
         cls.rescue_password = resc_server['adminPass']
 
         cls.servers_client.rescue_server(
-            cls.rescue_id, cls.rescue_password)
+            cls.rescue_id, adminPass=cls.rescue_password)
         cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
 
     def setUp(self):
@@ -111,7 +111,7 @@
     @attr(type='smoke')
     def test_rescue_unrescue_instance(self):
         resp, body = self.servers_client.rescue_server(
-            self.server_id, self.password)
+            self.server_id, adminPass=self.password)
         self.assertEqual(200, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         resp, body = self.servers_client.unrescue_server(self.server_id)
@@ -152,7 +152,8 @@
     @attr(type=['negative', 'gate'])
     def test_rescued_vm_attach_volume(self):
         # Rescue the server
-        self.servers_client.rescue_server(self.server_id, self.password)
+        self.servers_client.rescue_server(self.server_id,
+                                          adminPass=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
@@ -173,7 +174,8 @@
             self.volume_to_detach['id'], 'in-use')
 
         # Rescue the server
-        self.servers_client.rescue_server(self.server_id, self.password)
+        self.servers_client.rescue_server(self.server_id,
+                                          adminPass=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         # addCleanup is a LIFO queue
         self.addCleanup(self._detach, self.server_id,
@@ -190,7 +192,7 @@
     def test_rescued_vm_associate_dissociate_floating_ip(self):
         # Rescue the server
         self.servers_client.rescue_server(
-            self.server_id, self.password)
+            self.server_id, adminPass=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
@@ -210,7 +212,7 @@
     def test_rescued_vm_add_remove_security_group(self):
         # Rescue the server
         self.servers_client.rescue_server(
-            self.server_id, self.password)
+            self.server_id, adminPass=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
 
         # Add Security group
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 968ae47..7bbe30b 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -29,6 +29,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # This test needs a network and a subnet
+        cls.set_network_resources(network=True, subnet=True)
         super(VirtualInterfacesTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
         resp, server = cls.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index a2a6c11..d67e13a 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -27,6 +27,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # For this test no network resources are needed
+        cls.set_network_resources()
         super(VirtualInterfacesNegativeTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
 
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 327c7d1..03075df 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -30,6 +30,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # No network resources required for this test
+        cls.set_network_resources()
         super(AuthorizationTestJSON, cls).setUpClass()
         if not cls.multi_user:
             msg = "Need >1 user"
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index b0bffc4..56929a3 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -17,25 +17,35 @@
 
 
 from tempest.api.compute import base
+from tempest.openstack.common import log as logging
 from tempest import test
-import testtools
+
+
+LOG = logging.getLogger(__name__)
 
 
 class ExtensionsTestJSON(base.BaseV2ComputeTest):
     _interface = 'json'
 
-    @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
-                      'os-consoles extension not enabled.')
     @test.attr(type='gate')
     def test_list_extensions(self):
         # List of all extensions
+        if len(self.config.compute_feature_enabled.api_extensions) == 0:
+            raise self.skipException('There are not any extensions configured')
         resp, extensions = self.extensions_client.list_extensions()
-        self.assertIn("extensions", extensions)
         self.assertEqual(200, resp.status)
-        self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+        ext = self.config.compute_feature_enabled.api_extensions[0]
+        if ext == 'all':
+            self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+        elif ext:
+            self.assertIn(ext, map(lambda x: x['name'], extensions))
+        else:
+            raise self.skipException('There are not any extensions configured')
+        # Log extensions list
+        extension_list = map(lambda x: x['name'], extensions)
+        LOG.debug("Nova extensions: %s" % ','.join(extension_list))
 
-    @testtools.skipIf(not test.is_extension_enabled('os-consoles', 'compute'),
-                      'os-consoles extension not enabled.')
+    @test.requires_ext(extension='os-consoles', service='compute')
     @test.attr(type='gate')
     def test_get_extension(self):
         # get the specified extensions
diff --git a/tempest/api/compute/v3/admin/test_hosts_negative.py b/tempest/api/compute/v3/admin/test_hosts_negative.py
index 755fa2b..598a1d3 100644
--- a/tempest/api/compute/v3/admin/test_hosts_negative.py
+++ b/tempest/api/compute/v3/admin/test_hosts_negative.py
@@ -66,7 +66,9 @@
 
         self.assertRaises(exceptions.Unauthorized,
                           self.non_admin_client.update_host,
-                          hostname)
+                          hostname,
+                          status='enable',
+                          maintenance_mode='enable')
 
     @test.attr(type=['negative', 'gate'])
     def test_update_host_with_extra_param(self):
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
index cea6e92..d0cfc63 100644
--- a/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
@@ -16,22 +16,22 @@
 #    under the License.
 
 import datetime
-
-from tempest.api.compute import base
-from tempest.test import attr
 import urllib
 
+from tempest.api.compute import base
+from tempest import test
 
-class InstanceUsageAuditLogTestJSON(base.BaseV2ComputeAdminTest):
+
+class InstanceUsageAuditLogV3TestJSON(base.BaseV3ComputeAdminTest):
 
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(InstanceUsageAuditLogTestJSON, cls).setUpClass()
-        cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+        super(InstanceUsageAuditLogV3TestJSON, cls).setUpClass()
+        cls.adm_client = cls.instance_usages_audit_log_admin_client
 
-    @attr(type='gate')
+    @test.attr(type='gate')
     def test_list_instance_usage_audit_logs(self):
         # list instance usage audit logs
         resp, body = self.adm_client.list_instance_usage_audit_logs()
@@ -44,12 +44,12 @@
         for item in expected_items:
             self.assertIn(item, body)
 
-    @attr(type='gate')
-    def test_get_instance_usage_audit_log(self):
+    @test.attr(type='gate')
+    def test_list_instance_usage_audit_logs_with_filter_before(self):
         # Get instance usage audit log before specified time
-        now = datetime.datetime.now()
-        resp, body = self.adm_client.get_instance_usage_audit_log(
-            urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
+        ending_time = datetime.datetime(2012, 12, 24)
+        resp, body = self.adm_client.list_instance_usage_audit_logs(
+            urllib.quote(ending_time.strftime("%Y-%m-%d %H:%M:%S")))
 
         self.assertEqual(200, resp.status)
         expected_items = ['total_errors', 'total_instances', 'log',
@@ -58,7 +58,8 @@
                           'period_beginning', 'num_hosts_not_run']
         for item in expected_items:
             self.assertIn(item, body)
+        self.assertEqual(body['period_ending'], "2012-12-23 23:00:00")
 
 
-class InstanceUsageAuditLogTestXML(InstanceUsageAuditLogTestJSON):
+class InstanceUsageAuditLogV3TestXML(InstanceUsageAuditLogV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
index dcf41c5..6e919c9 100644
--- a/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
@@ -15,42 +15,34 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import datetime
-
 from tempest.api.compute import base
 from tempest import exceptions
-from tempest.test import attr
-import urllib
+from tempest import test
 
 
-class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class InstanceUsageLogNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
 
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(InstanceUsageAuditLogNegativeTestJSON, cls).setUpClass()
-        cls.adm_client = cls.os_adm.instance_usages_audit_log_client
+        super(InstanceUsageLogNegativeV3TestJSON, cls).setUpClass()
+        cls.adm_client = cls.instance_usages_audit_log_admin_client
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_instance_usage_audit_logs_with_nonadmin_user(self):
         # the instance_usage_audit_logs API just can be accessed by admin user
         self.assertRaises(exceptions.Unauthorized,
                           self.instance_usages_audit_log_client.
                           list_instance_usage_audit_logs)
-        now = datetime.datetime.now()
-        self.assertRaises(exceptions.Unauthorized,
-                          self.instance_usages_audit_log_client.
-                          get_instance_usage_audit_log,
-                          urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
 
-    @attr(type=['negative', 'gate'])
+    @test.attr(type=['negative', 'gate'])
     def test_get_instance_usage_audit_logs_with_invalid_time(self):
         self.assertRaises(exceptions.BadRequest,
-                          self.adm_client.get_instance_usage_audit_log,
+                          self.adm_client.list_instance_usage_audit_logs,
                           "invalid_time")
 
 
-class InstanceUsageAuditLogNegativeTestXML(
-    InstanceUsageAuditLogNegativeTestJSON):
+class InstanceUsageLogNegativeV3TestXML(
+    InstanceUsageLogNegativeV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_servers.py b/tempest/api/compute/v3/admin/test_servers.py
index 6fe3186..becdd78 100644
--- a/tempest/api/compute/v3/admin/test_servers.py
+++ b/tempest/api/compute/v3/admin/test_servers.py
@@ -21,7 +21,7 @@
 from tempest.test import skip_because
 
 
-class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
+class ServersAdminV3TestJSON(base.BaseV3ComputeAdminTest):
 
     """
     Tests Servers API using admin privileges
@@ -31,10 +31,10 @@
 
     @classmethod
     def setUpClass(cls):
-        super(ServersAdminTestJSON, cls).setUpClass()
-        cls.client = cls.os_adm.servers_client
+        super(ServersAdminV3TestJSON, cls).setUpClass()
+        cls.client = cls.servers_admin_client
         cls.non_admin_client = cls.servers_client
-        cls.flavors_client = cls.os_adm.flavors_client
+        cls.flavors_client = cls.flavors_admin_client
 
         cls.s1_name = data_utils.rand_name('server')
         resp, server = cls.create_test_server(name=cls.s1_name,
@@ -44,6 +44,7 @@
         cls.s2_name = data_utils.rand_name('server')
         resp, server = cls.create_test_server(name=cls.s2_name,
                                               wait_until='ACTIVE')
+        cls.s2_id = server['id']
 
     def _get_unused_flavor_id(self):
         flavor_id = data_utils.rand_int_id(start=1000)
@@ -114,6 +115,22 @@
             self.assertIn(key, str(diagnostic.keys()))
 
     @attr(type='gate')
+    def test_list_servers_filter_by_error_status(self):
+        # Filter the list of servers by server error status
+        params = {'status': 'error'}
+        resp, server = self.client.reset_state(self.s1_id, state='error')
+        resp, body = self.non_admin_client.list_servers(params)
+        # Reset server's state to 'active'
+        resp, server = self.client.reset_state(self.s1_id, state='active')
+        # Verify server's state
+        resp, server = self.client.get_server(self.s1_id)
+        self.assertEqual(server['status'], 'ACTIVE')
+        servers = body['servers']
+        # Verify error server in list result
+        self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
+
+    @attr(type='gate')
     def test_rebuild_server_in_error_state(self):
         # The server in error state should be rebuilt using the provided
         # image and changed to ACTIVE state
@@ -142,5 +159,5 @@
         self.assertEqual(self.image_ref_alt, rebuilt_image_id)
 
 
-class ServersAdminTestXML(ServersAdminTestJSON):
+class ServersAdminV3TestXML(ServersAdminV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index 77d873b..670bc2c 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -22,7 +22,7 @@
 from tempest.test import attr
 
 
-class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
+class ServersAdminNegativeV3TestJSON(base.BaseV3ComputeAdminTest):
 
     """
     Tests Servers API using admin privileges
@@ -32,10 +32,10 @@
 
     @classmethod
     def setUpClass(cls):
-        super(ServersAdminNegativeTestJSON, cls).setUpClass()
-        cls.client = cls.os_adm.servers_client
+        super(ServersAdminNegativeV3TestJSON, cls).setUpClass()
+        cls.client = cls.servers_admin_client
         cls.non_adm_client = cls.servers_client
-        cls.flavors_client = cls.os_adm.flavors_client
+        cls.flavors_client = cls.flavors_admin_client
         cls.identity_client = cls._get_identity_admin_client()
         tenant = cls.identity_client.get_tenant_by_name(
             cls.client.tenant_name)
@@ -139,5 +139,5 @@
                           server_id)
 
 
-class ServersAdminNegativeTestXML(ServersAdminNegativeTestJSON):
+class ServersAdminNegativeV3TestXML(ServersAdminNegativeV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
index 99f2c52..e08f16a 100644
--- a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
@@ -18,6 +18,7 @@
 import datetime
 
 from tempest.api.compute import base
+from tempest import test
 from tempest.test import attr
 import time
 
@@ -50,6 +51,7 @@
         # Returns formatted datetime
         return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
 
+    @test.skip_because(bug='1265416')
     @attr(type='gate')
     def test_list_usage_all_tenants(self):
         # Get usage for all tenants
@@ -60,6 +62,7 @@
         self.assertEqual(200, resp.status)
         self.assertEqual(len(tenant_usage), 8)
 
+    @test.skip_because(bug='1265416')
     @attr(type='gate')
     def test_get_usage_tenant(self):
         # Get usage for a specific tenant
@@ -71,6 +74,7 @@
         self.assertEqual(200, resp.status)
         self.assertEqual(len(tenant_usage), 8)
 
+    @test.skip_because(bug='1265416')
     @attr(type='gate')
     def test_get_usage_tenant_with_non_admin_user(self):
         # Get usage for a specific tenant with non admin user
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py
index ef49ed7..f9dbe86 100644
--- a/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py
@@ -19,6 +19,7 @@
 
 from tempest.api.compute import base
 from tempest import exceptions
+from tempest import test
 from tempest.test import attr
 
 
@@ -50,6 +51,7 @@
                           self.adm_client.get_tenant_usage,
                           '', params)
 
+    @test.skip_because(bug='1265416')
     @attr(type=['negative', 'gate'])
     def test_get_usage_tenant_with_invalid_date(self):
         # Get usage for tenant with invalid date
@@ -62,6 +64,7 @@
                           self.adm_client.get_tenant_usage,
                           tenant_id, params)
 
+    @test.skip_because(bug='1265416')
     @attr(type=['negative', 'gate'])
     def test_list_usage_all_tenants_with_non_admin_user(self):
         # Get usage for all tenants with non admin user
diff --git a/tempest/api/compute/v3/images/test_image_metadata.py b/tempest/api/compute/v3/images/test_image_metadata.py
new file mode 100644
index 0000000..76e0cae
--- /dev/null
+++ b/tempest/api/compute/v3/images/test_image_metadata.py
@@ -0,0 +1,114 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.test import attr
+
+
+class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesMetadataTestJSON, cls).setUpClass()
+        if not cls.config.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+        cls.servers_client = cls.servers_client
+        cls.client = cls.images_client
+
+        resp, server = cls.create_test_server(wait_until='ACTIVE')
+        cls.server_id = server['id']
+
+        # Snapshot the server once to save time
+        name = data_utils.rand_name('image')
+        resp, _ = cls.client.create_image(cls.server_id, name, {})
+        cls.image_id = resp['location'].rsplit('/', 1)[1]
+
+        cls.client.wait_for_image_status(cls.image_id, 'ACTIVE')
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.client.delete_image(cls.image_id)
+        super(ImagesMetadataTestJSON, cls).tearDownClass()
+
+    def setUp(self):
+        super(ImagesMetadataTestJSON, self).setUp()
+        meta = {'key1': 'value1', 'key2': 'value2'}
+        resp, _ = self.client.set_image_metadata(self.image_id, meta)
+        self.assertEqual(resp.status, 200)
+
+    @attr(type='gate')
+    def test_list_image_metadata(self):
+        # All metadata key/value pairs for an image should be returned
+        resp, resp_metadata = self.client.list_image_metadata(self.image_id)
+        expected = {'key1': 'value1', 'key2': 'value2'}
+        self.assertEqual(expected, resp_metadata)
+
+    @attr(type='gate')
+    def test_set_image_metadata(self):
+        # The metadata for the image should match the new values
+        req_metadata = {'meta2': 'value2', 'meta3': 'value3'}
+        resp, body = self.client.set_image_metadata(self.image_id,
+                                                    req_metadata)
+
+        resp, resp_metadata = self.client.list_image_metadata(self.image_id)
+        self.assertEqual(req_metadata, resp_metadata)
+
+    @attr(type='gate')
+    def test_update_image_metadata(self):
+        # The metadata for the image should match the updated values
+        req_metadata = {'key1': 'alt1', 'key3': 'value3'}
+        resp, metadata = self.client.update_image_metadata(self.image_id,
+                                                           req_metadata)
+
+        resp, resp_metadata = self.client.list_image_metadata(self.image_id)
+        expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
+        self.assertEqual(expected, resp_metadata)
+
+    @attr(type='gate')
+    def test_get_image_metadata_item(self):
+        # The value for a specific metadata key should be returned
+        resp, meta = self.client.get_image_metadata_item(self.image_id,
+                                                         'key2')
+        self.assertEqual('value2', meta['key2'])
+
+    @attr(type='gate')
+    def test_set_image_metadata_item(self):
+        # The value provided for the given meta item should be set for
+        # the image
+        meta = {'key1': 'alt'}
+        resp, body = self.client.set_image_metadata_item(self.image_id,
+                                                         'key1', meta)
+        resp, resp_metadata = self.client.list_image_metadata(self.image_id)
+        expected = {'key1': 'alt', 'key2': 'value2'}
+        self.assertEqual(expected, resp_metadata)
+
+    @attr(type='gate')
+    def test_delete_image_metadata_item(self):
+        # The metadata value/key pair should be deleted from the image
+        resp, body = self.client.delete_image_metadata_item(self.image_id,
+                                                            'key1')
+        resp, resp_metadata = self.client.list_image_metadata(self.image_id)
+        expected = {'key2': 'value2'}
+        self.assertEqual(expected, resp_metadata)
+
+
+class ImagesMetadataTestXML(ImagesMetadataTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/images/test_image_metadata_negative.py b/tempest/api/compute/v3/images/test_image_metadata_negative.py
new file mode 100644
index 0000000..1767e5d
--- /dev/null
+++ b/tempest/api/compute/v3/images/test_image_metadata_negative.py
@@ -0,0 +1,81 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesMetadataTestJSON, cls).setUpClass()
+        cls.client = cls.images_client
+
+    @attr(type=['negative', 'gate'])
+    def test_list_nonexistent_image_metadata(self):
+        # Negative test: List on nonexistent image
+        # metadata should not happen
+        self.assertRaises(exceptions.NotFound, self.client.list_image_metadata,
+                          data_utils.rand_uuid())
+
+    @attr(type=['negative', 'gate'])
+    def test_update_nonexistent_image_metadata(self):
+        # Negative test:An update should not happen for a non-existent image
+        meta = {'key1': 'alt1', 'key2': 'alt2'}
+        self.assertRaises(exceptions.NotFound,
+                          self.client.update_image_metadata,
+                          data_utils.rand_uuid(), meta)
+
+    @attr(type=['negative', 'gate'])
+    def test_get_nonexistent_image_metadata_item(self):
+        # Negative test: Get on non-existent image should not happen
+        self.assertRaises(exceptions.NotFound,
+                          self.client.get_image_metadata_item,
+                          data_utils.rand_uuid(), 'key2')
+
+    @attr(type=['negative', 'gate'])
+    def test_set_nonexistent_image_metadata(self):
+        # Negative test: Metadata should not be set to a non-existent image
+        meta = {'key1': 'alt1', 'key2': 'alt2'}
+        self.assertRaises(exceptions.NotFound, self.client.set_image_metadata,
+                          data_utils.rand_uuid(), meta)
+
+    @attr(type=['negative', 'gate'])
+    def test_set_nonexistent_image_metadata_item(self):
+        # Negative test: Metadata item should not be set to a
+        # nonexistent image
+        meta = {'key1': 'alt'}
+        self.assertRaises(exceptions.NotFound,
+                          self.client.set_image_metadata_item,
+                          data_utils.rand_uuid(), 'key1',
+                          meta)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_nonexistent_image_metadata_item(self):
+        # Negative test: Shouldn't be able to delete metadata
+        # item from non-existent image
+        self.assertRaises(exceptions.NotFound,
+                          self.client.delete_image_metadata_item,
+                          data_utils.rand_uuid(), 'key1')
+
+
+class ImagesMetadataTestXML(ImagesMetadataTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/images/test_images_oneserver.py b/tempest/api/compute/v3/images/test_images_oneserver.py
new file mode 100644
index 0000000..26cc3f6
--- /dev/null
+++ b/tempest/api/compute/v3/images/test_images_oneserver.py
@@ -0,0 +1,138 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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 testtools
+
+from tempest.api.compute import base
+from tempest import clients
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest.openstack.common import log as logging
+from tempest.test import attr
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class ImagesOneServerTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    def tearDown(self):
+        """Terminate test instances created after a test is executed."""
+        for image_id in self.image_ids:
+            self.client.delete_image(image_id)
+            self.image_ids.remove(image_id)
+        super(ImagesOneServerTestJSON, self).tearDown()
+
+    def setUp(self):
+        # NOTE(afazekas): Normally we use the same server with all test cases,
+        # but if it has an issue, we build a new one
+        super(ImagesOneServerTestJSON, self).setUp()
+        # Check if the server is in a clean state after test
+        try:
+            self.servers_client.wait_for_server_status(self.server_id,
+                                                       'ACTIVE')
+        except Exception:
+            LOG.exception('server %s timed out to become ACTIVE. rebuilding'
+                          % self.server_id)
+            # Rebuild server if cannot reach the ACTIVE state
+            # Usually it means the server had a serious accident
+            self.__class__.server_id = self.rebuild_server(self.server_id)
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesOneServerTestJSON, cls).setUpClass()
+        cls.client = cls.images_client
+        if not cls.config.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+        try:
+            resp, server = cls.create_test_server(wait_until='ACTIVE')
+            cls.server_id = server['id']
+        except Exception:
+            cls.tearDownClass()
+            raise
+
+        cls.image_ids = []
+
+        if cls.multi_user:
+            if cls.config.compute.allow_tenant_isolation:
+                creds = cls.isolated_creds.get_alt_creds()
+                username, tenant_name, password = creds
+                cls.alt_manager = clients.Manager(username=username,
+                                                  password=password,
+                                                  tenant_name=tenant_name)
+            else:
+                # Use the alt_XXX credentials in the config file
+                cls.alt_manager = clients.AltManager()
+            cls.alt_client = cls.alt_manager.images_client
+
+    def _get_default_flavor_disk_size(self, flavor_id):
+        resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
+        return flavor['disk']
+
+    @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
+                          'Environment unable to create images.')
+    @attr(type='smoke')
+    def test_create_delete_image(self):
+
+        # Create a new image
+        name = data_utils.rand_name('image')
+        meta = {'image_type': 'test'}
+        resp, body = self.client.create_image(self.server_id, name, meta)
+        self.assertEqual(202, resp.status)
+        image_id = data_utils.parse_image_id(resp['location'])
+        self.client.wait_for_image_status(image_id, 'ACTIVE')
+
+        # Verify the image was created correctly
+        resp, image = self.client.get_image(image_id)
+        self.assertEqual(name, image['name'])
+        self.assertEqual('test', image['metadata']['image_type'])
+
+        resp, original_image = self.client.get_image(self.image_ref)
+
+        # Verify minRAM is the same as the original image
+        self.assertEqual(image['minRam'], original_image['minRam'])
+
+        # Verify minDisk is the same as the original image or the flavor size
+        flavor_disk_size = self._get_default_flavor_disk_size(self.flavor_ref)
+        self.assertIn(str(image['minDisk']),
+                      (str(original_image['minDisk']), str(flavor_disk_size)))
+
+        # Verify the image was deleted correctly
+        resp, body = self.client.delete_image(image_id)
+        self.assertEqual('204', resp['status'])
+        self.client.wait_for_resource_deletion(image_id)
+
+    @attr(type=['gate'])
+    def test_create_image_specify_multibyte_character_image_name(self):
+        if self.__class__._interface == "xml":
+            # NOTE(sdague): not entirely accurage, but we'd need a ton of work
+            # in our XML client to make this good
+            raise self.skipException("Not testable in XML")
+        # prefix character is:
+        # http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
+        utf8_name = data_utils.rand_name(u'\xF0\x9F\x92\xA9')
+        resp, body = self.client.create_image(self.server_id, utf8_name)
+        image_id = data_utils.parse_image_id(resp['location'])
+        self.addCleanup(self.client.delete_image, image_id)
+        self.assertEqual('202', resp['status'])
+
+
+class ImagesOneServerTestXML(ImagesOneServerTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/images/test_images_oneserver_negative.py b/tempest/api/compute/v3/images/test_images_oneserver_negative.py
new file mode 100644
index 0000000..5e235d1
--- /dev/null
+++ b/tempest/api/compute/v3/images/test_images_oneserver_negative.py
@@ -0,0 +1,162 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest import clients
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+from tempest.test import attr
+from tempest.test import skip_because
+
+LOG = logging.getLogger(__name__)
+
+
+class ImagesOneServerNegativeTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    def tearDown(self):
+        """Terminate test instances created after a test is executed."""
+        for image_id in self.image_ids:
+            self.client.delete_image(image_id)
+            self.image_ids.remove(image_id)
+        super(ImagesOneServerNegativeTestJSON, self).tearDown()
+
+    def setUp(self):
+        # NOTE(afazekas): Normally we use the same server with all test cases,
+        # but if it has an issue, we build a new one
+        super(ImagesOneServerNegativeTestJSON, self).setUp()
+        # Check if the server is in a clean state after test
+        try:
+            self.servers_client.wait_for_server_status(self.server_id,
+                                                       'ACTIVE')
+        except Exception:
+            LOG.exception('server %s timed out to become ACTIVE. rebuilding'
+                          % self.server_id)
+            # Rebuild server if cannot reach the ACTIVE state
+            # Usually it means the server had a serious accident
+            self._reset_server()
+
+    def _reset_server(self):
+        self.__class__.server_id = self.rebuild_server(self.server_id)
+
+    @classmethod
+    def setUpClass(cls):
+        super(ImagesOneServerNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.images_client
+        if not cls.config.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+        try:
+            resp, server = cls.create_test_server(wait_until='ACTIVE')
+            cls.server_id = server['id']
+        except Exception:
+            cls.tearDownClass()
+            raise
+
+        cls.image_ids = []
+
+        if cls.multi_user:
+            if cls.config.compute.allow_tenant_isolation:
+                creds = cls.isolated_creds.get_alt_creds()
+                username, tenant_name, password = creds
+                cls.alt_manager = clients.Manager(username=username,
+                                                  password=password,
+                                                  tenant_name=tenant_name)
+            else:
+                # Use the alt_XXX credentials in the config file
+                cls.alt_manager = clients.AltManager()
+            cls.alt_client = cls.alt_manager.images_client
+
+    @skip_because(bug="1006725")
+    @attr(type=['negative', 'gate'])
+    def test_create_image_specify_multibyte_character_image_name(self):
+        if self.__class__._interface == "xml":
+            raise self.skipException("Not testable in XML")
+        # invalid multibyte sequence from:
+        # http://stackoverflow.com/questions/1301402/
+        #     example-invalid-utf8-string
+        invalid_name = data_utils.rand_name(u'\xc3\x28')
+        self.assertRaises(exceptions.BadRequest,
+                          self.client.create_image, self.server_id,
+                          invalid_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_image_specify_invalid_metadata(self):
+        # Return an error when creating image with invalid metadata
+        snapshot_name = data_utils.rand_name('test-snap-')
+        meta = {'': ''}
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          self.server_id, snapshot_name, meta)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_image_specify_metadata_over_limits(self):
+        # Return an error when creating image with meta data over 256 chars
+        snapshot_name = data_utils.rand_name('test-snap-')
+        meta = {'a' * 260: 'b' * 260}
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          self.server_id, snapshot_name, meta)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_second_image_when_first_image_is_being_saved(self):
+        # Disallow creating another image when first image is being saved
+
+        # Create first snapshot
+        snapshot_name = data_utils.rand_name('test-snap-')
+        resp, body = self.client.create_image(self.server_id,
+                                              snapshot_name)
+        self.assertEqual(202, resp.status)
+        image_id = data_utils.parse_image_id(resp['location'])
+        self.image_ids.append(image_id)
+        self.addCleanup(self._reset_server)
+
+        # Create second snapshot
+        alt_snapshot_name = data_utils.rand_name('test-snap-')
+        self.assertRaises(exceptions.Conflict, self.client.create_image,
+                          self.server_id, alt_snapshot_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_create_image_specify_name_over_256_chars(self):
+        # Return an error if snapshot name over 256 characters is passed
+
+        snapshot_name = data_utils.rand_name('a' * 260)
+        self.assertRaises(exceptions.BadRequest, self.client.create_image,
+                          self.server_id, snapshot_name)
+
+    @attr(type=['negative', 'gate'])
+    def test_delete_image_that_is_not_yet_active(self):
+        # Return an error while trying to delete an image what is creating
+
+        snapshot_name = data_utils.rand_name('test-snap-')
+        resp, body = self.client.create_image(self.server_id, snapshot_name)
+        self.assertEqual(202, resp.status)
+        image_id = data_utils.parse_image_id(resp['location'])
+        self.image_ids.append(image_id)
+        self.addCleanup(self._reset_server)
+
+        # Do not wait, attempt to delete the image, ensure it's successful
+        resp, body = self.client.delete_image(image_id)
+        self.assertEqual('204', resp['status'])
+        self.image_ids.remove(image_id)
+
+        self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
+
+
+class ImagesOneServerNegativeTestXML(ImagesOneServerNegativeTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/images/test_list_image_filters.py b/tempest/api/compute/v3/images/test_list_image_filters.py
new file mode 100644
index 0000000..bfdd8b2
--- /dev/null
+++ b/tempest/api/compute/v3/images/test_list_image_filters.py
@@ -0,0 +1,231 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# 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.api.compute import base
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+from tempest.test import attr
+
+
+LOG = logging.getLogger(__name__)
+
+
+class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ListImageFiltersTestJSON, cls).setUpClass()
+        if not cls.config.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+        cls.client = cls.images_client
+        cls.image_ids = []
+
+        try:
+            resp, cls.server1 = cls.create_test_server()
+            resp, cls.server2 = cls.create_test_server(wait_until='ACTIVE')
+            # NOTE(sdague) this is faster than doing the sync wait_util on both
+            cls.servers_client.wait_for_server_status(cls.server1['id'],
+                                                      'ACTIVE')
+
+            # Create images to be used in the filter tests
+            resp, cls.image1 = cls.create_image_from_server(
+                cls.server1['id'], wait_until='ACTIVE')
+            cls.image1_id = cls.image1['id']
+
+            # Servers have a hidden property for when they are being imaged
+            # Performing back-to-back create image calls on a single
+            # server will sometimes cause failures
+            resp, cls.image3 = cls.create_image_from_server(
+                cls.server2['id'], wait_until='ACTIVE')
+            cls.image3_id = cls.image3['id']
+
+            # Wait for the server to be active after the image upload
+            resp, cls.image2 = cls.create_image_from_server(
+                cls.server1['id'], wait_until='ACTIVE')
+            cls.image2_id = cls.image2['id']
+        except Exception:
+            LOG.exception('setUpClass failed')
+            cls.tearDownClass()
+            raise
+
+    @attr(type=['negative', 'gate'])
+    def test_get_image_not_existing(self):
+        # Check raises a NotFound
+        self.assertRaises(exceptions.NotFound, self.client.get_image,
+                          "nonexistingimageid")
+
+    @attr(type='gate')
+    def test_list_images_filter_by_status(self):
+        # The list of images should contain only images with the
+        # provided status
+        params = {'status': 'ACTIVE'}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_filter_by_name(self):
+        # List of all images should contain the expected images filtered
+        # by name
+        params = {'name': self.image1['name']}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_filter_by_server_id(self):
+        # The images should contain images filtered by server id
+        params = {'server': self.server1['id']}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]),
+                        "Failed to find image %s in images. Got images %s" %
+                        (self.image1_id, images))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_filter_by_server_ref(self):
+        # The list of servers should be filtered by server ref
+        server_links = self.server2['links']
+
+        # Try all server link types
+        for link in server_links:
+            params = {'server': link['href']}
+            resp, images = self.client.list_images(params)
+
+            self.assertFalse(any([i for i in images
+                                  if i['id'] == self.image1_id]))
+            self.assertFalse(any([i for i in images
+                                  if i['id'] == self.image2_id]))
+            self.assertTrue(any([i for i in images
+                                 if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_filter_by_type(self):
+        # The list of servers should be filtered by image type
+        params = {'type': 'snapshot'}
+        resp, images = self.client.list_images(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
+
+    @attr(type='gate')
+    def test_list_images_limit_results(self):
+        # Verify only the expected number of results are returned
+        params = {'limit': '1'}
+        resp, images = self.client.list_images(params)
+        # when _interface='xml', one element for images_links in images
+        # ref: Question #224349
+        self.assertEqual(1, len([x for x in images if 'id' in x]))
+
+    @attr(type='gate')
+    def test_list_images_filter_by_changes_since(self):
+        # Verify only updated images are returned in the detailed list
+
+        # Becoming ACTIVE will modify the updated time
+        # Filter by the image's created time
+        params = {'changes-since': self.image3['created']}
+        resp, images = self.client.list_images(params)
+        found = any([i for i in images if i['id'] == self.image3_id])
+        self.assertTrue(found)
+
+    @attr(type='gate')
+    def test_list_images_with_detail_filter_by_status(self):
+        # Detailed list of all images should only contain images
+        # with the provided status
+        params = {'status': 'ACTIVE'}
+        resp, images = self.client.list_images_with_detail(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_with_detail_filter_by_name(self):
+        # Detailed list of all images should contain the expected
+        # images filtered by name
+        params = {'name': self.image1['name']}
+        resp, images = self.client.list_images_with_detail(params)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_with_detail_limit_results(self):
+        # Verify only the expected number of results (with full details)
+        # are returned
+        params = {'limit': '1'}
+        resp, images = self.client.list_images_with_detail(params)
+        self.assertEqual(1, len(images))
+
+    @attr(type='gate')
+    def test_list_images_with_detail_filter_by_server_ref(self):
+        # Detailed list of servers should be filtered by server ref
+        server_links = self.server2['links']
+
+        # Try all server link types
+        for link in server_links:
+            params = {'server': link['href']}
+            resp, images = self.client.list_images_with_detail(params)
+
+            self.assertFalse(any([i for i in images
+                                  if i['id'] == self.image1_id]))
+            self.assertFalse(any([i for i in images
+                                  if i['id'] == self.image2_id]))
+            self.assertTrue(any([i for i in images
+                                 if i['id'] == self.image3_id]))
+
+    @attr(type='gate')
+    def test_list_images_with_detail_filter_by_type(self):
+        # The detailed list of servers should be filtered by image type
+        params = {'type': 'snapshot'}
+        resp, images = self.client.list_images_with_detail(params)
+        resp, image4 = self.client.get_image(self.image_ref)
+
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
+        self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+        self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
+
+    @attr(type='gate')
+    def test_list_images_with_detail_filter_by_changes_since(self):
+        # Verify an update image is returned
+
+        # Becoming ACTIVE will modify the updated time
+        # Filter by the image's created time
+        params = {'changes-since': self.image1['created']}
+        resp, images = self.client.list_images_with_detail(params)
+        self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+
+    @attr(type=['negative', 'gate'])
+    def test_get_nonexistant_image(self):
+        # Negative test: GET on non-existent image should fail
+        self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
+
+
+class ListImageFiltersTestXML(ListImageFiltersTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_attach_interfaces.py b/tempest/api/compute/v3/servers/test_attach_interfaces.py
index d12f708..d05296b 100644
--- a/tempest/api/compute/v3/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/v3/servers/test_attach_interfaces.py
@@ -26,6 +26,8 @@
     def setUpClass(cls):
         if not cls.config.service_available.neutron:
             raise cls.skipException("Neutron is required")
+        # This test class requires network and subnet
+        cls.set_network_resources(network=True, subnet=True)
         super(AttachInterfacesV3TestJSON, cls).setUpClass()
         cls.client = cls.interfaces_client
 
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index e1bb160..1555442 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -117,6 +117,104 @@
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
 
+class ServersWithSpecificFlavorV3TestJSON(base.BaseV3ComputeAdminTest):
+    _interface = 'json'
+    run_ssh = CONF.compute.run_ssh
+    disk_config = 'AUTO'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServersWithSpecificFlavorV3TestJSON, cls).setUpClass()
+        cls.meta = {'hello': 'world'}
+        cls.accessIPv4 = '1.1.1.1'
+        cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
+        cls.name = data_utils.rand_name('server')
+        file_contents = 'This is a test file.'
+        personality = [{'path': '/test.txt',
+                       'contents': base64.b64encode(file_contents)}]
+        cls.client = cls.servers_client
+        cls.flavor_client = cls.flavors_admin_client
+        cli_resp = cls.create_test_server(name=cls.name,
+                                          meta=cls.meta,
+                                          access_ip_v4=cls.accessIPv4,
+                                          access_ip_v6=cls.accessIPv6,
+                                          personality=personality,
+                                          disk_config=cls.disk_config)
+        cls.resp, cls.server_initial = cli_resp
+        cls.password = cls.server_initial['admin_password']
+        cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
+        resp, cls.server = cls.client.get_server(cls.server_initial['id'])
+
+    @testtools.skipIf(not run_ssh, 'Instance validation tests are disabled.')
+    @test.attr(type='gate')
+    def test_verify_created_server_ephemeral_disk(self):
+        # Verify that the ephemeral disk is created when creating server
+
+        def create_flavor_with_extra_specs(self):
+            flavor_with_eph_disk_name = data_utils.rand_name('eph_flavor')
+            flavor_with_eph_disk_id = data_utils.rand_int_id(start=1000)
+            ram = 512
+            vcpus = 1
+            disk = 10
+
+            # Create a flavor with extra specs
+            resp, flavor = (self.flavor_client.
+                            create_flavor(flavor_with_eph_disk_name,
+                                          ram, vcpus, disk,
+                                          flavor_with_eph_disk_id,
+                                          ephemeral=1, swap=1024, rxtx=1))
+            self.addCleanup(self.flavor_clean_up, flavor['id'])
+            self.assertEqual(200, resp.status)
+
+            return flavor['id']
+
+        def create_flavor_without_extra_specs(self):
+            flavor_no_eph_disk_name = data_utils.rand_name('no_eph_flavor')
+            flavor_no_eph_disk_id = data_utils.rand_int_id(start=1000)
+
+            ram = 512
+            vcpus = 1
+            disk = 10
+
+            # Create a flavor without extra specs
+            resp, flavor = (self.flavor_client.
+                            create_flavor(flavor_no_eph_disk_name,
+                                          ram, vcpus, disk,
+                                          flavor_no_eph_disk_id))
+            self.addCleanup(self.flavor_clean_up, flavor['id'])
+            self.assertEqual(200, resp.status)
+
+            return flavor['id']
+
+        def flavor_clean_up(self, flavor_id):
+            resp, body = self.flavor_client.delete_flavor(flavor_id)
+            self.assertEqual(resp.status, 202)
+            self.flavor_client.wait_for_resource_deletion(flavor_id)
+
+        flavor_with_eph_disk_id = self.create_flavor_with_extra_specs()
+        flavor_no_eph_disk_id = self.create_flavor_without_extra_specs()
+
+        admin_pass = self.image_ssh_password
+
+        resp, server_no_eph_disk = (self.
+                                    create_test_server(
+                                    wait_until='ACTIVE',
+                                    adminPass=admin_pass,
+                                    flavor=flavor_no_eph_disk_id))
+        resp, server_with_eph_disk = (self.create_test_server(
+                                      wait_until='ACTIVE',
+                                      adminPass=admin_pass,
+                                      flavor=flavor_with_eph_disk_id))
+        # Get partition number of server without extra specs.
+        linux_client = RemoteClient(server_no_eph_disk,
+                                    self.ssh_user, self.password)
+        partition_num = len(linux_client.get_partitions())
+
+        linux_client = RemoteClient(server_with_eph_disk,
+                                    self.ssh_user, self.password)
+        self.assertEqual(partition_num + 1, linux_client.get_partitions())
+
+
 class ServersV3TestManualDisk(ServersV3TestJSON):
     disk_config = 'MANUAL'
 
@@ -130,3 +228,7 @@
 
 class ServersV3TestXML(ServersV3TestJSON):
     _interface = 'xml'
+
+
+class ServersWithSpecificFlavorV3TestXML(ServersWithSpecificFlavorV3TestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_list_server_filters.py b/tempest/api/compute/v3/servers/test_list_server_filters.py
index 3dd7b0b..140cc2f 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -176,6 +176,23 @@
         self.assertEqual(['ACTIVE'] * 3, [x['status'] for x in servers])
 
     @attr(type='gate')
+    def test_list_servers_filter_by_shutoff_status(self):
+        # Filter the list of servers by server shutoff status
+        params = {'status': 'shutoff'}
+        self.client.stop(self.s1['id'])
+        self.client.wait_for_server_status(self.s1['id'],
+                                           'SHUTOFF')
+        resp, body = self.client.list_servers(params)
+        self.client.start(self.s1['id'])
+        self.client.wait_for_server_status(self.s1['id'],
+                                           'ACTIVE')
+        servers = body['servers']
+
+        self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s2['id'], map(lambda x: x['id'], servers))
+        self.assertNotIn(self.s3['id'], map(lambda x: x['id'], servers))
+
+    @attr(type='gate')
     def test_list_servers_filtered_by_name_wildcard(self):
         # List all servers that contains '-instance' in name
         params = {'name': '-instance'}
diff --git a/tempest/api/compute/v3/servers/test_list_servers_negative.py b/tempest/api/compute/v3/servers/test_list_servers_negative.py
index 3f7f885..b8e3d10 100644
--- a/tempest/api/compute/v3/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_list_servers_negative.py
@@ -18,69 +18,18 @@
 import datetime
 
 from tempest.api.compute import base
-from tempest import clients
 from tempest import exceptions
 from tempest.test import attr
 
 
 class ListServersNegativeV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
-
-    @classmethod
-    def _ensure_no_servers(cls, servers, username, tenant_name):
-        """
-        If there are servers and there is tenant isolation then a
-        skipException is raised to skip the test since it requires no servers
-        to already exist for the given user/tenant.
-        If there are servers and there is not tenant isolation then the test
-        blocks while the servers are being deleted.
-        """
-        if len(servers):
-            if not cls.config.compute.allow_tenant_isolation:
-                for srv in servers:
-                    cls.client.wait_for_server_termination(srv['id'],
-                                                           ignore_error=True)
-            else:
-                msg = ("User/tenant %(u)s/%(t)s already have "
-                       "existing server instances. Skipping test." %
-                       {'u': username, 't': tenant_name})
-                raise cls.skipException(msg)
+    force_tenant_isolation = True
 
     @classmethod
     def setUpClass(cls):
         super(ListServersNegativeV3TestJSON, cls).setUpClass()
         cls.client = cls.servers_client
-        cls.servers = []
-
-        if cls.multi_user:
-            if cls.config.compute.allow_tenant_isolation:
-                creds = cls.isolated_creds.get_alt_creds()
-                username, tenant_name, password = creds
-                cls.alt_manager = clients.Manager(username=username,
-                                                  password=password,
-                                                  tenant_name=tenant_name)
-            else:
-                # Use the alt_XXX credentials in the config file
-                cls.alt_manager = clients.AltManager()
-            cls.alt_client = cls.alt_manager.servers_client
-
-        # Under circumstances when there is not a tenant/user
-        # created for the test case, the test case checks
-        # to see if there are existing servers for the
-        # either the normal user/tenant or the alt user/tenant
-        # and if so, the whole test is skipped. We do this
-        # because we assume a baseline of no servers at the
-        # start of the test instead of destroying any existing
-        # servers.
-        resp, body = cls.client.list_servers()
-        cls._ensure_no_servers(body['servers'],
-                               cls.os.username,
-                               cls.os.tenant_name)
-
-        resp, body = cls.alt_client.list_servers()
-        cls._ensure_no_servers(body['servers'],
-                               cls.alt_manager.username,
-                               cls.alt_manager.tenant_name)
 
         # The following servers are created for use
         # by the test methods in this class. These
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 602bd5b..f462cc2 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -15,7 +15,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import base64
 import time
 
 import testtools
@@ -113,15 +112,11 @@
         # The server should be rebuilt using the provided image and data
         meta = {'rebuild': 'server'}
         new_name = data_utils.rand_name('server')
-        file_contents = 'Test server rebuild.'
-        personality = [{'path': 'rebuild.txt',
-                       'contents': base64.b64encode(file_contents)}]
         password = 'rebuildPassw0rd'
         resp, rebuilt_server = self.client.rebuild(self.server_id,
                                                    self.image_ref_alt,
                                                    name=new_name,
                                                    metadata=meta,
-                                                   personality=personality,
                                                    admin_password=password)
         self.addCleanup(self.client.rebuild, self.server_id, self.image_ref)
 
@@ -134,9 +129,9 @@
         # Verify the server properties after the rebuild completes
         self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
         resp, server = self.client.get_server(rebuilt_server['id'])
-        rebuilt_image_id = rebuilt_server['image']['id']
+        rebuilt_image_id = server['image']['id']
         self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
-        self.assertEqual(new_name, rebuilt_server['name'])
+        self.assertEqual(new_name, server['name'])
 
         if self.run_ssh:
             # Verify that the user can authenticate with the provided password
@@ -174,7 +169,7 @@
 
     def _detect_server_image_flavor(self, server_id):
         # Detects the current server image flavor ref.
-        resp, server = self.client.get_server(self.server_id)
+        resp, server = self.client.get_server(server_id)
         current_flavor = server['flavor']['id']
         new_flavor_ref = self.flavor_ref_alt \
             if current_flavor == self.flavor_ref else self.flavor_ref
diff --git a/tempest/api/compute/v3/servers/test_server_addresses.py b/tempest/api/compute/v3/servers/test_server_addresses.py
index 82588b6..ebaf68a 100644
--- a/tempest/api/compute/v3/servers/test_server_addresses.py
+++ b/tempest/api/compute/v3/servers/test_server_addresses.py
@@ -25,6 +25,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # This test module might use a network and a subnet
+        cls.set_network_resources(network=True, subnet=True)
         super(ServerAddressesV3Test, cls).setUpClass()
         cls.client = cls.servers_client
 
diff --git a/tempest/api/compute/v3/servers/test_server_metadata.py b/tempest/api/compute/v3/servers/test_server_metadata.py
index ee0f4a9..f36feb3 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata.py
@@ -20,12 +20,12 @@
 from tempest.test import attr
 
 
-class ServerMetadataTestJSON(base.BaseV2ComputeTest):
+class ServerMetadataV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @classmethod
     def setUpClass(cls):
-        super(ServerMetadataTestJSON, cls).setUpClass()
+        super(ServerMetadataV3TestJSON, cls).setUpClass()
         cls.client = cls.servers_client
         cls.quotas = cls.quotas_client
         cls.admin_client = cls._get_identity_admin_client()
@@ -37,7 +37,7 @@
         cls.server_id = server['id']
 
     def setUp(self):
-        super(ServerMetadataTestJSON, self).setUp()
+        super(ServerMetadataV3TestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
         resp, _ = self.client.set_server_metadata(self.server_id, meta)
         self.assertEqual(resp.status, 200)
@@ -88,7 +88,7 @@
         meta = {'key1': 'alt1', 'key3': 'value3'}
         resp, metadata = self.client.update_server_metadata(self.server_id,
                                                             meta)
-        self.assertEqual(200, resp.status)
+        self.assertEqual(201, resp.status)
 
         # Verify the values have been updated to the proper values
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
@@ -213,5 +213,5 @@
                           self.server_id, meta=meta, no_metadata_field=True)
 
 
-class ServerMetadataTestXML(ServerMetadataTestJSON):
+class ServerMetadataV3TestXML(ServerMetadataV3TestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_personality.py b/tempest/api/compute/v3/servers/test_server_personality.py
deleted file mode 100644
index c6d2e44..0000000
--- a/tempest/api/compute/v3/servers/test_server_personality.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack Foundation
-# 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 base64
-
-from tempest.api.compute import base
-from tempest import exceptions
-from tempest.test import attr
-
-
-class ServerPersonalityTestJSON(base.BaseV2ComputeTest):
-    _interface = 'json'
-
-    @classmethod
-    def setUpClass(cls):
-        super(ServerPersonalityTestJSON, cls).setUpClass()
-        cls.client = cls.servers_client
-        cls.user_client = cls.limits_client
-
-    @attr(type='gate')
-    def test_personality_files_exceed_limit(self):
-        # Server creation should fail if greater than the maximum allowed
-        # number of files are injected into the server.
-        file_contents = 'This is a test file.'
-        personality = []
-        max_file_limit = \
-            self.user_client.get_specific_absolute_limit("maxPersonality")
-        for i in range(0, int(max_file_limit) + 1):
-            path = 'etc/test' + str(i) + '.txt'
-            personality.append({'path': path,
-                                'contents': base64.b64encode(file_contents)})
-        self.assertRaises(exceptions.OverLimit, self.create_test_server,
-                          personality=personality)
-
-    @attr(type='gate')
-    def test_can_create_server_with_max_number_personality_files(self):
-        # Server should be created successfully if maximum allowed number of
-        # files is injected into the server during creation.
-        file_contents = 'This is a test file.'
-        max_file_limit = \
-            self.user_client.get_specific_absolute_limit("maxPersonality")
-        person = []
-        for i in range(0, int(max_file_limit)):
-            path = 'etc/test' + str(i) + '.txt'
-            person.append({
-                'path': path,
-                'contents': base64.b64encode(file_contents),
-            })
-        resp, server = self.create_test_server(personality=person)
-        self.assertEqual('202', resp['status'])
-
-
-class ServerPersonalityTestXML(ServerPersonalityTestJSON):
-    _interface = "xml"
diff --git a/tempest/api/compute/v3/servers/test_server_rescue.py b/tempest/api/compute/v3/servers/test_server_rescue.py
index eebd4d8..387fa88 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue.py
@@ -56,7 +56,7 @@
         cls.rescue_password = resc_server['admin_password']
 
         cls.servers_client.rescue_server(
-            cls.rescue_id, cls.rescue_password)
+            cls.rescue_id, admin_password=cls.rescue_password)
         cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
 
     def setUp(self):
@@ -93,7 +93,7 @@
     @attr(type='smoke')
     def test_rescue_unrescue_instance(self):
         resp, body = self.servers_client.rescue_server(
-            self.server_id, self.password)
+            self.server_id, admin_password=self.password)
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         resp, body = self.servers_client.unrescue_server(self.server_id)
@@ -134,7 +134,8 @@
     @attr(type=['negative', 'gate'])
     def test_rescued_vm_attach_volume(self):
         # Rescue the server
-        self.servers_client.rescue_server(self.server_id, self.password)
+        self.servers_client.rescue_server(self.server_id,
+                                          admin_password=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
@@ -155,7 +156,8 @@
             self.volume_to_detach['id'], 'in-use')
 
         # Rescue the server
-        self.servers_client.rescue_server(self.server_id, self.password)
+        self.servers_client.rescue_server(self.server_id,
+                                          admin_password=self.password)
         self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
         # addCleanup is a LIFO queue
         self.addCleanup(self._detach, self.server_id,
diff --git a/tempest/api/compute/v3/test_extensions.py b/tempest/api/compute/v3/test_extensions.py
index 2affd86..f37ebcf 100644
--- a/tempest/api/compute/v3/test_extensions.py
+++ b/tempest/api/compute/v3/test_extensions.py
@@ -17,19 +17,33 @@
 
 
 from tempest.api.compute import base
+from tempest.openstack.common import log as logging
 from tempest import test
 
 
+LOG = logging.getLogger(__name__)
+
+
 class ExtensionsV3TestJSON(base.BaseV3ComputeTest):
     _interface = 'json'
 
     @test.attr(type='gate')
     def test_list_extensions(self):
         # List of all extensions
+        if len(self.config.compute_feature_enabled.api_v3_extensions) == 0:
+            raise self.skipException('There are not any extensions configured')
         resp, extensions = self.extensions_client.list_extensions()
-        self.assertIn("extensions", extensions)
         self.assertEqual(200, resp.status)
-        self.assertTrue(self.extensions_client.is_enabled("Consoles"))
+        ext = self.config.compute_feature_enabled.api_v3_extensions[0]
+        if ext == 'all':
+            self.assertIn('Hosts', map(lambda x: x['name'], extensions))
+        elif ext:
+            self.assertIn(ext, map(lambda x: x['name'], extensions))
+        else:
+            raise self.skipException('There are not any extensions configured')
+        # Log extensions list
+        extension_list = map(lambda x: x['name'], extensions)
+        LOG.debug("Nova extensions: %s" % ','.join(extension_list))
 
     @test.attr(type='gate')
     def test_get_extension(self):
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index ae6996d..f6fed5b 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -18,6 +18,7 @@
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.test import attr
+from testtools.matchers import ContainsAll
 
 
 class VolumesGetTestJSON(base.BaseV2ComputeTest):
@@ -65,29 +66,10 @@
                          fetched_volume['id'],
                          'The fetched Volume is different '
                          'from the created Volume')
-        self.assertEqual(metadata,
-                         fetched_volume['metadata'],
-                         'The fetched Volume is different '
-                         'from the created Volume')
-
-    @attr(type='gate')
-    def test_volume_get_metadata_none(self):
-        # CREATE, GET empty metadata dict
-        v_name = data_utils.rand_name('Volume-')
-        # Create volume
-        resp, volume = self.client.create_volume(size=1,
-                                                 display_name=v_name,
-                                                 metadata={})
-        self.addCleanup(self._delete_volume, volume)
-        self.assertEqual(200, resp.status)
-        self.assertIn('id', volume)
-        self.assertIn('displayName', volume)
-        # Wait for Volume status to become ACTIVE
-        self.client.wait_for_volume_status(volume['id'], 'available')
-        # GET Volume
-        resp, fetched_volume = self.client.get_volume(volume['id'])
-        self.assertEqual(200, resp.status)
-        self.assertEqual(fetched_volume['metadata'], {})
+        self.assertThat(fetched_volume['metadata'].items(),
+                        ContainsAll(metadata.items()),
+                        'The fetched Volume metadata misses data '
+                        'from the created Volume')
 
     def _delete_volume(self, volume):
         # Delete the Volume created in this method
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1ecc90c..98b298e 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -17,21 +17,24 @@
 from tempest.api.identity import base
 from tempest import clients
 from tempest.common.utils.data_utils import rand_name
+from tempest import config
 from tempest import exceptions
 from tempest.openstack.common import timeutils
 from tempest.test import attr
 
+CONF = config.CONF
+
 
 class BaseTrustsV3Test(base.BaseIdentityAdminTest):
 
     def setUp(self):
         super(BaseTrustsV3Test, self).setUp()
         # Use alt_username as the trustee
-        self.trustee_username = self.config.identity.alt_username
+        if not CONF.identity_feature_enabled.trust:
+            raise self.skipException("Trusts aren't enabled")
 
+        self.trustee_username = self.config.identity.alt_username
         self.trust_id = None
-        self.create_trustor_and_roles()
-        self.addCleanup(self.cleanup_user_and_roles)
 
     def tearDown(self):
         if self.trust_id:
@@ -50,7 +53,7 @@
         # Create a trustor User
         self.trustor_username = rand_name('user-')
         u_desc = self.trustor_username + 'description'
-        u_email = self.trustor_username + '@testmail.tm'
+        u_email = self.trustor_username + '@testmail.xx'
         self.trustor_password = rand_name('pass-')
         resp, user = self.v3_client.create_user(
             self.trustor_username,
@@ -193,6 +196,7 @@
     def setUp(self):
         super(TrustsV3TestJSON, self).setUp()
         self.create_trustor_and_roles()
+        self.addCleanup(self.cleanup_user_and_roles)
 
     @attr(type='smoke')
     def test_trust_impersonate(self):
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 28ed5b6..b521bce 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -34,7 +34,8 @@
         super(BaseImageTest, cls).setUpClass()
         cls.created_images = []
         cls._interface = 'json'
-        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+        cls.isolated_creds = isolated_creds.IsolatedCreds(
+            cls.__name__, network_resources=cls.network_resources)
         if not cls.config.service_available.glance:
             skip_msg = ("%s skipped as glance is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
@@ -129,7 +130,7 @@
             raise cls.skipException(msg)
 
 
-class BaseV2MemeberImageTest(BaseImageTest):
+class BaseV2MemeberImageTest(BaseV2ImageTest):
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/network/admin/test_agent_management.py b/tempest/api/network/admin/test_agent_management.py
index 94659b2..4b36197 100644
--- a/tempest/api/network/admin/test_agent_management.py
+++ b/tempest/api/network/admin/test_agent_management.py
@@ -36,6 +36,12 @@
         agents = body['agents']
         self.assertIn(self.agent, agents)
 
+    @attr(type=['smoke'])
+    def test_list_agents_non_admin(self):
+        resp, body = self.client.list_agents()
+        self.assertEqual('200', resp['status'])
+        self.assertEqual(len(body["agents"]), 0)
+
     @attr(type='smoke')
     def test_show_agent(self):
         resp, body = self.admin_client.show_agent(self.agent['id'])
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index dcad101..fd3dc9f 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -50,6 +50,8 @@
 
     @classmethod
     def setUpClass(cls):
+        # Create no network resources for these test.
+        cls.set_network_resources()
         super(BaseNetworkTest, cls).setUpClass()
         os = clients.Manager(interface=cls._interface)
         cls.network_cfg = os.config.network
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 68ca66a..849d62e 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -127,6 +127,21 @@
         self.assertIsNotNone(found, msg)
 
     @attr(type='smoke')
+    def test_list_networks_fields(self):
+        # Verify listing some fields of the networks
+        resp, body = self.client.list_networks(fields='id')
+        self.assertEqual('200', resp['status'])
+        networks = body['networks']
+        found = None
+        for n in networks:
+            self.assertEqual(len(n), 1)
+            self.assertIn('id', n)
+            if (n['id'] == self.network['id']):
+                found = n['id']
+        self.assertIsNotNone(found,
+                             "Created network id not found in the list")
+
+    @attr(type='smoke')
     def test_show_subnet(self):
         # Verifies the details of a subnet
         resp, body = self.client.show_subnet(self.subnet['id'])
@@ -149,6 +164,21 @@
         self.assertIsNotNone(found, msg)
 
     @attr(type='smoke')
+    def test_list_subnets_fields(self):
+        # Verify listing some fields of the subnets
+        resp, body = self.client.list_subnets(fields='id')
+        self.assertEqual('200', resp['status'])
+        subnets = body['subnets']
+        found = None
+        for n in subnets:
+            self.assertEqual(len(n), 1)
+            self.assertIn('id', n)
+            if (n['id'] == self.subnet['id']):
+                found = n['id']
+        self.assertIsNotNone(found,
+                             "Created subnet id not found in the list")
+
+    @attr(type='smoke')
     def test_create_update_delete_port(self):
         # Verify that successful port creation, update & deletion
         resp, body = self.client.create_port(self.network['id'])
@@ -184,6 +214,21 @@
                 found = n['id']
         self.assertIsNotNone(found, "Port list doesn't contain created port")
 
+    @attr(type='smoke')
+    def test_list_ports_fields(self):
+        # Verify listing some fields of the ports
+        resp, body = self.client.list_ports(fields='id')
+        self.assertEqual('200', resp['status'])
+        ports_list = body['ports']
+        found = None
+        for n in ports_list:
+            self.assertEqual(len(n), 1)
+            self.assertIn('id', n)
+            if (n['id'] == self.port['id']):
+                found = n['id']
+        self.assertIsNotNone(found,
+                             "Created port id not found in the list")
+
 
 class NetworksTestXML(NetworksTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 9b0a3de..37b1f2c 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -82,6 +82,36 @@
                      for rule in rule_list_body['security_group_rules']]
         self.assertIn(rule_create_body['security_group_rule']['id'], rule_list)
 
+    @attr(type='smoke')
+    def test_create_security_group_rule_with_additional_args(self):
+        # Verify creating security group rule with the following
+        # arguments works: "protocol": "tcp", "port_range_max": 77,
+        # "port_range_min": 77, "direction":"ingress".
+        group_create_body, _ = self._create_security_group()
+
+        direction = 'ingress'
+        protocol = 'tcp'
+        port_range_min = 77
+        port_range_max = 77
+        resp, rule_create_body = self.client.create_security_group_rule(
+            group_create_body['security_group']['id'],
+            direction=direction,
+            protocol=protocol,
+            port_range_min=port_range_min,
+            port_range_max=port_range_max
+        )
+
+        self.assertEqual('201', resp['status'])
+        sec_group_rule = rule_create_body['security_group_rule']
+        self.addCleanup(self._delete_security_group_rule,
+                        sec_group_rule['id']
+                        )
+
+        self.assertEqual(sec_group_rule['direction'], direction)
+        self.assertEqual(sec_group_rule['protocol'], protocol)
+        self.assertEqual(int(sec_group_rule['port_range_min']), port_range_min)
+        self.assertEqual(int(sec_group_rule['port_range_max']), port_range_max)
+
 
 class SecGroupTestXML(SecGroupTest):
     _interface = 'xml'
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index e7cb806..b4928dd 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -32,7 +32,8 @@
         if not cls.config.service_available.swift:
             skip_msg = ("%s skipped as swift is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
-        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+        cls.isolated_creds = isolated_creds.IsolatedCreds(
+            cls.__name__, network_resources=cls.network_resources)
         if cls.config.compute.allow_tenant_isolation:
             # Get isolated creds for normal user
             creds = cls.isolated_creds.get_primary_creds()
@@ -101,6 +102,7 @@
 
         The containers should be visible from the container_client given.
         Will not throw any error if the containers don't exist.
+        Will not check that object and container deletions succeed.
 
         :param containers: list of container names to remove
         :param container_client: if None, use cls.container_client, this means
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index ac1c7d1..6312f69 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -14,21 +14,17 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import testtools
-
 from tempest.api.object_storage import base
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
-from tempest.test import attr
+from tempest import test
 
 CONF = config.CONF
 
 
 class AccountQuotasTest(base.BaseObjectTest):
-    accounts_quotas_available = \
-        CONF.object_storage_feature_enabled.accounts_quotas
 
     @classmethod
     def setUpClass(cls):
@@ -99,9 +95,8 @@
         cls.data.teardown_all()
         super(AccountQuotasTest, cls).tearDownClass()
 
-    @testtools.skipIf(not accounts_quotas_available,
-                      "Account Quotas middleware not available")
-    @attr(type="smoke")
+    @test.attr(type="smoke")
+    @test.requires_ext(extension='account_quotas', service='object')
     def test_upload_valid_object(self):
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string()
@@ -111,9 +106,8 @@
         self.assertEqual(resp["status"], "201")
         self.assertHeaders(resp, 'Object', 'PUT')
 
-    @testtools.skipIf(not accounts_quotas_available,
-                      "Account Quotas middleware not available")
-    @attr(type=["negative", "smoke"])
+    @test.attr(type=["negative", "smoke"])
+    @test.requires_ext(extension='account_quotas', service='object')
     def test_upload_large_object(self):
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string(30)
@@ -121,9 +115,8 @@
                           self.object_client.create_object,
                           self.container_name, object_name, data)
 
-    @testtools.skipIf(not accounts_quotas_available,
-                      "Account Quotas middleware not available")
-    @attr(type=["smoke"])
+    @test.attr(type=["smoke"])
+    @test.requires_ext(extension='account_quotas', service='object')
     def test_admin_modify_quota(self):
         """Test that the ResellerAdmin is able to modify and remove the quota
         on a user's account.
@@ -146,9 +139,8 @@
             self.assertEqual(resp["status"], "204")
             self.assertHeaders(resp, 'Account', 'POST')
 
-    @testtools.skipIf(not accounts_quotas_available,
-                      "Account Quotas middleware not available")
-    @attr(type=["negative", "smoke"])
+    @test.attr(type=["negative", "smoke"])
+    @test.requires_ext(extension='account_quotas', service='object')
     def test_user_modify_quota(self):
         """Test that a user is not able to modify or remove a quota on
         its account.
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 513d24a..103ee89 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -15,25 +15,19 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
 
 CONF = config.CONF
 QUOTA_BYTES = 10
 QUOTA_COUNT = 3
-SKIP_MSG = "Container quotas middleware not available."
 
 
 class ContainerQuotasTest(base.BaseObjectTest):
     """Attemps to test the perfect behavior of quotas in a container."""
-    container_quotas_available = \
-        CONF.object_storage_feature_enabled.container_quotas
 
     def setUp(self):
         """Creates and sets a container with quotas.
@@ -58,8 +52,8 @@
         self.delete_containers([self.container_name])
         super(ContainerQuotasTest, self).tearDown()
 
-    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
-    @attr(type="smoke")
+    @test.requires_ext(extension='container_quotas', service='object')
+    @test.attr(type="smoke")
     def test_upload_valid_object(self):
         """Attempts to uploads an object smaller than the bytes quota."""
         object_name = data_utils.rand_name(name="TestObject")
@@ -69,14 +63,14 @@
 
         resp, _ = self.object_client.create_object(
             self.container_name, object_name, data)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         nafter = self._get_bytes_used()
         self.assertEqual(nbefore + len(data), nafter)
 
-    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
-    @attr(type="smoke")
+    @test.requires_ext(extension='container_quotas', service='object')
+    @test.attr(type="smoke")
     def test_upload_large_object(self):
         """Attempts to upload an object lagger than the bytes quota."""
         object_name = data_utils.rand_name(name="TestObject")
@@ -91,8 +85,8 @@
         nafter = self._get_bytes_used()
         self.assertEqual(nbefore, nafter)
 
-    @testtools.skipIf(not container_quotas_available, SKIP_MSG)
-    @attr(type="smoke")
+    @test.requires_ext(extension='container_quotas', service='object')
+    @test.attr(type="smoke")
     def test_upload_too_many_objects(self):
         """Attempts to upload many objects that exceeds the count limit."""
         for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index dcfe219..ec78774 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -16,25 +16,38 @@
 #    under the License.
 
 import time
+import urlparse
 
 from tempest.api.object_storage import base
 from tempest.common.utils import data_utils
 from tempest.test import attr
-from tempest.test import skip_because
+from tempest.test import HTTP_SUCCESS
+
+# This test can be quite long to run due to its
+# dependency on container-sync process running interval.
+# You can obviously reduce the container-sync interval in the
+# container-server configuration.
 
 
 class ContainerSyncTest(base.BaseObjectTest):
+
     @classmethod
     def setUpClass(cls):
         super(ContainerSyncTest, cls).setUpClass()
         cls.containers = []
         cls.objects = []
+
+        # Default container-server config only allows localhost
+        cls.local_ip = '127.0.0.1'
+
+        # Must be configure according to container-sync interval
         container_sync_timeout = \
             int(cls.config.object_storage.container_sync_timeout)
         cls.container_sync_interval = \
             int(cls.config.object_storage.container_sync_interval)
         cls.attempts = \
             int(container_sync_timeout / cls.container_sync_interval)
+
         # define container and object clients
         cls.clients = {}
         cls.clients[data_utils.rand_name(name='TestContainerSync')] = \
@@ -51,8 +64,7 @@
             cls.delete_containers(cls.containers, client[0], client[1])
         super(ContainerSyncTest, cls).tearDownClass()
 
-    @skip_because(bug="1093743")
-    @attr(type='gate')
+    @attr(type='slow')
     def test_container_synchronization(self):
         # container to container synchronization
         # to allow/accept sync requests to/from other accounts
@@ -62,51 +74,53 @@
             cont_client = [self.clients[c][0] for c in cont]
             obj_client = [self.clients[c][1] for c in cont]
             # tell first container to synchronize to a second
+            client_proxy_ip = \
+                urlparse.urlparse(cont_client[1].base_url).netloc.split(':')[0]
+            client_base_url = \
+                cont_client[1].base_url.replace(client_proxy_ip,
+                                                self.local_ip)
             headers = {'X-Container-Sync-Key': 'sync_key',
                        'X-Container-Sync-To': "%s/%s" %
-                       (cont_client[1].base_url, str(cont[1]))}
+                       (client_base_url, str(cont[1]))}
             resp, body = \
                 cont_client[0].put(str(cont[0]), body=None, headers=headers)
-            self.assertIn(resp['status'], ('202', '201'),
-                          'Error installing X-Container-Sync-To '
-                          'for the container "%s"' % (cont[0]))
+            self.assertIn(int(resp['status']), HTTP_SUCCESS)
             # create object in container
             object_name = data_utils.rand_name(name='TestSyncObject')
             data = object_name[::-1]  # data_utils.arbitrary_string()
             resp, _ = obj_client[0].create_object(cont[0], object_name, data)
-            self.assertEqual(resp['status'], '201',
-                             'Error creating the object "%s" in'
-                             'the container "%s"'
-                             % (object_name, cont[0]))
+            self.assertIn(int(resp['status']), HTTP_SUCCESS)
             self.objects.append(object_name)
 
         # wait until container contents list is not empty
         cont_client = [self.clients[c][0] for c in self.containers]
         params = {'format': 'json'}
         while self.attempts > 0:
-            # get first container content
-            resp, object_list_0 = \
-                cont_client[0].\
-                list_container_contents(self.containers[0], params=params)
-            self.assertEqual(resp['status'], '200',
-                             'Error listing the destination container`s'
-                             ' "%s" contents' % (self.containers[0]))
-            object_list_0 = dict((obj['name'], obj) for obj in object_list_0)
-            # get second container content
-            resp, object_list_1 = \
-                cont_client[1].\
-                list_container_contents(self.containers[1], params=params)
-            self.assertEqual(resp['status'], '200',
-                             'Error listing the destination container`s'
-                             ' "%s" contents' % (self.containers[1]))
-            object_list_1 = dict((obj['name'], obj) for obj in object_list_1)
+            object_lists = []
+            for client_index in (0, 1):
+                resp, object_list = \
+                    cont_client[client_index].\
+                    list_container_contents(self.containers[client_index],
+                                            params=params)
+                self.assertIn(int(resp['status']), HTTP_SUCCESS)
+                object_lists.append(dict(
+                    (obj['name'], obj) for obj in object_list))
             # check that containers are not empty and have equal keys()
             # or wait for next attempt
-            if not object_list_0 or not object_list_1 or \
-                    set(object_list_0.keys()) != set(object_list_1.keys()):
+            if not object_lists[0] or not object_lists[1] or \
+                    set(object_lists[0].keys()) != set(object_lists[1].keys()):
                 time.sleep(self.container_sync_interval)
                 self.attempts -= 1
             else:
                 break
-        self.assertEqual(object_list_0, object_list_1,
+
+        self.assertEqual(object_lists[0], object_lists[1],
                          'Different object lists in containers.')
+
+        # Verify object content
+        obj_clients = [(self.clients[c][1], c) for c in self.containers]
+        for obj_client, cont in obj_clients:
+            for obj_name in object_lists[0]:
+                resp, object_content = obj_client.get_object(cont, obj_name)
+                self.assertIn(int(resp['status']), HTTP_SUCCESS)
+                self.assertEqual(object_content, obj_name[::-1])
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 41430c8..debd432 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -19,27 +19,14 @@
 from tempest.api.object_storage import base
 from tempest import clients
 from tempest.common import custom_matchers
-from tempest import config
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
-
-CONF = config.CONF
+from tempest import test
 
 
 class CrossdomainTest(base.BaseObjectTest):
-    crossdomain_available = \
-        CONF.object_storage_feature_enabled.crossdomain
 
     @classmethod
     def setUpClass(cls):
         super(CrossdomainTest, cls).setUpClass()
-
-        # skip this test if CORS isn't enabled in the conf file.
-        if not cls.crossdomain_available:
-            skip_msg = ("%s skipped as Crossdomain middleware not available"
-                        % cls.__name__)
-            raise cls.skipException(skip_msg)
-
         # creates a test user. The test user will set its base_url to the Swift
         # endpoint and test the healthcheck feature.
         cls.data.setup_test_user()
@@ -75,12 +62,13 @@
 
         super(CrossdomainTest, self).tearDown()
 
-    @attr('gate')
+    @test.attr('gate')
+    @test.requires_ext(extension='crossdomain', service='object')
     def test_get_crossdomain_policy(self):
         resp, body = self.os_test_user.account_client.get("crossdomain.xml",
                                                           {})
 
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertTrue(body.startswith(self.xml_start) and
                         body.endswith(self.xml_end))
 
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 4958f70..9c2834d 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -21,7 +21,6 @@
 from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.test import attr
-from tempest.test import skip_because
 
 
 class ObjectExpiryTest(base.BaseObjectTest):
@@ -33,31 +32,20 @@
 
     @classmethod
     def tearDownClass(cls):
-        """The test script fails in tear down class
-        as the container contains expired objects (LP bug 1069849).
-        But delete action for the expired object is raising
-        NotFound exception and also non empty container cannot be deleted.
-        """
         cls.delete_containers([cls.container_name])
         super(ObjectExpiryTest, cls).tearDownClass()
 
-    @skip_because(bug="1069849")
-    @attr(type='gate')
-    def test_get_object_after_expiry_time(self):
-        # TODO(harika-vakadi): similar test case has to be created for
-        # "X-Delete-At", after this test case works.
-
+    def _test_object_expiry(self, metadata):
         # create object
         object_name = data_utils.rand_name(name='TestObject')
-        data = data_utils.arbitrary_string()
         resp, _ = self.object_client.create_object(self.container_name,
-                                                   object_name, data)
-        # update object metadata with expiry time of 3 seconds
-        metadata = {'X-Delete-After': '3'}
+                                                   object_name, '')
+        # update object metadata
         resp, _ = \
             self.object_client.update_object_metadata(self.container_name,
                                                       object_name, metadata,
                                                       metadata_prefix='')
+        # verify object metadata
         resp, _ = \
             self.object_client.list_object_metadata(self.container_name,
                                                     object_name)
@@ -69,10 +57,20 @@
         self.assertEqual(resp['status'], '200')
         self.assertHeaders(resp, 'Object', 'GET')
         self.assertIn('x-delete-at', resp)
-        # check data
-        self.assertEqual(body, data)
+
         # sleep for over 5 seconds, so that object expires
         time.sleep(5)
+
         # object should not be there anymore
         self.assertRaises(exceptions.NotFound, self.object_client.get_object,
                           self.container_name, object_name)
+
+    @attr(type='gate')
+    def test_get_object_after_expiry_time(self):
+        metadata = {'X-Delete-After': '3'}
+        self._test_object_expiry(metadata)
+
+    @attr(type='gate')
+    def test_get_object_at_expiry_time(self):
+        metadata = {'X-Delete-At': str(int(time.time()) + 3)}
+        self._test_object_expiry(metadata)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index d0e5353..d1b3674 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -24,27 +24,16 @@
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import exceptions
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
 
 CONF = config.CONF
 
 
 class ObjectTempUrlTest(base.BaseObjectTest):
 
-    tempurl_available = \
-        CONF.object_storage_feature_enabled.tempurl
-
     @classmethod
     def setUpClass(cls):
         super(ObjectTempUrlTest, cls).setUpClass()
-
-        # skip this test if TempUrl isn't enabled in the conf file.
-        if not cls.tempurl_available:
-            skip_msg = ("%s skipped as TempUrl middleware not available"
-                        % cls.__name__)
-            raise cls.skipException(skip_msg)
-
         # create a container
         cls.container_name = data_utils.rand_name(name='TestContainer')
         cls.container_client.create_container(cls.container_name)
@@ -108,7 +97,8 @@
 
         return url
 
-    @attr(type='gate')
+    @test.attr(type='gate')
+    @test.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url(self):
         expires = self._get_expiry_date()
 
@@ -119,16 +109,17 @@
 
         # trying to get object using temp url within expiry time
         resp, body = self.object_client.get(url)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'GET')
         self.assertEqual(body, self.content)
 
         # Testing a HEAD on this Temp URL
         resp, body = self.object_client.head(url)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
-    @attr(type='gate')
+    @test.attr(type='gate')
+    @test.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url_key_2(self):
         key2 = 'Meta2-'
         metadata = {'Temp-URL-Key-2': key2}
@@ -149,10 +140,11 @@
                                  self.object_name, "GET",
                                  expires, key2)
         resp, body = self.object_client.get(url)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertEqual(body, self.content)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
+    @test.requires_ext(extension='tempurl', service='object')
     def test_put_object_using_temp_url(self):
         new_data = data_utils.arbitrary_string(
             size=len(self.object_name),
@@ -165,12 +157,12 @@
 
         # trying to put random data in the object using temp url
         resp, body = self.object_client.put(url, new_data, None)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # Testing a HEAD on this Temp URL
         resp, body = self.object_client.head(url)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
         # Validate that the content of the object has been modified
@@ -181,7 +173,8 @@
         _, body = self.object_client.get(url)
         self.assertEqual(body, new_data)
 
-    @attr(type='gate')
+    @test.attr(type='gate')
+    @test.requires_ext(extension='tempurl', service='object')
     def test_head_object_using_temp_url(self):
         expires = self._get_expiry_date()
 
@@ -192,10 +185,11 @@
 
         # Testing a HEAD on this Temp URL
         resp, body = self.object_client.head(url)
-        self.assertIn(int(resp['status']), HTTP_SUCCESS)
+        self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
-    @attr(type=['gate', 'negative'])
+    @test.attr(type=['gate', 'negative'])
+    @test.requires_ext(extension='tempurl', service='object')
     def test_get_object_after_expiration_time(self):
 
         expires = self._get_expiry_date(1)
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index eead234..282758c 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -112,6 +112,18 @@
         self.assertEqual('fluffy', stack['outputs'][0]['output_key'])
 
     @attr(type='gate')
+    def test_suspend_resume_stack(self):
+        """suspend and resume a stack."""
+        resp, suspend_stack = self.client.suspend_stack(self.stack_identifier)
+        self.assertEqual('200', resp['status'])
+        self.client.wait_for_stack_status(self.stack_identifier,
+                                          'SUSPEND_COMPLETE')
+        resp, resume_stack = self.client.resume_stack(self.stack_identifier)
+        self.assertEqual('200', resp['status'])
+        self.client.wait_for_stack_status(self.stack_identifier,
+                                          'RESUME_COMPLETE')
+
+    @attr(type='gate')
     def test_list_resources(self):
         """Getting list of created resources for the stack should be possible.
         """
diff --git a/tempest/api/telemetry/__init__.py b/tempest/api/telemetry/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/telemetry/__init__.py
diff --git a/tempest/api/telemetry/base.py b/tempest/api/telemetry/base.py
new file mode 100644
index 0000000..1f661a6
--- /dev/null
+++ b/tempest/api/telemetry/base.py
@@ -0,0 +1,27 @@
+#    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 config
+import tempest.test
+
+CONF = config.CONF
+
+
+class BaseTelemetryTest(tempest.test.BaseTestCase):
+
+    """Base test case class for all Telemetry API tests."""
+
+    @classmethod
+    def setUpClass(cls):
+        super(BaseTelemetryTest, cls).setUpClass()
+        if not CONF.service_available.ceilometer:
+            raise cls.skipException("Ceilometer support is required")
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index 90988a2..546c430 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -17,9 +17,13 @@
 
 
 from tempest.api.volume import base
+from tempest.openstack.common import log as logging
 from tempest.test import attr
 
 
+LOG = logging.getLogger(__name__)
+
+
 class ExtensionsTestJSON(base.BaseVolumeTest):
     _interface = 'json'
 
@@ -30,6 +34,8 @@
         self.assertEqual(200, resp.status)
         if len(self.config.volume_feature_enabled.api_extensions) == 0:
             raise self.skipException('There are not any extensions configured')
+        extension_list = [extension.get('alias') for extension in extensions]
+        LOG.debug("Cinder extensions: %s" % ','.join(extension_list))
         ext = self.config.volume_feature_enabled.api_extensions[0]
         if ext == 'all':
             self.assertIn('Hosts', map(lambda x: x['name'], extensions))
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
new file mode 100644
index 0000000..0326f3c
--- /dev/null
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -0,0 +1,119 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD
+# 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.api.volume import base
+from tempest import test
+
+
+class SnapshotMetadataTest(base.BaseVolumeV1Test):
+    _interface = "json"
+
+    @classmethod
+    def setUpClass(cls):
+        super(SnapshotMetadataTest, cls).setUpClass()
+        cls.client = cls.snapshots_client
+        # Create a volume
+        cls.volume = cls.create_volume()
+        # Create a snapshot
+        cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
+        cls.snapshot_id = cls.snapshot['id']
+
+    def tearDown(self):
+        # Update the metadata to {}
+        self.client.update_snapshot_metadata(self.snapshot_id, {})
+        super(SnapshotMetadataTest, self).tearDown()
+
+    @test.attr(type='gate')
+    def test_create_get_delete_snapshot_metadata(self):
+        # Create metadata for the snapshot
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+        expected = {"key2": "value2",
+                    "key3": "value3"}
+        resp, body = self.client.create_snapshot_metadata(self.snapshot_id,
+                                                          metadata)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the snapshot
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(metadata, body)
+        # Delete one item metadata of the snapshot
+        resp, body = self.client.delete_snapshot_metadata_item(
+            self.snapshot_id,
+            "key1")
+        self.assertEqual(200, resp.status)
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(expected, body)
+
+    @test.attr(type='gate')
+    def test_update_snapshot_metadata(self):
+        # Update metadata for the snapshot
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+        update = {"key3": "value3_update",
+                  "key4": "value4"}
+        # Create metadata for the snapshot
+        resp, body = self.client.create_snapshot_metadata(self.snapshot_id,
+                                                          metadata)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the snapshot
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(metadata, body)
+        # Update metadata item
+        resp, body = self.client.update_snapshot_metadata(
+            self.snapshot_id,
+            update)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the snapshot
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(update, body)
+
+    @test.attr(type='gate')
+    def test_update_snapshot_metadata_item(self):
+        # Update metadata item for the snapshot
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+        update_item = {"key3": "value3_update"}
+        expect = {"key1": "value1",
+                  "key2": "value2",
+                  "key3": "value3_update"}
+        # Create metadata for the snapshot
+        resp, body = self.client.create_snapshot_metadata(self.snapshot_id,
+                                                          metadata)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the snapshot
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(metadata, body)
+        # Update metadata item
+        resp, body = self.client.update_snapshot_metadata_item(
+            self.snapshot_id,
+            "key3",
+            update_item)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the snapshot
+        resp, body = self.client.get_snapshot_metadata(self.snapshot_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(expect, body)
+
+
+class SnapshotMetadataTestXML(SnapshotMetadataTest):
+    _interface = "xml"
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
new file mode 100644
index 0000000..0909ade
--- /dev/null
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -0,0 +1,124 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Huawei Technologies Co.,LTD
+# 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.api.volume.base import BaseVolumeTest
+from tempest import test
+
+
+class VolumeMetadataTest(BaseVolumeTest):
+    _interface = "json"
+
+    @classmethod
+    def setUpClass(cls):
+        super(VolumeMetadataTest, cls).setUpClass()
+        # Create a volume
+        cls.volume = cls.create_volume()
+        cls.volume_id = cls.volume['id']
+
+    @classmethod
+    def tearDownClass(cls):
+        super(VolumeMetadataTest, cls).tearDownClass()
+
+    def tearDown(self):
+        # Update the metadata to {}
+        self.volumes_client.update_volume_metadata(self.volume_id, {})
+        super(VolumeMetadataTest, self).tearDown()
+
+    @test.attr(type='gate')
+    def test_create_get_delete_volume_metadata(self):
+        # Create metadata for the volume
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+
+        rsp, body = self.volumes_client.create_volume_metadata(self.volume_id,
+                                                               metadata)
+        self.assertEqual(200, rsp.status)
+        # Get the metadata of the volume
+        resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(metadata, body)
+        # Delete one item metadata of the volume
+        rsp, body = self.volumes_client.delete_volume_metadata_item(
+            self.volume_id,
+            "key1")
+        self.assertEqual(200, rsp.status)
+        resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
+        self.assertNotIn("key1", body)
+
+    @test.attr(type='gate')
+    def test_update_volume_metadata(self):
+        # Update metadata for the volume
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+
+        update = {"key4": "value4",
+                  "key1": "value1_update"}
+
+        # Create metadata for the volume
+        resp, body = self.volumes_client.create_volume_metadata(
+            self.volume_id,
+            metadata)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the volume
+        resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(metadata, body)
+        # Update metadata
+        resp, body = self.volumes_client.update_volume_metadata(
+            self.volume_id,
+            update)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the volume
+        resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(update, body)
+
+    @test.attr(type='gate')
+    def test_update_volume_metadata_item(self):
+        # Update metadata item for the volume
+        metadata = {"key1": "value1",
+                    "key2": "value2",
+                    "key3": "value3"}
+        create_expect = {"key1": "value1",
+                         "key2": "value2",
+                         "key3": "value3"}
+        update_item = {"key3": "value3_update"}
+        expect = {"key1": "value1",
+                  "key2": "value2",
+                  "key3": "value3_update"}
+        # Create metadata for the volume
+        resp, body = self.volumes_client.create_volume_metadata(
+            self.volume_id,
+            metadata)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(create_expect, body)
+        # Update metadata item
+        resp, body = self.volumes_client.update_volume_metadata_item(
+            self.volume_id,
+            "key3",
+            update_item)
+        self.assertEqual(200, resp.status)
+        # Get the metadata of the volume
+        resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(expect, body)
+
+
+class VolumeMetadataTestXML(VolumeMetadataTest):
+    _interface = "xml"
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 49a2f74..fa3f924 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -21,6 +21,7 @@
 from tempest.common.utils import data_utils
 from tempest.openstack.common import log as logging
 from tempest.test import attr
+from testtools.matchers import ContainsAll
 
 LOG = logging.getLogger(__name__)
 
@@ -110,7 +111,12 @@
             for key in params:
                 msg = "Failed to list volumes %s by %s" % \
                       ('details' if with_detail else '', key)
-                self.assertEqual(params[key], volume[key], msg)
+                if key == 'metadata':
+                    self.assertThat(volume[key].items(),
+                                    ContainsAll(params[key].items()),
+                                    msg)
+                else:
+                    self.assertEqual(params[key], volume[key], msg)
 
     @attr(type='smoke')
     def test_volume_list(self):
diff --git a/tempest/cli/simple_read_only/test_ceilometer.py b/tempest/cli/simple_read_only/test_ceilometer.py
index 8bdd633..f14b35b 100644
--- a/tempest/cli/simple_read_only/test_ceilometer.py
+++ b/tempest/cli/simple_read_only/test_ceilometer.py
@@ -15,17 +15,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo.config import cfg
-
-import tempest.cli
+from tempest import cli
+from tempest import config
 from tempest.openstack.common import log as logging
 
-CONF = cfg.CONF
+CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
 
-class SimpleReadOnlyCeilometerClientTest(tempest.cli.ClientTestBase):
+class SimpleReadOnlyCeilometerClientTest(cli.ClientTestBase):
     """Basic, read-only tests for Ceilometer CLI client.
 
     Checks return values and output of read-only commands.
@@ -36,7 +35,7 @@
     @classmethod
     def setUpClass(cls):
         if (not CONF.service_available.ceilometer):
-            msg = ("Skiping all Ceilometer cli tests because it is"
+            msg = ("Skipping all Ceilometer cli tests because it is "
                    "not available")
             raise cls.skipException(msg)
         super(SimpleReadOnlyCeilometerClientTest, cls).setUpClass()
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 61ffc25..7aa57c0 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -18,18 +18,17 @@
 import re
 import subprocess
 
-from oslo.config import cfg
-
-import tempest.cli
+from tempest import cli
+from tempest import config
 from tempest.openstack.common import log as logging
 from tempest import test
 
-CONF = cfg.CONF
+CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
 
-class SimpleReadOnlyNeutronClientTest(tempest.cli.ClientTestBase):
+class SimpleReadOnlyNeutronClientTest(cli.ClientTestBase):
     """Basic, read-only tests for Neutron CLI client.
 
     Checks return values and output of read-only commands.
@@ -40,7 +39,7 @@
     @classmethod
     def setUpClass(cls):
         if (not CONF.service_available.neutron):
-            msg = "Skiping all Neutron cli tests because it is not available"
+            msg = "Skipping all Neutron cli tests because it is not available"
             raise cls.skipException(msg)
         super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
 
@@ -89,7 +88,7 @@
     def _test_neutron_lbaas_command(self, command):
         try:
             self.neutron(command)
-        except tempest.cli.CommandFailed as e:
+        except cli.CommandFailed as e:
             if '404 Not Found' not in e.stderr:
                 self.fail('%s: Unexpected failure.' % command)
 
@@ -156,7 +155,7 @@
                                'router-show', 'agent-update', 'help'))
         self.assertFalse(wanted_commands - commands)
 
-     # Optional arguments:
+    # Optional arguments:
 
     @test.attr(type='smoke')
     def test_neutron_version(self):
diff --git a/tempest/clients.py b/tempest/clients.py
index b0b9409..ae7c0d7 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -18,6 +18,8 @@
 from tempest import config
 from tempest import exceptions
 from tempest.openstack.common import log as logging
+from tempest.services.baremetal.v1.client_json import BaremetalClientJSON
+from tempest.services.baremetal.v1.client_xml import BaremetalClientXML
 from tempest.services import botoclients
 from tempest.services.compute.json.aggregates_client import \
     AggregatesClientJSON
@@ -62,6 +64,8 @@
 from tempest.services.compute.v3.json.hosts_client import HostsV3ClientJSON
 from tempest.services.compute.v3.json.hypervisor_client import \
     HypervisorV3ClientJSON
+from tempest.services.compute.v3.json.instance_usage_audit_log_client import \
+    InstanceUsagesAuditLogV3ClientJSON
 from tempest.services.compute.v3.json.interfaces_client import \
     InterfacesV3ClientJSON
 from tempest.services.compute.v3.json.keypairs_client import \
@@ -88,6 +92,8 @@
 from tempest.services.compute.v3.xml.hosts_client import HostsV3ClientXML
 from tempest.services.compute.v3.xml.hypervisor_client import \
     HypervisorV3ClientXML
+from tempest.services.compute.v3.xml.instance_usage_audit_log_client import \
+    InstanceUsagesAuditLogV3ClientXML
 from tempest.services.compute.v3.xml.interfaces_client import \
     InterfacesV3ClientXML
 from tempest.services.compute.v3.xml.keypairs_client import KeyPairsV3ClientXML
@@ -235,6 +241,7 @@
         if interface == 'xml':
             self.certificates_client = CertificatesClientXML(*client_args)
             self.certificates_v3_client = CertificatesV3ClientXML(*client_args)
+            self.baremetal_client = BaremetalClientXML(*client_args)
             self.servers_client = ServersClientXML(*client_args)
             self.servers_v3_client = ServersV3ClientXML(*client_args)
             self.limits_client = LimitsClientXML(*client_args)
@@ -285,6 +292,8 @@
             self.credentials_client = CredentialsClientXML(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientXML(*client_args)
+            self.instance_usages_audit_log_v3_client = \
+                InstanceUsagesAuditLogV3ClientXML(*client_args)
             self.volume_hosts_client = VolumeHostsClientXML(*client_args)
             self.volumes_extension_client = VolumeExtensionClientXML(
                 *client_args)
@@ -298,6 +307,7 @@
             self.certificates_client = CertificatesClientJSON(*client_args)
             self.certificates_v3_client = CertificatesV3ClientJSON(
                 *client_args)
+            self.baremetal_client = BaremetalClientJSON(*client_args)
             self.servers_client = ServersClientJSON(*client_args)
             self.servers_v3_client = ServersV3ClientJSON(*client_args)
             self.limits_client = LimitsClientJSON(*client_args)
@@ -348,6 +358,8 @@
             self.credentials_client = CredentialsClientJSON(*client_args)
             self.instance_usages_audit_log_client = \
                 InstanceUsagesAuditLogClientJSON(*client_args)
+            self.instance_usages_audit_log_v3_client = \
+                InstanceUsagesAuditLogV3ClientJSON(*client_args)
             self.volume_hosts_client = VolumeHostsClientJSON(*client_args)
             self.volumes_extension_client = VolumeExtensionClientJSON(
                 *client_args)
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index 307d5db..81b5153 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -125,7 +125,7 @@
             elif key == 'content-type' and not value:
                 return InvalidFormat(key, value)
             elif key == 'x-trans-id' and \
-                not re.match("^tx[0-9a-f]*-[0-9a-f]*$", value):
+                not re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*", value):
                 return InvalidFormat(key, value)
             elif key == 'date' and not value:
                 return InvalidFormat(key, value)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index da60318..9c0d7f6 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -32,7 +32,8 @@
 class IsolatedCreds(object):
 
     def __init__(self, name, tempest_client=True, interface='json',
-                 password='pass'):
+                 password='pass', network_resources=None):
+        self.network_resources = network_resources
         self.isolated_creds = {}
         self.isolated_net_resources = {}
         self.ports = []
@@ -198,15 +199,33 @@
         network = None
         subnet = None
         router = None
+        # Make sure settings
+        if self.network_resources:
+            if self.network_resources['router']:
+                if (not self.network_resources['subnet'] or
+                    not self.network_resources['network']):
+                    raise exceptions.InvalidConfiguration(
+                        'A router requires a subnet and network')
+            elif self.network_resources['subnet']:
+                if not self.network_resources['network']:
+                    raise exceptions.InvalidConfiguration(
+                        'A subnet requires a network')
+            elif self.network_resources['dhcp']:
+                raise exceptions.InvalidConfiguration('DHCP requires a subnet')
+
         data_utils.rand_name_root = data_utils.rand_name(self.name)
-        network_name = data_utils.rand_name_root + "-network"
-        network = self._create_network(network_name, tenant_id)
+        if not self.network_resources or self.network_resources['network']:
+            network_name = data_utils.rand_name_root + "-network"
+            network = self._create_network(network_name, tenant_id)
         try:
-            subnet_name = data_utils.rand_name_root + "-subnet"
-            subnet = self._create_subnet(subnet_name, tenant_id, network['id'])
-            router_name = data_utils.rand_name_root + "-router"
-            router = self._create_router(router_name, tenant_id)
-            self._add_router_interface(router['id'], subnet['id'])
+            if not self.network_resources or self.network_resources['subnet']:
+                subnet_name = data_utils.rand_name_root + "-subnet"
+                subnet = self._create_subnet(subnet_name, tenant_id,
+                                             network['id'])
+            if not self.network_resources or self.network_resources['router']:
+                router_name = data_utils.rand_name_root + "-router"
+                router = self._create_router(router_name, tenant_id)
+                self._add_router_interface(router['id'], subnet['id'])
         except Exception:
             if router:
                 self._clear_isolated_router(router['id'], router['name'])
@@ -230,14 +249,25 @@
         if not self.tempest_client:
             body = {'subnet': {'name': subnet_name, 'tenant_id': tenant_id,
                                'network_id': network_id, 'ip_version': 4}}
+            if self.network_resources:
+                body['enable_dhcp'] = self.network_resources['dhcp']
         base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
         mask_bits = CONF.network.tenant_network_mask_bits
         for subnet_cidr in base_cidr.subnet(mask_bits):
             try:
                 if self.tempest_client:
-                    resp, resp_body = self.network_admin_client.create_subnet(
-                        network_id, str(subnet_cidr), name=subnet_name,
-                        tenant_id=tenant_id)
+                    if self.network_resources:
+                        resp, resp_body = self.network_admin_client.\
+                            create_subnet(
+                                network_id, str(subnet_cidr),
+                                name=subnet_name,
+                                tenant_id=tenant_id,
+                                enable_dhcp=self.network_resources['dhcp'])
+                    else:
+                        resp, resp_body = self.network_admin_client.\
+                            create_subnet(network_id, str(subnet_cidr),
+                                          name=subnet_name,
+                                          tenant_id=tenant_id)
                 else:
                     body['subnet']['cidr'] = str(subnet_cidr)
                     resp_body = self.network_admin_client.create_subnet(body)
@@ -431,26 +461,27 @@
         net_client = self.network_admin_client
         for cred in self.isolated_net_resources:
             network, subnet, router = self.isolated_net_resources.get(cred)
-            try:
-                if self.tempest_client:
-                    net_client.remove_router_interface_with_subnet_id(
-                        router['id'], subnet['id'])
-                else:
-                    body = {'subnet_id': subnet['id']}
-                    net_client.remove_interface_router(router['id'], body)
-            except exceptions.NotFound:
-                LOG.warn('router with name: %s not found for delete' %
-                         router['name'])
-                pass
-            self._clear_isolated_router(router['id'], router['name'])
-            # TODO(mlavalle) This method call will be removed once patch
-            # https://review.openstack.org/#/c/46563/ merges in Neutron
-            self._cleanup_ports(network['id'])
-            self._clear_isolated_subnet(subnet['id'], subnet['name'])
-            self._clear_isolated_network(network['id'], network['name'])
-            LOG.info("Cleared isolated network resources: \n"
-                     + " network: %s, subnet: %s, router: %s"
-                     % (network['name'], subnet['name'], router['name']))
+            if self.network_resources.get('router'):
+                try:
+                    if self.tempest_client:
+                        net_client.remove_router_interface_with_subnet_id(
+                            router['id'], subnet['id'])
+                    else:
+                        body = {'subnet_id': subnet['id']}
+                        net_client.remove_interface_router(router['id'], body)
+                except exceptions.NotFound:
+                    LOG.warn('router with name: %s not found for delete' %
+                             router['name'])
+                    pass
+                self._clear_isolated_router(router['id'], router['name'])
+            if self.network_resources.get('network'):
+                # TODO(mlavalle) This method call will be removed once patch
+                # https://review.openstack.org/#/c/46563/ merges in Neutron
+                self._cleanup_ports(network['id'])
+            if self.network_resources.get('subnet'):
+                self._clear_isolated_subnet(subnet['id'], subnet['name'])
+            if self.network_resources.get('network'):
+                self._clear_isolated_network(network['id'], network['name'])
 
     def clear_isolated_creds(self):
         if not self.isolated_creds:
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index 4f93e1c..339d22a 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -40,6 +40,21 @@
     return random.randint(start, end)
 
 
+def rand_mac_address():
+    """Generate an Ethernet MAC address."""
+    # NOTE(vish): We would prefer to use 0xfe here to ensure that linux
+    #             bridge mac addresses don't change, but it appears to
+    #             conflict with libvirt, so we use the next highest octet
+    #             that has the unicast and locally administered bits set
+    #             properly: 0xfa.
+    #             Discussion: https://bugs.launchpad.net/nova/+bug/921838
+    mac = [0xfa, 0x16, 0x3e,
+           random.randint(0x00, 0xff),
+           random.randint(0x00, 0xff),
+           random.randint(0x00, 0xff)]
+    return ':'.join(["%02x" % x for x in mac])
+
+
 def build_url(host, port, api_version=None, path=None,
               params=None, use_ssl=False):
     """Build the request URL from given host, port, path and parameters."""
diff --git a/tempest/common/utils/test_utils.py b/tempest/common/utils/test_utils.py
new file mode 100644
index 0000000..4f1e02c
--- /dev/null
+++ b/tempest/common/utils/test_utils.py
@@ -0,0 +1,136 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 Hewlett-Packard, Ltd.
+#
+#    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.common.utils import misc
+from tempest import config
+from tempest.scenario import manager
+
+import json
+import re
+import string
+import unicodedata
+
+CONF = config.CONF
+
+
+@misc.singleton
+class ImageUtils(object):
+
+    default_ssh_user = 'root'
+
+    def __init__(self):
+        # Load configuration items
+        self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
+        self.non_ssh_image_pattern = \
+            CONF.input_scenario.non_ssh_image_regex
+        # Setup clients
+        ocm = manager.OfficialClientManager(CONF.identity.username,
+                                            CONF.identity.password,
+                                            CONF.identity.tenant_name)
+        self.client = ocm.compute_client
+
+    def ssh_user(self, image_id):
+        _image = self.client.images.get(image_id)
+        for regex, user in self.ssh_users:
+            # First match wins
+            if re.match(regex, _image.name) is not None:
+                return user
+        else:
+            return self.default_ssh_user
+
+    def _is_sshable_image(self, image):
+        return not re.search(pattern=self.non_ssh_image_pattern,
+                             string=str(image.name))
+
+    def is_sshable_image(self, image_id):
+        _image = self.client.images.get(image_id)
+        return self._is_sshable_image(_image)
+
+    def _is_flavor_enough(self, flavor, image):
+        return image.minDisk <= flavor.disk
+
+    def is_flavor_enough(self, flavor_id, image_id):
+        _image = self.client.images.get(image_id)
+        _flavor = self.client.flavors.get(flavor_id)
+        return self._is_flavor_enough(_flavor, _image)
+
+
+@misc.singleton
+class InputScenarioUtils(object):
+
+    """
+    Example usage:
+
+    import testscenarios
+    (...)
+    load_tests = testscenarios.load_tests_apply_scenarios
+
+
+    class TestInputScenario(manager.OfficialClientTest):
+
+        scenario_utils = test_utils.InputScenarioUtils()
+        scenario_flavor = scenario_utils.scenario_flavors
+        scenario_image = scenario_utils.scenario_images
+        scenarios = testscenarios.multiply_scenarios(scenario_image,
+                                                     scenario_flavor)
+
+        def test_create_server_metadata(self):
+            name = rand_name('instance')
+            _ = self.compute_client.servers.create(name=name,
+                                                   flavor=self.flavor_ref,
+                                                   image=self.image_ref)
+    """
+    validchars = "-_.{ascii}{digit}".format(ascii=string.ascii_letters,
+                                            digit=string.digits)
+
+    def __init__(self):
+        ocm = manager.OfficialClientManager(CONF.identity.username,
+                                            CONF.identity.password,
+                                            CONF.identity.tenant_name)
+        self.client = ocm.compute_client
+        self.image_pattern = CONF.input_scenario.image_regex
+        self.flavor_pattern = CONF.input_scenario.flavor_regex
+
+    def _normalize_name(self, name):
+        nname = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore')
+        nname = ''.join(c for c in nname if c in self.validchars)
+        return nname
+
+    @property
+    def scenario_images(self):
+        """
+        :return: a scenario with name and uuid of images
+        """
+        if not hasattr(self, '_scenario_images'):
+            images = self.client.images.list(detailed=False)
+            self._scenario_images = [
+                (self._normalize_name(i.name), dict(image_ref=i.id))
+                for i in images if re.search(self.image_pattern, str(i.name))
+            ]
+        return self._scenario_images
+
+    @property
+    def scenario_flavors(self):
+        """
+        :return: a scenario with name and uuid of flavors
+        """
+        if not hasattr(self, '_scenario_flavors'):
+            flavors = self.client.flavors.list(detailed=False)
+            self._scenario_flavors = [
+                (self._normalize_name(f.name), dict(flavor_ref=f.id))
+                for f in flavors if re.search(self.flavor_pattern, str(f.name))
+            ]
+        return self._scenario_flavors
diff --git a/tempest/config.py b/tempest/config.py
index d42edc9..3c7241a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -78,7 +78,7 @@
                secret=True),
     cfg.StrOpt('admin_username',
                default='admin',
-               help="Administrative Username to use for"
+               help="Administrative Username to use for "
                     "Keystone API requests."),
     cfg.StrOpt('admin_tenant_name',
                default='admin',
@@ -90,6 +90,16 @@
                secret=True),
 ]
 
+identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
+                                      title='Enabled Identity Features')
+
+IdentityFeatureGroup = [
+    cfg.BoolOpt('trust',
+                default=True,
+                help='Does the identity service have delegation and '
+                     'impersonation enabled')
+]
+
 compute_group = cfg.OptGroup(name='compute',
                              title='Compute Service Options')
 
@@ -398,11 +408,11 @@
                     "one is used."),
     cfg.IntOpt('container_sync_timeout',
                default=120,
-               help="Number of seconds to time on waiting for a container"
+               help="Number of seconds to time on waiting for a container "
                     "to container synchronization complete."),
     cfg.IntOpt('container_sync_interval',
                default=5,
-               help="Number of seconds to wait while looping to check the"
+               help="Number of seconds to wait while looping to check the "
                     "status of a container to container synchronization"),
     cfg.StrOpt('operator_role',
                default='Member',
@@ -415,19 +425,11 @@
     title='Enabled object-storage features')
 
 ObjectStoreFeaturesGroup = [
-    cfg.BoolOpt('container_quotas',
-                default=True,
-                help="Set to True if the Container Quota middleware "
-                     "is enabled"),
-    cfg.BoolOpt('accounts_quotas',
-                default=True,
-                help="Set to True if the Account Quota middleware is enabled"),
-    cfg.BoolOpt('crossdomain',
-                default=True,
-                help="Set to True if the Crossdomain middleware is enabled"),
-    cfg.BoolOpt('tempurl',
-                default=True,
-                help="Set to True if the TempURL middleware is enabled"),
+    cfg.ListOpt('discoverable_apis',
+                default=['all'],
+                help="A list of the enabled optional discoverable apis. "
+                     "A single entry, all, indicates that all of these "
+                     "features are expected to be enabled"),
 ]
 
 
@@ -644,6 +646,9 @@
     cfg.BoolOpt('savanna',
                 default=False,
                 help="Whether or not Savanna is expected to be available"),
+    cfg.BoolOpt('ironic',
+                default=False,
+                help="Whether or not Ironic is expected to be available"),
 ]
 
 debug_group = cfg.OptGroup(name="debug",
@@ -655,6 +660,37 @@
                 help="Enable diagnostic commands"),
 ]
 
+input_scenario_group = cfg.OptGroup(name="input-scenario",
+                                    title="Filters and values for"
+                                          " input scenarios")
+
+InputScenarioGroup = [
+    cfg.StrOpt('image_regex',
+               default='^cirros-0.3.1-x86_64-uec$',
+               help="Matching images become parameters for scenario tests"),
+    cfg.StrOpt('flavor_regex',
+               default='^m1.(micro|nano|tiny)$',
+               help="Matching flavors become parameters for scenario tests"),
+    cfg.StrOpt('non_ssh_image_regex',
+               default='^.*[Ww]in.*$',
+               help="SSH verification in tests is skipped"
+                    "for matching images"),
+    cfg.StrOpt('ssh_user_regex',
+               default="[[\"^.*[Cc]irros.*$\", \"root\"]]",
+               help="List of user mapped to regex "
+                    "to matching image names."),
+]
+
+
+baremetal_group = cfg.OptGroup(name='baremetal',
+                               title='Baremetal provisioning service options')
+
+BaremetalGroup = [
+    cfg.StrOpt('catalog_type',
+               default='baremetal',
+               help="Catalog type of the baremetal provisioning service."),
+]
+
 
 # this should never be called outside of this class
 class TempestConfigPrivate(object):
@@ -698,6 +734,8 @@
         register_opt_group(cfg.CONF, compute_features_group,
                            ComputeFeaturesGroup)
         register_opt_group(cfg.CONF, identity_group, IdentityGroup)
+        register_opt_group(cfg.CONF, identity_feature_group,
+                           IdentityFeatureGroup)
         register_opt_group(cfg.CONF, image_group, ImageGroup)
         register_opt_group(cfg.CONF, image_feature_group, ImageFeaturesGroup)
         register_opt_group(cfg.CONF, network_group, NetworkGroup)
@@ -721,9 +759,12 @@
         register_opt_group(cfg.CONF, service_available_group,
                            ServiceAvailableGroup)
         register_opt_group(cfg.CONF, debug_group, DebugGroup)
+        register_opt_group(cfg.CONF, baremetal_group, BaremetalGroup)
+        register_opt_group(cfg.CONF, input_scenario_group, InputScenarioGroup)
         self.compute = cfg.CONF.compute
         self.compute_feature_enabled = cfg.CONF['compute-feature-enabled']
         self.identity = cfg.CONF.identity
+        self.identity_feature_enabled = cfg.CONF['identity-feature-enabled']
         self.images = cfg.CONF.image
         self.image_feature_enabled = cfg.CONF['image-feature-enabled']
         self.network = cfg.CONF.network
@@ -743,6 +784,8 @@
         self.scenario = cfg.CONF.scenario
         self.service_available = cfg.CONF.service_available
         self.debug = cfg.CONF.debug
+        self.baremetal = cfg.CONF.baremetal
+        self.input_scenario = cfg.CONF['input-scenario']
         if not self.compute_admin.username:
             self.compute_admin.username = self.identity.admin_username
             self.compute_admin.password = self.identity.admin_password
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 409fcc2..ecb55f9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -235,7 +235,8 @@
     def setUpClass(cls):
         super(OfficialClientTest, cls).setUpClass()
         cls.isolated_creds = isolated_creds.IsolatedCreds(
-            __name__, tempest_client=False)
+            __name__, tempest_client=False,
+            network_resources=cls.network_resources)
 
         username, password, tenant_name = cls.credentials()
 
@@ -527,6 +528,13 @@
             private_key = self.keypair.private_key
         return RemoteClient(ip, username, pkey=private_key)
 
+    def _log_console_output(self, servers=None):
+        if not servers:
+            servers = self.compute_client.servers.list()
+        for server in servers:
+            LOG.debug('Console output for %s', server.id)
+            LOG.debug(server.get_console_output())
+
 
 class NetworkScenarioTest(OfficialClientTest):
     """
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
new file mode 100644
index 0000000..3ae9567
--- /dev/null
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# 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.common import tempest_fixtures as fixtures
+from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
+from tempest.scenario import manager
+from tempest import test
+
+
+LOG = logging.getLogger(__name__)
+
+
+class TestAggregatesBasicOps(manager.OfficialClientTest):
+    """
+    Creates an aggregate within an availability zone
+    Adds a host to the aggregate
+    Checks aggregate details
+    Updates aggregate's name
+    Removes host from aggregate
+    Deletes aggregate
+    """
+    @classmethod
+    def credentials(cls):
+        return cls.admin_credentials()
+
+    def _create_aggregate(self, aggregate_name, availability_zone=None):
+        aggregate = self.compute_client.aggregates.create(aggregate_name,
+                                                          availability_zone)
+        self.assertEqual(aggregate.name, aggregate_name)
+        self.assertEqual(aggregate.availability_zone, availability_zone)
+        self.set_resource(aggregate.id, aggregate)
+        LOG.debug("Aggregate %s created." % (aggregate.name))
+        return aggregate
+
+    def _delete_aggregate(self, aggregate):
+        self.compute_client.aggregates.delete(aggregate.id)
+        self.remove_resource(aggregate.id)
+        LOG.debug("Aggregate %s deleted. " % (aggregate.name))
+
+    def _get_host_name(self):
+        hosts = self.compute_client.hosts.list()
+        self.assertTrue(len(hosts) >= 1)
+        hostname = hosts[0].host_name
+        return hostname
+
+    def _add_host(self, aggregate_name, host):
+        aggregate = self.compute_client.aggregates.add_host(aggregate_name,
+                                                            host)
+        self.assertIn(host, aggregate.hosts)
+        LOG.debug("Host %s added to Aggregate %s." % (host, aggregate.name))
+
+    def _remove_host(self, aggregate_name, host):
+        aggregate = self.compute_client.aggregates.remove_host(aggregate_name,
+                                                               host)
+        self.assertNotIn(host, aggregate.hosts)
+        LOG.debug("Host %s removed to Aggregate %s." % (host, aggregate.name))
+
+    def _check_aggregate_details(self, aggregate, aggregate_name, azone,
+                                 hosts, metadata):
+        aggregate = self.compute_client.aggregates.get(aggregate.id)
+        self.assertEqual(aggregate_name, aggregate.name)
+        self.assertEqual(azone, aggregate.availability_zone)
+        self.assertEqual(aggregate.hosts, hosts)
+        for meta_key in metadata.keys():
+            self.assertIn(meta_key, aggregate.metadata)
+            self.assertEqual(metadata[meta_key], aggregate.metadata[meta_key])
+        LOG.debug("Aggregate %s details match." % aggregate.name)
+
+    def _set_aggregate_metadata(self, aggregate, meta):
+        aggregate = self.compute_client.aggregates.set_metadata(aggregate.id,
+                                                                meta)
+
+        for key, value in meta.items():
+            self.assertEqual(meta[key], aggregate.metadata[key])
+        LOG.debug("Aggregate %s metadata updated successfully." %
+                  aggregate.name)
+
+    def _update_aggregate(self, aggregate, aggregate_name,
+                          availability_zone):
+        values = {}
+        if aggregate_name:
+            values.update({'name': aggregate_name})
+        if availability_zone:
+            values.update({'availability_zone': availability_zone})
+        if values.keys():
+            aggregate = self.compute_client.aggregates.update(aggregate.id,
+                                                              values)
+            for key, values in values.items():
+                self.assertEqual(getattr(aggregate, key), values)
+        return aggregate
+
+    @test.services('compute')
+    def test_aggregate_basic_ops(self):
+        self.useFixture(fixtures.LockFixture('availability_zone'))
+        az = 'foo_zone'
+        aggregate_name = rand_name('aggregate-scenario')
+        aggregate = self._create_aggregate(aggregate_name, az)
+
+        metadata = {'meta_key': 'meta_value'}
+        self._set_aggregate_metadata(aggregate, metadata)
+
+        host = self._get_host_name()
+        self._add_host(aggregate, host)
+        self._check_aggregate_details(aggregate, aggregate_name, az, [host],
+                                      metadata)
+
+        aggregate_name = rand_name('renamed-aggregate-scenario')
+        aggregate = self._update_aggregate(aggregate, aggregate_name, None)
+
+        additional_metadata = {'foo': 'bar'}
+        self._set_aggregate_metadata(aggregate, additional_metadata)
+
+        metadata.update(additional_metadata)
+        self._check_aggregate_details(aggregate, aggregate.name, az, [host],
+                                      metadata)
+
+        self._remove_host(aggregate, host)
+        self._delete_aggregate(aggregate)
diff --git a/tempest/scenario/test_cross_tenant_connectivity.py b/tempest/scenario/test_cross_tenant_connectivity.py
index faba987..a269017 100644
--- a/tempest/scenario/test_cross_tenant_connectivity.py
+++ b/tempest/scenario/test_cross_tenant_connectivity.py
@@ -371,6 +371,7 @@
                             msg)
         except Exception:
             debug.log_ip_ns()
+            self._log_console_output(servers=self.servers)
             raise
 
     def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
index 1081a3e..9e08bb6 100644
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ b/tempest/scenario/test_dashboard_basic_ops.py
@@ -34,6 +34,7 @@
 
     @classmethod
     def setUpClass(cls):
+        cls.set_network_resources()
         super(TestDashboardBasicOps, cls).setUpClass()
 
         if not cls.config.service_available.horizon:
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 7f8d3e4..30468fa 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -36,6 +36,11 @@
 
     """
 
+    @classmethod
+    def setUpClass(cls):
+        cls.set_network_resources()
+        super(TestLargeOpsScenario, cls).setUpClass()
+
     def _wait_for_server_status(self, status):
         for server in self.servers:
             self.status_timeout(
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 8a51cd1..890d00f 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -131,7 +131,12 @@
         self.server.add_floating_ip(self.floating_ip)
 
     def ssh_to_server(self):
-        self.linux_client = self.get_remote_client(self.floating_ip.ip)
+        try:
+            self.linux_client = self.get_remote_client(self.floating_ip.ip)
+        except Exception:
+            LOG.exception('ssh to server failed')
+            self._log_console_output()
+            raise
 
     def check_partitions(self):
         partitions = self.linux_client.get_partitions()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 3618a14..414aae6 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -19,11 +19,9 @@
 from tempest.common import debug
 from tempest.common.utils import data_utils
 from tempest import config
-from tempest.openstack.common import jsonutils
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 
-import tempest.test
 from tempest.test import attr
 from tempest.test import services
 
@@ -31,45 +29,6 @@
 LOG = logging.getLogger(__name__)
 
 
-class FloatingIPCheckTracker(object):
-    """
-    Checking VM connectivity through floating IP addresses is bound to fail
-    if the floating IP has not actually been associated with the VM yet.
-    This helper class facilitates checking for floating IP assignments on
-    VMs. It only checks for a given IP address once.
-    """
-
-    def __init__(self, compute_client, floating_ip_map):
-        self.compute_client = compute_client
-        self.unchecked = floating_ip_map.copy()
-
-    def run_checks(self):
-        """Check for any remaining unverified floating IPs
-
-        Gets VM details from nova and checks for floating IPs
-        within the returned information. Returns true when all
-        checks are complete and is suitable for use with
-        tempest.test.call_until_true()
-        """
-        to_delete = []
-        loggable_map = {}
-        for check_addr, server in self.unchecked.iteritems():
-            serverdata = self.compute_client.servers.get(server.id)
-            ip_addr = [addr for sublist in serverdata.networks.values() for
-                       addr in sublist]
-            if check_addr.floating_ip_address in ip_addr:
-                to_delete.append(check_addr)
-            else:
-                loggable_map[server.id] = check_addr
-
-        for to_del in to_delete:
-            del self.unchecked[to_del]
-
-        LOG.debug('Unchecked floating IPs: %s',
-                  jsonutils.dumps(loggable_map))
-        return len(self.unchecked) == 0
-
-
 class TestNetworkBasicOps(manager.NetworkScenarioTest):
 
     """
@@ -229,20 +188,11 @@
                                                     key.private_key)
         except Exception:
             LOG.exception('Tenant connectivity check failed')
+            self._log_console_output(
+                servers=[server for server, _key in self.servers])
             debug.log_ip_ns()
             raise
 
-    def _wait_for_floating_ip_association(self):
-        ip_tracker = FloatingIPCheckTracker(self.compute_client,
-                                            self.floating_ips)
-
-        self.assertTrue(
-            tempest.test.call_until_true(
-                ip_tracker.run_checks, CONF.compute.build_timeout,
-                CONF.compute.build_interval),
-            "Timed out while waiting for the floating IP assignments "
-            "to propagate")
-
     def _create_and_associate_floating_ips(self):
         public_network_id = CONF.network.public_network_id
         for server in self.servers.keys():
@@ -253,6 +203,7 @@
         # The target login is assumed to have been configured for
         # key-based authentication by cloud-init.
         ssh_login = CONF.compute.image_ssh_user
+        LOG.debug('checking network connections')
         try:
             for floating_ip, server in self.floating_ips.iteritems():
                 ip_address = floating_ip.floating_ip_address
@@ -265,6 +216,8 @@
                                             should_connect=should_connect)
         except Exception:
             LOG.exception('Public network connectivity check failed')
+            self._log_console_output(
+                servers=[server for server, _key in self.servers])
             debug.log_ip_ns()
             raise
 
@@ -290,11 +243,9 @@
         self._check_networks()
         self._create_servers()
         self._create_and_associate_floating_ips()
-        self._wait_for_floating_ip_association()
         self._check_tenant_network_connectivity()
         self._check_public_network_connectivity(should_connect=True)
         self._disassociate_floating_ips()
         self._check_public_network_connectivity(should_connect=False)
         self._reassociate_floating_ips()
-        self._wait_for_floating_ip_association()
         self._check_public_network_connectivity(should_connect=True)
diff --git a/tempest/scenario/test_network_quotas.py b/tempest/scenario/test_network_quotas.py
deleted file mode 100644
index cb7aa0b..0000000
--- a/tempest/scenario/test_network_quotas.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2013 Hewlett-Packard Development Company, L.P.
-# 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 neutronclient.common import exceptions as exc
-
-from tempest.scenario.manager import NetworkScenarioTest
-from tempest.test import services
-
-
-class TestNetworkQuotaBasic(NetworkScenarioTest):
-    """
-    This test suite contains tests that each loop trying to grab a
-    particular resource until a quota limit is hit.
-    For sanity, there is a maximum number of iterations - if this is hit
-    the test fails. Covers network, subnet, port.
-    """
-
-    @classmethod
-    def check_preconditions(cls):
-        super(TestNetworkQuotaBasic, cls).check_preconditions()
-
-    @classmethod
-    def setUpClass(cls):
-        super(TestNetworkQuotaBasic, cls).setUpClass()
-        cls.check_preconditions()
-        cls.networks = []
-        cls.subnets = []
-        cls.ports = []
-
-    @services('network')
-    def test_create_network_until_quota_hit(self):
-        hit_limit = False
-        networknum = self._get_tenant_own_network_num(self.tenant_id)
-        max = self._show_quota_network(self.tenant_id) - networknum
-        for n in xrange(max):
-            try:
-                self.networks.append(
-                    self._create_network(self.tenant_id,
-                                         namestart='network-quotatest-'))
-            except exc.NeutronClientException as e:
-                if (e.status_code != 409):
-                    raise
-                hit_limit = True
-                break
-        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
-
-        try:
-            self.networks.append(
-                self._create_network(self.tenant_id,
-                                     namestart='network-quotatest-'))
-        except exc.NeutronClientException as e:
-            if (e.status_code != 409):
-                raise
-            hit_limit = True
-        self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
-
-    @services('network')
-    def test_create_subnet_until_quota_hit(self):
-        if not self.networks:
-            self.networks.append(
-                self._create_network(self.tenant_id,
-                                     namestart='network-quotatest-'))
-        hit_limit = False
-        subnetnum = self._get_tenant_own_subnet_num(self.tenant_id)
-        max = self._show_quota_subnet(self.tenant_id) - subnetnum
-        for n in xrange(max):
-            try:
-                self.subnets.append(
-                    self._create_subnet(self.networks[0],
-                                        namestart='subnet-quotatest-'))
-            except exc.NeutronClientException as e:
-                if (e.status_code != 409):
-                    raise
-                hit_limit = True
-                break
-        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
-
-        try:
-            self.subnets.append(
-                self._create_subnet(self.networks[0],
-                                    namestart='subnet-quotatest-'))
-        except exc.NeutronClientException as e:
-            if (e.status_code != 409):
-                raise
-            hit_limit = True
-        self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
-
-    @services('network')
-    def test_create_ports_until_quota_hit(self):
-        if not self.networks:
-            self.networks.append(
-                self._create_network(self.tenant_id,
-                                     namestart='network-quotatest-'))
-        hit_limit = False
-        portnum = self._get_tenant_own_port_num(self.tenant_id)
-        max = self._show_quota_port(self.tenant_id) - portnum
-        for n in xrange(max):
-            try:
-                self.ports.append(
-                    self._create_port(self.networks[0],
-                                      namestart='port-quotatest-'))
-            except exc.NeutronClientException as e:
-                if (e.status_code != 409):
-                    raise
-                hit_limit = True
-                break
-        self.assertFalse(hit_limit, "Failed: Hit quota limit !")
-
-        try:
-            self.ports.append(
-                self._create_port(self.networks[0],
-                                  namestart='port-quotatest-'))
-        except exc.NeutronClientException as e:
-            if (e.status_code != 409):
-                raise
-            hit_limit = True
-        self.assertTrue(hit_limit, "Failed: Did not hit quota limit !")
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 112c8a2..2c9446f 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -33,6 +33,7 @@
 
     @classmethod
     def setUpClass(cls):
+        cls.set_network_resources()
         super(TestServerAdvancedOps, cls).setUpClass()
 
         if not cls.config.compute_feature_enabled.resize:
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 1e1a310..279b80e 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -16,12 +16,21 @@
 #    under the License.
 
 from tempest.common.utils import data_utils
+from tempest.common.utils import test_utils
 from tempest.openstack.common import log as logging
 from tempest.scenario import manager
 from tempest.test import services
 
+import testscenarios
+
 LOG = logging.getLogger(__name__)
 
+# NOTE(andreaf) - nose does not honour the load_tests protocol
+# however it's test discovery regex will match anything
+# which includes _tests. So nose would require some further
+# investigation to be supported with this
+load_tests = testscenarios.load_tests_apply_scenarios
+
 
 class TestServerBasicOps(manager.OfficialClientTest):
 
@@ -37,6 +46,37 @@
      * Terminate the instance
     """
 
+    scenario_utils = test_utils.InputScenarioUtils()
+    scenario_flavor = scenario_utils.scenario_flavors
+    scenario_image = scenario_utils.scenario_images
+
+    scenarios = testscenarios.multiply_scenarios(scenario_image,
+                                                 scenario_flavor)
+
+    def setUp(self):
+        super(TestServerBasicOps, self).setUp()
+        # Setup image and flavor the test instance
+        # Support both configured and injected values
+        if not hasattr(self, 'image_ref'):
+            self.image_ref = self.config.compute.image_ref
+        if not hasattr(self, 'flavor_ref'):
+            self.flavor_ref = self.config.compute.flavor_ref
+        self.image_utils = test_utils.ImageUtils()
+        if not self.image_utils.is_flavor_enough(self.flavor_ref,
+                                                 self.image_ref):
+            raise self.skipException(
+                '{image} does not fit in {flavor}'.format(
+                    image=self.image_ref, flavor=self.flavor_ref
+                )
+            )
+        self.run_ssh = self.config.compute.run_ssh and \
+            self.image_utils.is_sshable_image(self.image_ref)
+        self.ssh_user = self.image_utils.ssh_user(self.image_ref)
+        LOG.debug('Starting test for i:{image}, f:{flavor}. '
+                  'Run ssh: {ssh}, user: {ssh_user}'.format(
+                      image=self.image_ref, flavor=self.flavor_ref,
+                      ssh=self.run_ssh, ssh_user=self.ssh_user))
+
     def add_keypair(self):
         self.keypair = self.create_keypair()
 
@@ -53,10 +93,13 @@
         self._create_loginable_secgroup_rule_nova(secgroup_id=self.secgroup.id)
 
     def boot_instance(self):
+        # Create server with image and flavor from input scenario
         create_kwargs = {
             'key_name': self.keypair.id
         }
-        instance = self.create_server(create_kwargs=create_kwargs)
+        instance = self.create_server(image=self.image_ref,
+                                      flavor=self.flavor_ref,
+                                      create_kwargs=create_kwargs)
         self.set_resource('instance', instance)
 
     def pause_server(self):
@@ -100,6 +143,24 @@
         instance.delete()
         self.remove_resource('instance')
 
+    def verify_ssh(self):
+        if self.run_ssh:
+            # Obtain a floating IP
+            floating_ip = self.compute_client.floating_ips.create()
+            # Attach a floating IP
+            instance = self.get_resource('instance')
+            instance.add_floating_ip(floating_ip)
+            # Check ssh
+            try:
+                self.get_remote_client(
+                    server_or_ip=floating_ip.ip,
+                    username=self.image_utils.ssh_user(self.image_ref),
+                    private_key=self.keypair.private)
+            except Exception:
+                LOG.exception('ssh to server failed')
+                self._log_console_output()
+                raise
+
     @services('compute', 'network')
     def test_server_basicops(self):
         self.add_keypair()
@@ -109,4 +170,5 @@
         self.unpause_server()
         self.suspend_server()
         self.resume_server()
+        self.verify_ssh()
         self.terminate_instance()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 00139f0..874bc60 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,10 +15,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.openstack.common import log
 from tempest.scenario import manager
 from tempest.test import services
 
 
+LOG = log.getLogger(__name__)
+
+
 class TestSnapshotPattern(manager.OfficialClientTest):
     """
     This test is for snapshotting an instance and booting with it.
@@ -40,7 +44,11 @@
         self.keypair = self.create_keypair()
 
     def _ssh_to_server(self, server_or_ip):
-        linux_client = self.get_remote_client(server_or_ip)
+        try:
+            linux_client = self.get_remote_client(server_or_ip)
+        except Exception:
+            LOG.exception()
+            self._log_console_output()
         return linux_client.ssh_client
 
     def _write_timestamp(self, server_or_ip):
diff --git a/tempest/scenario/test_swift_basic_ops.py b/tempest/scenario/test_swift_basic_ops.py
index 6763503..4ef2aca 100644
--- a/tempest/scenario/test_swift_basic_ops.py
+++ b/tempest/scenario/test_swift_basic_ops.py
@@ -39,6 +39,7 @@
 
     @classmethod
     def setUpClass(cls):
+        cls.set_network_resources()
         super(TestSwiftBasicOps, cls).setUpClass()
         if not cls.config.service_available.swift:
             skip_msg = ("%s skipped as swift is not available" %
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index fa9a228..7d30478 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -13,10 +13,15 @@
 #    under the License.
 
 from tempest.common.utils import data_utils
+from tempest.openstack.common import log
 from tempest.scenario import manager
+import tempest.test
 from tempest.test import services
 
 
+LOG = log.getLogger(__name__)
+
+
 class TestVolumeBootPattern(manager.OfficialClientTest):
 
     """
@@ -96,8 +101,14 @@
             network_name_for_ssh = self.config.compute.network_for_ssh
             ip = server.networks[network_name_for_ssh][0]
 
-        client = self.get_remote_client(ip,
-                                        private_key=keypair.private_key)
+        try:
+            client = self.get_remote_client(
+                ip,
+                private_key=keypair.private_key)
+        except Exception:
+            LOG.exception('ssh to server failed')
+            self._log_console_output()
+            raise
         return client.ssh_client
 
     def _get_content(self, ssh_client):
@@ -117,6 +128,7 @@
         actual = self._get_content(ssh_client)
         self.assertEqual(expected, actual)
 
+    @tempest.test.skip_because(bug="1270608")
     @services('compute', 'volume', 'image')
     def test_volume_boot_pattern(self):
         keypair = self.create_keypair()
diff --git a/tempest/services/baremetal/__init__.py b/tempest/services/baremetal/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/baremetal/__init__.py
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
new file mode 100644
index 0000000..3d4fa50
--- /dev/null
+++ b/tempest/services/baremetal/base.py
@@ -0,0 +1,197 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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 functools
+import json
+
+import six
+
+from tempest.common import rest_client
+
+
+def handle_errors(f):
+    """A decorator that allows to ignore certain types of errors."""
+
+    @functools.wraps(f)
+    def wrapper(*args, **kwargs):
+        param_name = 'ignore_errors'
+        ignored_errors = kwargs.get(param_name, tuple())
+
+        if param_name in kwargs:
+            del kwargs[param_name]
+
+        try:
+            return f(*args, **kwargs)
+        except ignored_errors:
+            # Silently ignore errors
+            pass
+
+    return wrapper
+
+
+class BaremetalClient(rest_client.RestClient):
+    """
+    Base Tempest REST client for Ironic API.
+
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(BaremetalClient, self).__init__(config, username, password,
+                                              auth_url, tenant_name)
+        self.service = self.config.baremetal.catalog_type
+        self.uri_prefix = ''
+
+    def serialize(self, object_type, object_dict):
+        """Serialize an Ironic object."""
+
+        raise NotImplementedError
+
+    def deserialize(self, object_str):
+        """Deserialize an Ironic object."""
+
+        raise NotImplementedError
+
+    def _get_uri(self, resource_name, uuid=None, permanent=False):
+        """
+        Get URI for a specific resource or object.
+
+        :param resource_name: The name of the REST resource, e.g., 'nodes'.
+        :param uuid: The unique identifier of an object in UUID format.
+        :return: Relative URI for the resource or object.
+
+        """
+        prefix = self.uri_prefix if not permanent else ''
+
+        return '{pref}/{res}{uuid}'.format(pref=prefix,
+                                           res=resource_name,
+                                           uuid='/%s' % uuid if uuid else '')
+
+    def _make_patch(self, allowed_attributes, **kw):
+        """
+        Create a JSON patch according to RFC 6902.
+
+        :param allowed_attributes: An iterable object that contains a set of
+            allowed attributes for an object.
+        :param **kw: Attributes and new values for them.
+        :return: A JSON path that sets values of the specified attributes to
+            the new ones.
+
+        """
+        def get_change(kw, path='/'):
+            for name, value in six.iteritems(kw):
+                if isinstance(value, dict):
+                    for ch in get_change(value, path + '%s/' % name):
+                        yield ch
+                else:
+                    yield {'path': path + name,
+                           'value': value,
+                           'op': 'replace'}
+
+        patch = [ch for ch in get_change(kw)
+                 if ch['path'].lstrip('/') in allowed_attributes]
+
+        return patch
+
+    def _list_request(self, resource, permanent=False):
+        """
+        Get the list of objects of the specified type.
+
+        :param resource: The name of the REST resource, e.g., 'nodes'.
+        :return: A tuple with the server response and deserialized JSON list
+                 of objects
+
+        """
+        uri = self._get_uri(resource, permanent=permanent)
+
+        resp, body = self.get(uri, self.headers)
+
+        return resp, self.deserialize(body)
+
+    def _show_request(self, resource, uuid, permanent=False):
+        """
+        Gets a specific object of the specified type.
+
+        :param uuid: Unique identifier of the object in UUID format.
+        :return: Serialized object as a dictionary.
+
+        """
+        uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
+        resp, body = self.get(uri, self.headers)
+
+        return resp, self.deserialize(body)
+
+    def _create_request(self, resource, object_type, object_dict):
+        """
+        Create an object of the specified type.
+
+        :param resource: The name of the REST resource, e.g., 'nodes'.
+        :param object_dict: A Python dict that represents an object of the
+                            specified type.
+        :return: A tuple with the server response and the deserialized created
+                 object.
+
+        """
+        body = self.serialize(object_type, object_dict)
+        uri = self._get_uri(resource)
+
+        resp, body = self.post(uri, headers=self.headers, body=body)
+
+        return resp, self.deserialize(body)
+
+    def _delete_request(self, resource, uuid):
+        """
+        Delete specified object.
+
+        :param resource: The name of the REST resource, e.g., 'nodes'.
+        :param uuid: The unique identifier of an object in UUID format.
+        :return: A tuple with the server response and the response body.
+
+        """
+        uri = self._get_uri(resource, uuid)
+
+        resp, body = self.delete(uri, self.headers)
+        return resp, body
+
+    def _patch_request(self, resource, uuid, patch_object):
+        """
+        Update specified object with JSON-patch.
+
+        :param resource: The name of the REST resource, e.g., 'nodes'.
+        :param uuid: The unique identifier of an object in UUID format.
+        :return: A tuple with the server response and the serialized patched
+                 object.
+
+        """
+        uri = self._get_uri(resource, uuid)
+        patch_body = json.dumps(patch_object)
+
+        resp, body = self.patch(uri, headers=self.headers, body=patch_body)
+        return resp, self.deserialize(body)
+
+    @handle_errors
+    def get_api_description(self):
+        """Retrieves all versions of the Ironic API."""
+
+        return self._list_request('', permanent=True)
+
+    @handle_errors
+    def get_version_description(self, version='v1'):
+        """
+        Retrieves the desctription of the API.
+
+        :param version: The version of the API. Default: 'v1'.
+        :return: Serialized description of API resources.
+
+        """
+        return self._list_request(version, permanent=True)
diff --git a/tempest/services/baremetal/v1/__init__.py b/tempest/services/baremetal/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/baremetal/v1/__init__.py
diff --git a/tempest/services/baremetal/v1/base_v1.py b/tempest/services/baremetal/v1/base_v1.py
new file mode 100644
index 0000000..5fdf036
--- /dev/null
+++ b/tempest/services/baremetal/v1/base_v1.py
@@ -0,0 +1,209 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.services.baremetal import base
+
+
+class BaremetalClientV1(base.BaremetalClient):
+    """
+    Base Tempest REST client for Ironic API v1.
+
+    Specific implementations must implement serialize and deserialize
+    methods in order to send requests to Ironic.
+
+    """
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(BaremetalClientV1, self).__init__(config, username, password,
+                                                auth_url, tenant_name)
+        self.version = '1'
+        self.uri_prefix = 'v%s' % self.version
+
+    @base.handle_errors
+    def list_nodes(self):
+        """List all existing nodes."""
+        return self._list_request('nodes')
+
+    @base.handle_errors
+    def list_chassis(self):
+        """List all existing chassis."""
+        return self._list_request('chassis')
+
+    @base.handle_errors
+    def list_ports(self):
+        """List all existing ports."""
+        return self._list_request('ports')
+
+    @base.handle_errors
+    def show_node(self, uuid):
+        """
+        Gets a specific node.
+
+        :param uuid: Unique identifier of the node in UUID format.
+        :return: Serialized node as a dictionary.
+
+        """
+        return self._show_request('nodes', uuid)
+
+    @base.handle_errors
+    def show_chassis(self, uuid):
+        """
+        Gets a specific chassis.
+
+        :param uuid: Unique identifier of the chassis in UUID format.
+        :return: Serialized chassis as a dictionary.
+
+        """
+        return self._show_request('chassis', uuid)
+
+    @base.handle_errors
+    def show_port(self, uuid):
+        """
+        Gets a specific port.
+
+        :param uuid: Unique identifier of the port in UUID format.
+        :return: Serialized port as a dictionary.
+
+        """
+        return self._show_request('ports', uuid)
+
+    @base.handle_errors
+    def create_node(self, chassis_id, **kwargs):
+        """
+        Create a baremetal node with the specified parameters.
+
+        :param cpu_arch: CPU architecture of the node. Default: x86_64.
+        :param cpu_num: Number of CPUs. Default: 8.
+        :param storage: Disk size. Default: 1024.
+        :param memory: Available RAM. Default: 4096.
+        :param driver: Driver name. Default: "fake"
+        :return: A tuple with the server response and the created node.
+
+        """
+        node = {'chassis_uuid': chassis_id,
+                'properties': {'cpu_arch': kwargs.get('cpu_arch', 'x86_64'),
+                               'cpu_num': kwargs.get('cpu_num', 8),
+                               'storage': kwargs.get('storage', 1024),
+                               'memory': kwargs.get('memory', 4096)},
+                'driver': kwargs.get('driver', 'fake')}
+
+        return self._create_request('nodes', 'node', node)
+
+    @base.handle_errors
+    def create_chassis(self, **kwargs):
+        """
+        Create a chassis with the specified parameters.
+
+        :param description: The description of the chassis.
+            Default: test-chassis
+        :return: A tuple with the server response and the created chassis.
+
+        """
+        chassis = {'description': kwargs.get('description', 'test-chassis')}
+
+        return self._create_request('chassis', 'chassis', chassis)
+
+    @base.handle_errors
+    def create_port(self, node_id, **kwargs):
+        """
+        Create a port with the specified parameters.
+
+        :param node_id: The ID of the node which owns the port.
+        :param address: MAC address of the port. Default: 01:23:45:67:89:0A.
+        :return: A tuple with the server response and the created port.
+
+        """
+        port = {'address': kwargs.get('address', '01:23:45:67:89:0A'),
+                'node_uuid': node_id}
+
+        return self._create_request('ports', 'port', port)
+
+    @base.handle_errors
+    def delete_node(self, uuid):
+        """
+        Deletes a node having the specified UUID.
+
+        :param uuid: The unique identifier of the node.
+        :return: A tuple with the server response and the response body.
+
+        """
+        return self._delete_request('nodes', uuid)
+
+    @base.handle_errors
+    def delete_chassis(self, uuid):
+        """
+        Deletes a chassis having the specified UUID.
+
+        :param uuid: The unique identifier of the chassis.
+        :return: A tuple with the server response and the response body.
+
+        """
+        return self._delete_request('chassis', uuid)
+
+    @base.handle_errors
+    def delete_port(self, uuid):
+        """
+        Deletes a port having the specified UUID.
+
+        :param uuid: The unique identifier of the port.
+        :return: A tuple with the server response and the response body.
+
+        """
+        return self._delete_request('ports', uuid)
+
+    @base.handle_errors
+    def update_node(self, uuid, **kwargs):
+        """
+        Update the specified node.
+
+        :param uuid: The unique identifier of the node.
+        :return: A tuple with the server response and the updated node.
+
+        """
+        node_attributes = ('properties/cpu_arch',
+                           'properties/cpu_num',
+                           'properties/storage',
+                           'properties/memory',
+                           'driver')
+
+        patch = self._make_patch(node_attributes, **kwargs)
+
+        return self._patch_request('nodes', uuid, patch)
+
+    @base.handle_errors
+    def update_chassis(self, uuid, **kwargs):
+        """
+        Update the specified chassis.
+
+        :param uuid: The unique identifier of the chassis.
+        :return: A tuple with the server response and the updated chassis.
+
+        """
+        chassis_attributes = ('description',)
+        patch = self._make_patch(chassis_attributes, **kwargs)
+
+        return self._patch_request('chassis', uuid, patch)
+
+    @base.handle_errors
+    def update_port(self, uuid, **kwargs):
+        """
+        Update the specified port.
+
+        :param uuid: The unique identifier of the port.
+        :return: A tuple with the server response and the updated port.
+
+        """
+        port_attributes = ('address',)
+        patch = self._make_patch(port_attributes, **kwargs)
+
+        return self._patch_request('ports', uuid, patch)
diff --git a/tempest/services/baremetal/v1/client_json.py b/tempest/services/baremetal/v1/client_json.py
new file mode 100644
index 0000000..fa7cd67
--- /dev/null
+++ b/tempest/services/baremetal/v1/client_json.py
@@ -0,0 +1,28 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.services.baremetal.v1 import base_v1
+
+
+class BaremetalClientJSON(base_v1.BaremetalClientV1):
+    """Tempest REST client for Ironic JSON API v1."""
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(BaremetalClientJSON, self).__init__(config, username, password,
+                                                  auth_url, tenant_name)
+
+        self.serialize = lambda obj_type, obj_body: json.dumps(obj_body)
+        self.deserialize = json.loads
diff --git a/tempest/services/baremetal/v1/client_xml.py b/tempest/services/baremetal/v1/client_xml.py
new file mode 100644
index 0000000..a9b5a77
--- /dev/null
+++ b/tempest/services/baremetal/v1/client_xml.py
@@ -0,0 +1,57 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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.common import rest_client
+from tempest.services.baremetal.v1 import base_v1 as base
+from tempest.services.compute.xml import common as xml
+
+
+class BaremetalClientXML(rest_client.RestClientXML, base.BaremetalClientV1):
+    """Tempest REST client for Ironic XML API v1."""
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(BaremetalClientXML, self).__init__(config, username, password,
+                                                 auth_url, tenant_name)
+
+        self.serialize = self.json_to_xml
+        self.deserialize = xml.xml_to_json
+
+    def json_to_xml(self, object_type, object_dict):
+        """
+        Brainlessly converts a specification of an object to XML string.
+
+        :param object_type: Kind of the object.
+        :param object_dict: Specification of the object attributes as a dict.
+        :return: An XML string that corresponds to the specification.
+
+        """
+        root = xml.Element(object_type)
+
+        for attr_name, value in object_dict:
+            # Handle nested dictionaries
+            if isinstance(value, dict):
+                value = self.json_to_xml(attr_name, value)
+
+            root.append(xml.Element(attr_name, value))
+
+        return str(xml.Document(root))
+
+    def _patch_request(self, resource_name, uuid, patch_object):
+        """Changes Content-Type header to application/json for jsonpatch."""
+
+        self.headers['Content-Type'] = 'application/json'
+        try:
+            super(self)._patch_request(self, resource_name, uuid, patch_object)
+        finally:
+            self.headers['Content-Type'] = 'application/xml'
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index ad5354c..c293f80 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -31,7 +31,7 @@
         url = 'extensions'
         resp, body = self.get(url)
         body = json.loads(body)
-        return resp, body
+        return resp, body['extensions']
 
     def is_enabled(self, extension):
         _, extensions = self.list_extensions()
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index eb1a0c3..2eae39c 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -397,9 +397,9 @@
                               'os-virtual-interfaces']))
         return resp, json.loads(body)
 
-    def rescue_server(self, server_id, adminPass=None):
+    def rescue_server(self, server_id, **kwargs):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, adminPass=adminPass)
+        return self.action(server_id, 'rescue', None, **kwargs)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index 6e0dc9d..bd8287e 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -32,7 +32,7 @@
         url = 'extensions'
         resp, body = self.get(url)
         body = json.loads(body)
-        return resp, body
+        return resp, body['extensions']
 
     def is_enabled(self, extension):
         _, extensions = self.list_extensions()
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index e99ac91..a790c40 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -61,7 +61,7 @@
             'id': flavor_id,
         }
         if kwargs.get('ephemeral'):
-            post_body['OS-FLV-EXT-DATA:ephemeral'] = kwargs.get('ephemeral')
+            post_body['ephemeral'] = kwargs.get('ephemeral')
         if kwargs.get('swap'):
             post_body['swap'] = kwargs.get('swap')
         if kwargs.get('rxtx'):
diff --git a/tempest/services/compute/v3/json/instance_usage_audit_log_client.py b/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
index 07ce1bb..bd73c13 100644
--- a/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
@@ -20,21 +20,18 @@
 from tempest.common.rest_client import RestClient
 
 
-class InstanceUsagesAuditLogClientJSON(RestClient):
+class InstanceUsagesAuditLogV3ClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(InstanceUsagesAuditLogClientJSON, self).__init__(
+        super(InstanceUsagesAuditLogV3ClientJSON, self).__init__(
             config, username, password, auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        self.service = self.config.compute.catalog_v3_type
 
-    def list_instance_usage_audit_logs(self):
-        url = 'os-instance_usage_audit_log'
-        resp, body = self.get(url)
-        body = json.loads(body)
-        return resp, body["instance_usage_audit_logs"]
-
-    def get_instance_usage_audit_log(self, time_before):
-        url = 'os-instance_usage_audit_log/%s' % time_before
+    def list_instance_usage_audit_logs(self, time_before=None):
+        if time_before:
+            url = 'os-instance-usage-audit-log?before=%s' % time_before
+        else:
+            url = 'os-instance-usage-audit-log'
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body["instance_usage_audit_log"]
diff --git a/tempest/services/compute/v3/json/limits_client.py b/tempest/services/compute/v3/json/limits_client.py
deleted file mode 100644
index 3e53e3e..0000000
--- a/tempest/services/compute/v3/json/limits_client.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 OpenStack Foundation
-# 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 LimitsClientJSON(RestClient):
-
-    def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(LimitsClientJSON, self).__init__(config, username, password,
-                                               auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
-
-    def get_absolute_limits(self):
-        resp, body = self.get("limits")
-        body = json.loads(body)
-        return resp, body['limits']['absolute']
-
-    def get_specific_absolute_limit(self, absolute_limit):
-        resp, body = self.get("limits")
-        body = json.loads(body)
-        if absolute_limit not in body['limits']['absolute']:
-            return None
-        else:
-            return body['limits']['absolute'][absolute_limit]
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index da31036..eba3be5 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -45,8 +45,6 @@
         admin_password: Sets the initial root password.
         key_name: Key name of keypair that was created earlier.
         meta: A dictionary of values to be used as metadata.
-        personality: A list of dictionaries for files to be injected into
-        the server.
         security_groups: A list of security group dicts.
         networks: A list of network dicts with UUID and fixed_ip.
         user_data: User data for instance.
@@ -64,7 +62,7 @@
             'flavor_ref': flavor_ref
         }
 
-        for option in ['personality', 'admin_password', 'key_name', 'networks',
+        for option in ['admin_password', 'key_name', 'networks',
                        ('os-security-groups:security_groups',
                         'security_groups'),
                        ('os-user-data:user_data', 'user_data'),
@@ -77,7 +75,7 @@
                        ('metadata', 'meta'),
                        ('os-disk-config:disk_config', 'disk_config'),
                        ('os-multiple-create:return_reservation_id',
-                       'return_reservation_id')]:
+                        'return_reservation_id')]:
             if isinstance(option, tuple):
                 post_param = option[0]
                 key = option[1]
@@ -103,7 +101,6 @@
         Updates the properties of an existing server.
         server_id: The id of an existing server.
         name: The name of the server.
-        personality: A list of files to be injected into the server.
         access_ip_v4: The IPv4 access address for the server.
         access_ip_v6: The IPv6 access address for the server.
         """
@@ -163,10 +160,12 @@
         body = json.loads(body)
         return resp, body
 
-    def wait_for_server_status(self, server_id, status, extra_timeout=0):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0,
+                               raise_on_error=True):
         """Waits for a server to reach a given status."""
         return waiters.wait_for_server_status(self, server_id, status,
-                                              extra_timeout=extra_timeout)
+                                              extra_timeout=extra_timeout,
+                                              raise_on_error=raise_on_error)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -346,19 +345,19 @@
         return self.action(server_id, 'unlock', None, **kwargs)
 
     def suspend_server(self, server_id, **kwargs):
-        """Suspends the provded server."""
+        """Suspends the provided server."""
         return self.action(server_id, 'suspend', None, **kwargs)
 
     def resume_server(self, server_id, **kwargs):
-        """Un-suspends the provded server."""
+        """Un-suspends the provided server."""
         return self.action(server_id, 'resume', None, **kwargs)
 
     def pause_server(self, server_id, **kwargs):
-        """Pauses the provded server."""
+        """Pauses the provided server."""
         return self.action(server_id, 'pause', None, **kwargs)
 
     def unpause_server(self, server_id, **kwargs):
-        """Un-pauses the provded server."""
+        """Un-pauses the provided server."""
         return self.action(server_id, 'unpause', None, **kwargs)
 
     def reset_state(self, server_id, state='error'):
@@ -377,10 +376,9 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, admin_password=None):
+    def rescue_server(self, server_id, **kwargs):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None,
-                           admin_password=admin_password)
+        return self.action(server_id, 'rescue', None, **kwargs)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/v3/xml/extensions_client.py b/tempest/services/compute/v3/xml/extensions_client.py
index 8f97692..926770e 100644
--- a/tempest/services/compute/v3/xml/extensions_client.py
+++ b/tempest/services/compute/v3/xml/extensions_client.py
@@ -37,7 +37,7 @@
         url = 'extensions'
         resp, body = self.get(url, self.headers)
         body = self._parse_array(etree.fromstring(body))
-        return resp, {'extensions': body}
+        return resp, body
 
     def is_enabled(self, extension):
         _, extensions = self.list_extensions()
diff --git a/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py b/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py
index 175997b..b8429b2 100644
--- a/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/v3/xml/instance_usage_audit_log_client.py
@@ -21,21 +21,18 @@
 from tempest.services.compute.xml.common import xml_to_json
 
 
-class InstanceUsagesAuditLogClientXML(RestClientXML):
+class InstanceUsagesAuditLogV3ClientXML(RestClientXML):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(InstanceUsagesAuditLogClientXML, self).__init__(
+        super(InstanceUsagesAuditLogV3ClientXML, self).__init__(
             config, username, password, auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
+        self.service = self.config.compute.catalog_v3_type
 
-    def list_instance_usage_audit_logs(self):
-        url = 'os-instance_usage_audit_log'
+    def list_instance_usage_audit_logs(self, time_before=None):
+        if time_before:
+            url = 'os-instance-usage-audit-log?before=%s' % time_before
+        else:
+            url = 'os-instance-usage-audit-log'
         resp, body = self.get(url, self.headers)
         instance_usage_audit_logs = xml_to_json(etree.fromstring(body))
         return resp, instance_usage_audit_logs
-
-    def get_instance_usage_audit_log(self, time_before):
-        url = 'os-instance_usage_audit_log/%s' % time_before
-        resp, body = self.get(url, self.headers)
-        instance_usage_audit_log = xml_to_json(etree.fromstring(body))
-        return resp, instance_usage_audit_log
diff --git a/tempest/services/compute/v3/xml/limits_client.py b/tempest/services/compute/v3/xml/limits_client.py
deleted file mode 100644
index 704de52..0000000
--- a/tempest/services/compute/v3/xml/limits_client.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-#
-# Copyright 2012 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 objectify
-
-from tempest.common.rest_client import RestClientXML
-
-NS = "{http://docs.openstack.org/common/api/v1.0}"
-
-
-class LimitsClientXML(RestClientXML):
-
-    def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(LimitsClientXML, self).__init__(config, username, password,
-                                              auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
-
-    def get_absolute_limits(self):
-        resp, body = self.get("limits", self.headers)
-        body = objectify.fromstring(body)
-        lim = NS + 'absolute'
-        ret = {}
-
-        for el in body[lim].iterchildren():
-            attributes = el.attrib
-            ret[attributes['name']] = attributes['value']
-        return resp, ret
-
-    def get_specific_absolute_limit(self, absolute_limit):
-        resp, body = self.get("limits", self.headers)
-        body = objectify.fromstring(body)
-        lim = NS + 'absolute'
-        ret = {}
-
-        for el in body[lim].iterchildren():
-            attributes = el.attrib
-            ret[attributes['name']] = attributes['value']
-        if absolute_limit not in ret:
-            return None
-        else:
-            return ret[absolute_limit]
diff --git a/tempest/services/compute/v3/xml/servers_client.py b/tempest/services/compute/v3/xml/servers_client.py
index a5cc291..68831cd 100644
--- a/tempest/services/compute/v3/xml/servers_client.py
+++ b/tempest/services/compute/v3/xml/servers_client.py
@@ -194,6 +194,10 @@
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
+    def migrate_server(self, server_id, **kwargs):
+        """Migrates the given server ."""
+        return self.action(server_id, 'migrate', None, **kwargs)
+
     def lock_server(self, server_id, **kwargs):
         """Locks the given server."""
         return self.action(server_id, 'lock', None, **kwargs)
@@ -300,8 +304,6 @@
         admin_password: Sets the initial root password.
         key_name: Key name of keypair that was created earlier.
         meta: A dictionary of values to be used as metadata.
-        personality: A list of dictionaries for files to be injected into
-        the server.
         security_groups: A list of security group dicts.
         networks: A list of network dicts with UUID and fixed_ip.
         user_data: User data for instance.
@@ -400,22 +402,16 @@
                 meta.append(Text(v))
                 metadata.append(meta)
 
-        if 'personality' in kwargs:
-            personality = Element('personality')
-            server.append(personality)
-            for k in kwargs['personality']:
-                temp = Element('file', path=k['path'])
-                temp.append(Text(k['contents']))
-                personality.append(temp)
-
         resp, body = self.post('servers', str(Document(server)), self.headers)
         server = self._parse_server(etree.fromstring(body))
         return resp, server
 
-    def wait_for_server_status(self, server_id, status, extra_timeout=0):
+    def wait_for_server_status(self, server_id, status, extra_timeout=0,
+                               raise_on_error=True):
         """Waits for a server to reach a given status."""
         return waiters.wait_for_server_status(self, server_id, status,
-                                              extra_timeout=extra_timeout)
+                                              extra_timeout=extra_timeout,
+                                              raise_on_error=raise_on_error)
 
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
@@ -608,7 +604,7 @@
     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 = Element("metadata", key=k)
             meta_element.append(Text(v))
             doc.append(meta_element)
         resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
@@ -624,10 +620,9 @@
         return self.action(server_id, 'get_console_output', 'output',
                            length=length)
 
-    def rescue_server(self, server_id, admin_password=None):
+    def rescue_server(self, server_id, **kwargs):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None,
-                           admin_password=admin_password)
+        return self.action(server_id, 'rescue', None, **kwargs)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index d2a18a1..76ad1aa 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -124,9 +124,9 @@
         if tag.startswith("{"):
             ns, tag = tag.split("}", 1)
         if plurals is not None and tag in plurals:
-                json[tag] = parse_array(child)
+                json[tag] = parse_array(child, plurals)
         else:
-            json[tag] = xml_to_json(child)
+            json[tag] = xml_to_json(child, plurals)
     return json
 
 
diff --git a/tempest/services/compute/xml/extensions_client.py b/tempest/services/compute/xml/extensions_client.py
index b17fc4f..8b7b90a 100644
--- a/tempest/services/compute/xml/extensions_client.py
+++ b/tempest/services/compute/xml/extensions_client.py
@@ -37,7 +37,7 @@
         url = 'extensions'
         resp, body = self.get(url, self.headers)
         body = self._parse_array(etree.fromstring(body))
-        return resp, {'extensions': body}
+        return resp, body
 
     def is_enabled(self, extension):
         _, extensions = self.list_extensions()
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 68f6cf0..628c6ee 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -595,9 +595,9 @@
         virt_int = self._parse_xml_virtual_interfaces(etree.fromstring(body))
         return resp, virt_int
 
-    def rescue_server(self, server_id, adminPass=None):
+    def rescue_server(self, server_id, **kwargs):
         """Rescue the provided server."""
-        return self.action(server_id, 'rescue', None, adminPass=adminPass)
+        return self.action(server_id, 'rescue', None, **kwargs)
 
     def unrescue_server(self, server_id):
         """Unrescue the provided server."""
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index e896e0d..d5ae828 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -102,6 +102,20 @@
         body = json.loads(body)
         return resp, body['stack']
 
+    def suspend_stack(self, stack_identifier):
+        """Suspend a stack."""
+        url = 'stacks/%s/actions' % stack_identifier
+        body = {'suspend': None}
+        resp, body = self.post(url, json.dumps(body), self.headers)
+        return resp, body
+
+    def resume_stack(self, stack_identifier):
+        """Resume a stack."""
+        url = 'stacks/%s/actions' % stack_identifier
+        body = {'resume': None}
+        resp, body = self.post(url, json.dumps(body), self.headers)
+        return resp, body
+
     def list_resources(self, stack_identifier):
         """Returns the details of a single resource."""
         url = "stacks/%s/resources" % stack_identifier
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 9435122..c270de8 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -149,3 +149,40 @@
         url = 'snapshots/%s/action' % str(snapshot_id)
         resp, body = self.post(url, post_body, self.headers)
         return resp, body
+
+    def create_snapshot_metadata(self, snapshot_id, metadata):
+        """Create metadata for the snapshot."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "snapshots/%s/metadata" % str(snapshot_id)
+        resp, body = self.post(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def get_snapshot_metadata(self, snapshot_id):
+        """Get metadata of the snapshot."""
+        url = "snapshots/%s/metadata" % str(snapshot_id)
+        resp, body = self.get(url, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def update_snapshot_metadata(self, snapshot_id, metadata):
+        """Update metadata for the snapshot."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "snapshots/%s/metadata" % str(snapshot_id)
+        resp, body = self.put(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def update_snapshot_metadata_item(self, snapshot_id, id, meta_item):
+        """Update metadata item for the snapshot."""
+        put_body = json.dumps({'meta': meta_item})
+        url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+        resp, body = self.put(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['meta']
+
+    def delete_snapshot_metadata_item(self, snapshot_id, id):
+        """Delete metadata item for the snapshot."""
+        url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+        resp, body = self.delete(url, self.headers)
+        return resp, body
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 967dc09..afba4b0 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -263,3 +263,40 @@
         resp, body = self.post('volumes/%s/action' % volume_id, post_body,
                                self.headers)
         return resp, body
+
+    def create_volume_metadata(self, volume_id, metadata):
+        """Create metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % str(volume_id)
+        resp, body = self.post(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def get_volume_metadata(self, volume_id):
+        """Get metadata of the volume."""
+        url = "volumes/%s/metadata" % str(volume_id)
+        resp, body = self.get(url, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def update_volume_metadata(self, volume_id, metadata):
+        """Update metadata for the volume."""
+        put_body = json.dumps({'metadata': metadata})
+        url = "volumes/%s/metadata" % str(volume_id)
+        resp, body = self.put(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['metadata']
+
+    def update_volume_metadata_item(self, volume_id, id, meta_item):
+        """Update metadata item for the volume."""
+        put_body = json.dumps({'meta': meta_item})
+        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+        resp, body = self.put(url, put_body, self.headers)
+        body = json.loads(body)
+        return resp, body['meta']
+
+    def delete_volume_metadata_item(self, volume_id, id):
+        """Delete metadata item for the volume."""
+        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+        resp, body = self.delete(url, self.headers)
+        return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 5d59b07..3a70eab 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -22,6 +22,7 @@
 from tempest.openstack.common import log as logging
 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
 from tempest.services.compute.xml.common import XMLNS_11
 
@@ -170,3 +171,57 @@
         if body:
             body = xml_to_json(etree.fromstring(body))
         return resp, body
+
+    def _metadata_body(self, meta):
+        post_body = Element('metadata')
+        for k, v in meta.items():
+            data = Element('meta', key=k)
+            data.append(Text(v))
+            post_body.append(data)
+        return post_body
+
+    def _parse_key_value(self, node):
+        """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
+        data = {}
+        for node in node.getchildren():
+            data[node.get('key')] = node.text
+        return data
+
+    def create_snapshot_metadata(self, snapshot_id, metadata):
+        """Create metadata for the snapshot."""
+        post_body = self._metadata_body(metadata)
+        resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
+                               str(Document(post_body)),
+                               self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def get_snapshot_metadata(self, snapshot_id):
+        """Get metadata of the snapshot."""
+        url = "snapshots/%s/metadata" % str(snapshot_id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def update_snapshot_metadata(self, snapshot_id, metadata):
+        """Update metadata for the snapshot."""
+        put_body = self._metadata_body(metadata)
+        url = "snapshots/%s/metadata" % str(snapshot_id)
+        resp, body = self.put(url, str(Document(put_body)), self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def update_snapshot_metadata_item(self, snapshot_id, id, meta_item):
+        """Update metadata item for the snapshot."""
+        for k, v in meta_item.items():
+            put_body = Element('meta', key=k)
+            put_body.append(Text(v))
+        url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+        resp, body = self.put(url, str(Document(put_body)), self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def delete_snapshot_metadata_item(self, snapshot_id, id):
+        """Delete metadata item for the snapshot."""
+        url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+        return self.delete(url)
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 1fc63e9..f175138 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -356,3 +356,57 @@
         if body:
             body = xml_to_json(etree.fromstring(body))
         return resp, body
+
+    def _metadata_body(self, meta):
+        post_body = Element('metadata')
+        for k, v in meta.items():
+            data = Element('meta', key=k)
+            data.append(Text(v))
+            post_body.append(data)
+        return post_body
+
+    def _parse_key_value(self, node):
+        """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
+        data = {}
+        for node in node.getchildren():
+            data[node.get('key')] = node.text
+        return data
+
+    def create_volume_metadata(self, volume_id, metadata):
+        """Create metadata for the volume."""
+        post_body = self._metadata_body(metadata)
+        resp, body = self.post('volumes/%s/metadata' % volume_id,
+                               str(Document(post_body)),
+                               self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def get_volume_metadata(self, volume_id):
+        """Get metadata of the volume."""
+        url = "volumes/%s/metadata" % str(volume_id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def update_volume_metadata(self, volume_id, metadata):
+        """Update metadata for the volume."""
+        put_body = self._metadata_body(metadata)
+        url = "volumes/%s/metadata" % str(volume_id)
+        resp, body = self.put(url, str(Document(put_body)), self.headers)
+        body = self._parse_key_value(etree.fromstring(body))
+        return resp, body
+
+    def update_volume_metadata_item(self, volume_id, id, meta_item):
+        """Update metadata item for the volume."""
+        for k, v in meta_item.items():
+            put_body = Element('meta', key=k)
+            put_body.append(Text(v))
+        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+        resp, body = self.put(url, str(Document(put_body)), self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def delete_volume_metadata_item(self, volume_id, id):
+        """Delete metadata item for the volume."""
+        url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+        return self.delete(url)
diff --git a/tempest/stress/__init__.py b/tempest/stress/__init__.py
index 1caf74a..e69de29 100644
--- a/tempest/stress/__init__.py
+++ b/tempest/stress/__init__.py
@@ -1,13 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, 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.
diff --git a/tempest/stress/actions/__init__.py b/tempest/stress/actions/__init__.py
index 1caf74a..e69de29 100644
--- a/tempest/stress/actions/__init__.py
+++ b/tempest/stress/actions/__init__.py
@@ -1,13 +0,0 @@
-# Copyright 2013 Quanta Research Cambridge, 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.
diff --git a/tempest/stress/etc/ssh_floating.json b/tempest/stress/etc/ssh_floating.json
index 0cb6776..e03fd4f 100644
--- a/tempest/stress/etc/ssh_floating.json
+++ b/tempest/stress/etc/ssh_floating.json
@@ -8,7 +8,7 @@
              "new_floating": true,
              "verify": ["check_icmp_echo", "check_port_ssh"],
              "check_timeout": 120,
-             "check_inerval": 1,
+             "check_interval": 1,
              "wait_after_vm_create": true,
              "wait_for_disassociate": true,
              "reboot": false}
diff --git a/tempest/test.py b/tempest/test.py
index 342846f..931aa5d 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -163,6 +163,7 @@
         'compute_v3': configs.compute_feature_enabled.api_v3_extensions,
         'volume': configs.volume_feature_enabled.api_extensions,
         'network': configs.network_feature_enabled.api_extensions,
+        'object': configs.object_storage_feature_enabled.discoverable_apis,
     }
     if config_dict[service][0] == 'all':
         return True
@@ -229,6 +230,8 @@
 
     setUpClassCalled = False
 
+    network_resources = {}
+
     @classmethod
     def setUpClass(cls):
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
@@ -276,7 +279,8 @@
         """
         Returns an Openstack client manager
         """
-        cls.isolated_creds = isolated_creds.IsolatedCreds(cls.__name__)
+        cls.isolated_creds = isolated_creds.IsolatedCreds(
+            cls.__name__, network_resources=cls.network_resources)
 
         force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
         if (cls.config.compute.allow_tenant_isolation or
@@ -318,6 +322,27 @@
             cls.config.identity.uri
         )
 
+    @classmethod
+    def set_network_resources(self, network=False, router=False, subnet=False,
+                              dhcp=False):
+        """Specify which network resources should be created
+
+        @param network
+        @param router
+        @param subnet
+        @param dhcp
+        """
+        # network resources should be set only once from callers
+        # in order to ensure that even if it's called multiple times in
+        # a chain of overloaded methods, the attribute is set only
+        # in the leaf class
+        if not self.network_resources:
+            self.network_resources = {
+                'network': network,
+                'router': router,
+                'subnet': subnet,
+                'dhcp': dhcp}
+
 
 def call_until_true(func, duration, sleep_for):
     """
diff --git a/test-requirements.txt b/test-requirements.txt
index 9486244..d7340f3 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -6,3 +6,4 @@
 oslo.sphinx
 mox>=0.5.3
 mock>=1.0
+coverage>=3.6
diff --git a/tox.ini b/tox.ini
index 6d596e3..88f2537 100644
--- a/tox.ini
+++ b/tox.ini
@@ -25,6 +25,10 @@
 setenv = OS_TEST_PATH=./tempest/tests
 commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
 
+[testenv:cover]
+setenv = OS_TEST_PATH=./tempest/tests
+commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
+
 [testenv:all]
 setenv = VIRTUAL_ENV={envdir}
 commands =