Merge "Remove invalid use of kwargs from image clients"
diff --git a/README.rst b/README.rst
index 9aaea24..ba93712 100644
--- a/README.rst
+++ b/README.rst
@@ -54,13 +54,13 @@
 
 .. note::
 
-    If you have a running devstack environment, tempest will be
+    If you have a running devstack environment, Tempest will be
     automatically configured and placed in ``/opt/stack/tempest``. It
     will have a configuration file already set up to work with your
     devstack installation.
 
 Tempest is not tied to any single test runner, but `testr`_ is the most commonly
-used tool. Also, the nosetests test runner is **not** recommended to run tempest.
+used tool. Also, the nosetests test runner is **not** recommended to run Tempest.
 
 After setting up your configuration file, you can execute the set of Tempest
 tests by using ``testr`` ::
@@ -80,7 +80,7 @@
    $> tox -efull
 
 which will run the same set of tests as the OpenStack gate. (it's exactly how
-the gate invokes tempest) Or::
+the gate invokes Tempest) Or::
 
   $> tox -esmoke
 
@@ -90,13 +90,13 @@
 Configuration
 -------------
 
-Detailed configuration of tempest is beyond the scope of this
+Detailed configuration of Tempest is beyond the scope of this
 document see :ref:`tempest-configuration` for more details on configuring
-tempest. The etc/tempest.conf.sample attempts to be a self documenting version
+Tempest. The etc/tempest.conf.sample attempts to be a self documenting version
 of the configuration.
 
 You can generate a new sample tempest.conf file, run the following
-command from the top level of the tempest directory:
+command from the top level of the Tempest directory:
 
   tox -egenconfig
 
@@ -106,7 +106,7 @@
 Unit Tests
 ----------
 
-Tempest also has a set of unit tests which test the tempest code itself. These
+Tempest also has a set of unit tests which test the Tempest code itself. These
 tests can be run by specifing the test discovery path::
 
     $> OS_TEST_PATH=./tempest/tests testr run --parallel
@@ -114,7 +114,7 @@
 By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
 should only be run on the unit test directory. The default value of OS_TEST_PATH
 is OS_TEST_PATH=./tempest/test_discover which will only run test discover on the
-tempest suite.
+Tempest suite.
 
 Alternatively, you can use the run_tests.sh script which will create a venv and
 run the unit tests. There are also the py26, py27, or py33 tox jobs which will
@@ -124,10 +124,10 @@
 ----------
 
 Starting in the kilo release the OpenStack services dropped all support for
-python 2.6. This change has been mirrored in tempest, starting after the
-tempest-2 tag. This means that proposed changes to tempest which only fix
+python 2.6. This change has been mirrored in Tempest, starting after the
+tempest-2 tag. This means that proposed changes to Tempest which only fix
 python 2.6 compatibility will be rejected, and moving forward more features not
-present in python 2.6 will be used. If you're running you're OpenStack services
-on an earlier release with python 2.6 you can easily run tempest against it
+present in python 2.6 will be used. If you're running your OpenStack services
+on an earlier release with python 2.6 you can easily run Tempest against it
 from a remote system running python 2.7. (or deploy a cloud guest in your cloud
 that has python 2.7)
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 0246488..2e57007 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -116,6 +116,11 @@
 # Roles to assign to all users created by tempest (list value)
 #tempest_roles =
 
+# Only applicable when identity.auth_version is v3.Domain within which
+# isolated credentials are provisioned.The default "None" means that
+# the domain from theadmin user is used instead. (string value)
+#tenant_isolation_domain_name = <None>
+
 
 [baremetal]
 
@@ -422,6 +427,11 @@
 # Does the test environment have the ec2 api running? (boolean value)
 #ec2_api = true
 
+# Does Nova preserve preexisting ports from Neutron when deleting an
+# instance? This should be set to True if testing Kilo+ Nova. (boolean
+# value)
+#preserve_ports = false
+
 
 [dashboard]
 
@@ -873,6 +883,9 @@
 # Allowed values: public, admin, internal, publicURL, adminURL, internalURL
 #endpoint_type = publicURL
 
+# Role required for users to be able to manage stacks (string value)
+#stack_owner_role = heat_stack_owner
+
 # Time in seconds between build status checks. (integer value)
 #build_interval = 1
 
diff --git a/requirements.txt b/requirements.txt
index e6fc09b..35b5144 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,22 +9,20 @@
 boto>=2.32.1
 paramiko>=1.13.0
 netaddr>=0.7.12
-python-ceilometerclient>=1.0.6
 python-glanceclient>=0.15.0
 python-cinderclient>=1.1.0
 python-heatclient>=0.3.0
-python-ironicclient>=0.2.1
-python-saharaclient>=0.7.6
+python-saharaclient>=0.8.0
 python-swiftclient>=2.2.0
 testrepository>=0.0.18
-oslo.concurrency>=1.4.1         # Apache-2.0
-oslo.config>=1.6.0  # Apache-2.0
-oslo.i18n>=1.3.0  # Apache-2.0
-oslo.log>=0.4.0  # Apache-2.0
-oslo.serialization>=1.2.0               # Apache-2.0
-oslo.utils>=1.2.0                       # Apache-2.0
+oslo.concurrency>=1.8.0,<1.9.0         # Apache-2.0
+oslo.config>=1.9.3,<1.10.0  # Apache-2.0
+oslo.i18n>=1.5.0,<1.6.0  # Apache-2.0
+oslo.log>=1.0.0,<1.1.0  # Apache-2.0
+oslo.serialization>=1.4.0,<1.5.0               # Apache-2.0
+oslo.utils>=1.4.0,<1.5.0                       # Apache-2.0
 six>=1.9.0
 iso8601>=0.1.9
 fixtures>=0.3.14
 testscenarios>=0.4
-tempest-lib>=0.3.0
+tempest-lib>=0.4.0
diff --git a/tempest/api/baremetal/admin/base.py b/tempest/api/baremetal/admin/base.py
index 2834b2b..cf2484d 100644
--- a/tempest/api/baremetal/admin/base.py
+++ b/tempest/api/baremetal/admin/base.py
@@ -16,6 +16,7 @@
 from tempest_lib import exceptions as lib_exc
 
 from tempest import clients
+from tempest.common import credentials
 from tempest import config
 from tempest import test
 
@@ -69,7 +70,11 @@
     @classmethod
     def setup_credentials(cls):
         super(BaseBaremetalTest, cls).setup_credentials()
-        cls.mgr = clients.AdminManager()
+        if (not hasattr(cls, 'isolated_creds') or
+            not cls.isolated_creds.name == cls.__name__):
+            cls.isolated_creds = credentials.get_isolated_credentials(
+                name=cls.__name__, network_resources=cls.network_resources)
+        cls.mgr = clients.Manager(cls.isolated_creds.get_admin_creds())
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index 578f73b..6d79a77 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -39,7 +39,7 @@
 
     @test.idempotent_id('49667619-5af9-4c63-ab5d-2cfdd1c8f7f1')
     @testtools.skipIf(CONF.service_available.neutron,
-                      "Skipped because neutron do not support all_tenants"
+                      "Skipped because neutron does not support all_tenants "
                       "search filter.")
     @test.attr(type='smoke')
     @test.services('network')
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 543dea1..882ef98 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -31,8 +31,8 @@
     @classmethod
     def setup_credentials(cls):
         super(BaseIdentityAdminTest, cls).setup_credentials()
-        cls.os_adm = clients.AdminManager()
-        cls.os = clients.Manager()
+        cls.os = cls.get_client_manager()
+        cls.os_adm = clients.Manager(cls.isolated_creds.get_admin_creds())
 
     @classmethod
     def disable_user(cls, user_name):
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 1877bbf..59fdec0 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -18,6 +18,7 @@
 import yaml
 
 from tempest import clients
+from tempest.common import credentials
 from tempest import config
 import tempest.test
 
@@ -38,7 +39,19 @@
     @classmethod
     def setup_credentials(cls):
         super(BaseOrchestrationTest, cls).setup_credentials()
-        cls.os = clients.Manager()
+        if (not hasattr(cls, 'isolated_creds') or
+            not cls.isolated_creds.name == cls.__name__):
+            cls.isolated_creds = credentials.get_isolated_credentials(
+                name=cls.__name__, network_resources=cls.network_resources)
+        stack_owner_role = CONF.orchestration.stack_owner_role
+        if not cls.isolated_creds.is_role_available(stack_owner_role):
+            skip_msg = ("%s skipped because the configured credential provider"
+                        " is not able to provide credentials with the %s role "
+                        "assigned." % (cls.__name__, stack_owner_role))
+            raise cls.skipException(skip_msg)
+        else:
+            cls.os = clients.Manager(cls.isolated_creds.get_creds_by_roles(
+                [stack_owner_role]))
 
     @classmethod
     def setup_clients(cls):
@@ -70,7 +83,7 @@
     @classmethod
     def _get_identity_admin_client(cls):
         """Returns an instance of the Identity Admin API client."""
-        manager = clients.AdminManager()
+        manager = clients.Manager(cls.isolated_creds.get_admin_creds())
         admin_client = manager.identity_client
         return admin_client
 
