Migrate tempest-lib code into new lib dir

This commit migrates all of the code from tempest-lib as of it's
current HEAD, 6ad0ce42c2791a28125d38b40e7dcddf32dbeed7. The only
changes made to the tempest-lib code is to update the imports and
other references to tempest_lib. Since in it's new home it should
be tempest.lib.

Partially implements bp tempest-lib-reintegration

Change-Id: Iadc1b61953a86fa9de34e285a0bb083b1ba06fa8
diff --git a/tempest/lib/services/__init__.py b/tempest/lib/services/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/__init__.py
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/compute/__init__.py
diff --git a/tempest/lib/services/compute/agents_client.py b/tempest/lib/services/compute/agents_client.py
new file mode 100644
index 0000000..8b11e64
--- /dev/null
+++ b/tempest/lib/services/compute/agents_client.py
@@ -0,0 +1,63 @@
+# 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import agents as schema
+from tempest.lib.common import rest_client
+
+
+class AgentsClient(rest_client.RestClient):
+    """Tests Agents API"""
+
+    def list_agents(self, **params):
+        """List all agent builds."""
+        url = 'os-agents'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_agents, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_agent(self, **kwargs):
+        """Create an agent build.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#agentbuild
+        """
+        post_body = json.dumps({'agent': kwargs})
+        resp, body = self.post('os-agents', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_agent(self, agent_id):
+        """Delete an existing agent build."""
+        resp, body = self.delete("os-agents/%s" % agent_id)
+        self.validate_response(schema.delete_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_agent(self, agent_id, **kwargs):
+        """Update an agent build.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updatebuild
+        """
+        put_body = json.dumps({'para': kwargs})
+        resp, body = self.put('os-agents/%s' % agent_id, put_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_agent, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
new file mode 100644
index 0000000..b481674
--- /dev/null
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -0,0 +1,116 @@
+# Copyright 2013 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import aggregates as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class AggregatesClient(rest_client.RestClient):
+
+    def list_aggregates(self):
+        """Get aggregate list."""
+        resp, body = self.get("os-aggregates")
+        body = json.loads(body)
+        self.validate_response(schema.list_aggregates, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_aggregate(self, aggregate_id):
+        """Get details of the given aggregate."""
+        resp, body = self.get("os-aggregates/%s" % aggregate_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_aggregate(self, **kwargs):
+        """Create a new aggregate.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createaggregate
+        """
+        post_body = json.dumps({'aggregate': kwargs})
+        resp, body = self.post('os-aggregates', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_aggregate(self, aggregate_id, **kwargs):
+        """Update an aggregate.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateaggregate
+        """
+        put_body = json.dumps({'aggregate': kwargs})
+        resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.update_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_aggregate(self, aggregate_id):
+        """Delete the given aggregate."""
+        resp, body = self.delete("os-aggregates/%s" % aggregate_id)
+        self.validate_response(schema.delete_aggregate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_aggregate(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'aggregate'
+
+    def add_host(self, aggregate_id, **kwargs):
+        """Add a host to the given aggregate.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#addhost
+        """
+        post_body = json.dumps({'add_host': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_add_remove_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_host(self, aggregate_id, **kwargs):
+        """Remove a host from the given aggregate.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#removehost
+        """
+        post_body = json.dumps({'remove_host': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_add_remove_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_metadata(self, aggregate_id, **kwargs):
+        """Replace the aggregate's existing metadata with new metadata."""
+        post_body = json.dumps({'set_metadata': kwargs})
+        resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.aggregate_set_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/availability_zone_client.py b/tempest/lib/services/compute/availability_zone_client.py
new file mode 100644
index 0000000..00f66d6
--- /dev/null
+++ b/tempest/lib/services/compute/availability_zone_client.py
@@ -0,0 +1,35 @@
+# Copyright 2013 NEC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import availability_zone \
+    as schema
+from tempest.lib.common import rest_client
+
+
+class AvailabilityZoneClient(rest_client.RestClient):
+
+    def list_availability_zones(self, detail=False):
+        url = 'os-availability-zone'
+        schema_list = schema.list_availability_zone_list
+        if detail:
+            url += '/detail'
+            schema_list = schema.list_availability_zone_list_detail
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema_list, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
new file mode 100644
index 0000000..d9a712e
--- /dev/null
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -0,0 +1,42 @@
+# Copyright 2015 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import baremetal_nodes \
+    as schema
+from tempest.lib.common import rest_client
+
+
+class BaremetalNodesClient(rest_client.RestClient):
+    """Tests Baremetal API"""
+
+    def list_baremetal_nodes(self, **params):
+        """List all baremetal nodes."""
+        url = 'os-baremetal-nodes'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_baremetal_nodes, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_baremetal_node(self, baremetal_node_id):
+        """Return the details of a single baremetal node."""
+        url = 'os-baremetal-nodes/%s' % baremetal_node_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_baremetal_node, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/certificates_client.py b/tempest/lib/services/compute/certificates_client.py
new file mode 100644
index 0000000..76d830e
--- /dev/null
+++ b/tempest/lib/services/compute/certificates_client.py
@@ -0,0 +1,37 @@
+# Copyright 2013 IBM Corp
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import certificates as schema
+from tempest.lib.common import rest_client
+
+
+class CertificatesClient(rest_client.RestClient):
+
+    def show_certificate(self, certificate_id):
+        url = "os-certificates/%s" % certificate_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_certificate, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_certificate(self):
+        """Create a certificate."""
+        url = "os-certificates"
+        resp, body = self.post(url, None)
+        body = json.loads(body)
+        self.validate_response(schema.create_certificate, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/extensions_client.py b/tempest/lib/services/compute/extensions_client.py
new file mode 100644
index 0000000..85f8f0c
--- /dev/null
+++ b/tempest/lib/services/compute/extensions_client.py
@@ -0,0 +1,34 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import extensions as schema
+from tempest.lib.common import rest_client
+
+
+class ExtensionsClient(rest_client.RestClient):
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_extensions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_extension(self, extension_alias):
+        resp, body = self.get('extensions/%s' % extension_alias)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/fixed_ips_client.py b/tempest/lib/services/compute/fixed_ips_client.py
new file mode 100644
index 0000000..76ec59f
--- /dev/null
+++ b/tempest/lib/services/compute/fixed_ips_client.py
@@ -0,0 +1,40 @@
+# Copyright 2013 IBM Corp
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import fixed_ips as schema
+from tempest.lib.common import rest_client
+
+
+class FixedIPsClient(rest_client.RestClient):
+
+    def show_fixed_ip(self, fixed_ip):
+        url = "os-fixed-ips/%s" % fixed_ip
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_fixed_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reserve_fixed_ip(self, fixed_ip, **kwargs):
+        """Reserve/Unreserve a fixed IP.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#reserveIP
+        """
+        url = "os-fixed-ips/%s/action" % fixed_ip
+        resp, body = self.post(url, json.dumps(kwargs))
+        self.validate_response(schema.reserve_unreserve_fixed_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
new file mode 100644
index 0000000..50f1dcc
--- /dev/null
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -0,0 +1,176 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import flavors as schema
+from tempest.lib.api_schema.response.compute.v2_1 import flavors_access \
+    as schema_access
+from tempest.lib.api_schema.response.compute.v2_1 import flavors_extra_specs \
+    as schema_extra_specs
+from tempest.lib.common import rest_client
+
+
+class FlavorsClient(rest_client.RestClient):
+
+    def list_flavors(self, detail=False, **params):
+        url = 'flavors'
+        _schema = schema.list_flavors
+
+        if detail:
+            url += '/detail'
+            _schema = schema.list_flavors_details
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_flavor(self, flavor_id):
+        resp, body = self.get("flavors/%s" % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_flavor(self, **kwargs):
+        """Create a new flavor or instance type.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#create-flavors
+        """
+        if kwargs.get('ephemeral'):
+            kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral')
+        if kwargs.get('is_public'):
+            kwargs['os-flavor-access:is_public'] = kwargs.pop('is_public')
+
+        post_body = json.dumps({'flavor': kwargs})
+        resp, body = self.post('flavors', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_flavor(self, flavor_id):
+        """Delete the given flavor."""
+        resp, body = self.delete("flavors/{0}".format(flavor_id))
+        self.validate_response(schema.delete_flavor, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        # Did not use show_flavor(id) for verification as it gives
+        # 200 ok even for deleted id. LP #981263
+        # we can remove the loop here and use get by ID when bug gets sortedout
+        flavors = self.list_flavors(detail=True)['flavors']
+        for flavor in flavors:
+            if flavor['id'] == id:
+                return False
+        return True
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'flavor'
+
+    def set_flavor_extra_spec(self, flavor_id, **kwargs):
+        """Set extra Specs to the mentioned flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateFlavorExtraSpec
+        """
+        post_body = json.dumps({'extra_specs': kwargs})
+        resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_flavor_extra_specs(self, flavor_id):
+        """Get extra Specs details of the mentioned flavor."""
+        resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_flavor_extra_spec(self, flavor_id, key):
+        """Get extra Specs key-value of the mentioned flavor and key."""
+        resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id,
+                              key))
+        body = json.loads(body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
+        """Update specified extra Specs of the mentioned flavor and key.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateflavorspec
+        """
+        resp, body = self.put('flavors/%s/os-extra_specs/%s' %
+                              (flavor_id, key), json.dumps(kwargs))
+        body = json.loads(body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def unset_flavor_extra_spec(self, flavor_id, key):
+        """Unset extra Specs from the mentioned flavor."""
+        resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
+                                 (flavor_id, key))
+        self.validate_response(schema.unset_flavor_extra_specs, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_flavor_access(self, flavor_id):
+        """Get flavor access information given the flavor id."""
+        resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_flavor_access(self, flavor_id, tenant_id):
+        """Add flavor access for the specified tenant."""
+        post_body = {
+            'addTenantAccess': {
+                'tenant': tenant_id
+            }
+        }
+        post_body = json.dumps(post_body)
+        resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_flavor_access(self, flavor_id, tenant_id):
+        """Remove flavor access from the specified tenant."""
+        post_body = {
+            'removeTenantAccess': {
+                'tenant': tenant_id
+            }
+        }
+        post_body = json.dumps(post_body)
+        resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema_access.add_remove_list_flavor_access,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ip_pools_client.py b/tempest/lib/services/compute/floating_ip_pools_client.py
new file mode 100644
index 0000000..d4a0193
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ip_pools_client.py
@@ -0,0 +1,34 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
+from tempest.lib.common import rest_client
+
+
+class FloatingIPPoolsClient(rest_client.RestClient):
+
+    def list_floating_ip_pools(self, params=None):
+        """Gets all floating IP Pools list."""
+        url = 'os-floating-ip-pools'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ip_pools, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ips_bulk_client.py b/tempest/lib/services/compute/floating_ips_bulk_client.py
new file mode 100644
index 0000000..bfcf74b
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ips_bulk_client.py
@@ -0,0 +1,50 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
+from tempest.lib.common import rest_client
+
+
+class FloatingIPsBulkClient(rest_client.RestClient):
+
+    def create_floating_ips_bulk(self, ip_range, pool, interface):
+        """Allocate floating IPs in bulk."""
+        post_body = {
+            'ip_range': ip_range,
+            'pool': pool,
+            'interface': interface
+        }
+        post_body = json.dumps({'floating_ips_bulk_create': post_body})
+        resp, body = self.post('os-floating-ips-bulk', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_floating_ips_bulk(self):
+        """Gets all floating IPs in bulk."""
+        resp, body = self.get('os-floating-ips-bulk')
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_floating_ips_bulk(self, ip_range):
+        """Deletes the provided floating IPs in bulk."""
+        post_body = json.dumps({'ip_range': ip_range})
+        resp, body = self.put('os-floating-ips-bulk/delete', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.delete_floating_ips_bulk, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/floating_ips_client.py b/tempest/lib/services/compute/floating_ips_client.py
new file mode 100644
index 0000000..2569bf9
--- /dev/null
+++ b/tempest/lib/services/compute/floating_ips_client.py
@@ -0,0 +1,103 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import floating_ips as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class FloatingIPsClient(rest_client.RestClient):
+
+    def list_floating_ips(self, **params):
+        """Returns a list of all floating IPs filtered by any parameters."""
+        url = 'os-floating-ips'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_floating_ips, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_floating_ip(self, floating_ip_id):
+        """Get the details of a floating IP."""
+        url = "os-floating-ips/%s" % floating_ip_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_floating_ip(self, **kwargs):
+        """Allocate a floating IP to the project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createFloatingIP
+        """
+        url = 'os-floating-ips'
+        post_body = json.dumps(kwargs)
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_floating_ip(self, floating_ip_id):
+        """Deletes the provided floating IP from the project."""
+        url = "os-floating-ips/%s" % floating_ip_id
+        resp, body = self.delete(url)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def associate_floating_ip_to_server(self, floating_ip, server_id):
+        """Associate the provided floating IP to a specific server."""
+        url = "servers/%s/action" % server_id
+        post_body = {
+            'addFloatingIp': {
+                'address': floating_ip,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post(url, post_body)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def disassociate_floating_ip_from_server(self, floating_ip, server_id):
+        """Disassociate the provided floating IP from a specific server."""
+        url = "servers/%s/action" % server_id
+        post_body = {
+            'removeFloatingIp': {
+                'address': floating_ip,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post(url, post_body)
+        self.validate_response(schema.add_remove_floating_ip, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_floating_ip(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Returns the primary type of resource this client works with."""
+        return 'floating_ip'
diff --git a/tempest/lib/services/compute/hosts_client.py b/tempest/lib/services/compute/hosts_client.py
new file mode 100644
index 0000000..269160e
--- /dev/null
+++ b/tempest/lib/services/compute/hosts_client.py
@@ -0,0 +1,85 @@
+# Copyright 2013 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import hosts as schema
+from tempest.lib.common import rest_client
+
+
+class HostsClient(rest_client.RestClient):
+
+    def list_hosts(self, **params):
+        """List all hosts."""
+
+        url = 'os-hosts'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_hosts, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_host(self, hostname):
+        """Show detail information for the host."""
+
+        resp, body = self.get("os-hosts/%s" % hostname)
+        body = json.loads(body)
+        self.validate_response(schema.get_host_detail, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_host(self, hostname, **kwargs):
+        """Update a host.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#enablehost
+        """
+
+        request_body = {
+            'status': None,
+            'maintenance_mode': None,
+        }
+        request_body.update(**kwargs)
+        request_body = json.dumps(request_body)
+
+        resp, body = self.put("os-hosts/%s" % hostname, request_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def startup_host(self, hostname):
+        """Startup a host."""
+
+        resp, body = self.get("os-hosts/%s/startup" % hostname)
+        body = json.loads(body)
+        self.validate_response(schema.startup_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def shutdown_host(self, hostname):
+        """Shutdown a host."""
+
+        resp, body = self.get("os-hosts/%s/shutdown" % hostname)
+        body = json.loads(body)
+        self.validate_response(schema.shutdown_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reboot_host(self, hostname):
+        """Reboot a host."""
+
+        resp, body = self.get("os-hosts/%s/reboot" % hostname)
+        body = json.loads(body)
+        self.validate_response(schema.reboot_host, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/hypervisor_client.py b/tempest/lib/services/compute/hypervisor_client.py
new file mode 100644
index 0000000..2e6df1f
--- /dev/null
+++ b/tempest/lib/services/compute/hypervisor_client.py
@@ -0,0 +1,70 @@
+# Copyright 2013 IBM 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import hypervisors as schema
+from tempest.lib.common import rest_client
+
+
+class HypervisorClient(rest_client.RestClient):
+
+    def list_hypervisors(self, detail=False):
+        """List hypervisors information."""
+        url = 'os-hypervisors'
+        _schema = schema.list_search_hypervisors
+        if detail:
+            url += '/detail'
+            _schema = schema.list_hypervisors_detail
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor(self, hypervisor_id):
+        """Display the details of the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s' % hypervisor_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_servers_on_hypervisor(self, hypervisor_name):
+        """List instances belonging to the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisors_servers, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor_statistics(self):
+        """Get hypervisor statistics over all compute nodes."""
+        resp, body = self.get('os-hypervisors/statistics')
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor_statistics, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_hypervisor_uptime(self, hypervisor_id):
+        """Display the uptime of the specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_hypervisor_uptime, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def search_hypervisor(self, hypervisor_name):
+        """Search specified hypervisor."""
+        resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name)
+        body = json.loads(body)
+        self.validate_response(schema.list_search_hypervisors, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
new file mode 100644
index 0000000..30ff484
--- /dev/null
+++ b/tempest/lib/services/compute/images_client.py
@@ -0,0 +1,142 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import images as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class ImagesClient(rest_client.RestClient):
+
+    def create_image(self, server_id, **kwargs):
+        """Create an image of the original server.
+
+        Available params: see http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#createImage
+        """
+
+        post_body = {'createImage': kwargs}
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/action' % server_id,
+                               post_body)
+        self.validate_response(schema.create_image, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_images(self, detail=False, **params):
+        """Return a list of all images filtered by any parameter.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listImages
+        """
+        url = 'images'
+        _schema = schema.list_images
+        if detail:
+            url += '/detail'
+            _schema = schema.list_images_details
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image(self, image_id):
+        """Return the details of a single image."""
+        resp, body = self.get("images/%s" % image_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        self.validate_response(schema.get_image, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image(self, image_id):
+        """Delete the provided image."""
+        resp, body = self.delete("images/%s" % image_id)
+        self.validate_response(schema.delete, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_image_metadata(self, image_id):
+        """List all metadata items for an image."""
+        resp, body = self.get("images/%s/metadata" % image_id)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_image_metadata(self, image_id, meta):
+        """Set the metadata for an image.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createImageMetadata
+        """
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.put('images/%s/metadata' % image_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_image_metadata(self, image_id, meta):
+        """Update the metadata for an image.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateImageMetadata
+        """
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.post('images/%s/metadata' % image_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_image_metadata_item(self, image_id, key):
+        """Return the value for a specific image metadata key."""
+        resp, body = self.get("images/%s/metadata/%s" % (image_id, key))
+        body = json.loads(body)
+        self.validate_response(schema.image_meta_item, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_image_metadata_item(self, image_id, key, meta):
+        """Set the value for a specific image metadata key.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#setImageMetadataItem
+        """
+        post_body = json.dumps({'meta': meta})
+        resp, body = self.put('images/%s/metadata/%s' % (image_id, key),
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.image_meta_item, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_image_metadata_item(self, image_id, key):
+        """Delete a single image metadata key/value pair."""
+        resp, body = self.delete("images/%s/metadata/%s" %
+                                 (image_id, key))
+        self.validate_response(schema.delete, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_image(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'image'
diff --git a/tempest/lib/services/compute/instance_usage_audit_log_client.py b/tempest/lib/services/compute/instance_usage_audit_log_client.py
new file mode 100644
index 0000000..4651b2a
--- /dev/null
+++ b/tempest/lib/services/compute/instance_usage_audit_log_client.py
@@ -0,0 +1,38 @@
+# Copyright 2013 IBM 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    instance_usage_audit_logs as schema
+from tempest.lib.common import rest_client
+
+
+class InstanceUsagesAuditLogClient(rest_client.RestClient):
+
+    def list_instance_usage_audit_logs(self):
+        url = 'os-instance_usage_audit_log'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_instance_usage_audit_log,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_instance_usage_audit_log(self, time_before):
+        url = 'os-instance_usage_audit_log/%s' % time_before
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_instance_usage_audit_log, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/interfaces_client.py b/tempest/lib/services/compute/interfaces_client.py
new file mode 100644
index 0000000..e7da5a1
--- /dev/null
+++ b/tempest/lib/services/compute/interfaces_client.py
@@ -0,0 +1,55 @@
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import interfaces as schema
+from tempest.lib.common import rest_client
+
+
+class InterfacesClient(rest_client.RestClient):
+
+    def list_interfaces(self, server_id):
+        resp, body = self.get('servers/%s/os-interface' % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_interface(self, server_id, **kwargs):
+        """Create an interface.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createAttachInterface
+        """
+        post_body = {'interfaceAttachment': kwargs}
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/os-interface' % server_id,
+                               body=post_body)
+        body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_interface(self, server_id, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server_id,
+                                                              port_id))
+        body = json.loads(body)
+        self.validate_response(schema.get_create_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_interface(self, server_id, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server_id,
+                                                                 port_id))
+        self.validate_response(schema.delete_interface, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/keypairs_client.py b/tempest/lib/services/compute/keypairs_client.py
new file mode 100644
index 0000000..3e3cf8d
--- /dev/null
+++ b/tempest/lib/services/compute/keypairs_client.py
@@ -0,0 +1,51 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import keypairs as schema
+from tempest.lib.common import rest_client
+
+
+class KeyPairsClient(rest_client.RestClient):
+
+    def list_keypairs(self):
+        resp, body = self.get("os-keypairs")
+        body = json.loads(body)
+        self.validate_response(schema.list_keypairs, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_keypair(self, keypair_name):
+        resp, body = self.get("os-keypairs/%s" % keypair_name)
+        body = json.loads(body)
+        self.validate_response(schema.get_keypair, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_keypair(self, **kwargs):
+        """Create a keypair.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createKeypair
+        """
+        post_body = json.dumps({'keypair': kwargs})
+        resp, body = self.post("os-keypairs", body=post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_keypair, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_keypair(self, keypair_name):
+        resp, body = self.delete("os-keypairs/%s" % keypair_name)
+        self.validate_response(schema.delete_keypair, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/limits_client.py b/tempest/lib/services/compute/limits_client.py
new file mode 100644
index 0000000..c7eba4e
--- /dev/null
+++ b/tempest/lib/services/compute/limits_client.py
@@ -0,0 +1,28 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import limits as schema
+from tempest.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+
+    def show_limits(self):
+        resp, body = self.get("limits")
+        body = json.loads(body)
+        self.validate_response(schema.get_limit, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
new file mode 100644
index 0000000..21bc37a
--- /dev/null
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -0,0 +1,38 @@
+# Copyright 2014 NEC Corporation.
+#
+#    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_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import migrations as schema
+from tempest.lib.common import rest_client
+
+
+class MigrationsClient(rest_client.RestClient):
+
+    def list_migrations(self, **params):
+        """List all migrations.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#returnmigrations
+        """
+
+        url = 'os-migrations'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_migrations, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/networks_client.py b/tempest/lib/services/compute/networks_client.py
new file mode 100644
index 0000000..c0eb5ff
--- /dev/null
+++ b/tempest/lib/services/compute/networks_client.py
@@ -0,0 +1,33 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class NetworksClient(rest_client.RestClient):
+
+    def list_networks(self):
+        resp, body = self.get("os-networks")
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_network(self, network_id):
+        resp, body = self.get("os-networks/%s" % network_id)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
new file mode 100644
index 0000000..ff4eec0
--- /dev/null
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -0,0 +1,48 @@
+# Copyright 2012 NTT Data
+# 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_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1\
+    import quota_classes as classes_schema
+from tempest.lib.common import rest_client
+
+
+class QuotaClassesClient(rest_client.RestClient):
+
+    def show_quota_class_set(self, quota_class_id):
+        """List the quota class set for a quota class."""
+
+        url = 'os-quota-class-sets/%s' % quota_class_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(classes_schema.get_quota_class_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_class_set(self, quota_class_id, **kwargs):
+        """Update the quota class's limits for one or more resources.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updatequota
+        """
+        post_body = json.dumps({'quota_class_set': kwargs})
+
+        resp, body = self.put('os-quota-class-sets/%s' % quota_class_id,
+                              post_body)
+
+        body = json.loads(body)
+        self.validate_response(classes_schema.update_quota_class_set,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
new file mode 100644
index 0000000..697d004
--- /dev/null
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -0,0 +1,68 @@
+# Copyright 2012 NTT Data
+# 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_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
+from tempest.lib.common import rest_client
+
+
+class QuotasClient(rest_client.RestClient):
+
+    def show_quota_set(self, tenant_id, user_id=None):
+        """List the quota set for a tenant."""
+
+        url = 'os-quota-sets/%s' % tenant_id
+        if user_id:
+            url += '?user_id=%s' % user_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_quota_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_default_quota_set(self, tenant_id):
+        """List the default quota set for a tenant."""
+
+        url = 'os-quota-sets/%s/defaults' % tenant_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_quota_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_quota_set(self, tenant_id, user_id=None, **kwargs):
+        """Updates the tenant's quota limits for one or more resources.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updatesquotatenant
+        """
+
+        post_body = json.dumps({'quota_set': kwargs})
+
+        if user_id:
+            resp, body = self.put('os-quota-sets/%s?user_id=%s' %
+                                  (tenant_id, user_id), post_body)
+        else:
+            resp, body = self.put('os-quota-sets/%s' % tenant_id,
+                                  post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.update_quota_set, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        resp, body = self.delete('os-quota-sets/%s' % tenant_id)
+        self.validate_response(schema.delete_quota, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_group_default_rules_client.py b/tempest/lib/services/compute/security_group_default_rules_client.py
new file mode 100644
index 0000000..e5f291c
--- /dev/null
+++ b/tempest/lib/services/compute/security_group_default_rules_client.py
@@ -0,0 +1,64 @@
+# 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    security_group_default_rule as schema
+from tempest.lib.common import rest_client
+
+
+class SecurityGroupDefaultRulesClient(rest_client.RestClient):
+
+    def create_security_default_group_rule(self, **kwargs):
+        """Create security group default rule.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html
+                              #createSecGroupDefaultRule
+        """
+        post_body = json.dumps({'security_group_default_rule': kwargs})
+        url = 'os-security-group-default-rules'
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group_default_rule(self,
+                                           security_group_default_rule_id):
+        """Delete the provided Security Group default rule."""
+        resp, body = self.delete('os-security-group-default-rules/%s' % (
+            security_group_default_rule_id))
+        self.validate_response(schema.delete_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_security_group_default_rules(self):
+        """List all Security Group default rules."""
+        resp, body = self.get('os-security-group-default-rules')
+        body = json.loads(body)
+        self.validate_response(schema.list_security_group_default_rules,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_security_group_default_rule(self, security_group_default_rule_id):
+        """Return the details of provided Security Group default rule."""
+        resp, body = self.get('os-security-group-default-rules/%s' %
+                              security_group_default_rule_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_security_group_default_rule,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_group_rules_client.py b/tempest/lib/services/compute/security_group_rules_client.py
new file mode 100644
index 0000000..c0e1245
--- /dev/null
+++ b/tempest/lib/services/compute/security_group_rules_client.py
@@ -0,0 +1,43 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    security_groups as schema
+from tempest.lib.common import rest_client
+
+
+class SecurityGroupRulesClient(rest_client.RestClient):
+
+    def create_security_group_rule(self, **kwargs):
+        """Create a new security group rule.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createSecGroupRule
+        """
+        post_body = json.dumps({'security_group_rule': kwargs})
+        url = 'os-security-group-rules'
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_security_group_rule, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group_rule(self, group_rule_id):
+        """Deletes the provided Security Group rule."""
+        resp, body = self.delete('os-security-group-rules/%s' %
+                                 group_rule_id)
+        self.validate_response(schema.delete_security_group_rule, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/security_groups_client.py b/tempest/lib/services/compute/security_groups_client.py
new file mode 100644
index 0000000..4db98c9
--- /dev/null
+++ b/tempest/lib/services/compute/security_groups_client.py
@@ -0,0 +1,89 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import \
+    security_groups as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class SecurityGroupsClient(rest_client.RestClient):
+
+    def list_security_groups(self, **params):
+        """List all security groups for a user."""
+
+        url = 'os-security-groups'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_security_groups, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_security_group(self, security_group_id):
+        """Get the details of a Security Group."""
+        url = "os-security-groups/%s" % security_group_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.get_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_security_group(self, **kwargs):
+        """Create a new security group.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createSecGroup
+        """
+        post_body = json.dumps({'security_group': kwargs})
+        resp, body = self.post('os-security-groups', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.get_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_security_group(self, security_group_id, **kwargs):
+        """Update a security group.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateSecGroup
+        """
+        post_body = json.dumps({'security_group': kwargs})
+        resp, body = self.put('os-security-groups/%s' % security_group_id,
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_security_group(self, security_group_id):
+        """Delete the provided Security Group."""
+        resp, body = self.delete(
+            'os-security-groups/%s' % security_group_id)
+        self.validate_response(schema.delete_security_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_security_group(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'security_group'
diff --git a/tempest/lib/services/compute/server_groups_client.py b/tempest/lib/services/compute/server_groups_client.py
new file mode 100644
index 0000000..ea60e98
--- /dev/null
+++ b/tempest/lib/services/compute/server_groups_client.py
@@ -0,0 +1,56 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.common import rest_client
+
+
+class ServerGroupsClient(rest_client.RestClient):
+
+    def create_server_group(self, **kwargs):
+        """Create the server group.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createServerGroup
+        """
+        post_body = json.dumps({'server_group': kwargs})
+        resp, body = self.post('os-server-groups', post_body)
+
+        body = json.loads(body)
+        self.validate_response(schema.create_show_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server_group(self, server_group_id):
+        """Delete the given server-group."""
+        resp, body = self.delete("os-server-groups/%s" % server_group_id)
+        self.validate_response(schema.delete_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_server_groups(self):
+        """List the server-groups."""
+        resp, body = self.get("os-server-groups")
+        body = json.loads(body)
+        self.validate_response(schema.list_server_groups, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server_group(self, server_group_id):
+        """Get the details of given server_group."""
+        resp, body = self.get("os-server-groups/%s" % server_group_id)
+        body = json.loads(body)
+        self.validate_response(schema.create_show_server_group, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
new file mode 100644
index 0000000..46c4a49
--- /dev/null
+++ b/tempest/lib/services/compute/servers_client.py
@@ -0,0 +1,570 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import servers as schema
+from tempest.lib.common import rest_client
+
+
+class ServersClient(rest_client.RestClient):
+
+    def __init__(self, auth_provider, service, region,
+                 enable_instance_password=True, **kwargs):
+        super(ServersClient, self).__init__(
+            auth_provider, service, region, **kwargs)
+        self.enable_instance_password = enable_instance_password
+
+    def create_server(self, **kwargs):
+        """Create server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createServer
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        :param scheduler_hints: The name is changed to os:scheduler_hints and
+        the parameter is set in the same level as the parameter 'server'.
+        """
+        body = copy.deepcopy(kwargs)
+        if body.get('disk_config'):
+            body['OS-DCF:diskConfig'] = body.pop('disk_config')
+
+        hints = None
+        if body.get('scheduler_hints'):
+            hints = {'os:scheduler_hints': body.pop('scheduler_hints')}
+
+        post_body = {'server': body}
+
+        if hints:
+            post_body = dict(post_body.items() + hints.items())
+
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers', post_body)
+
+        body = json.loads(body)
+        # NOTE(maurosr): this deals with the case of multiple server create
+        # with return reservation id set True
+        if 'reservation_id' in body:
+            return rest_client.ResponseBody(resp, body)
+        if self.enable_instance_password:
+            create_schema = schema.create_server_with_admin_pass
+        else:
+            create_schema = schema.create_server
+        self.validate_response(create_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_server(self, server_id, **kwargs):
+        """Update server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#updateServer
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        if kwargs.get('disk_config'):
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+
+        post_body = json.dumps({'server': kwargs})
+        resp, body = self.put("servers/%s" % server_id, post_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server(self, server_id):
+        """Get server details."""
+        resp, body = self.get("servers/%s" % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.get_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server(self, server_id):
+        """Delete server."""
+        resp, body = self.delete("servers/%s" % server_id)
+        self.validate_response(schema.delete_server, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_servers(self, detail=False, **params):
+        """List servers.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listServers
+                          and http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#listDetailServers
+        """
+
+        url = 'servers'
+        _schema = schema.list_servers
+
+        if detail:
+            url += '/detail'
+            _schema = schema.list_servers_detail
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(_schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_addresses(self, server_id):
+        """Lists all addresses for a server."""
+        resp, body = self.get("servers/%s/ips" % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_addresses, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_addresses_by_network(self, server_id, network_id):
+        """Lists all addresses of a specific network type for a server."""
+        resp, body = self.get("servers/%s/ips/%s" %
+                              (server_id, network_id))
+        body = json.loads(body)
+        self.validate_response(schema.list_addresses_by_network, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def action(self, server_id, action_name,
+               schema=schema.server_actions_common_schema,
+               **kwargs):
+        post_body = json.dumps({action_name: kwargs})
+        resp, body = self.post('servers/%s/action' % server_id,
+                               post_body)
+        if body:
+            body = json.loads(body)
+        self.validate_response(schema, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_backup(self, server_id, **kwargs):
+        """Backup a server instance.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createBackup
+        """
+        return self.action(server_id, "createBackup", **kwargs)
+
+    def change_password(self, server_id, **kwargs):
+        """Change the root password for the server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#changePassword
+        """
+        return self.action(server_id, 'changePassword', **kwargs)
+
+    def show_password(self, server_id):
+        resp, body = self.get("servers/%s/os-server-password" %
+                              server_id)
+        body = json.loads(body)
+        self.validate_response(schema.show_password, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_password(self, server_id):
+        """Removes the encrypted server password from the metadata server
+
+        Note that this does not actually change the instance server
+        password.
+        """
+        resp, body = self.delete("servers/%s/os-server-password" %
+                                 server_id)
+        self.validate_response(schema.server_actions_delete_password,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reboot_server(self, server_id, **kwargs):
+        """Reboot a server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#reboot
+        """
+        return self.action(server_id, 'reboot', **kwargs)
+
+    def rebuild_server(self, server_id, image_ref, **kwargs):
+        """Rebuild a server with a new image.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#rebuild
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        kwargs['imageRef'] = image_ref
+        if 'disk_config' in kwargs:
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+        if self.enable_instance_password:
+            rebuild_schema = schema.rebuild_server_with_admin_pass
+        else:
+            rebuild_schema = schema.rebuild_server
+        return self.action(server_id, 'rebuild',
+                           rebuild_schema, **kwargs)
+
+    def resize_server(self, server_id, flavor_ref, **kwargs):
+        """Change the flavor of a server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#resize
+
+        Most parameters except the following are passed to the API without
+        any changes.
+        :param disk_config: The name is changed to OS-DCF:diskConfig
+        """
+        kwargs['flavorRef'] = flavor_ref
+        if 'disk_config' in kwargs:
+            kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config')
+        return self.action(server_id, 'resize', **kwargs)
+
+    def confirm_resize_server(self, server_id, **kwargs):
+        """Confirm the flavor change for a server.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#confirmResize
+        """
+        return self.action(server_id, 'confirmResize',
+                           schema.server_actions_confirm_resize,
+                           **kwargs)
+
+    def revert_resize_server(self, server_id, **kwargs):
+        """Revert a server back to its original flavor.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#revertResize
+        """
+        return self.action(server_id, 'revertResize', **kwargs)
+
+    def list_server_metadata(self, server_id):
+        resp, body = self.get("servers/%s/metadata" % server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_server_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_server_metadata(self, server_id, meta, no_metadata_field=False):
+        if no_metadata_field:
+            post_body = ""
+        else:
+            post_body = json.dumps({'metadata': meta})
+        resp, body = self.put('servers/%s/metadata' % server_id,
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.set_server_metadata, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_server_metadata(self, server_id, meta):
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.post('servers/%s/metadata' % server_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.update_server_metadata,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_server_metadata_item(self, server_id, key):
+        resp, body = self.get("servers/%s/metadata/%s" % (server_id, key))
+        body = json.loads(body)
+        self.validate_response(schema.set_show_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def set_server_metadata_item(self, server_id, key, meta):
+        post_body = json.dumps({'meta': meta})
+        resp, body = self.put('servers/%s/metadata/%s' % (server_id, key),
+                              post_body)
+        body = json.loads(body)
+        self.validate_response(schema.set_show_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_server_metadata_item(self, server_id, key):
+        resp, body = self.delete("servers/%s/metadata/%s" %
+                                 (server_id, key))
+        self.validate_response(schema.delete_server_metadata_item,
+                               resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def stop_server(self, server_id, **kwargs):
+        return self.action(server_id, 'os-stop', **kwargs)
+
+    def start_server(self, server_id, **kwargs):
+        return self.action(server_id, 'os-start', **kwargs)
+
+    def attach_volume(self, server_id, **kwargs):
+        """Attaches a volume to a server instance."""
+        post_body = json.dumps({'volumeAttachment': kwargs})
+        resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
+                               post_body)
+        body = json.loads(body)
+        self.validate_response(schema.attach_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_attached_volume(self, server_id, attachment_id, **kwargs):
+        """Swaps a volume attached to an instance for another volume"""
+        post_body = json.dumps({'volumeAttachment': kwargs})
+        resp, body = self.put('servers/%s/os-volume_attachments/%s' %
+                              (server_id, attachment_id),
+                              post_body)
+        self.validate_response(schema.update_attached_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def detach_volume(self, server_id, volume_id):  # noqa
+        """Detaches a volume from a server instance."""
+        resp, body = self.delete('servers/%s/os-volume_attachments/%s' %
+                                 (server_id, volume_id))
+        self.validate_response(schema.detach_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume_attachment(self, server_id, volume_id):
+        """Return details about the given volume attachment."""
+        resp, body = self.get('servers/%s/os-volume_attachments/%s' % (
+            server_id, volume_id))
+        body = json.loads(body)
+        self.validate_response(schema.show_volume_attachment, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_volume_attachments(self, server_id):
+        """Returns the list of volume attachments for a given instance."""
+        resp, body = self.get('servers/%s/os-volume_attachments' % (
+            server_id))
+        body = json.loads(body)
+        self.validate_response(schema.list_volume_attachments, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def add_security_group(self, server_id, **kwargs):
+        """Add a security group to the server.
+
+        Available params: TODO
+        """
+        # TODO(oomichi): The api-site doesn't contain this API description.
+        # So the above should be changed to the api-site link after
+        # adding the description on the api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
+        return self.action(server_id, 'addSecurityGroup', **kwargs)
+
+    def remove_security_group(self, server_id, **kwargs):
+        """Remove a security group from the server.
+
+        Available params: TODO
+        """
+        # TODO(oomichi): The api-site doesn't contain this API description.
+        # So the above should be changed to the api-site link after
+        # adding the description on the api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199
+        return self.action(server_id, 'removeSecurityGroup', **kwargs)
+
+    def live_migrate_server(self, server_id, **kwargs):
+        """This should be called with administrator privileges.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#migrateLive
+        """
+        return self.action(server_id, 'os-migrateLive', **kwargs)
+
+    def migrate_server(self, server_id, **kwargs):
+        """Migrate a server to a new host.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#migrate
+        """
+        return self.action(server_id, 'migrate', **kwargs)
+
+    def lock_server(self, server_id, **kwargs):
+        """Lock the given server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#lock
+        """
+        return self.action(server_id, 'lock', **kwargs)
+
+    def unlock_server(self, server_id, **kwargs):
+        """UNlock the given server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#unlock
+        """
+        return self.action(server_id, 'unlock', **kwargs)
+
+    def suspend_server(self, server_id, **kwargs):
+        """Suspend the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#suspend
+        """
+        return self.action(server_id, 'suspend', **kwargs)
+
+    def resume_server(self, server_id, **kwargs):
+        """Un-suspend the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#resume
+        """
+        return self.action(server_id, 'resume', **kwargs)
+
+    def pause_server(self, server_id, **kwargs):
+        """Pause the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#pause
+        """
+        return self.action(server_id, 'pause', **kwargs)
+
+    def unpause_server(self, server_id, **kwargs):
+        """Un-pause the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#unpause
+        """
+        return self.action(server_id, 'unpause', **kwargs)
+
+    def reset_state(self, server_id, **kwargs):
+        """Reset the state of a server to active/error.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#resetState
+        """
+        return self.action(server_id, 'os-resetState', **kwargs)
+
+    def shelve_server(self, server_id, **kwargs):
+        """Shelve the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#shelve
+        """
+        return self.action(server_id, 'shelve', **kwargs)
+
+    def unshelve_server(self, server_id, **kwargs):
+        """Un-shelve the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#unshelve
+        """
+        return self.action(server_id, 'unshelve', **kwargs)
+
+    def shelve_offload_server(self, server_id, **kwargs):
+        """Shelve-offload the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#shelveOffload
+        """
+        return self.action(server_id, 'shelveOffload', **kwargs)
+
+    def get_console_output(self, server_id, **kwargs):
+        """Get console output.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#getConsoleOutput
+        """
+        return self.action(server_id, 'os-getConsoleOutput',
+                           schema.get_console_output, **kwargs)
+
+    def list_virtual_interfaces(self, server_id):
+        """List the virtual interfaces used in an instance."""
+        resp, body = self.get('/'.join(['servers', server_id,
+                              'os-virtual-interfaces']))
+        body = json.loads(body)
+        self.validate_response(schema.list_virtual_interfaces, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def rescue_server(self, server_id, **kwargs):
+        """Rescue the provided server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#rescue
+        """
+        return self.action(server_id, 'rescue', schema.rescue_server, **kwargs)
+
+    def unrescue_server(self, server_id):
+        """Unrescue the provided server."""
+        return self.action(server_id, 'unrescue')
+
+    def show_server_diagnostics(self, server_id):
+        """Get the usage data for a server."""
+        resp, body = self.get("servers/%s/diagnostics" % server_id)
+        return rest_client.ResponseBody(resp, json.loads(body))
+
+    def list_instance_actions(self, server_id):
+        """List the provided server action."""
+        resp, body = self.get("servers/%s/os-instance-actions" %
+                              server_id)
+        body = json.loads(body)
+        self.validate_response(schema.list_instance_actions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_instance_action(self, server_id, request_id):
+        """Returns the action details of the provided server."""
+        resp, body = self.get("servers/%s/os-instance-actions/%s" %
+                              (server_id, request_id))
+        body = json.loads(body)
+        self.validate_response(schema.show_instance_action, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def force_delete_server(self, server_id, **kwargs):
+        """Force delete a server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#forceDelete
+        """
+        return self.action(server_id, 'forceDelete', **kwargs)
+
+    def restore_soft_deleted_server(self, server_id, **kwargs):
+        """Restore a soft-deleted server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#restore
+        """
+        return self.action(server_id, 'restore', **kwargs)
+
+    def reset_network(self, server_id, **kwargs):
+        """Reset the Network of a server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#resetNetwork
+        """
+        return self.action(server_id, 'resetNetwork', **kwargs)
+
+    def inject_network_info(self, server_id, **kwargs):
+        """Inject the Network Info into server.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#injectNetworkInfo
+        """
+        return self.action(server_id, 'injectNetworkInfo', **kwargs)
+
+    def get_vnc_console(self, server_id, **kwargs):
+        """Get URL of VNC console.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#getVNCConsole
+        """
+        return self.action(server_id, "os-getVNCConsole",
+                           schema.get_vnc_console, **kwargs)
+
+    def add_fixed_ip(self, server_id, **kwargs):
+        """Add a fixed IP to server instance.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#addFixedIp
+        """
+        return self.action(server_id, 'addFixedIp', **kwargs)
+
+    def remove_fixed_ip(self, server_id, **kwargs):
+        """Remove input fixed IP from input server instance.
+
+        Available params: http://developer.openstack.org/
+                          api-ref-compute-v2.1.html#removeFixedIp
+        """
+        return self.action(server_id, 'removeFixedIp', **kwargs)
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
new file mode 100644
index 0000000..06aad77
--- /dev/null
+++ b/tempest/lib/services/compute/services_client.py
@@ -0,0 +1,58 @@
+# Copyright 2013 NEC Corporation
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import services as schema
+from tempest.lib.common import rest_client
+
+
+class ServicesClient(rest_client.RestClient):
+
+    def list_services(self, **params):
+        url = 'os-services'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_services, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def enable_service(self, **kwargs):
+        """Enable service on a host.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#enableScheduling
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/enable', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.enable_disable_service, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def disable_service(self, **kwargs):
+        """Disable service on a host.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#disableScheduling
+        """
+        post_body = json.dumps(kwargs)
+        resp, body = self.put('os-services/disable', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.enable_disable_service, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/snapshots_client.py b/tempest/lib/services/compute/snapshots_client.py
new file mode 100644
index 0000000..de776bd
--- /dev/null
+++ b/tempest/lib/services/compute/snapshots_client.py
@@ -0,0 +1,76 @@
+# Copyright 2015 Fujitsu(fnst) 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 oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import snapshots as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class SnapshotsClient(rest_client.RestClient):
+
+    def create_snapshot(self, volume_id, **kwargs):
+        """Create a snapshot.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createSnapshot
+        """
+        post_body = {
+            'volume_id': volume_id
+        }
+        post_body.update(kwargs)
+        post_body = json.dumps({'snapshot': post_body})
+        resp, body = self.post('os-snapshots', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_snapshot(self, snapshot_id):
+        url = "os-snapshots/%s" % snapshot_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_snapshots(self, detail=False, params=None):
+        url = 'os-snapshots'
+
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_snapshots, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_snapshot(self, snapshot_id):
+        resp, body = self.delete("os-snapshots/%s" % snapshot_id)
+        self.validate_response(schema.delete_snapshot, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_snapshot(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'snapshot'
diff --git a/tempest/lib/services/compute/tenant_networks_client.py b/tempest/lib/services/compute/tenant_networks_client.py
new file mode 100644
index 0000000..44a97a9
--- /dev/null
+++ b/tempest/lib/services/compute/tenant_networks_client.py
@@ -0,0 +1,34 @@
+# Copyright 2015 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.compute.v2_1 import tenant_networks
+from tempest.lib.common import rest_client
+
+
+class TenantNetworksClient(rest_client.RestClient):
+
+    def list_tenant_networks(self):
+        resp, body = self.get("os-tenant-networks")
+        body = json.loads(body)
+        self.validate_response(tenant_networks.list_tenant_networks, resp,
+                               body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant_network(self, network_id):
+        resp, body = self.get("os-tenant-networks/%s" % network_id)
+        body = json.loads(body)
+        self.validate_response(tenant_networks.get_tenant_network, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
new file mode 100644
index 0000000..e8da465
--- /dev/null
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -0,0 +1,43 @@
+# Copyright 2013 NEC Corporation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import tenant_usages
+from tempest.lib.common import rest_client
+
+
+class TenantUsagesClient(rest_client.RestClient):
+
+    def list_tenant_usages(self, **params):
+        url = 'os-simple-tenant-usage'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(tenant_usages.list_tenant_usage, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant_usage(self, tenant_id, **params):
+        url = 'os-simple-tenant-usage/%s' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(tenant_usages.get_tenant_usage, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
new file mode 100644
index 0000000..5898f93
--- /dev/null
+++ b/tempest/lib/services/compute/versions_client.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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_serialization import jsonutils as json
+from six.moves import urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+
+    def _get_base_version_url(self):
+        # NOTE: The URL which is gotten from keystone's catalog contains
+        # API version and project-id like "v2/{project-id}", but we need
+        # to access the URL which doesn't contain them for getting API
+        # versions. For that, here should use raw_request() instead of
+        # get().
+        endpoint = self.base_url
+        url = urllib.parse.urlparse(endpoint)
+        return '%s://%s/' % (url.scheme, url.netloc)
+
+    def list_versions(self):
+        version_url = self._get_base_version_url()
+        resp, body = self.raw_request(version_url, 'GET')
+        body = json.loads(body)
+        self.validate_response(schema.list_versions, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_version_by_url(self, version_url):
+        """Get the version document by url.
+
+        This gets the version document for a url, useful in testing
+        the contents of things like /v2/ or /v2.1/ in Nova. That
+        controller needs authenticated access, so we have to get
+        ourselves a token before making the request.
+
+        """
+        # we need a token for this request
+        resp, body = self.raw_request(version_url, 'GET',
+                                      {'X-Auth-Token': self.token})
+        body = json.loads(body)
+        self.validate_response(schema.get_one_version, resp, body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/compute/volumes_client.py b/tempest/lib/services/compute/volumes_client.py
new file mode 100644
index 0000000..45a44de
--- /dev/null
+++ b/tempest/lib/services/compute/volumes_client.py
@@ -0,0 +1,76 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.api_schema.response.compute.v2_1 import volumes as schema
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+
+
+class VolumesClient(rest_client.RestClient):
+
+    def list_volumes(self, detail=False, **params):
+        """List all the volumes created."""
+        url = 'os-volumes'
+
+        if detail:
+            url += '/detail'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.list_volumes, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_volume(self, volume_id):
+        """Return the details of a single volume."""
+        url = "os-volumes/%s" % volume_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_volume(self, **kwargs):
+        """Create a new Volume.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-compute-v2.1.html#createVolume
+        """
+        post_body = json.dumps({'volume': kwargs})
+        resp, body = self.post('os-volumes', post_body)
+        body = json.loads(body)
+        self.validate_response(schema.create_get_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_volume(self, volume_id):
+        """Delete the Specified Volume."""
+        resp, body = self.delete("os-volumes/%s" % volume_id)
+        self.validate_response(schema.delete_volume, resp, body)
+        return rest_client.ResponseBody(resp, body)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_volume(id)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    @property
+    def resource_type(self):
+        """Return the primary type of resource this client works with."""
+        return 'volume'
diff --git a/tempest/lib/services/identity/__init__.py b/tempest/lib/services/identity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/identity/__init__.py
diff --git a/tempest/lib/services/identity/v2/__init__.py b/tempest/lib/services/identity/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/identity/v2/__init__.py
diff --git a/tempest/lib/services/identity/v2/token_client.py b/tempest/lib/services/identity/v2/token_client.py
new file mode 100644
index 0000000..0350175
--- /dev/null
+++ b/tempest/lib/services/identity/v2/token_client.py
@@ -0,0 +1,121 @@
+# Copyright 2015 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 oslo_log import log as logging
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+
+
+class TokenClient(rest_client.RestClient):
+
+    def __init__(self, auth_url, disable_ssl_certificate_validation=None,
+                 ca_certs=None, trace_requests=None):
+        dscv = disable_ssl_certificate_validation
+        super(TokenClient, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests)
+
+        if auth_url is None:
+            raise exceptions.IdentityError("Couldn't determine auth_url")
+
+        # Normalize URI to ensure /tokens is in it.
+        if 'tokens' not in auth_url:
+            auth_url = auth_url.rstrip('/') + '/tokens'
+
+        self.auth_url = auth_url
+
+    def auth(self, user, password, tenant=None):
+        creds = {
+            'auth': {
+                'passwordCredentials': {
+                    'username': user,
+                    'password': password,
+                },
+            }
+        }
+
+        if tenant:
+            creds['auth']['tenantName'] = tenant
+
+        body = json.dumps(creds, sort_keys=True)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(200, resp.status)
+
+        return rest_client.ResponseBody(resp, body['access'])
+
+    def auth_token(self, token_id, tenant=None):
+        creds = {
+            'auth': {
+                'token': {
+                    'id': token_id,
+                },
+            }
+        }
+
+        if tenant:
+            creds['auth']['tenantName'] = tenant
+
+        body = json.dumps(creds)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(200, resp.status)
+
+        return rest_client.ResponseBody(resp, body['access'])
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None):
+        """A simple HTTP request interface."""
+        if headers is None:
+            headers = self.get_headers(accept_type="json")
+        elif extra_headers:
+            try:
+                headers.update(self.get_headers(accept_type="json"))
+            except (ValueError, TypeError):
+                headers = self.get_headers(accept_type="json")
+
+        resp, resp_body = self.raw_request(url, method,
+                                           headers=headers, body=body)
+        self._log_request(method, url, resp, req_headers=headers,
+                          req_body='<omitted>', resp_body=resp_body)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise exceptions.Unauthorized(resp_body['error']['message'])
+        elif resp.status not in [200, 201]:
+            raise exceptions.IdentityError(
+                'Unexpected status code {0}'.format(resp.status))
+
+        return resp, json.loads(resp_body)
+
+    def get_token(self, user, password, tenant, auth_data=False):
+        """Returns (token id, token data) for supplied credentials."""
+        body = self.auth(user, password, tenant)
+
+        if auth_data:
+            return body['token']['id'], body
+        else:
+            return body['token']['id']
+
+
+class TokenClientJSON(TokenClient):
+    LOG = logging.getLogger(__name__)
+
+    def _warn(self):
+        self.LOG.warning("%s class was deprecated and renamed to %s" %
+                         (self.__class__.__name__, 'TokenClient'))
+
+    def __init__(self, *args, **kwargs):
+        self._warn()
+        super(TokenClientJSON, self).__init__(*args, **kwargs)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/identity/v3/__init__.py
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
new file mode 100644
index 0000000..f342a49
--- /dev/null
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -0,0 +1,183 @@
+# Copyright 2015 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 oslo_log import log as logging
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions
+
+
+class V3TokenClient(rest_client.RestClient):
+
+    def __init__(self, auth_url, disable_ssl_certificate_validation=None,
+                 ca_certs=None, trace_requests=None):
+        dscv = disable_ssl_certificate_validation
+        super(V3TokenClient, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests)
+
+        if auth_url is None:
+            raise exceptions.IdentityError("Couldn't determine auth_url")
+
+        if 'auth/tokens' not in auth_url:
+            auth_url = auth_url.rstrip('/') + '/auth/tokens'
+
+        self.auth_url = auth_url
+
+    def auth(self, user_id=None, username=None, password=None, project_id=None,
+             project_name=None, user_domain_id=None, user_domain_name=None,
+             project_domain_id=None, project_domain_name=None, domain_id=None,
+             domain_name=None, token=None):
+        """Obtains a token from the authentication service
+
+        :param user_id: user id
+        :param username: user name
+        :param user_domain_id: the user domain id
+        :param user_domain_name: the user domain name
+        :param project_domain_id: the project domain id
+        :param project_domain_name: the project domain name
+        :param domain_id: a domain id to scope to
+        :param domain_name: a domain name to scope to
+        :param project_id: a project id to scope to
+        :param project_name: a project name to scope to
+        :param token: a token to re-scope.
+
+        Accepts different combinations of credentials.
+        Sample sample valid combinations:
+        - token
+        - token, project_name, project_domain_id
+        - user_id, password
+        - username, password, user_domain_id
+        - username, password, project_name, user_domain_id, project_domain_id
+        Validation is left to the server side.
+        """
+        creds = {
+            'auth': {
+                'identity': {
+                    'methods': [],
+                }
+            }
+        }
+        id_obj = creds['auth']['identity']
+        if token:
+            id_obj['methods'].append('token')
+            id_obj['token'] = {
+                'id': token
+            }
+
+        if (user_id or username) and password:
+            id_obj['methods'].append('password')
+            id_obj['password'] = {
+                'user': {
+                    'password': password,
+                }
+            }
+            if user_id:
+                id_obj['password']['user']['id'] = user_id
+            else:
+                id_obj['password']['user']['name'] = username
+
+            _domain = None
+            if user_domain_id is not None:
+                _domain = dict(id=user_domain_id)
+            elif user_domain_name is not None:
+                _domain = dict(name=user_domain_name)
+            if _domain:
+                id_obj['password']['user']['domain'] = _domain
+
+        if (project_id or project_name):
+            _project = dict()
+
+            if project_id:
+                _project['id'] = project_id
+            elif project_name:
+                _project['name'] = project_name
+
+            if project_domain_id is not None:
+                _project['domain'] = {'id': project_domain_id}
+            elif project_domain_name is not None:
+                _project['domain'] = {'name': project_domain_name}
+
+            creds['auth']['scope'] = dict(project=_project)
+        elif domain_id:
+            creds['auth']['scope'] = dict(domain={'id': domain_id})
+        elif domain_name:
+            creds['auth']['scope'] = dict(domain={'name': domain_name})
+
+        body = json.dumps(creds, sort_keys=True)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def request(self, method, url, extra_headers=False, headers=None,
+                body=None):
+        """A simple HTTP request interface."""
+        if headers is None:
+            # Always accept 'json', for xml token client too.
+            # Because XML response is not easily
+            # converted to the corresponding JSON one
+            headers = self.get_headers(accept_type="json")
+        elif extra_headers:
+            try:
+                headers.update(self.get_headers(accept_type="json"))
+            except (ValueError, TypeError):
+                headers = self.get_headers(accept_type="json")
+
+        resp, resp_body = self.raw_request(url, method,
+                                           headers=headers, body=body)
+        self._log_request(method, url, resp, req_headers=headers,
+                          req_body='<omitted>', resp_body=resp_body)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise exceptions.Unauthorized(resp_body['error']['message'])
+        elif resp.status not in [200, 201, 204]:
+            raise exceptions.IdentityError(
+                'Unexpected status code {0}'.format(resp.status))
+
+        return resp, json.loads(resp_body)
+
+    def get_token(self, **kwargs):
+        """Returns (token id, token data) for supplied credentials"""
+
+        auth_data = kwargs.pop('auth_data', False)
+
+        if not (kwargs.get('user_domain_id') or
+                kwargs.get('user_domain_name')):
+            kwargs['user_domain_name'] = 'Default'
+
+        if not (kwargs.get('project_domain_id') or
+                kwargs.get('project_domain_name')):
+            kwargs['project_domain_name'] = 'Default'
+
+        body = self.auth(**kwargs)
+
+        token = body.response.get('x-subject-token')
+        if auth_data:
+            return token, body['token']
+        else:
+            return token
+
+
+class V3TokenClientJSON(V3TokenClient):
+    LOG = logging.getLogger(__name__)
+
+    def _warn(self):
+        self.LOG.warning("%s class was deprecated and renamed to %s" %
+                         (self.__class__.__name__, 'V3TokenClient'))
+
+    def __init__(self, *args, **kwargs):
+        self._warn()
+        super(V3TokenClientJSON, self).__init__(*args, **kwargs)
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/network/__init__.py
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
new file mode 100644
index 0000000..c5d4c66
--- /dev/null
+++ b/tempest/lib/services/network/agents_client.py
@@ -0,0 +1,68 @@
+# Copyright 2015 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.lib.services.network import base
+
+
+class AgentsClient(base.BaseNetworkClient):
+
+    def update_agent(self, agent_id, **kwargs):
+        """Update agent."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526673
+        uri = '/agents/%s' % agent_id
+        return self.update_resource(uri, kwargs)
+
+    def show_agent(self, agent_id, **fields):
+        uri = '/agents/%s' % agent_id
+        return self.show_resource(uri, **fields)
+
+    def list_agents(self, **filters):
+        uri = '/agents'
+        return self.list_resources(uri, **filters)
+
+    def list_routers_on_l3_agent(self, agent_id):
+        uri = '/agents/%s/l3-routers' % agent_id
+        return self.list_resources(uri)
+
+    def create_router_on_l3_agent(self, agent_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526670
+        uri = '/agents/%s/l3-routers' % agent_id
+        return self.create_resource(uri, kwargs)
+
+    def delete_router_from_l3_agent(self, agent_id, router_id):
+        uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
+        return self.delete_resource(uri)
+
+    def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self.list_resources(uri)
+
+    def delete_network_from_dhcp_agent(self, agent_id, network_id):
+        uri = '/agents/%s/dhcp-networks/%s' % (agent_id,
+                                               network_id)
+        return self.delete_resource(uri)
+
+    def add_dhcp_agent_to_network(self, agent_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526212
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
new file mode 100644
index 0000000..a6ada04
--- /dev/null
+++ b/tempest/lib/services/network/base.py
@@ -0,0 +1,71 @@
+#    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_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class BaseNetworkClient(rest_client.RestClient):
+
+    """Base class for Tempest REST clients for Neutron.
+
+    Child classes use v2 of the Neutron API, since the V1 API has been
+    removed from the code base.
+    """
+
+    version = '2.0'
+    uri_prefix = "v2.0"
+
+    def list_resources(self, uri, **filters):
+        req_uri = self.uri_prefix + uri
+        if filters:
+            req_uri += '?' + urllib.urlencode(filters, doseq=1)
+        resp, body = self.get(req_uri)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_resource(self, uri):
+        req_uri = self.uri_prefix + uri
+        resp, body = self.delete(req_uri)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_resource(self, uri, **fields):
+        # fields is a dict which key is 'fields' and value is a
+        # list of field's name. An example:
+        # {'fields': ['id', 'name']}
+        req_uri = self.uri_prefix + uri
+        if fields:
+            req_uri += '?' + urllib.urlencode(fields, doseq=1)
+        resp, body = self.get(req_uri)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_resource(self, uri, post_data):
+        req_uri = self.uri_prefix + uri
+        req_post_data = json.dumps(post_data)
+        resp, body = self.post(req_uri, req_post_data)
+        body = json.loads(body)
+        self.expected_success(201, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_resource(self, uri, post_data):
+        req_uri = self.uri_prefix + uri
+        req_post_data = json.dumps(post_data)
+        resp, body = self.put(req_uri, req_post_data)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/extensions_client.py b/tempest/lib/services/network/extensions_client.py
new file mode 100644
index 0000000..3910c84
--- /dev/null
+++ b/tempest/lib/services/network/extensions_client.py
@@ -0,0 +1,24 @@
+#    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.lib.services.network import base
+
+
+class ExtensionsClient(base.BaseNetworkClient):
+
+    def show_extension(self, ext_alias, **fields):
+        uri = '/extensions/%s' % ext_alias
+        return self.show_resource(uri, **fields)
+
+    def list_extensions(self, **filters):
+        uri = '/extensions'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/floating_ips_client.py b/tempest/lib/services/network/floating_ips_client.py
new file mode 100644
index 0000000..1968e05
--- /dev/null
+++ b/tempest/lib/services/network/floating_ips_client.py
@@ -0,0 +1,38 @@
+#    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.lib.services.network import base
+
+
+class FloatingIPsClient(base.BaseNetworkClient):
+
+    def create_floatingip(self, **kwargs):
+        uri = '/floatingips'
+        post_data = {'floatingip': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_floatingip(self, floatingip_id, **kwargs):
+        uri = '/floatingips/%s' % floatingip_id
+        post_data = {'floatingip': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_floatingip(self, floatingip_id, **fields):
+        uri = '/floatingips/%s' % floatingip_id
+        return self.show_resource(uri, **fields)
+
+    def delete_floatingip(self, floatingip_id):
+        uri = '/floatingips/%s' % floatingip_id
+        return self.delete_resource(uri)
+
+    def list_floatingips(self, **filters):
+        uri = '/floatingips'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/metering_label_rules_client.py b/tempest/lib/services/network/metering_label_rules_client.py
new file mode 100644
index 0000000..36cf8e3
--- /dev/null
+++ b/tempest/lib/services/network/metering_label_rules_client.py
@@ -0,0 +1,33 @@
+#    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.lib.services.network import base
+
+
+class MeteringLabelRulesClient(base.BaseNetworkClient):
+
+    def create_metering_label_rule(self, **kwargs):
+        uri = '/metering/metering-label-rules'
+        post_data = {'metering_label_rule': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_metering_label_rule(self, metering_label_rule_id, **fields):
+        uri = '/metering/metering-label-rules/%s' % metering_label_rule_id
+        return self.show_resource(uri, **fields)
+
+    def delete_metering_label_rule(self, metering_label_rule_id):
+        uri = '/metering/metering-label-rules/%s' % metering_label_rule_id
+        return self.delete_resource(uri)
+
+    def list_metering_label_rules(self, **filters):
+        uri = '/metering/metering-label-rules'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/metering_labels_client.py b/tempest/lib/services/network/metering_labels_client.py
new file mode 100644
index 0000000..2350ecd
--- /dev/null
+++ b/tempest/lib/services/network/metering_labels_client.py
@@ -0,0 +1,33 @@
+#    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.lib.services.network import base
+
+
+class MeteringLabelsClient(base.BaseNetworkClient):
+
+    def create_metering_label(self, **kwargs):
+        uri = '/metering/metering-labels'
+        post_data = {'metering_label': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_metering_label(self, metering_label_id, **fields):
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.show_resource(uri, **fields)
+
+    def delete_metering_label(self, metering_label_id):
+        uri = '/metering/metering-labels/%s' % metering_label_id
+        return self.delete_resource(uri)
+
+    def list_metering_labels(self, **filters):
+        uri = '/metering/metering-labels'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/networks_client.py b/tempest/lib/services/network/networks_client.py
new file mode 100644
index 0000000..0926634
--- /dev/null
+++ b/tempest/lib/services/network/networks_client.py
@@ -0,0 +1,47 @@
+#    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.lib.services.network import base
+
+
+class NetworksClient(base.BaseNetworkClient):
+
+    def create_network(self, **kwargs):
+        uri = '/networks'
+        post_data = {'network': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_network(self, network_id, **kwargs):
+        uri = '/networks/%s' % network_id
+        post_data = {'network': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_network(self, network_id, **fields):
+        uri = '/networks/%s' % network_id
+        return self.show_resource(uri, **fields)
+
+    def delete_network(self, network_id):
+        uri = '/networks/%s' % network_id
+        return self.delete_resource(uri)
+
+    def list_networks(self, **filters):
+        uri = '/networks'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_networks(self, **kwargs):
+        """Create multiple networks in a single request.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#bulkCreateNetwork
+        """
+        uri = '/networks'
+        return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/ports_client.py b/tempest/lib/services/network/ports_client.py
new file mode 100644
index 0000000..1793d6a
--- /dev/null
+++ b/tempest/lib/services/network/ports_client.py
@@ -0,0 +1,47 @@
+#    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.lib.services.network import base
+
+
+class PortsClient(base.BaseNetworkClient):
+
+    def create_port(self, **kwargs):
+        uri = '/ports'
+        post_data = {'port': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_port(self, port_id, **kwargs):
+        uri = '/ports/%s' % port_id
+        post_data = {'port': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_port(self, port_id, **fields):
+        uri = '/ports/%s' % port_id
+        return self.show_resource(uri, **fields)
+
+    def delete_port(self, port_id):
+        uri = '/ports/%s' % port_id
+        return self.delete_resource(uri)
+
+    def list_ports(self, **filters):
+        uri = '/ports'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_ports(self, **kwargs):
+        """Create multiple ports in a single request.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#bulkCreatePorts
+        """
+        uri = '/ports'
+        return self.create_resource(uri, kwargs)
diff --git a/tempest/lib/services/network/quotas_client.py b/tempest/lib/services/network/quotas_client.py
new file mode 100644
index 0000000..b5cf35b
--- /dev/null
+++ b/tempest/lib/services/network/quotas_client.py
@@ -0,0 +1,35 @@
+# Copyright 2015 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.lib.services.network import base
+
+
+class QuotasClient(base.BaseNetworkClient):
+
+    def update_quotas(self, tenant_id, **kwargs):
+        put_body = {'quota': kwargs}
+        uri = '/quotas/%s' % tenant_id
+        return self.update_resource(uri, put_body)
+
+    def reset_quotas(self, tenant_id):
+        uri = '/quotas/%s' % tenant_id
+        return self.delete_resource(uri)
+
+    def show_quotas(self, tenant_id, **fields):
+        uri = '/quotas/%s' % tenant_id
+        return self.show_resource(uri, **fields)
+
+    def list_quotas(self, **filters):
+        uri = '/quotas'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/security_group_rules_client.py b/tempest/lib/services/network/security_group_rules_client.py
new file mode 100644
index 0000000..944eba6
--- /dev/null
+++ b/tempest/lib/services/network/security_group_rules_client.py
@@ -0,0 +1,33 @@
+#    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.lib.services.network import base
+
+
+class SecurityGroupRulesClient(base.BaseNetworkClient):
+
+    def create_security_group_rule(self, **kwargs):
+        uri = '/security-group-rules'
+        post_data = {'security_group_rule': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_security_group_rule(self, security_group_rule_id, **fields):
+        uri = '/security-group-rules/%s' % security_group_rule_id
+        return self.show_resource(uri, **fields)
+
+    def delete_security_group_rule(self, security_group_rule_id):
+        uri = '/security-group-rules/%s' % security_group_rule_id
+        return self.delete_resource(uri)
+
+    def list_security_group_rules(self, **filters):
+        uri = '/security-group-rules'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/security_groups_client.py b/tempest/lib/services/network/security_groups_client.py
new file mode 100644
index 0000000..0e25339
--- /dev/null
+++ b/tempest/lib/services/network/security_groups_client.py
@@ -0,0 +1,38 @@
+#    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.lib.services.network import base
+
+
+class SecurityGroupsClient(base.BaseNetworkClient):
+
+    def create_security_group(self, **kwargs):
+        uri = '/security-groups'
+        post_data = {'security_group': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_security_group(self, security_group_id, **kwargs):
+        uri = '/security-groups/%s' % security_group_id
+        post_data = {'security_group': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_security_group(self, security_group_id, **fields):
+        uri = '/security-groups/%s' % security_group_id
+        return self.show_resource(uri, **fields)
+
+    def delete_security_group(self, security_group_id):
+        uri = '/security-groups/%s' % security_group_id
+        return self.delete_resource(uri)
+
+    def list_security_groups(self, **filters):
+        uri = '/security-groups'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/network/subnetpools_client.py b/tempest/lib/services/network/subnetpools_client.py
new file mode 100644
index 0000000..12349b1
--- /dev/null
+++ b/tempest/lib/services/network/subnetpools_client.py
@@ -0,0 +1,40 @@
+# Copyright 2015 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.lib.services.network import base
+
+
+class SubnetpoolsClient(base.BaseNetworkClient):
+
+    def list_subnetpools(self, **filters):
+        uri = '/subnetpools'
+        return self.list_resources(uri, **filters)
+
+    def create_subnetpool(self, **kwargs):
+        uri = '/subnetpools'
+        post_data = {'subnetpool': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def show_subnetpool(self, subnetpool_id, **fields):
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.show_resource(uri, **fields)
+
+    def update_subnetpool(self, subnetpool_id, **kwargs):
+        uri = '/subnetpools/%s' % subnetpool_id
+        post_data = {'subnetpool': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def delete_subnetpool(self, subnetpool_id):
+        uri = '/subnetpools/%s' % subnetpool_id
+        return self.delete_resource(uri)
diff --git a/tempest/lib/services/network/subnets_client.py b/tempest/lib/services/network/subnets_client.py
new file mode 100644
index 0000000..63ed13e
--- /dev/null
+++ b/tempest/lib/services/network/subnets_client.py
@@ -0,0 +1,47 @@
+#    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.lib.services.network import base
+
+
+class SubnetsClient(base.BaseNetworkClient):
+
+    def create_subnet(self, **kwargs):
+        uri = '/subnets'
+        post_data = {'subnet': kwargs}
+        return self.create_resource(uri, post_data)
+
+    def update_subnet(self, subnet_id, **kwargs):
+        uri = '/subnets/%s' % subnet_id
+        post_data = {'subnet': kwargs}
+        return self.update_resource(uri, post_data)
+
+    def show_subnet(self, subnet_id, **fields):
+        uri = '/subnets/%s' % subnet_id
+        return self.show_resource(uri, **fields)
+
+    def delete_subnet(self, subnet_id):
+        uri = '/subnets/%s' % subnet_id
+        return self.delete_resource(uri)
+
+    def list_subnets(self, **filters):
+        uri = '/subnets'
+        return self.list_resources(uri, **filters)
+
+    def create_bulk_subnets(self, **kwargs):
+        """Create multiple subnets in a single request.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-networking-v2.html#bulkCreateSubnet
+        """
+        uri = '/subnets'
+        return self.create_resource(uri, kwargs)