diff --git a/tempest/api_schema/response/compute/agents.py b/tempest/api_schema/response/compute/agents.py
deleted file mode 100644
index e5f3a8d..0000000
--- a/tempest/api_schema/response/compute/agents.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2014 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.
-
-list_agents = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'agents': {
-                'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'agent_id': {'type': 'integer'},
-                        'hypervisor': {'type': 'string'},
-                        'os': {'type': 'string'},
-                        'architecture': {'type': 'string'},
-                        'version': {'type': 'string'},
-                        'url': {'type': 'string', 'format': 'uri'},
-                        'md5hash': {'type': 'string'}
-                    },
-                    'required': ['agent_id', 'hypervisor', 'os',
-                                 'architecture', 'version', 'url', 'md5hash']
-                }
-            }
-        },
-        'required': ['agents']
-    }
-}
-
-common_create_agent = {
-    'type': 'object',
-    'properties': {
-        'agent': {
-            'type': 'object',
-            'properties': {
-                'agent_id': {'type': ['integer', 'string']},
-                'hypervisor': {'type': 'string'},
-                'os': {'type': 'string'},
-                'architecture': {'type': 'string'},
-                'version': {'type': 'string'},
-                'url': {'type': 'string', 'format': 'uri'},
-                'md5hash': {'type': 'string'}
-            },
-            'required': ['agent_id', 'hypervisor', 'os', 'architecture',
-                         'version', 'url', 'md5hash']
-        }
-    },
-    'required': ['agent']
-}
diff --git a/tempest/api_schema/response/compute/v2/agents.py b/tempest/api_schema/response/compute/v2/agents.py
deleted file mode 100644
index d827377..0000000
--- a/tempest/api_schema/response/compute/v2/agents.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2014 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_schema.response.compute import agents
-
-create_agent = {
-    'status_code': [200],
-    'response_body': agents.common_create_agent
-}
-
-delete_agent = {
-    'status_code': [200]
-}
diff --git a/tempest/api_schema/response/compute/v2/aggregates.py b/tempest/api_schema/response/compute/v2/aggregates.py
deleted file mode 100644
index d87e4de..0000000
--- a/tempest/api_schema/response/compute/v2/aggregates.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2014 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.
-
-import copy
-
-from tempest.api_schema.response.compute import aggregates
-
-delete_aggregate = {
-    'status_code': [200]
-}
-
-create_aggregate = copy.deepcopy(aggregates.common_create_aggregate)
-# V2 API's response status_code is 200
-create_aggregate['status_code'] = [200]
diff --git a/tempest/api_schema/response/compute/v2/certificates.py b/tempest/api_schema/response/compute/v2/certificates.py
deleted file mode 100644
index bda6075..0000000
--- a/tempest/api_schema/response/compute/v2/certificates.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2014 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.
-
-import copy
-
-from tempest.api_schema.response.compute import certificates
-
-create_certificate = copy.deepcopy(certificates._common_schema)
diff --git a/tempest/api_schema/response/compute/v2/interfaces.py b/tempest/api_schema/response/compute/v2/interfaces.py
deleted file mode 100644
index 64d161d..0000000
--- a/tempest/api_schema/response/compute/v2/interfaces.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2014 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_schema.response.compute import interfaces as common_schema
-
-list_interfaces = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'interfaceAttachments': {
-                'type': 'array',
-                'items': common_schema.interface_common_info
-            }
-        },
-        'required': ['interfaceAttachments']
-    }
-}
diff --git a/tempest/api_schema/response/compute/v2/__init__.py b/tempest/api_schema/response/compute/v2_1/__init__.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/__init__.py
rename to tempest/api_schema/response/compute/v2_1/__init__.py
diff --git a/tempest/api_schema/response/compute/v2_1/agents.py b/tempest/api_schema/response/compute/v2_1/agents.py
new file mode 100644
index 0000000..84c5fd3
--- /dev/null
+++ b/tempest/api_schema/response/compute/v2_1/agents.py
@@ -0,0 +1,57 @@
+# Copyright 2014 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.
+
+common_agent_info = {
+    'type': 'object',
+    'properties': {
+        'agent_id': {'type': ['integer', 'string']},
+        'hypervisor': {'type': 'string'},
+        'os': {'type': 'string'},
+        'architecture': {'type': 'string'},
+        'version': {'type': 'string'},
+        'url': {'type': 'string', 'format': 'uri'},
+        'md5hash': {'type': 'string'}
+    },
+    'required': ['agent_id', 'hypervisor', 'os', 'architecture',
+                 'version', 'url', 'md5hash']
+}
+
+list_agents = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'agents': {
+                'type': 'array',
+                'items': common_agent_info
+            }
+        },
+        'required': ['agents']
+    }
+}
+
+create_agent = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'agent': common_agent_info
+        },
+        'required': ['agent']
+    }
+}
+
+delete_agent = {
+    'status_code': [200]
+}
diff --git a/tempest/api_schema/response/compute/aggregates.py b/tempest/api_schema/response/compute/v2_1/aggregates.py
similarity index 67%
rename from tempest/api_schema/response/compute/aggregates.py
rename to tempest/api_schema/response/compute/v2_1/aggregates.py
index fc20885..c935592 100644
--- a/tempest/api_schema/response/compute/aggregates.py
+++ b/tempest/api_schema/response/compute/v2_1/aggregates.py
@@ -27,33 +27,15 @@
         'updated_at': {'type': ['string', 'null']}
     },
     'required': ['availability_zone', 'created_at', 'deleted',
-                 'deleted_at', 'id', 'name', 'updated_at']
+                 'deleted_at', 'id', 'name', 'updated_at'],
 }
 
-aggregate = copy.deepcopy(aggregate_for_create)
-aggregate['properties'].update({
+common_aggregate_info = copy.deepcopy(aggregate_for_create)
+common_aggregate_info['properties'].update({
     'hosts': {'type': 'array'},
     'metadata': {'type': 'object'}
 })
-aggregate['required'].extend(['hosts', 'metadata'])
-
-aggregate = {
-    'type': 'object',
-    'properties': {
-        'availability_zone': {'type': ['string', 'null']},
-        'created_at': {'type': 'string'},
-        'deleted': {'type': 'boolean'},
-        'deleted_at': {'type': ['string', 'null']},
-        'hosts': {'type': 'array'},
-        'id': {'type': 'integer'},
-        'metadata': {'type': 'object'},
-        'name': {'type': 'string'},
-        'updated_at': {'type': ['string', 'null']}
-    },
-    'required': ['availability_zone', 'created_at', 'deleted',
-                 'deleted_at', 'hosts', 'id', 'metadata',
-                 'name', 'updated_at']
-}
+common_aggregate_info['required'].extend(['hosts', 'metadata'])
 
 list_aggregates = {
     'status_code': [200],
@@ -62,10 +44,10 @@
         'properties': {
             'aggregates': {
                 'type': 'array',
-                'items': aggregate
+                'items': common_aggregate_info
             }
         },
-        'required': ['aggregates']
+        'required': ['aggregates'],
     }
 }
 
@@ -74,9 +56,9 @@
     'response_body': {
         'type': 'object',
         'properties': {
-            'aggregate': aggregate
+            'aggregate': common_aggregate_info
         },
-        'required': ['aggregate']
+        'required': ['aggregate'],
     }
 }
 
@@ -88,13 +70,18 @@
         'type': 'string'
     }
 
-common_create_aggregate = {
+delete_aggregate = {
+    'status_code': [200]
+}
+
+create_aggregate = {
+    'status_code': [200],
     'response_body': {
         'type': 'object',
         'properties': {
             'aggregate': aggregate_for_create
         },
-        'required': ['aggregate']
+        'required': ['aggregate'],
     }
 }
 
diff --git a/tempest/api_schema/response/compute/v2/availability_zone.py b/tempest/api_schema/response/compute/v2_1/availability_zone.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/availability_zone.py
rename to tempest/api_schema/response/compute/v2_1/availability_zone.py
diff --git a/tempest/api_schema/response/compute/certificates.py b/tempest/api_schema/response/compute/v2_1/certificates.py
similarity index 89%
rename from tempest/api_schema/response/compute/certificates.py
rename to tempest/api_schema/response/compute/v2_1/certificates.py
index caac2ab..35445d8 100644
--- a/tempest/api_schema/response/compute/certificates.py
+++ b/tempest/api_schema/response/compute/v2_1/certificates.py
@@ -25,13 +25,15 @@
                     'data': {'type': 'string'},
                     'private_key': {'type': 'string'},
                 },
-                'required': ['data', 'private_key'],
+                'required': ['data', 'private_key']
             }
         },
-        'required': ['certificate'],
+        'required': ['certificate']
     }
 }
 
 get_certificate = copy.deepcopy(_common_schema)
 get_certificate['response_body']['properties']['certificate'][
     'properties']['private_key'].update({'type': 'null'})
+
+create_certificate = copy.deepcopy(_common_schema)
diff --git a/tempest/api_schema/response/compute/v2/extensions.py b/tempest/api_schema/response/compute/v2_1/extensions.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/extensions.py
rename to tempest/api_schema/response/compute/v2_1/extensions.py
diff --git a/tempest/api_schema/response/compute/v2/fixed_ips.py b/tempest/api_schema/response/compute/v2_1/fixed_ips.py
similarity index 96%
rename from tempest/api_schema/response/compute/v2/fixed_ips.py
rename to tempest/api_schema/response/compute/v2_1/fixed_ips.py
index 446633f..13e70bf 100644
--- a/tempest/api_schema/response/compute/v2/fixed_ips.py
+++ b/tempest/api_schema/response/compute/v2_1/fixed_ips.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-fixed_ips = {
+get_fixed_ip = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
@@ -35,7 +35,7 @@
     }
 }
 
-fixed_ip_action = {
+reserve_fixed_ip = {
     'status_code': [202],
     'response_body': {'type': 'string'}
 }
diff --git a/tempest/api_schema/response/compute/v2/flavors.py b/tempest/api_schema/response/compute/v2_1/flavors.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/flavors.py
rename to tempest/api_schema/response/compute/v2_1/flavors.py
diff --git a/tempest/api_schema/response/compute/v2/floating_ips.py b/tempest/api_schema/response/compute/v2_1/floating_ips.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/floating_ips.py
rename to tempest/api_schema/response/compute/v2_1/floating_ips.py
diff --git a/tempest/api_schema/response/compute/v2/hosts.py b/tempest/api_schema/response/compute/v2_1/hosts.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/hosts.py
rename to tempest/api_schema/response/compute/v2_1/hosts.py
diff --git a/tempest/api_schema/response/compute/v2/hypervisors.py b/tempest/api_schema/response/compute/v2_1/hypervisors.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/hypervisors.py
rename to tempest/api_schema/response/compute/v2_1/hypervisors.py
diff --git a/tempest/api_schema/response/compute/v2/images.py b/tempest/api_schema/response/compute/v2_1/images.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/images.py
rename to tempest/api_schema/response/compute/v2_1/images.py
diff --git a/tempest/api_schema/response/compute/v2/instance_usage_audit_logs.py b/tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/instance_usage_audit_logs.py
rename to tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
diff --git a/tempest/api_schema/response/compute/interfaces.py b/tempest/api_schema/response/compute/v2_1/interfaces.py
similarity index 74%
rename from tempest/api_schema/response/compute/interfaces.py
rename to tempest/api_schema/response/compute/v2_1/interfaces.py
index fd53eb3..4de3309 100644
--- a/tempest/api_schema/response/compute/interfaces.py
+++ b/tempest/api_schema/response/compute/v2_1/interfaces.py
@@ -14,10 +14,6 @@
 
 from tempest.api_schema.response.compute import parameter_types
 
-delete_interface = {
-    'status_code': [202]
-}
-
 interface_common_info = {
     'type': 'object',
     'properties': {
@@ -45,3 +41,32 @@
     },
     'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr']
 }
+
+get_create_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interfaceAttachment': interface_common_info
+        },
+        'required': ['interfaceAttachment']
+    }
+}
+
+list_interfaces = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'interfaceAttachments': {
+                'type': 'array',
+                'items': interface_common_info
+            }
+        },
+        'required': ['interfaceAttachments']
+    }
+}
+
+delete_interface = {
+    'status_code': [202]
+}
diff --git a/tempest/api_schema/response/compute/v2/keypairs.py b/tempest/api_schema/response/compute/v2_1/keypairs.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/keypairs.py
rename to tempest/api_schema/response/compute/v2_1/keypairs.py
diff --git a/tempest/api_schema/response/compute/v2/limits.py b/tempest/api_schema/response/compute/v2_1/limits.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/limits.py
rename to tempest/api_schema/response/compute/v2_1/limits.py
diff --git a/tempest/api_schema/response/compute/v2/quota_classes.py b/tempest/api_schema/response/compute/v2_1/quota_classes.py
similarity index 95%
rename from tempest/api_schema/response/compute/v2/quota_classes.py
rename to tempest/api_schema/response/compute/v2_1/quota_classes.py
index 5474a89..a7374df 100644
--- a/tempest/api_schema/response/compute/v2/quota_classes.py
+++ b/tempest/api_schema/response/compute/v2_1/quota_classes.py
@@ -15,7 +15,7 @@
 
 import copy
 
-from tempest.api_schema.response.compute.v2 import quotas
+from tempest.api_schema.response.compute.v2_1 import quotas
 
 # NOTE(mriedem): os-quota-class-sets responses are the same as os-quota-sets
 # except for the key in the response body is quota_class_set instead of
diff --git a/tempest/api_schema/response/compute/v2/quotas.py b/tempest/api_schema/response/compute/v2_1/quotas.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/quotas.py
rename to tempest/api_schema/response/compute/v2_1/quotas.py
diff --git a/tempest/api_schema/response/compute/v2/security_group_default_rule.py b/tempest/api_schema/response/compute/v2_1/security_group_default_rule.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/security_group_default_rule.py
rename to tempest/api_schema/response/compute/v2_1/security_group_default_rule.py
diff --git a/tempest/api_schema/response/compute/v2/security_groups.py b/tempest/api_schema/response/compute/v2_1/security_groups.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/security_groups.py
rename to tempest/api_schema/response/compute/v2_1/security_groups.py
diff --git a/tempest/api_schema/response/compute/v2/servers.py b/tempest/api_schema/response/compute/v2_1/servers.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/servers.py
rename to tempest/api_schema/response/compute/v2_1/servers.py
diff --git a/tempest/api_schema/response/compute/v2/tenant_networks.py b/tempest/api_schema/response/compute/v2_1/tenant_networks.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/tenant_networks.py
rename to tempest/api_schema/response/compute/v2_1/tenant_networks.py
diff --git a/tempest/api_schema/response/compute/v2/tenant_usages.py b/tempest/api_schema/response/compute/v2_1/tenant_usages.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/tenant_usages.py
rename to tempest/api_schema/response/compute/v2_1/tenant_usages.py
diff --git a/tempest/api_schema/response/compute/v2/volumes.py b/tempest/api_schema/response/compute/v2_1/volumes.py
similarity index 100%
rename from tempest/api_schema/response/compute/v2/volumes.py
rename to tempest/api_schema/response/compute/v2_1/volumes.py
diff --git a/tempest/cli/simple_read_only/telemetry/__init__.py b/tempest/cli/simple_read_only/telemetry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/cli/simple_read_only/telemetry/__init__.py
+++ /dev/null
diff --git a/tempest/cli/simple_read_only/telemetry/test_ceilometer.py b/tempest/cli/simple_read_only/telemetry/test_ceilometer.py
deleted file mode 100644
index b5e570b..0000000
--- a/tempest/cli/simple_read_only/telemetry/test_ceilometer.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# 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 oslo_log import log as logging
-
-from tempest import cli
-from tempest import config
-from tempest import test
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class SimpleReadOnlyCeilometerClientTest(cli.ClientTestBase):
-    """Basic, read-only tests for Ceilometer CLI client.
-
-    Checks return values and output of read-only commands.
-    These tests do not presume any content, nor do they create
-    their own. They only verify the structure of output if present.
-    """
-
-    @classmethod
-    def resource_setup(cls):
-        if (not CONF.service_available.ceilometer):
-            msg = ("Skipping all Ceilometer cli tests because it is "
-                   "not available")
-            raise cls.skipException(msg)
-        super(SimpleReadOnlyCeilometerClientTest, cls).resource_setup()
-
-    def ceilometer(self, *args, **kwargs):
-        return self.clients.ceilometer(
-            *args, endpoint_type=CONF.telemetry.endpoint_type, **kwargs)
-
-    @test.idempotent_id('ab717d43-a9c4-4dcf-bad8-c4777933a970')
-    def test_ceilometer_meter_list(self):
-        self.ceilometer('meter-list')
-
-    @test.attr(type='slow')
-    @test.idempotent_id('fe2e52a4-a99b-426e-a52d-d0bde50f3e4c')
-    def test_ceilometer_resource_list(self):
-        self.ceilometer('resource-list')
-
-    @test.idempotent_id('eede695c-f3bf-449f-a420-02f3cc426d52')
-    def test_ceilometermeter_alarm_list(self):
-        self.ceilometer('alarm-list')
-
-    @test.idempotent_id('0586bcc4-8e35-415f-8f23-77b590042684')
-    def test_ceilometer_version(self):
-        self.ceilometer('', flags='--version')
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index c8b2b93..8e9a018 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -35,9 +35,9 @@
 
 class Accounts(cred_provider.CredentialProvider):
 
-    def __init__(self, name):
-        super(Accounts, self).__init__(name)
-        self.name = name
+    def __init__(self, identity_version=None, name=None):
+        super(Accounts, self).__init__(identity_version=identity_version,
+                                       name=name)
         if os.path.isfile(CONF.auth.test_accounts_file):
             accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
             self.use_default_creds = False
@@ -45,13 +45,7 @@
             accounts = {}
             self.use_default_creds = True
         self.hash_dict = self.get_hash_dict(accounts)
-        # FIXME(dhellmann): The configuration option is not part of
-        # the API of the library, because if we change the option name
-        # or group it will break this use. Tempest needs to set this
-        # value somewhere that it owns, and then use
-        # lockutils.set_defaults() to tell oslo.concurrency what value
-        # to use.
-        self.accounts_dir = os.path.join(CONF.oslo_concurrency.lock_path,
+        self.accounts_dir = os.path.join(lockutils.get_lock_path(CONF),
                                          'test_accounts')
         self.isolated_creds = {}
 
@@ -208,7 +202,8 @@
         if self.isolated_creds.get('primary'):
             return self.isolated_creds.get('primary')
         creds = self._get_creds()
-        primary_credential = cred_provider.get_credentials(**creds)
+        primary_credential = cred_provider.get_credentials(
+            identity_version=self.identity_version, **creds)
         self.isolated_creds['primary'] = primary_credential
         return primary_credential
 
@@ -216,7 +211,8 @@
         if self.isolated_creds.get('alt'):
             return self.isolated_creds.get('alt')
         creds = self._get_creds()
-        alt_credential = cred_provider.get_credentials(**creds)
+        alt_credential = cred_provider.get_credentials(
+            identity_version=self.identity_version, **creds)
         self.isolated_creds['alt'] = alt_credential
         return alt_credential
 
@@ -232,7 +228,8 @@
             new_index = str(roles) + '-' + str(len(self.isolated_creds))
             self.isolated_creds[new_index] = exist_creds
         creds = self._get_creds(roles=roles)
-        role_credential = cred_provider.get_credentials(**creds)
+        role_credential = cred_provider.get_credentials(
+            identity_version=self.identity_version, **creds)
         self.isolated_creds[str(roles)] = role_credential
         return role_credential
 
@@ -300,10 +297,11 @@
             return self.isolated_creds.get('primary')
         if not self.use_default_creds:
             creds = self.get_creds(0)
-            primary_credential = cred_provider.get_credentials(**creds)
+            primary_credential = cred_provider.get_credentials(
+                identity_version=self.identity_version, **creds)
         else:
             primary_credential = cred_provider.get_configured_credentials(
-                'user')
+                credential_type='user', identity_version=self.identity_version)
         self.isolated_creds['primary'] = primary_credential
         return primary_credential
 
@@ -312,10 +310,12 @@
             return self.isolated_creds.get('alt')
         if not self.use_default_creds:
             creds = self.get_creds(1)
-            alt_credential = cred_provider.get_credentials(**creds)
+            alt_credential = cred_provider.get_credentials(
+                identity_version=self.identity_version, **creds)
         else:
             alt_credential = cred_provider.get_configured_credentials(
-                'alt_user')
+                credential_type='alt_user',
+                identity_version=self.identity_version)
         self.isolated_creds['alt'] = alt_credential
         return alt_credential
 
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index bff9a0a..b0300cb 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -83,7 +83,7 @@
         domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
                             if 'domain' in x)
         if not domain_fields.intersection(kwargs.keys()):
-            kwargs['user_domain_name'] = CONF.identity.admin_domain_name
+            params['user_domain_name'] = CONF.identity.admin_domain_name
         auth_url = CONF.identity.uri_v3
     else:
         auth_url = CONF.identity.uri
@@ -95,8 +95,25 @@
 
 @six.add_metaclass(abc.ABCMeta)
 class CredentialProvider(object):
-    def __init__(self, name, password='pass', network_resources=None):
-        self.name = name
+    def __init__(self, identity_version=None, name=None, password='pass',
+                 network_resources=None):
+        """A CredentialProvider supplies credentials to test classes.
+        :param identity_version If specified it will return credentials of the
+                                corresponding identity version, otherwise it
+                                uses auth_version from configuration
+        :param name Name of the calling test. Included in provisioned
+                    credentials when credentials are provisioned on the fly
+        :param password Used for provisioned credentials when credentials are
+                        provisioned on the fly
+        :param network_resources Network resources required for the credentials
+        """
+        # TODO(andreaf) name and password are tenant isolation specific, and
+        # could be removed from this abstract class
+        self.name = name or "test_creds"
+        self.identity_version = identity_version or CONF.identity.auth_version
+        if not auth.is_identity_version_supported(self.identity_version):
+            raise exceptions.InvalidIdentityVersion(
+                identity_version=self.identity_version)
 
     @abc.abstractmethod
     def get_primary_creds(self):
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 5f582c1..22fc9c3 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -12,8 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import abc
 import netaddr
 from oslo_log import log as logging
+import six
 from tempest_lib.common.utils import data_utils
 from tempest_lib import exceptions as lib_exc
 
@@ -21,22 +23,142 @@
 from tempest.common import cred_provider
 from tempest import config
 from tempest import exceptions
+from tempest.services.identity.v2.json import identity_client as v2_identity
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
+@six.add_metaclass(abc.ABCMeta)
+class CredsClient(object):
+    """This class is a wrapper around the identity clients, to provide a
+     single interface for managing credentials in both v2 and v3 cases.
+     It's not bound to created credentials, only to a specific set of admin
+     credentials used for generating credentials.
+    """
+
+    def __init__(self, identity_client):
+        # The client implies version and credentials
+        self.identity_client = identity_client
+        self.credentials = self.identity_client.auth_provider.credentials
+
+    def create_user(self, username, password, project, email):
+        user = self.identity_client.create_user(
+            username, password, project['id'], email)
+        return user
+
+    @abc.abstractmethod
+    def create_project(self, name, description):
+        pass
+
+    def assign_user_role(self, user, project, role_name):
+        try:
+            roles = self._list_roles()
+            role = next(r for r in roles if r['name'] == role_name)
+        except StopIteration:
+            msg = 'No "%s" role found' % role_name
+            raise lib_exc.NotFound(msg)
+        try:
+            self.identity_client.assign_user_role(project['id'], user['id'],
+                                                  role['id'])
+        except lib_exc.Conflict:
+            LOG.debug("Role %s already assigned on project %s for user %s" % (
+                role['id'], project['id'], user['id']))
+
+    @abc.abstractmethod
+    def get_credentials(self, user, project, password):
+        pass
+
+    def delete_user(self, user_id):
+        self.identity_client.delete_user(user_id)
+
+    def _list_roles(self):
+        roles = self.identity_client.list_roles()
+        return roles
+
+
+class V2CredsClient(CredsClient):
+
+    def create_project(self, name, description):
+        tenant = self.identity_client.create_tenant(
+            name=name, description=description)
+        return tenant
+
+    def get_credentials(self, user, project, password):
+        return cred_provider.get_credentials(
+            identity_version='v2',
+            username=user['name'], user_id=user['id'],
+            tenant_name=project['name'], tenant_id=project['id'],
+            password=password)
+
+    def delete_project(self, project_id):
+        self.identity_client.delete_tenant(project_id)
+
+
+class V3CredsClient(CredsClient):
+
+    def __init__(self, identity_client, domain_name):
+        super(V3CredsClient, self).__init__(identity_client)
+        try:
+            # Domain names must be unique, in any case a list is returned,
+            # selecting the first (and only) element
+            self.creds_domain = self.identity_client.list_domains(
+                params={'name': domain_name})[0]
+        except lib_exc.NotFound:
+            # TODO(andrea) we could probably create the domain on the fly
+            msg = "Configured domain %s could not be found" % domain_name
+            raise exceptions.InvalidConfiguration(msg)
+
+    def create_project(self, name, description):
+        project = self.identity_client.create_project(
+            name=name, description=description,
+            domain_id=self.creds_domain['id'])
+        return project
+
+    def get_credentials(self, user, project, password):
+        return cred_provider.get_credentials(
+            identity_version='v3',
+            username=user['name'], user_id=user['id'],
+            project_name=project['name'], project_id=project['id'],
+            password=password,
+            project_domain_name=self.creds_domain['name'])
+
+    def delete_project(self, project_id):
+        self.identity_client.delete_project(project_id)
+
+
+def get_creds_client(identity_client, project_domain_name=None):
+    if isinstance(identity_client, v2_identity.IdentityClientJSON):
+        return V2CredsClient(identity_client)
+    else:
+        return V3CredsClient(identity_client, project_domain_name)
+
+
 class IsolatedCreds(cred_provider.CredentialProvider):
 
-    def __init__(self, name, password='pass', network_resources=None):
-        super(IsolatedCreds, self).__init__(name, password, network_resources)
+    def __init__(self, identity_version=None, name=None, password='pass',
+                 network_resources=None):
+        super(IsolatedCreds, self).__init__(identity_version, name, password,
+                                            network_resources)
         self.network_resources = network_resources
         self.isolated_creds = {}
         self.isolated_net_resources = {}
         self.ports = []
         self.password = password
+        self.default_admin_creds = cred_provider.get_configured_credentials(
+            'identity_admin', fill_in=True,
+            identity_version=self.identity_version)
         self.identity_admin_client, self.network_admin_client = (
             self._get_admin_clients())
+        # Domain where isolated credentials are provisioned (v3 only).
+        # Use that of the admin account is None is configured.
+        self.creds_domain_name = None
+        if self.identity_version == 'v3':
+            self.creds_domain_name = (
+                CONF.auth.tenant_isolation_domain_name or
+                self.default_admin_creds.project_domain_name)
+        self.creds_client = get_creds_client(
+            self.identity_admin_client, self.creds_domain_name)
 
     def _get_admin_clients(self):
         """
@@ -45,57 +167,11 @@
             identity
             network
         """
-        os = clients.AdminManager()
-        return os.identity_client, os.network_client
-
-    def _create_tenant(self, name, description):
-        tenant = self.identity_admin_client.create_tenant(
-            name=name, description=description)
-        return tenant
-
-    def _get_tenant_by_name(self, name):
-        tenant = self.identity_admin_client.get_tenant_by_name(name)
-        return tenant
-
-    def _create_user(self, username, password, tenant, email):
-        user = self.identity_admin_client.create_user(
-            username, password, tenant['id'], email)
-        return user
-
-    def _get_user(self, tenant, username):
-        user = self.identity_admin_client.get_user_by_username(
-            tenant['id'], username)
-        return user
-
-    def _list_roles(self):
-        roles = self.identity_admin_client.list_roles()
-        return roles
-
-    def _assign_user_role(self, tenant, user, role_name):
-        role = None
-        try:
-            roles = self._list_roles()
-            role = next(r for r in roles if r['name'] == role_name)
-        except StopIteration:
-            msg = 'No "%s" role found' % role_name
-            raise lib_exc.NotFound(msg)
-        try:
-            self.identity_admin_client.assign_user_role(tenant['id'],
-                                                        user['id'],
-                                                        role['id'])
-        except lib_exc.Conflict:
-            LOG.warning('Trying to add %s for user %s in tenant %s but they '
-                        ' were already granted that role' % (role_name,
-                                                             user['name'],
-                                                             tenant['name']))
-
-    def _delete_user(self, user):
-        self.identity_admin_client.delete_user(user)
-
-    def _delete_tenant(self, tenant):
-        if CONF.service_available.neutron:
-            self._cleanup_default_secgroup(tenant)
-        self.identity_admin_client.delete_tenant(tenant)
+        os = clients.Manager(self.default_admin_creds)
+        if self.identity_version == 'v2':
+            return os.identity_client, os.network_client
+        else:
+            return os.identity_v3_client, os.network_client
 
     def _create_creds(self, suffix="", admin=False, roles=None):
         """Create random credentials under the following schema.
@@ -112,31 +188,26 @@
         else:
             root = self.name
 
-        tenant_name = data_utils.rand_name(root) + suffix
-        tenant_desc = tenant_name + "-desc"
-        tenant = self._create_tenant(name=tenant_name,
-                                     description=tenant_desc)
+        project_name = data_utils.rand_name(root) + suffix
+        project_desc = project_name + "-desc"
+        project = self.creds_client.create_project(
+            name=project_name, description=project_desc)
 
         username = data_utils.rand_name(root) + suffix
         email = data_utils.rand_name(root) + suffix + "@example.com"
-        user = self._create_user(username, self.password,
-                                 tenant, email)
+        user = self.creds_client.create_user(
+            username, self.password, project, email)
         if admin:
-            self._assign_user_role(tenant, user, CONF.identity.admin_role)
+            self.creds_client.assign_user_role(user, project,
+                                               CONF.identity.admin_role)
         # Add roles specified in config file
         for conf_role in CONF.auth.tempest_roles:
-            self._assign_user_role(tenant, user, conf_role)
+            self.creds_client.assign_user_role(user, project, conf_role)
         # Add roles requested by caller
         if roles:
             for role in roles:
-                self._assign_user_role(tenant, user, role)
-        return self._get_credentials(user, tenant)
-
-    def _get_credentials(self, user, tenant):
-        return cred_provider.get_credentials(
-            username=user['name'], user_id=user['id'],
-            tenant_name=tenant['name'], tenant_id=tenant['id'],
-            password=self.password)
+                self.creds_client.assign_user_role(user, project, role)
+        return self.creds_client.get_credentials(user, project, self.password)
 
     def _create_network_resources(self, tenant_id):
         network = None
@@ -371,12 +442,14 @@
         self._clear_isolated_net_resources()
         for creds in self.isolated_creds.itervalues():
             try:
-                self._delete_user(creds.user_id)
+                self.creds_client.delete_user(creds.user_id)
             except lib_exc.NotFound:
                 LOG.warn("user with name: %s not found for delete" %
                          creds.username)
             try:
-                self._delete_tenant(creds.tenant_id)
+                if CONF.service_available.neutron:
+                    self._cleanup_default_secgroup(creds.tenant_id)
+                self.creds_client.delete_project(creds.tenant_id)
             except lib_exc.NotFound:
                 LOG.warn("tenant with name: %s not found for delete" %
                          creds.tenant_name)
diff --git a/tempest/config.py b/tempest/config.py
index a127194..4ee4669 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -60,7 +60,13 @@
                      "number of concurrent test processes."),
     cfg.ListOpt('tempest_roles',
                 help="Roles to assign to all users created by tempest",
-                default=[])
+                default=[]),
+    cfg.StrOpt('tenant_isolation_domain_name',
+               default=None,
+               help="Only applicable when identity.auth_version is v3."
+                    "Domain within which isolated credentials are provisioned."
+                    "The default \"None\" means that the domain from the"
+                    "admin user is used instead.")
 ]
 
 identity_group = cfg.OptGroup(name='identity',
@@ -351,7 +357,13 @@
                      'images of running instances?'),
     cfg.BoolOpt('ec2_api',
                 default=True,
-                help='Does the test environment have the ec2 api running?')
+                help='Does the test environment have the ec2 api running?'),
+    # TODO(mriedem): Remove preserve_ports once juno-eol happens.
+    cfg.BoolOpt('preserve_ports',
+                default=False,
+                help='Does Nova preserve preexisting ports from Neutron '
+                     'when deleting an instance? This should be set to True '
+                     'if testing Kilo+ Nova.')
 ]
 
 
@@ -690,6 +702,8 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the orchestration service."),
+    cfg.StrOpt('stack_owner_role', default='heat_stack_owner',
+               help='Role required for users to be able to manage stacks'),
     cfg.IntOpt('build_interval',
                default=1,
                help="Time in seconds between build status checks."),
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 81e771c..f8cc17c 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -14,7 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import os
 import subprocess
 
 import netaddr
@@ -24,7 +23,6 @@
 from tempest_lib import exceptions as lib_exc
 
 from tempest import clients
-from tempest.common import cred_provider
 from tempest.common import credentials
 from tempest.common.utils.linux import remote_client
 from tempest import config
@@ -50,7 +48,6 @@
         cls.manager = clients.Manager(
             credentials=cls.credentials()
         )
-        cls.admin_manager = clients.Manager(cls.admin_credentials())
 
     @classmethod
     def setup_clients(cls):
@@ -63,7 +60,6 @@
         # Compute image client
         cls.images_client = cls.manager.images_client
         cls.keypairs_client = cls.manager.keypairs_client
-        cls.networks_client = cls.admin_manager.networks_client
         # Nova security groups client
         cls.security_groups_client = cls.manager.security_groups_client
         cls.servers_client = cls.manager.servers_client
@@ -542,6 +538,14 @@
         super(NetworkScenarioTest, cls).skip_checks()
         if not CONF.service_available.neutron:
             raise cls.skipException('Neutron not available')
+        if not credentials.is_admin_available():
+            msg = ("Missing Identity Admin API credentials in configuration.")
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_credentials(cls):
+        super(NetworkScenarioTest, cls).setup_credentials()
+        cls.admin_manager = clients.Manager(cls.admin_credentials())
 
     @classmethod
     def resource_setup(cls):
@@ -1283,9 +1287,17 @@
     """
 
     @classmethod
+    def skip_checks(cls):
+        super(EncryptionScenarioTest, cls).skip_checks()
+        if not credentials.is_admin_available():
+            msg = ("Missing Identity Admin API credentials in configuration.")
+            raise cls.skipException(msg)
+
+    @classmethod
     def setup_clients(cls):
         super(EncryptionScenarioTest, cls).setup_clients()
-        cls.admin_volume_types_client = cls.admin_manager.volume_types_client
+        admin_manager = clients.Manager(cls.admin_credentials())
+        cls.admin_volume_types_client = admin_manager.volume_types_client
 
     def _wait_for_volume_status(self, status):
         self.status_timeout(
@@ -1324,49 +1336,6 @@
             control_location=control_location)
 
 
-class OrchestrationScenarioTest(ScenarioTest):
-    """
-    Base class for orchestration scenario tests
-    """
-
-    @classmethod
-    def skip_checks(cls):
-        super(OrchestrationScenarioTest, cls).skip_checks()
-        if not CONF.service_available.heat:
-            raise cls.skipException("Heat support is required")
-
-    @classmethod
-    def credentials(cls):
-        admin_creds = cred_provider.get_configured_credentials(
-            'identity_admin')
-        creds = cred_provider.get_configured_credentials('user')
-        admin_creds.tenant_name = creds.tenant_name
-        return admin_creds
-
-    def _load_template(self, base_file, file_name):
-        filepath = os.path.join(os.path.dirname(os.path.realpath(base_file)),
-                                file_name)
-        with open(filepath) as f:
-            return f.read()
-
-    @classmethod
-    def _stack_rand_name(cls):
-        return data_utils.rand_name(cls.__name__ + '-')
-
-    @classmethod
-    def _get_default_network(cls):
-        networks = cls.networks_client.list_networks()
-        for net in networks:
-            if net['label'] == CONF.compute.fixed_network_name:
-                return net
-
-    @staticmethod
-    def _stack_output(stack, output_key):
-        """Return a stack output value for a given key."""
-        return next((o['output_value'] for o in stack['outputs']
-                    if o['output_key'] == output_key), None)
-
-
 class SwiftScenarioTest(ScenarioTest):
     """
     Provide harness to do Swift scenario tests.
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index bb668f7..3d6abff 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -15,7 +15,6 @@
 
 from oslo_log import log as logging
 from tempest_lib.common.utils import data_utils
-from tempest_lib import decorators
 import testtools
 
 from tempest import config
@@ -94,8 +93,8 @@
         self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
         self._check_network_connectivity()
 
-    @decorators.skip_because(bug="1323658")
     @test.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
+    @test.stresstest(class_setup_per='process')
     @test.services('compute', 'network')
     def test_server_connectivity_stop_start(self):
         self._setup_network_and_servers()
@@ -147,7 +146,6 @@
         self.servers_client.resume_server(self.server['id'])
         self._wait_server_status_and_check_network_connectivity()
 
-    @decorators.skip_because(bug="1323658")
     @test.idempotent_id('719eb59d-2f42-4b66-b8b1-bb1254473967')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index af7b683..bb19853 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -101,13 +101,19 @@
         self.servers = []
 
     def _setup_network_and_servers(self, **kwargs):
+        boot_with_port = kwargs.pop('boot_with_port', False)
         self.security_group = \
             self._create_security_group(tenant_id=self.tenant_id)
         self.network, self.subnet, self.router = self.create_networks(**kwargs)
         self.check_networks()
 
+        self.port_id = None
+        if boot_with_port:
+            # create a port on the network and boot with that
+            self.port_id = self._create_port(self.network['id']).id
+
         name = data_utils.rand_name('server-smoke')
-        server = self._create_server(name, self.network)
+        server = self._create_server(name, self.network, self.port_id)
         self._check_tenant_network_connectivity()
 
         floating_ip = self.create_floating_ip(server)
@@ -141,7 +147,7 @@
             self.assertIn(self.router.id,
                           seen_router_ids)
 
-    def _create_server(self, name, network):
+    def _create_server(self, name, network, port_id=None):
         keypair = self.create_keypair()
         self.keypairs[keypair['name']] = keypair
         security_groups = [{'name': self.security_group['name']}]
@@ -152,6 +158,8 @@
             'key_name': keypair['name'],
             'security_groups': security_groups,
         }
+        if port_id is not None:
+            create_kwargs['networks'][0]['port'] = port_id
         server = self.create_server(name=name, create_kwargs=create_kwargs)
         self.servers.append(server)
         return server
@@ -605,3 +613,39 @@
         self.check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of instance port to True")
+
+    @test.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
+    @testtools.skipUnless(CONF.compute_feature_enabled.preserve_ports,
+                          'Preserving ports on instance delete may not be '
+                          'supported in the version of Nova being tested.')
+    @test.attr(type='smoke')
+    @test.services('compute', 'network')
+    def test_preserve_preexisting_port(self):
+        """Tests that a pre-existing port provided on server boot is not
+        deleted if the server is deleted.
+
+        Nova should unbind the port from the instance on delete if the port was
+        not created by Nova as part of the boot request.
+        """
+        # Setup the network, create a port and boot the server from that port.
+        self._setup_network_and_servers(boot_with_port=True)
+        _, server = self.floating_ip_tuple
+        self.assertIsNotNone(self.port_id,
+                             'Server should have been created from a '
+                             'pre-existing port.')
+        # Assert the port is bound to the server.
+        port_list = self._list_ports(device_id=server['id'],
+                                     network_id=self.network['id'])
+        self.assertEqual(1, len(port_list),
+                         'There should only be one port created for '
+                         'server %s.' % server['id'])
+        self.assertEqual(self.port_id, port_list[0]['id'])
+        # Delete the server.
+        self.servers_client.delete_server(server['id'])
+        self.servers_client.wait_for_server_termination(server['id'])
+        # Assert the port still exists on the network but is unbound from
+        # the deleted server.
+        port = self.network_client.show_port(self.port_id)['port']
+        self.assertEqual(self.network['id'], port['network_id'])
+        self.assertEqual('', port['device_id'])
+        self.assertEqual('', port['device_owner'])
diff --git a/tempest/services/compute/json/agents_client.py b/tempest/services/compute/json/agents_client.py
index e17495f..403437d 100644
--- a/tempest/services/compute/json/agents_client.py
+++ b/tempest/services/compute/json/agents_client.py
@@ -15,8 +15,7 @@
 import json
 import urllib
 
-from tempest.api_schema.response.compute import agents as common_schema
-from tempest.api_schema.response.compute.v2 import agents as schema
+from tempest.api_schema.response.compute.v2_1 import agents as schema
 from tempest.common import service_client
 
 
@@ -32,7 +31,7 @@
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(common_schema.list_agents, resp, body)
+        self.validate_response(schema.list_agents, resp, body)
         return service_client.ResponseBodyList(resp, body['agents'])
 
     def create_agent(self, **kwargs):
diff --git a/tempest/services/compute/json/aggregates_client.py b/tempest/services/compute/json/aggregates_client.py
index 10955fd..36a347b 100644
--- a/tempest/services/compute/json/aggregates_client.py
+++ b/tempest/services/compute/json/aggregates_client.py
@@ -17,8 +17,7 @@
 
 from tempest_lib import exceptions as lib_exc
 
-from tempest.api_schema.response.compute import aggregates as schema
-from tempest.api_schema.response.compute.v2 import aggregates as v2_schema
+from tempest.api_schema.response.compute.v2_1 import aggregates as schema
 from tempest.common import service_client
 
 
@@ -44,7 +43,7 @@
         resp, body = self.post('os-aggregates', post_body)
 
         body = json.loads(body)
-        self.validate_response(v2_schema.create_aggregate, resp, body)
+        self.validate_response(schema.create_aggregate, resp, body)
         return service_client.ResponseBody(resp, body['aggregate'])
 
     def update_aggregate(self, aggregate_id, name, availability_zone=None):
@@ -63,7 +62,7 @@
     def delete_aggregate(self, aggregate_id):
         """Deletes the given aggregate."""
         resp, body = self.delete("os-aggregates/%s" % str(aggregate_id))
-        self.validate_response(v2_schema.delete_aggregate, resp, body)
+        self.validate_response(schema.delete_aggregate, resp, body)
         return service_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
diff --git a/tempest/services/compute/json/availability_zone_client.py b/tempest/services/compute/json/availability_zone_client.py
index 343c412..b541a2c 100644
--- a/tempest/services/compute/json/availability_zone_client.py
+++ b/tempest/services/compute/json/availability_zone_client.py
@@ -15,7 +15,8 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import availability_zone as schema
+from tempest.api_schema.response.compute.v2_1 import availability_zone \
+    as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/certificates_client.py b/tempest/services/compute/json/certificates_client.py
index 4a30f1e..e6b72bb 100644
--- a/tempest/services/compute/json/certificates_client.py
+++ b/tempest/services/compute/json/certificates_client.py
@@ -15,8 +15,7 @@
 
 import json
 
-from tempest.api_schema.response.compute import certificates as schema
-from tempest.api_schema.response.compute.v2 import certificates as v2schema
+from tempest.api_schema.response.compute.v2_1 import certificates as schema
 from tempest.common import service_client
 
 
@@ -34,5 +33,5 @@
         url = "os-certificates"
         resp, body = self.post(url, None)
         body = json.loads(body)
-        self.validate_response(v2schema.create_certificate, resp, body)
+        self.validate_response(schema.create_certificate, resp, body)
         return service_client.ResponseBody(resp, body['certificate'])
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index 09561b3..5c69085 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -15,7 +15,7 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import extensions as schema
+from tempest.api_schema.response.compute.v2_1 import extensions as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/fixed_ips_client.py b/tempest/services/compute/json/fixed_ips_client.py
index 31cf5b2..7ba424f 100644
--- a/tempest/services/compute/json/fixed_ips_client.py
+++ b/tempest/services/compute/json/fixed_ips_client.py
@@ -15,7 +15,7 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import fixed_ips as schema
+from tempest.api_schema.response.compute.v2_1 import fixed_ips as schema
 from tempest.common import service_client
 
 
@@ -25,12 +25,12 @@
         url = "os-fixed-ips/%s" % (fixed_ip)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.fixed_ips, resp, body)
+        self.validate_response(schema.get_fixed_ip, resp, body)
         return service_client.ResponseBody(resp, body['fixed_ip'])
 
     def reserve_fixed_ip(self, ip, body):
         """This reserves and unreserves fixed ips."""
         url = "os-fixed-ips/%s/action" % (ip)
         resp, body = self.post(url, json.dumps(body))
-        self.validate_response(schema.fixed_ip_action, resp, body)
+        self.validate_response(schema.reserve_fixed_ip, resp, body)
         return service_client.ResponseBody(resp)
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 433c325..25b1869 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -20,7 +20,7 @@
 from tempest.api_schema.response.compute import flavors_access as schema_access
 from tempest.api_schema.response.compute import flavors_extra_specs \
     as schema_extra_specs
-from tempest.api_schema.response.compute.v2 import flavors as v2schema
+from tempest.api_schema.response.compute.v2_1 import flavors as v2schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 0354ba4..5bad527 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -18,7 +18,7 @@
 
 from tempest_lib import exceptions as lib_exc
 
-from tempest.api_schema.response.compute.v2 import floating_ips as schema
+from tempest.api_schema.response.compute.v2_1 import floating_ips as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index b06378b..de925a9 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -16,7 +16,7 @@
 import urllib
 
 from tempest.api_schema.response.compute import hosts as schema
-from tempest.api_schema.response.compute.v2 import hosts as v2_schema
+from tempest.api_schema.response.compute.v2_1 import hosts as v2_schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/hypervisor_client.py b/tempest/services/compute/json/hypervisor_client.py
index 380b5ce..bf4bc7f 100644
--- a/tempest/services/compute/json/hypervisor_client.py
+++ b/tempest/services/compute/json/hypervisor_client.py
@@ -16,7 +16,7 @@
 import json
 
 from tempest.api_schema.response.compute import hypervisors as common_schema
-from tempest.api_schema.response.compute.v2 import hypervisors as v2schema
+from tempest.api_schema.response.compute.v2_1 import hypervisors as v2schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 0ceb6d1..1223fef 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -18,7 +18,7 @@
 
 from tempest_lib import exceptions as lib_exc
 
-from tempest.api_schema.response.compute.v2 import images as schema
+from tempest.api_schema.response.compute.v2_1 import images as schema
 from tempest.common import service_client
 from tempest.common import waiters
 
diff --git a/tempest/services/compute/json/instance_usage_audit_log_client.py b/tempest/services/compute/json/instance_usage_audit_log_client.py
index 551d751..33ba76f 100644
--- a/tempest/services/compute/json/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/json/instance_usage_audit_log_client.py
@@ -15,8 +15,8 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import instance_usage_audit_logs \
-    as schema
+from tempest.api_schema.response.compute.v2_1 import \
+    instance_usage_audit_logs as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
index 0c5516c..c3bfa99 100644
--- a/tempest/services/compute/json/interfaces_client.py
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -16,9 +16,8 @@
 import json
 import time
 
-from tempest.api_schema.response.compute import interfaces as common_schema
 from tempest.api_schema.response.compute import servers as servers_schema
-from tempest.api_schema.response.compute.v2 import interfaces as schema
+from tempest.api_schema.response.compute.v2_1 import interfaces as schema
 from tempest.common import service_client
 from tempest import exceptions
 
@@ -46,17 +45,19 @@
         resp, body = self.post('servers/%s/os-interface' % server,
                                body=post_body)
         body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
         return service_client.ResponseBody(resp, body['interfaceAttachment'])
 
     def show_interface(self, server, port_id):
         resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
         body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
         return service_client.ResponseBody(resp, body['interfaceAttachment'])
 
     def delete_interface(self, server, port_id):
         resp, body = self.delete('servers/%s/os-interface/%s' % (server,
                                                                  port_id))
-        self.validate_response(common_schema.delete_interface, resp, body)
+        self.validate_response(schema.delete_interface, resp, body)
         return service_client.ResponseBody(resp, body)
 
     def wait_for_interface_status(self, server, port_id, status):
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index 18729c3..722aefa 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -16,7 +16,7 @@
 import json
 
 from tempest.api_schema.response.compute import keypairs as common_schema
-from tempest.api_schema.response.compute.v2 import keypairs as schema
+from tempest.api_schema.response.compute.v2_1 import keypairs as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/limits_client.py b/tempest/services/compute/json/limits_client.py
index 8769906..d2aaec6 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -15,7 +15,7 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import limits as schema
+from tempest.api_schema.response.compute.v2_1 import limits as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index ea0f423..89f4acd 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -15,9 +15,9 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2\
+from tempest.api_schema.response.compute.v2_1\
     import quota_classes as classes_schema
-from tempest.api_schema.response.compute.v2 import quotas as schema
+from tempest.api_schema.response.compute.v2_1 import quotas as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/security_group_default_rules_client.py b/tempest/services/compute/json/security_group_default_rules_client.py
index b370e00..3bf3263 100644
--- a/tempest/services/compute/json/security_group_default_rules_client.py
+++ b/tempest/services/compute/json/security_group_default_rules_client.py
@@ -15,7 +15,7 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import \
+from tempest.api_schema.response.compute.v2_1 import \
     security_group_default_rule as schema
 from tempest.common import service_client
 
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index 5aefa7b..d8c8d63 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -18,7 +18,7 @@
 
 from tempest_lib import exceptions as lib_exc
 
-from tempest.api_schema.response.compute.v2 import security_groups as schema
+from tempest.api_schema.response.compute.v2_1 import security_groups as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index bd4fd0e..bd27668 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -21,7 +21,7 @@
 from tempest_lib import exceptions as lib_exc
 
 from tempest.api_schema.response.compute import servers as common_schema
-from tempest.api_schema.response.compute.v2 import servers as schema
+from tempest.api_schema.response.compute.v2_1 import servers as schema
 from tempest.common import service_client
 from tempest.common import waiters
 from tempest import exceptions
diff --git a/tempest/services/compute/json/tenant_networks_client.py b/tempest/services/compute/json/tenant_networks_client.py
index c86c817..11251f6 100644
--- a/tempest/services/compute/json/tenant_networks_client.py
+++ b/tempest/services/compute/json/tenant_networks_client.py
@@ -14,7 +14,7 @@
 
 import json
 
-from tempest.api_schema.response.compute.v2 import tenant_networks as schema
+from tempest.api_schema.response.compute.v2_1 import tenant_networks as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index bbc1051..ff6e7a2 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -16,7 +16,7 @@
 import json
 import urllib
 
-from tempest.api_schema.response.compute.v2 import tenant_usages as schema
+from tempest.api_schema.response.compute.v2_1 import tenant_usages as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index b2d5cf9..ba5921e 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -19,7 +19,7 @@
 
 from tempest_lib import exceptions as lib_exc
 
-from tempest.api_schema.response.compute.v2 import volumes as schema
+from tempest.api_schema.response.compute.v2_1 import volumes as schema
 from tempest.common import service_client
 from tempest import exceptions
 
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index be5aa80..bc90fd1 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -242,9 +242,12 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp, body)
 
-    def list_domains(self):
+    def list_domains(self, params=None):
         """List Domains."""
-        resp, body = self.get('domains')
+        url = 'domains'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return service_client.ResponseBodyList(resp, body['domains'])
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index d095b53..e84d627 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -132,7 +132,14 @@
         computes = _get_compute_nodes(controller, ssh_user, ssh_key)
         for node in computes:
             do_ssh("rm -f %s" % logfiles, node, ssh_user, ssh_key)
+    skip = False
     for test in tests:
+        for service in test.get('required_services', []):
+            if not CONF.service_available.get(service):
+                skip = True
+                break
+        if skip:
+            break
         if test.get('use_admin', False):
             manager = admin_manager
         else:
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
index dffc469..9cee316 100644
--- a/tempest/stress/etc/stress-tox-job.json
+++ b/tempest/stress/etc/stress-tox-job.json
@@ -15,5 +15,14 @@
   "use_admin": false,
   "use_isolated_tenants": false,
   "kwargs": {}
+  },
+  {"action": "tempest.stress.actions.unit_test.UnitTest",
+  "threads": 4,
+  "use_admin": false,
+  "use_isolated_tenants": false,
+  "required_services": ["neutron"],
+  "kwargs": {"test_method": "tempest.scenario.test_network_advanced_server_ops.TestNetworkAdvancedServerOps.test_server_connectivity_stop_start",
+             "class_setup_per": "process"}
   }
 ]
+
diff --git a/tempest/tests/common/test_accounts.py b/tempest/tests/common/test_accounts.py
index 29fe902..2a98a06 100644
--- a/tempest/tests/common/test_accounts.py
+++ b/tempest/tests/common/test_accounts.py
@@ -17,6 +17,7 @@
 
 import mock
 from oslo_concurrency.fixture import lockutils as lockutils_fixtures
+from oslo_concurrency import lockutils
 from oslo_config import cfg
 from oslotest import mockpatch
 
@@ -81,7 +82,7 @@
     def test_get_hash(self):
         self.stubs.Set(token_client.TokenClientJSON, 'raw_request',
                        fake_identity._fake_v2_response)
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         hash_list = self._get_hash_list(self.test_accounts)
         test_cred_dict = self.test_accounts[3]
         test_creds = auth.get_credentials(fake_identity.FAKE_AUTH_URL,
@@ -90,7 +91,7 @@
         self.assertEqual(hash_list[3], results)
 
     def test_get_hash_dict(self):
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         hash_dict = test_account_class.get_hash_dict(self.test_accounts)
         hash_list = self._get_hash_list(self.test_accounts)
         for hash in hash_list:
@@ -101,7 +102,7 @@
         # Emulate the lock existing on the filesystem
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
         with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            test_account_class = accounts.Accounts('test_name')
+            test_account_class = accounts.Accounts('v2', 'test_name')
             res = test_account_class._create_hash_file('12345')
         self.assertFalse(res, "_create_hash_file should return False if the "
                          "pseudo-lock file already exists")
@@ -110,7 +111,7 @@
         # Emulate the lock not existing on the filesystem
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
         with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            test_account_class = accounts.Accounts('test_name')
+            test_account_class = accounts.Accounts('v2', 'test_name')
             res = test_account_class._create_hash_file('12345')
         self.assertTrue(res, "_create_hash_file should return True if the "
                         "pseudo-lock doesn't already exist")
@@ -122,17 +123,11 @@
         hash_list = self._get_hash_list(self.test_accounts)
         mkdir_mock = self.useFixture(mockpatch.Patch('os.mkdir'))
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         with mock.patch('__builtin__.open', mock.mock_open(),
                         create=True) as open_mock:
             test_account_class._get_free_hash(hash_list)
-            # FIXME(dhellmann): The configuration option is not part
-            # of the API of the library, because if we change the
-            # option name or group it will break this use. Tempest
-            # needs to set this value somewhere that it owns, and then
-            # use lockutils.set_defaults() to tell oslo.concurrency
-            # what value to use.
-            lock_path = os.path.join(accounts.CONF.oslo_concurrency.lock_path,
+            lock_path = os.path.join(lockutils.get_lock_path(accounts.CONF),
                                      'test_accounts',
                                      hash_list[0])
             open_mock.assert_called_once_with(lock_path, 'w')
@@ -147,7 +142,7 @@
         self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
         # Emulate all lcoks in list are in use
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         with mock.patch('__builtin__.open', mock.mock_open(), create=True):
             self.assertRaises(exceptions.InvalidConfiguration,
                               test_account_class._get_free_hash, hash_list)
@@ -157,7 +152,7 @@
         # Emulate no pre-existing lock
         self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
         hash_list = self._get_hash_list(self.test_accounts)
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
 
         def _fake_is_file(path):
             # Fake isfile() to return that the path exists unless a specific
@@ -170,13 +165,7 @@
         with mock.patch('__builtin__.open', mock.mock_open(),
                         create=True) as open_mock:
             test_account_class._get_free_hash(hash_list)
-            # FIXME(dhellmann): The configuration option is not part
-            # of the API of the library, because if we change the
-            # option name or group it will break this use. Tempest
-            # needs to set this value somewhere that it owns, and then
-            # use lockutils.set_defaults() to tell oslo.concurrency
-            # what value to use.
-            lock_path = os.path.join(accounts.CONF.oslo_concurrency.lock_path,
+            lock_path = os.path.join(lockutils.get_lock_path(accounts.CONF),
                                      'test_accounts',
                                      hash_list[3])
             open_mock.assert_has_calls([mock.call(lock_path, 'w')])
@@ -188,17 +177,11 @@
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
         # Pretend the lock dir is empty
         self.useFixture(mockpatch.Patch('os.listdir', return_value=[]))
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
         rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
         test_account_class.remove_hash(hash_list[2])
-        # FIXME(dhellmann): The configuration option is not part of
-        # the API of the library, because if we change the option name
-        # or group it will break this use. Tempest needs to set this
-        # value somewhere that it owns, and then use
-        # lockutils.set_defaults() to tell oslo.concurrency what value
-        # to use.
-        hash_path = os.path.join(accounts.CONF.oslo_concurrency.lock_path,
+        hash_path = os.path.join(lockutils.get_lock_path(accounts.CONF),
                                  'test_accounts',
                                  hash_list[2])
         lock_path = os.path.join(accounts.CONF.oslo_concurrency.lock_path,
@@ -214,24 +197,18 @@
         # Pretend the lock dir is empty
         self.useFixture(mockpatch.Patch('os.listdir', return_value=[
             hash_list[1], hash_list[4]]))
-        test_account_class = accounts.Accounts('test_name')
+        test_account_class = accounts.Accounts('v2', 'test_name')
         remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
         rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
         test_account_class.remove_hash(hash_list[2])
-        # FIXME(dhellmann): The configuration option is not part of
-        # the API of the library, because if we change the option name
-        # or group it will break this use. Tempest needs to set this
-        # value somewhere that it owns, and then use
-        # lockutils.set_defaults() to tell oslo.concurrency what value
-        # to use.
-        hash_path = os.path.join(accounts.CONF.oslo_concurrency.lock_path,
+        hash_path = os.path.join(lockutils.get_lock_path(accounts.CONF),
                                  'test_accounts',
                                  hash_list[2])
         remove_mock.mock.assert_called_once_with(hash_path)
         rmdir_mock.mock.assert_not_called()
 
     def test_is_multi_user(self):
-        test_accounts_class = accounts.Accounts('test_name')
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
         self.assertTrue(test_accounts_class.is_multi_user())
 
     def test_is_not_multi_user(self):
@@ -239,14 +216,14 @@
         self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
             return_value=self.test_accounts))
-        test_accounts_class = accounts.Accounts('test_name')
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
         self.assertFalse(test_accounts_class.is_multi_user())
 
     def test__get_creds_by_roles_one_role(self):
         self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
             return_value=self.test_accounts))
-        test_accounts_class = accounts.Accounts('test_name')
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
         hashes = test_accounts_class.hash_dict['roles']['role4']
         temp_hash = hashes[0]
         get_free_hash_mock = self.useFixture(mockpatch.PatchObject(
@@ -263,7 +240,7 @@
         self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
             return_value=self.test_accounts))
-        test_accounts_class = accounts.Accounts('test_name')
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
         hashes = test_accounts_class.hash_dict['roles']['role4']
         hashes2 = test_accounts_class.hash_dict['roles']['role2']
         hashes = list(set(hashes) & set(hashes2))
@@ -282,7 +259,7 @@
         self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
             return_value=self.test_accounts))
-        test_accounts_class = accounts.Accounts('test_name')
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
         hashes = test_accounts_class.hash_dict['creds'].keys()
         admin_hashes = test_accounts_class.hash_dict['roles'][
             cfg.CONF.identity.admin_role]
@@ -321,7 +298,7 @@
         self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
 
     def test_get_creds(self):
-        test_accounts_class = accounts.NotLockingAccounts('test_name')
+        test_accounts_class = accounts.NotLockingAccounts('v2', 'test_name')
         for i in xrange(len(self.test_accounts)):
             creds = test_accounts_class.get_creds(i)
             msg = "Empty credentials returned for ID %s" % str(i)
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index 7ab3f1e..82cbde9 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -44,7 +44,7 @@
         self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
 
     def test_tempest_client(self):
-        iso_creds = isolated_creds.IsolatedCreds('test class')
+        iso_creds = isolated_creds.IsolatedCreds(name='test class')
         self.assertTrue(isinstance(iso_creds.identity_admin_client,
                                    json_iden_client.IdentityClientJSON))
         self.assertTrue(isinstance(iso_creds.network_admin_client,
@@ -139,7 +139,7 @@
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_primary_creds(self, MockRestClient):
         cfg.CONF.set_default('neutron', False, 'service_available')
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -155,7 +155,7 @@
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_admin_creds(self, MockRestClient):
         cfg.CONF.set_default('neutron', False, 'service_available')
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_list_roles('1234', 'admin')
         self._mock_user_create('1234', 'fake_admin_user')
@@ -179,7 +179,7 @@
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_role_creds(self, MockRestClient):
         cfg.CONF.set_default('neutron', False, 'service_available')
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds('v2', 'test class',
                                                  password='fake_password')
         self._mock_list_2_roles()
         self._mock_user_create('1234', 'fake_role_user')
@@ -207,7 +207,7 @@
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_all_cred_cleanup(self, MockRestClient):
         cfg.CONF.set_default('neutron', False, 'service_available')
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         roles_fix = self._mock_list_role()
@@ -251,7 +251,7 @@
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_alt_creds(self, MockRestClient):
         cfg.CONF.set_default('neutron', False, 'service_available')
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -266,7 +266,7 @@
 
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_network_creation(self, MockRestClient):
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -298,7 +298,7 @@
                                          "description": args['name'],
                                          "security_group_rules": [],
                                          "id": "sg-%s" % args['tenant_id']}]}
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         # Create primary tenant and network
         self._mock_assign_user_role()
@@ -415,7 +415,7 @@
 
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_network_alt_creation(self, MockRestClient):
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         self._mock_list_role()
@@ -441,7 +441,7 @@
 
     @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_network_admin_creation(self, MockRestClient):
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
         self._mock_assign_user_role()
         self._mock_user_create('1234', 'fake_admin_user')
@@ -473,7 +473,7 @@
             'subnet': False,
             'dhcp': False,
         }
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password',
                                                  network_resources=net_dict)
         self._mock_assign_user_role()
@@ -509,7 +509,7 @@
             'subnet': False,
             'dhcp': False,
         }
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password',
                                                  network_resources=net_dict)
         self._mock_assign_user_role()
@@ -527,7 +527,7 @@
             'subnet': True,
             'dhcp': False,
         }
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password',
                                                  network_resources=net_dict)
         self._mock_assign_user_role()
@@ -545,7 +545,7 @@
             'subnet': False,
             'dhcp': True,
         }
-        iso_creds = isolated_creds.IsolatedCreds('test class',
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password',
                                                  network_resources=net_dict)
         self._mock_assign_user_role()
diff --git a/test-requirements.txt b/test-requirements.txt
index 6a9111e..76ae521 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,9 +5,9 @@
 # needed for doc build
 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
 python-subunit>=0.0.18
-oslosphinx>=2.2.0  # Apache-2.0
+oslosphinx>=2.5.0,<2.6.0  # Apache-2.0
 mox>=0.5.3
 mock>=1.0
 coverage>=3.6
-oslotest>=1.2.0  # Apache-2.0
-stevedore>=1.1.0  # Apache-2.0
+oslotest>=1.5.1,<1.6.0  # Apache-2.0
+stevedore>=1.3.0,<1.4.0  # Apache-2.0