Initial copy of api tests from tempest

This change is the result of running
tools/copy_api_tests_from_tempest.sh.

Change-Id: Ica02dbe1ed26f1bc9526ea9682756ebc5877cf4a
diff --git a/neutron/tests/tempest/services/__init__.py b/neutron/tests/tempest/services/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/__init__.py
diff --git a/neutron/tests/tempest/services/botoclients.py b/neutron/tests/tempest/services/botoclients.py
new file mode 100644
index 0000000..e2e080b
--- /dev/null
+++ b/neutron/tests/tempest/services/botoclients.py
@@ -0,0 +1,235 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import ConfigParser
+import contextlib
+from tempest_lib import exceptions as lib_exc
+import types
+import urlparse
+
+from neutron.tests.tempest import config
+from neutron.tests.tempest import exceptions
+
+import boto
+import boto.ec2
+import boto.s3.connection
+
+CONF = config.CONF
+
+
+class BotoClientBase(object):
+
+    ALLOWED_METHODS = set()
+
+    def __init__(self, username=None, password=None,
+                 auth_url=None, tenant_name=None,
+                 *args, **kwargs):
+        # FIXME(andreaf) replace credentials and auth_url with auth_provider
+
+        insecure_ssl = CONF.identity.disable_ssl_certificate_validation
+        self.ca_cert = CONF.identity.ca_certificates_file
+
+        self.connection_timeout = str(CONF.boto.http_socket_timeout)
+        self.num_retries = str(CONF.boto.num_retries)
+        self.build_timeout = CONF.boto.build_timeout
+        self.ks_cred = {"username": username,
+                        "password": password,
+                        "auth_url": auth_url,
+                        "tenant_name": tenant_name,
+                        "insecure": insecure_ssl,
+                        "cacert": self.ca_cert}
+
+    def _keystone_aws_get(self):
+        # FIXME(andreaf) Move EC2 credentials to AuthProvider
+        import keystoneclient.v2_0.client
+
+        keystone = keystoneclient.v2_0.client.Client(**self.ks_cred)
+        ec2_cred_list = keystone.ec2.list(keystone.auth_user_id)
+        ec2_cred = None
+        for cred in ec2_cred_list:
+            if cred.tenant_id == keystone.auth_tenant_id:
+                ec2_cred = cred
+                break
+        else:
+            ec2_cred = keystone.ec2.create(keystone.auth_user_id,
+                                           keystone.auth_tenant_id)
+        if not all((ec2_cred, ec2_cred.access, ec2_cred.secret)):
+            raise lib_exc.NotFound("Unable to get access and secret keys")
+        return ec2_cred
+
+    def _config_boto_timeout(self, timeout, retries):
+        try:
+            boto.config.add_section("Boto")
+        except ConfigParser.DuplicateSectionError:
+            pass
+        boto.config.set("Boto", "http_socket_timeout", timeout)
+        boto.config.set("Boto", "num_retries", retries)
+
+    def _config_boto_ca_certificates_file(self, ca_cert):
+        if ca_cert is None:
+            return
+
+        try:
+            boto.config.add_section("Boto")
+        except ConfigParser.DuplicateSectionError:
+            pass
+        boto.config.set("Boto", "ca_certificates_file", ca_cert)
+
+    def __getattr__(self, name):
+        """Automatically creates methods for the allowed methods set."""
+        if name in self.ALLOWED_METHODS:
+            def func(self, *args, **kwargs):
+                with contextlib.closing(self.get_connection()) as conn:
+                    return getattr(conn, name)(*args, **kwargs)
+
+            func.__name__ = name
+            setattr(self, name, types.MethodType(func, self, self.__class__))
+            setattr(self.__class__, name,
+                    types.MethodType(func, None, self.__class__))
+            return getattr(self, name)
+        else:
+            raise AttributeError(name)
+
+    def get_connection(self):
+        self._config_boto_timeout(self.connection_timeout, self.num_retries)
+        self._config_boto_ca_certificates_file(self.ca_cert)
+        if not all((self.connection_data["aws_access_key_id"],
+                   self.connection_data["aws_secret_access_key"])):
+            if all([self.ks_cred.get('auth_url'),
+                    self.ks_cred.get('username'),
+                    self.ks_cred.get('tenant_name'),
+                    self.ks_cred.get('password')]):
+                ec2_cred = self._keystone_aws_get()
+                self.connection_data["aws_access_key_id"] = \
+                    ec2_cred.access
+                self.connection_data["aws_secret_access_key"] = \
+                    ec2_cred.secret
+            else:
+                raise exceptions.InvalidConfiguration(
+                    "Unable to get access and secret keys")
+        return self.connect_method(**self.connection_data)
+
+
+class APIClientEC2(BotoClientBase):
+
+    def connect_method(self, *args, **kwargs):
+        return boto.connect_ec2(*args, **kwargs)
+
+    def __init__(self, *args, **kwargs):
+        super(APIClientEC2, self).__init__(*args, **kwargs)
+        insecure_ssl = CONF.identity.disable_ssl_certificate_validation
+        aws_access = CONF.boto.aws_access
+        aws_secret = CONF.boto.aws_secret
+        purl = urlparse.urlparse(CONF.boto.ec2_url)
+
+        region_name = CONF.compute.region
+        if not region_name:
+            region_name = CONF.identity.region
+        region = boto.ec2.regioninfo.RegionInfo(name=region_name,
+                                                endpoint=purl.hostname)
+        port = purl.port
+        if port is None:
+            if purl.scheme is not "https":
+                port = 80
+            else:
+                port = 443
+        else:
+            port = int(port)
+        self.connection_data = {"aws_access_key_id": aws_access,
+                                "aws_secret_access_key": aws_secret,
+                                "is_secure": purl.scheme == "https",
+                                "validate_certs": not insecure_ssl,
+                                "region": region,
+                                "host": purl.hostname,
+                                "port": port,
+                                "path": purl.path}
+
+    ALLOWED_METHODS = set(('create_key_pair', 'get_key_pair',
+                           'delete_key_pair', 'import_key_pair',
+                           'get_all_key_pairs',
+                           'get_all_tags',
+                           'create_image', 'get_image',
+                           'register_image', 'deregister_image',
+                           'get_all_images', 'get_image_attribute',
+                           'modify_image_attribute', 'reset_image_attribute',
+                           'get_all_kernels',
+                           'create_volume', 'delete_volume',
+                           'get_all_volume_status', 'get_all_volumes',
+                           'get_volume_attribute', 'modify_volume_attribute'
+                           'bundle_instance', 'cancel_spot_instance_requests',
+                           'confirm_product_instanc',
+                           'get_all_instance_status', 'get_all_instances',
+                           'get_all_reserved_instances',
+                           'get_all_spot_instance_requests',
+                           'get_instance_attribute', 'monitor_instance',
+                           'monitor_instances', 'unmonitor_instance',
+                           'unmonitor_instances',
+                           'purchase_reserved_instance_offering',
+                           'reboot_instances', 'request_spot_instances',
+                           'reset_instance_attribute', 'run_instances',
+                           'start_instances', 'stop_instances',
+                           'terminate_instances',
+                           'attach_network_interface', 'attach_volume',
+                           'detach_network_interface', 'detach_volume',
+                           'get_console_output',
+                           'delete_network_interface', 'create_subnet',
+                           'create_network_interface', 'delete_subnet',
+                           'get_all_network_interfaces',
+                           'allocate_address', 'associate_address',
+                           'disassociate_address', 'get_all_addresses',
+                           'release_address',
+                           'create_snapshot', 'delete_snapshot',
+                           'get_all_snapshots', 'get_snapshot_attribute',
+                           'modify_snapshot_attribute',
+                           'reset_snapshot_attribute', 'trim_snapshots',
+                           'get_all_regions', 'get_all_zones',
+                           'get_all_security_groups', 'create_security_group',
+                           'delete_security_group', 'authorize_security_group',
+                           'authorize_security_group_egress',
+                           'revoke_security_group',
+                           'revoke_security_group_egress'))
+
+
+class ObjectClientS3(BotoClientBase):
+
+    def connect_method(self, *args, **kwargs):
+        return boto.connect_s3(*args, **kwargs)
+
+    def __init__(self, *args, **kwargs):
+        super(ObjectClientS3, self).__init__(*args, **kwargs)
+        insecure_ssl = CONF.identity.disable_ssl_certificate_validation
+        aws_access = CONF.boto.aws_access
+        aws_secret = CONF.boto.aws_secret
+        purl = urlparse.urlparse(CONF.boto.s3_url)
+        port = purl.port
+        if port is None:
+            if purl.scheme is not "https":
+                port = 80
+            else:
+                port = 443
+        else:
+            port = int(port)
+        self.connection_data = {"aws_access_key_id": aws_access,
+                                "aws_secret_access_key": aws_secret,
+                                "is_secure": purl.scheme == "https",
+                                "validate_certs": not insecure_ssl,
+                                "host": purl.hostname,
+                                "port": port,
+                                "calling_format": boto.s3.connection.
+                                OrdinaryCallingFormat()}
+
+    ALLOWED_METHODS = set(('create_bucket', 'delete_bucket', 'generate_url',
+                           'get_all_buckets', 'get_bucket', 'delete_key',
+                           'lookup'))
diff --git a/neutron/tests/tempest/services/identity/__init__.py b/neutron/tests/tempest/services/identity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/__init__.py
diff --git a/neutron/tests/tempest/services/identity/v2/__init__.py b/neutron/tests/tempest/services/identity/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v2/__init__.py
diff --git a/neutron/tests/tempest/services/identity/v2/json/__init__.py b/neutron/tests/tempest/services/identity/v2/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v2/json/__init__.py
diff --git a/neutron/tests/tempest/services/identity/v2/json/identity_client.py b/neutron/tests/tempest/services/identity/v2/json/identity_client.py
new file mode 100644
index 0000000..e3e7290
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v2/json/identity_client.py
@@ -0,0 +1,271 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+from tempest_lib import exceptions as lib_exc
+
+from neutron.tests.tempest.common import service_client
+
+
+class IdentityClientJSON(service_client.ServiceClient):
+
+    def has_admin_extensions(self):
+        """
+        Returns True if the KSADM Admin Extensions are supported
+        False otherwise
+        """
+        if hasattr(self, '_has_admin_extensions'):
+            return self._has_admin_extensions
+        # Try something that requires admin
+        try:
+            self.list_roles()
+            self._has_admin_extensions = True
+        except Exception:
+            self._has_admin_extensions = False
+        return self._has_admin_extensions
+
+    def create_role(self, name):
+        """Create a role."""
+        post_body = {
+            'name': name,
+        }
+        post_body = json.dumps({'role': post_body})
+        resp, body = self.post('OS-KSADM/roles', post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def get_role(self, role_id):
+        """Get a role by its id."""
+        resp, body = self.get('OS-KSADM/roles/%s' % role_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['role'])
+
+    def create_tenant(self, name, **kwargs):
+        """
+        Create a tenant
+        name (required): New tenant name
+        description: Description of new tenant (default is none)
+        enabled <true|false>: Initial tenant status (default is true)
+        """
+        post_body = {
+            'name': name,
+            'description': kwargs.get('description', ''),
+            'enabled': kwargs.get('enabled', True),
+        }
+        post_body = json.dumps({'tenant': post_body})
+        resp, body = self.post('tenants', post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def delete_role(self, role_id):
+        """Delete a role."""
+        resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
+        self.expected_success(204, resp.status)
+        return resp, body
+
+    def list_user_roles(self, tenant_id, user_id):
+        """Returns a list of roles assigned to a user for a tenant."""
+        url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBodyList(resp, self._parse_resp(body))
+
+    def assign_user_role(self, tenant_id, user_id, role_id):
+        """Add roles to a user on a tenant."""
+        resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
+                              (tenant_id, user_id, role_id), "")
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def remove_user_role(self, tenant_id, user_id, role_id):
+        """Removes a role assignment for a user on a tenant."""
+        resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
+                                 (tenant_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_tenant(self, tenant_id):
+        """Delete a tenant."""
+        resp, body = self.delete('tenants/%s' % str(tenant_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def get_tenant(self, tenant_id):
+        """Get tenant details."""
+        resp, body = self.get('tenants/%s' % str(tenant_id))
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def list_roles(self):
+        """Returns roles."""
+        resp, body = self.get('OS-KSADM/roles')
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBodyList(resp, self._parse_resp(body))
+
+    def list_tenants(self):
+        """Returns tenants."""
+        resp, body = self.get('tenants')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['tenants'])
+
+    def get_tenant_by_name(self, tenant_name):
+        tenants = self.list_tenants()
+        for tenant in tenants:
+            if tenant['name'] == tenant_name:
+                return tenant
+        raise lib_exc.NotFound('No such tenant')
+
+    def update_tenant(self, tenant_id, **kwargs):
+        """Updates a tenant."""
+        body = self.get_tenant(tenant_id)
+        name = kwargs.get('name', body['name'])
+        desc = kwargs.get('description', body['description'])
+        en = kwargs.get('enabled', body['enabled'])
+        post_body = {
+            'id': tenant_id,
+            'name': name,
+            'description': desc,
+            'enabled': en,
+        }
+        post_body = json.dumps({'tenant': post_body})
+        resp, body = self.post('tenants/%s' % tenant_id, post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def create_user(self, name, password, tenant_id, email, **kwargs):
+        """Create a user."""
+        post_body = {
+            'name': name,
+            'password': password,
+            'email': email
+        }
+        if tenant_id is not None:
+            post_body['tenantId'] = tenant_id
+        if kwargs.get('enabled') is not None:
+            post_body['enabled'] = kwargs.get('enabled')
+        post_body = json.dumps({'user': post_body})
+        resp, body = self.post('users', post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def update_user(self, user_id, **kwargs):
+        """Updates a user."""
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def get_user(self, user_id):
+        """GET a user."""
+        resp, body = self.get("users/%s" % user_id)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def delete_user(self, user_id):
+        """Delete a user."""
+        resp, body = self.delete("users/%s" % user_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def get_users(self):
+        """Get the list of users."""
+        resp, body = self.get("users")
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBodyList(resp, self._parse_resp(body))
+
+    def enable_disable_user(self, user_id, enabled):
+        """Enables or disables a user."""
+        put_body = {
+            'enabled': enabled
+        }
+        put_body = json.dumps({'user': put_body})
+        resp, body = self.put('users/%s/enabled' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def get_token(self, token_id):
+        """Get token details."""
+        resp, body = self.get("tokens/%s" % token_id)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def delete_token(self, token_id):
+        """Delete a token."""
+        resp, body = self.delete("tokens/%s" % token_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_users_for_tenant(self, tenant_id):
+        """List users for a Tenant."""
+        resp, body = self.get('/tenants/%s/users' % tenant_id)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBodyList(resp, self._parse_resp(body))
+
+    def get_user_by_username(self, tenant_id, username):
+        users = self.list_users_for_tenant(tenant_id)
+        for user in users:
+            if user['name'] == username:
+                return user
+        raise lib_exc.NotFound('No such user')
+
+    def create_service(self, name, type, **kwargs):
+        """Create a service."""
+        post_body = {
+            'name': name,
+            'type': type,
+            'description': kwargs.get('description')
+        }
+        post_body = json.dumps({'OS-KSADM:service': post_body})
+        resp, body = self.post('/OS-KSADM/services', post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def get_service(self, service_id):
+        """Get Service."""
+        url = '/OS-KSADM/services/%s' % service_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def list_services(self):
+        """List Service - Returns Services."""
+        resp, body = self.get('/OS-KSADM/services')
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBodyList(resp, self._parse_resp(body))
+
+    def delete_service(self, service_id):
+        """Delete Service."""
+        url = '/OS-KSADM/services/%s' % service_id
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def update_user_password(self, user_id, new_pass):
+        """Update User Password."""
+        put_body = {
+            'password': new_pass,
+            'id': user_id
+        }
+        put_body = json.dumps({'user': put_body})
+        resp, body = self.put('users/%s/OS-KSADM/password' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
+    def list_extensions(self):
+        """List all the extensions."""
+        resp, body = self.get('/extensions')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp,
+                                               body['extensions']['values'])
diff --git a/neutron/tests/tempest/services/identity/v2/json/token_client.py b/neutron/tests/tempest/services/identity/v2/json/token_client.py
new file mode 100644
index 0000000..51d9db0
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v2/json/token_client.py
@@ -0,0 +1,110 @@
+# 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.
+
+import json
+from tempest_lib.common import rest_client
+from tempest_lib import exceptions as lib_exc
+
+from neutron.tests.tempest.common import service_client
+from neutron.tests.tempest import exceptions
+
+
+class TokenClientJSON(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(TokenClientJSON, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests)
+
+        # 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)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(200, resp.status)
+
+        return service_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 service_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)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise lib_exc.Unauthorized(resp_body['error']['message'])
+        elif resp.status not in [200, 201]:
+            raise exceptions.IdentityError(
+                'Unexpected status code {0}'.format(resp.status))
+
+        if isinstance(resp_body, str):
+            resp_body = json.loads(resp_body)
+        return resp, 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']
diff --git a/neutron/tests/tempest/services/identity/v3/__init__.py b/neutron/tests/tempest/services/identity/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/__init__.py
diff --git a/neutron/tests/tempest/services/identity/v3/json/__init__.py b/neutron/tests/tempest/services/identity/v3/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/__init__.py
diff --git a/neutron/tests/tempest/services/identity/v3/json/credentials_client.py b/neutron/tests/tempest/services/identity/v3/json/credentials_client.py
new file mode 100644
index 0000000..4300c0f
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/credentials_client.py
@@ -0,0 +1,83 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from neutron.tests.tempest.common import service_client
+
+
+class CredentialsClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_credential(self, access_key, secret_key, user_id, project_id):
+        """Creates a credential."""
+        blob = "{\"access\": \"%s\", \"secret\": \"%s\"}" % (
+            access_key, secret_key)
+        post_body = {
+            "blob": blob,
+            "project_id": project_id,
+            "type": "ec2",
+            "user_id": user_id
+        }
+        post_body = json.dumps({'credential': post_body})
+        resp, body = self.post('credentials', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        body['credential']['blob'] = json.loads(body['credential']['blob'])
+        return service_client.ResponseBody(resp, body['credential'])
+
+    def update_credential(self, credential_id, **kwargs):
+        """Updates a credential."""
+        body = self.get_credential(credential_id)
+        cred_type = kwargs.get('type', body['type'])
+        access_key = kwargs.get('access_key', body['blob']['access'])
+        secret_key = kwargs.get('secret_key', body['blob']['secret'])
+        project_id = kwargs.get('project_id', body['project_id'])
+        user_id = kwargs.get('user_id', body['user_id'])
+        blob = "{\"access\": \"%s\", \"secret\": \"%s\"}" % (
+            access_key, secret_key)
+        post_body = {
+            "blob": blob,
+            "project_id": project_id,
+            "type": cred_type,
+            "user_id": user_id
+        }
+        post_body = json.dumps({'credential': post_body})
+        resp, body = self.patch('credentials/%s' % credential_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        body['credential']['blob'] = json.loads(body['credential']['blob'])
+        return service_client.ResponseBody(resp, body['credential'])
+
+    def get_credential(self, credential_id):
+        """To GET Details of a credential."""
+        resp, body = self.get('credentials/%s' % credential_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        body['credential']['blob'] = json.loads(body['credential']['blob'])
+        return service_client.ResponseBody(resp, body['credential'])
+
+    def list_credentials(self):
+        """Lists out all the available credentials."""
+        resp, body = self.get('credentials')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['credentials'])
+
+    def delete_credential(self, credential_id):
+        """Deletes a credential."""
+        resp, body = self.delete('credentials/%s' % credential_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/neutron/tests/tempest/services/identity/v3/json/endpoints_client.py b/neutron/tests/tempest/services/identity/v3/json/endpoints_client.py
new file mode 100644
index 0000000..b60dd26
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/endpoints_client.py
@@ -0,0 +1,87 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from neutron.tests.tempest.common import service_client
+
+
+class EndPointClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def list_endpoints(self):
+        """GET endpoints."""
+        resp, body = self.get('endpoints')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['endpoints'])
+
+    def create_endpoint(self, service_id, interface, url, **kwargs):
+        """Create endpoint.
+
+        Normally this function wouldn't allow setting values that are not
+        allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+        """
+        region = kwargs.get('region', None)
+        if 'force_enabled' in kwargs:
+            enabled = kwargs.get('force_enabled', None)
+        else:
+            enabled = kwargs.get('enabled', None)
+        post_body = {
+            'service_id': service_id,
+            'interface': interface,
+            'url': url,
+            'region': region,
+            'enabled': enabled
+        }
+        post_body = json.dumps({'endpoint': post_body})
+        resp, body = self.post('endpoints', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['endpoint'])
+
+    def update_endpoint(self, endpoint_id, service_id=None, interface=None,
+                        url=None, region=None, enabled=None, **kwargs):
+        """Updates an endpoint with given parameters.
+
+        Normally this function wouldn't allow setting values that are not
+        allowed for 'enabled'. Use `force_enabled` to set a non-boolean.
+
+        """
+        post_body = {}
+        if service_id is not None:
+            post_body['service_id'] = service_id
+        if interface is not None:
+            post_body['interface'] = interface
+        if url is not None:
+            post_body['url'] = url
+        if region is not None:
+            post_body['region'] = region
+        if 'force_enabled' in kwargs:
+            post_body['enabled'] = kwargs['force_enabled']
+        elif enabled is not None:
+            post_body['enabled'] = enabled
+        post_body = json.dumps({'endpoint': post_body})
+        resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['endpoint'])
+
+    def delete_endpoint(self, endpoint_id):
+        """Delete endpoint."""
+        resp_header, resp_body = self.delete('endpoints/%s' % endpoint_id)
+        self.expected_success(204, resp_header.status)
+        return service_client.ResponseBody(resp_header, resp_body)
diff --git a/neutron/tests/tempest/services/identity/v3/json/identity_client.py b/neutron/tests/tempest/services/identity/v3/json/identity_client.py
new file mode 100644
index 0000000..f8dd4f7
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/identity_client.py
@@ -0,0 +1,523 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+import urllib
+
+from neutron.tests.tempest.common import service_client
+
+
+class IdentityV3ClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_user(self, user_name, password=None, project_id=None,
+                    email=None, domain_id='default', **kwargs):
+        """Creates a user."""
+        en = kwargs.get('enabled', True)
+        description = kwargs.get('description', None)
+        default_project_id = kwargs.get('default_project_id')
+        post_body = {
+            'project_id': project_id,
+            'default_project_id': default_project_id,
+            'description': description,
+            'domain_id': domain_id,
+            'email': email,
+            'enabled': en,
+            'name': user_name,
+            'password': password
+        }
+        post_body = json.dumps({'user': post_body})
+        resp, body = self.post('users', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['user'])
+
+    def update_user(self, user_id, name, **kwargs):
+        """Updates a user."""
+        body = self.get_user(user_id)
+        email = kwargs.get('email', body['email'])
+        en = kwargs.get('enabled', body['enabled'])
+        project_id = kwargs.get('project_id', body['project_id'])
+        if 'default_project_id' in body.keys():
+            default_project_id = kwargs.get('default_project_id',
+                                            body['default_project_id'])
+        else:
+            default_project_id = kwargs.get('default_project_id')
+        description = kwargs.get('description', body['description'])
+        domain_id = kwargs.get('domain_id', body['domain_id'])
+        post_body = {
+            'name': name,
+            'email': email,
+            'enabled': en,
+            'project_id': project_id,
+            'default_project_id': default_project_id,
+            'id': user_id,
+            'domain_id': domain_id,
+            'description': description
+        }
+        post_body = json.dumps({'user': post_body})
+        resp, body = self.patch('users/%s' % user_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['user'])
+
+    def update_user_password(self, user_id, password, original_password):
+        """Updates a user password."""
+        update_user = {
+            'password': password,
+            'original_password': original_password
+        }
+        update_user = json.dumps({'user': update_user})
+        resp, _ = self.post('users/%s/password' % user_id, update_user)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp)
+
+    def list_user_projects(self, user_id):
+        """Lists the projects on which a user has roles assigned."""
+        resp, body = self.get('users/%s/projects' % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['projects'])
+
+    def get_users(self, params=None):
+        """Get the list of users."""
+        url = 'users'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['users'])
+
+    def get_user(self, user_id):
+        """GET a user."""
+        resp, body = self.get("users/%s" % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['user'])
+
+    def delete_user(self, user_id):
+        """Deletes a User."""
+        resp, body = self.delete("users/%s" % user_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_project(self, name, **kwargs):
+        """Creates a project."""
+        description = kwargs.get('description', None)
+        en = kwargs.get('enabled', True)
+        domain_id = kwargs.get('domain_id', 'default')
+        post_body = {
+            'description': description,
+            'domain_id': domain_id,
+            'enabled': en,
+            'name': name
+        }
+        post_body = json.dumps({'project': post_body})
+        resp, body = self.post('projects', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['project'])
+
+    def list_projects(self, params=None):
+        url = "projects"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['projects'])
+
+    def update_project(self, project_id, **kwargs):
+        body = self.get_project(project_id)
+        name = kwargs.get('name', body['name'])
+        desc = kwargs.get('description', body['description'])
+        en = kwargs.get('enabled', body['enabled'])
+        domain_id = kwargs.get('domain_id', body['domain_id'])
+        post_body = {
+            'id': project_id,
+            'name': name,
+            'description': desc,
+            'enabled': en,
+            'domain_id': domain_id,
+        }
+        post_body = json.dumps({'project': post_body})
+        resp, body = self.patch('projects/%s' % project_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['project'])
+
+    def get_project(self, project_id):
+        """GET a Project."""
+        resp, body = self.get("projects/%s" % project_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['project'])
+
+    def delete_project(self, project_id):
+        """Delete a project."""
+        resp, body = self.delete('projects/%s' % str(project_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_role(self, name):
+        """Create a Role."""
+        post_body = {
+            'name': name
+        }
+        post_body = json.dumps({'role': post_body})
+        resp, body = self.post('roles', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['role'])
+
+    def get_role(self, role_id):
+        """GET a Role."""
+        resp, body = self.get('roles/%s' % str(role_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['role'])
+
+    def list_roles(self):
+        """Get the list of Roles."""
+        resp, body = self.get("roles")
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def update_role(self, name, role_id):
+        """Create a Role."""
+        post_body = {
+            'name': name
+        }
+        post_body = json.dumps({'role': post_body})
+        resp, body = self.patch('roles/%s' % str(role_id), post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['role'])
+
+    def delete_role(self, role_id):
+        """Delete a role."""
+        resp, body = self.delete('roles/%s' % str(role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def assign_user_role(self, project_id, user_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/users/%s/roles/%s' %
+                              (project_id, user_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_domain(self, name, **kwargs):
+        """Creates a domain."""
+        description = kwargs.get('description', None)
+        en = kwargs.get('enabled', True)
+        post_body = {
+            'description': description,
+            'enabled': en,
+            'name': name
+        }
+        post_body = json.dumps({'domain': post_body})
+        resp, body = self.post('domains', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['domain'])
+
+    def delete_domain(self, domain_id):
+        """Delete a domain."""
+        resp, body = self.delete('domains/%s' % str(domain_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_domains(self):
+        """List Domains."""
+        resp, body = self.get('domains')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['domains'])
+
+    def update_domain(self, domain_id, **kwargs):
+        """Updates a domain."""
+        body = self.get_domain(domain_id)
+        description = kwargs.get('description', body['description'])
+        en = kwargs.get('enabled', body['enabled'])
+        name = kwargs.get('name', body['name'])
+        post_body = {
+            'description': description,
+            'enabled': en,
+            'name': name
+        }
+        post_body = json.dumps({'domain': post_body})
+        resp, body = self.patch('domains/%s' % domain_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['domain'])
+
+    def get_domain(self, domain_id):
+        """Get Domain details."""
+        resp, body = self.get('domains/%s' % domain_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['domain'])
+
+    def get_token(self, resp_token):
+        """Get token details."""
+        headers = {'X-Subject-Token': resp_token}
+        resp, body = self.get("auth/tokens", headers=headers)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['token'])
+
+    def delete_token(self, resp_token):
+        """Deletes token."""
+        headers = {'X-Subject-Token': resp_token}
+        resp, body = self.delete("auth/tokens", headers=headers)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_group(self, name, **kwargs):
+        """Creates a group."""
+        description = kwargs.get('description', None)
+        domain_id = kwargs.get('domain_id', 'default')
+        project_id = kwargs.get('project_id', None)
+        post_body = {
+            'description': description,
+            'domain_id': domain_id,
+            'project_id': project_id,
+            'name': name
+        }
+        post_body = json.dumps({'group': post_body})
+        resp, body = self.post('groups', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['group'])
+
+    def get_group(self, group_id):
+        """Get group details."""
+        resp, body = self.get('groups/%s' % group_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['group'])
+
+    def list_groups(self):
+        """Lists the groups."""
+        resp, body = self.get('groups')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['groups'])
+
+    def update_group(self, group_id, **kwargs):
+        """Updates a group."""
+        body = self.get_group(group_id)
+        name = kwargs.get('name', body['name'])
+        description = kwargs.get('description', body['description'])
+        post_body = {
+            'name': name,
+            'description': description
+        }
+        post_body = json.dumps({'group': post_body})
+        resp, body = self.patch('groups/%s' % group_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['group'])
+
+    def delete_group(self, group_id):
+        """Delete a group."""
+        resp, body = self.delete('groups/%s' % str(group_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def add_group_user(self, group_id, user_id):
+        """Add user into group."""
+        resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
+                              None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_group_users(self, group_id):
+        """List users in group."""
+        resp, body = self.get('groups/%s/users' % group_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['users'])
+
+    def list_user_groups(self, user_id):
+        """Lists groups which a user belongs to."""
+        resp, body = self.get('users/%s/groups' % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['groups'])
+
+    def delete_group_user(self, group_id, user_id):
+        """Delete user in group."""
+        resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def assign_user_role_on_project(self, project_id, user_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/users/%s/roles/%s' %
+                              (project_id, user_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def assign_user_role_on_domain(self, domain_id, user_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/users/%s/roles/%s' %
+                              (domain_id, user_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_user_roles_on_project(self, project_id, user_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/users/%s/roles' %
+                              (project_id, user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def list_user_roles_on_domain(self, domain_id, user_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/users/%s/roles' %
+                              (domain_id, user_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def revoke_role_from_user_on_project(self, project_id, user_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/users/%s/roles/%s' %
+                                 (project_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def revoke_role_from_user_on_domain(self, domain_id, user_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/users/%s/roles/%s' %
+                                 (domain_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def assign_group_role_on_project(self, project_id, group_id, role_id):
+        """Add roles to a user on a project."""
+        resp, body = self.put('projects/%s/groups/%s/roles/%s' %
+                              (project_id, group_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def assign_group_role_on_domain(self, domain_id, group_id, role_id):
+        """Add roles to a user on a domain."""
+        resp, body = self.put('domains/%s/groups/%s/roles/%s' %
+                              (domain_id, group_id, role_id), None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_group_roles_on_project(self, project_id, group_id):
+        """list roles of a user on a project."""
+        resp, body = self.get('projects/%s/groups/%s/roles' %
+                              (project_id, group_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def list_group_roles_on_domain(self, domain_id, group_id):
+        """list roles of a user on a domain."""
+        resp, body = self.get('domains/%s/groups/%s/roles' %
+                              (domain_id, group_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def revoke_role_from_group_on_project(self, project_id, group_id, role_id):
+        """Delete role of a user on a project."""
+        resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
+                                 (project_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def revoke_role_from_group_on_domain(self, domain_id, group_id, role_id):
+        """Delete role of a user on a domain."""
+        resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
+                                 (domain_id, group_id, role_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_trust(self, trustor_user_id, trustee_user_id, project_id,
+                     role_names, impersonation, expires_at):
+        """Creates a trust."""
+        roles = [{'name': n} for n in role_names]
+        post_body = {
+            'trustor_user_id': trustor_user_id,
+            'trustee_user_id': trustee_user_id,
+            'project_id': project_id,
+            'impersonation': impersonation,
+            'roles': roles,
+            'expires_at': expires_at
+        }
+        post_body = json.dumps({'trust': post_body})
+        resp, body = self.post('OS-TRUST/trusts', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['trust'])
+
+    def delete_trust(self, trust_id):
+        """Deletes a trust."""
+        resp, body = self.delete("OS-TRUST/trusts/%s" % trust_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def get_trusts(self, trustor_user_id=None, trustee_user_id=None):
+        """GET trusts."""
+        if trustor_user_id:
+            resp, body = self.get("OS-TRUST/trusts?trustor_user_id=%s"
+                                  % trustor_user_id)
+        elif trustee_user_id:
+            resp, body = self.get("OS-TRUST/trusts?trustee_user_id=%s"
+                                  % trustee_user_id)
+        else:
+            resp, body = self.get("OS-TRUST/trusts")
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['trusts'])
+
+    def get_trust(self, trust_id):
+        """GET trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s" % trust_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['trust'])
+
+    def get_trust_roles(self, trust_id):
+        """GET roles delegated by a trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s/roles" % trust_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['roles'])
+
+    def get_trust_role(self, trust_id, role_id):
+        """GET role delegated by a trust."""
+        resp, body = self.get("OS-TRUST/trusts/%s/roles/%s"
+                              % (trust_id, role_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['role'])
+
+    def check_trust_role(self, trust_id, role_id):
+        """HEAD Check if role is delegated by a trust."""
+        resp, body = self.head("OS-TRUST/trusts/%s/roles/%s"
+                               % (trust_id, role_id))
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/neutron/tests/tempest/services/identity/v3/json/policy_client.py b/neutron/tests/tempest/services/identity/v3/json/policy_client.py
new file mode 100644
index 0000000..2e44185
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/policy_client.py
@@ -0,0 +1,69 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from neutron.tests.tempest.common import service_client
+
+
+class PolicyClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_policy(self, blob, type):
+        """Creates a Policy."""
+        post_body = {
+            "blob": blob,
+            "type": type
+        }
+        post_body = json.dumps({'policy': post_body})
+        resp, body = self.post('policies', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['policy'])
+
+    def list_policies(self):
+        """Lists the policies."""
+        resp, body = self.get('policies')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['policies'])
+
+    def get_policy(self, policy_id):
+        """Lists out the given policy."""
+        url = 'policies/%s' % policy_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['policy'])
+
+    def update_policy(self, policy_id, **kwargs):
+        """Updates a policy."""
+        type = kwargs.get('type')
+        post_body = {
+            'type': type
+        }
+        post_body = json.dumps({'policy': post_body})
+        url = 'policies/%s' % policy_id
+        resp, body = self.patch(url, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['policy'])
+
+    def delete_policy(self, policy_id):
+        """Deletes the policy."""
+        url = "policies/%s" % policy_id
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/neutron/tests/tempest/services/identity/v3/json/region_client.py b/neutron/tests/tempest/services/identity/v3/json/region_client.py
new file mode 100644
index 0000000..e173aa5
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/region_client.py
@@ -0,0 +1,77 @@
+# Copyright 2014 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 json
+import urllib
+
+from neutron.tests.tempest.common import service_client
+
+
+class RegionClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_region(self, description, **kwargs):
+        """Create region."""
+        req_body = {
+            'description': description,
+        }
+        if kwargs.get('parent_region_id'):
+            req_body['parent_region_id'] = kwargs.get('parent_region_id')
+        req_body = json.dumps({'region': req_body})
+        if kwargs.get('unique_region_id'):
+            resp, body = self.put(
+                'regions/%s' % kwargs.get('unique_region_id'), req_body)
+        else:
+            resp, body = self.post('regions', req_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['region'])
+
+    def update_region(self, region_id, **kwargs):
+        """Updates a region."""
+        post_body = {}
+        if 'description' in kwargs:
+            post_body['description'] = kwargs.get('description')
+        if 'parent_region_id' in kwargs:
+            post_body['parent_region_id'] = kwargs.get('parent_region_id')
+        post_body = json.dumps({'region': post_body})
+        resp, body = self.patch('regions/%s' % region_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['region'])
+
+    def get_region(self, region_id):
+        """Get region."""
+        url = 'regions/%s' % region_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['region'])
+
+    def list_regions(self, params=None):
+        """List regions."""
+        url = 'regions'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['regions'])
+
+    def delete_region(self, region_id):
+        """Delete region."""
+        resp, body = self.delete('regions/%s' % region_id)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/neutron/tests/tempest/services/identity/v3/json/service_client.py b/neutron/tests/tempest/services/identity/v3/json/service_client.py
new file mode 100644
index 0000000..529693e
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/service_client.py
@@ -0,0 +1,73 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+
+from neutron.tests.tempest.common import service_client
+
+
+class ServiceClientJSON(service_client.ServiceClient):
+    api_version = "v3"
+
+    def update_service(self, service_id, **kwargs):
+        """Updates a service."""
+        body = self.get_service(service_id)
+        name = kwargs.get('name', body['name'])
+        type = kwargs.get('type', body['type'])
+        desc = kwargs.get('description', body['description'])
+        patch_body = {
+            'description': desc,
+            'type': type,
+            'name': name
+        }
+        patch_body = json.dumps({'service': patch_body})
+        resp, body = self.patch('services/%s' % service_id, patch_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['service'])
+
+    def get_service(self, service_id):
+        """Get Service."""
+        url = 'services/%s' % service_id
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['service'])
+
+    def create_service(self, serv_type, name=None, description=None,
+                       enabled=True):
+        body_dict = {
+            'name': name,
+            'type': serv_type,
+            'enabled': enabled,
+            'description': description,
+        }
+        body = json.dumps({'service': body_dict})
+        resp, body = self.post("services", body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body["service"])
+
+    def delete_service(self, serv_id):
+        url = "services/" + serv_id
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_services(self):
+        resp, body = self.get('services')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBodyList(resp, body['services'])
diff --git a/neutron/tests/tempest/services/identity/v3/json/token_client.py b/neutron/tests/tempest/services/identity/v3/json/token_client.py
new file mode 100644
index 0000000..61cbf60
--- /dev/null
+++ b/neutron/tests/tempest/services/identity/v3/json/token_client.py
@@ -0,0 +1,137 @@
+# 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.
+
+import json
+from tempest_lib.common import rest_client
+from tempest_lib import exceptions as lib_exc
+
+from neutron.tests.tempest.common import service_client
+from neutron.tests.tempest import exceptions
+
+
+class V3TokenClientJSON(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(V3TokenClientJSON, self).__init__(
+            None, None, None, disable_ssl_certificate_validation=dscv,
+            ca_certs=ca_certs, trace_requests=trace_requests)
+        if not auth_url:
+            raise exceptions.InvalidConfiguration('you must specify a v3 uri '
+                                                  'if using the v3 identity '
+                                                  'api')
+        if 'auth/tokens' not in auth_url:
+            auth_url = auth_url.rstrip('/') + '/auth/tokens'
+
+        self.auth_url = auth_url
+
+    def auth(self, user=None, password=None, project=None, user_type='id',
+             user_domain=None, project_domain=None, token=None):
+        """
+        :param user: user id or name, as specified in user_type
+        :param user_domain: the user domain
+        :param project_domain: the project domain
+        :param token: a token to re-scope.
+
+        Accepts different combinations of credentials. Restrictions:
+        - project and domain are only name (no id)
+        Sample sample valid combinations:
+        - token
+        - token, project, project_domain
+        - user_id, password
+        - username, password, user_domain
+        - username, password, project, user_domain, project_domain
+        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 and password:
+            id_obj['methods'].append('password')
+            id_obj['password'] = {
+                'user': {
+                    'password': password,
+                }
+            }
+            if user_type == 'id':
+                id_obj['password']['user']['id'] = user
+            else:
+                id_obj['password']['user']['name'] = user
+            if user_domain is not None:
+                _domain = dict(name=user_domain)
+                id_obj['password']['user']['domain'] = _domain
+        if project is not None:
+            _domain = dict(name=project_domain)
+            _project = dict(name=project, domain=_domain)
+            scope = dict(project=_project)
+            creds['auth']['scope'] = scope
+
+        body = json.dumps(creds)
+        resp, body = self.post(self.auth_url, body=body)
+        self.expected_success(201, resp.status)
+        return service_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)
+
+        if resp.status in [401, 403]:
+            resp_body = json.loads(resp_body)
+            raise lib_exc.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, user, password, project=None, project_domain='Default',
+                  user_domain='Default', auth_data=False):
+        """
+        :param user: username
+        Returns (token id, token data) for supplied credentials
+        """
+        body = self.auth(user, password, project, user_type='name',
+                         user_domain=user_domain,
+                         project_domain=project_domain)
+
+        token = body.response.get('x-subject-token')
+        if auth_data:
+            return token, body['token']
+        else:
+            return token
diff --git a/neutron/tests/tempest/services/network/__init__.py b/neutron/tests/tempest/services/network/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/network/__init__.py
diff --git a/neutron/tests/tempest/services/network/json/__init__.py b/neutron/tests/tempest/services/network/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/neutron/tests/tempest/services/network/json/__init__.py
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
new file mode 100644
index 0000000..0e37c3d
--- /dev/null
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -0,0 +1,581 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import json
+import time
+import urllib
+
+from tempest_lib import exceptions as lib_exc
+
+from neutron.tests.tempest.common import service_client
+from neutron.tests.tempest.common.utils import misc
+from neutron.tests.tempest import exceptions
+
+
+class NetworkClientJSON(service_client.ServiceClient):
+
+    """
+    Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
+    V1 API has been removed from the code base.
+
+    Implements create, delete, update, list and show for the basic Neutron
+    abstractions (networks, sub-networks, routers, ports and floating IP):
+
+    Implements add/remove interface to router using subnet ID / port ID
+
+    It also implements list, show, update and reset for OpenStack Networking
+    quotas
+    """
+
+    version = '2.0'
+    uri_prefix = "v2.0"
+
+    def get_uri(self, plural_name):
+        # get service prefix from resource name
+
+        # The following list represents resource names that do not require
+        # changing underscore to a hyphen
+        hyphen_exceptions = ["health_monitors", "firewall_rules",
+                             "firewall_policies"]
+        # the following map is used to construct proper URI
+        # for the given neutron resource
+        service_resource_prefix_map = {
+            'networks': '',
+            'subnets': '',
+            'ports': '',
+            'pools': 'lb',
+            'vips': 'lb',
+            'health_monitors': 'lb',
+            'members': 'lb',
+            'ipsecpolicies': 'vpn',
+            'vpnservices': 'vpn',
+            'ikepolicies': 'vpn',
+            'ipsec-site-connections': 'vpn',
+            'metering_labels': 'metering',
+            'metering_label_rules': 'metering',
+            'firewall_rules': 'fw',
+            'firewall_policies': 'fw',
+            'firewalls': 'fw'
+        }
+        service_prefix = service_resource_prefix_map.get(
+            plural_name)
+        if plural_name not in hyphen_exceptions:
+            plural_name = plural_name.replace("_", "-")
+        if service_prefix:
+            uri = '%s/%s/%s' % (self.uri_prefix, service_prefix,
+                                plural_name)
+        else:
+            uri = '%s/%s' % (self.uri_prefix, plural_name)
+        return uri
+
+    def pluralize(self, resource_name):
+        # get plural from map or just add 's'
+
+        # map from resource name to a plural name
+        # needed only for those which can't be constructed as name + 's'
+        resource_plural_map = {
+            'security_groups': 'security_groups',
+            'security_group_rules': 'security_group_rules',
+            'ipsecpolicy': 'ipsecpolicies',
+            'ikepolicy': 'ikepolicies',
+            'ipsec_site_connection': 'ipsec-site-connections',
+            'quotas': 'quotas',
+            'firewall_policy': 'firewall_policies'
+        }
+        return resource_plural_map.get(resource_name, resource_name + 's')
+
+    def _lister(self, plural_name):
+        def _list(**filters):
+            uri = self.get_uri(plural_name)
+            if filters:
+                uri += '?' + urllib.urlencode(filters, doseq=1)
+            resp, body = self.get(uri)
+            result = {plural_name: self.deserialize_list(body)}
+            self.expected_success(200, resp.status)
+            return service_client.ResponseBody(resp, result)
+
+        return _list
+
+    def _deleter(self, resource_name):
+        def _delete(resource_id):
+            plural = self.pluralize(resource_name)
+            uri = '%s/%s' % (self.get_uri(plural), resource_id)
+            resp, body = self.delete(uri)
+            self.expected_success(204, resp.status)
+            return service_client.ResponseBody(resp, body)
+
+        return _delete
+
+    def _shower(self, resource_name):
+        def _show(resource_id, **fields):
+            # fields is a dict which key is 'fields' and value is a
+            # list of field's name. An example:
+            # {'fields': ['id', 'name']}
+            plural = self.pluralize(resource_name)
+            uri = '%s/%s' % (self.get_uri(plural), resource_id)
+            if fields:
+                uri += '?' + urllib.urlencode(fields, doseq=1)
+            resp, body = self.get(uri)
+            body = self.deserialize_single(body)
+            self.expected_success(200, resp.status)
+            return service_client.ResponseBody(resp, body)
+
+        return _show
+
+    def _creater(self, resource_name):
+        def _create(**kwargs):
+            plural = self.pluralize(resource_name)
+            uri = self.get_uri(plural)
+            post_data = self.serialize({resource_name: kwargs})
+            resp, body = self.post(uri, post_data)
+            body = self.deserialize_single(body)
+            self.expected_success(201, resp.status)
+            return service_client.ResponseBody(resp, body)
+
+        return _create
+
+    def _updater(self, resource_name):
+        def _update(res_id, **kwargs):
+            plural = self.pluralize(resource_name)
+            uri = '%s/%s' % (self.get_uri(plural), res_id)
+            post_data = self.serialize({resource_name: kwargs})
+            resp, body = self.put(uri, post_data)
+            body = self.deserialize_single(body)
+            self.expected_success(200, resp.status)
+            return service_client.ResponseBody(resp, body)
+
+        return _update
+
+    def __getattr__(self, name):
+        method_prefixes = ["list_", "delete_", "show_", "create_", "update_"]
+        method_functors = [self._lister,
+                           self._deleter,
+                           self._shower,
+                           self._creater,
+                           self._updater]
+        for index, prefix in enumerate(method_prefixes):
+            prefix_len = len(prefix)
+            if name[:prefix_len] == prefix:
+                return method_functors[index](name[prefix_len:])
+        raise AttributeError(name)
+
+    # Common methods that are hard to automate
+    def create_bulk_network(self, names):
+        network_list = [{'name': name} for name in names]
+        post_data = {'networks': network_list}
+        body = self.serialize_list(post_data, "networks", "network")
+        uri = self.get_uri("networks")
+        resp, body = self.post(uri, body)
+        body = {'networks': self.deserialize_list(body)}
+        self.expected_success(201, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_bulk_subnet(self, subnet_list):
+        post_data = {'subnets': subnet_list}
+        body = self.serialize_list(post_data, 'subnets', 'subnet')
+        uri = self.get_uri('subnets')
+        resp, body = self.post(uri, body)
+        body = {'subnets': self.deserialize_list(body)}
+        self.expected_success(201, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_bulk_port(self, port_list):
+        post_data = {'ports': port_list}
+        body = self.serialize_list(post_data, 'ports', 'port')
+        uri = self.get_uri('ports')
+        resp, body = self.post(uri, body)
+        body = {'ports': self.deserialize_list(body)}
+        self.expected_success(201, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def wait_for_resource_deletion(self, resource_type, id):
+        """Waits for a resource to be deleted."""
+        start_time = int(time.time())
+        while True:
+            if self.is_resource_deleted(resource_type, id):
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                raise exceptions.TimeoutException
+            time.sleep(self.build_interval)
+
+    def is_resource_deleted(self, resource_type, id):
+        method = 'show_' + resource_type
+        try:
+            getattr(self, method)(id)
+        except AttributeError:
+            raise Exception("Unknown resource type %s " % resource_type)
+        except lib_exc.NotFound:
+            return True
+        return False
+
+    def wait_for_resource_status(self, fetch, status, interval=None,
+                                 timeout=None):
+        """
+        @summary: Waits for a network resource to reach a status
+        @param fetch: the callable to be used to query the resource status
+        @type fecth: callable that takes no parameters and returns the resource
+        @param status: the status that the resource has to reach
+        @type status: String
+        @param interval: the number of seconds to wait between each status
+          query
+        @type interval: Integer
+        @param timeout: the maximum number of seconds to wait for the resource
+          to reach the desired status
+        @type timeout: Integer
+        """
+        if not interval:
+            interval = self.build_interval
+        if not timeout:
+            timeout = self.build_timeout
+        start_time = time.time()
+
+        while time.time() - start_time <= timeout:
+            resource = fetch()
+            if resource['status'] == status:
+                return
+            time.sleep(interval)
+
+        # At this point, the wait has timed out
+        message = 'Resource %s' % (str(resource))
+        message += ' failed to reach status %s' % status
+        message += ' (current: %s)' % resource['status']
+        message += ' within the required time %s' % timeout
+        caller = misc.find_test_caller()
+        if caller:
+            message = '(%s) %s' % (caller, message)
+        raise exceptions.TimeoutException(message)
+
+    def deserialize_single(self, body):
+        return json.loads(body)
+
+    def deserialize_list(self, body):
+        res = json.loads(body)
+        # expecting response in form
+        # {'resources': [ res1, res2] } => when pagination disabled
+        # {'resources': [..], 'resources_links': {}} => if pagination enabled
+        for k in res.keys():
+            if k.endswith("_links"):
+                continue
+            return res[k]
+
+    def serialize(self, data):
+        return json.dumps(data)
+
+    def serialize_list(self, data, root=None, item=None):
+        return self.serialize(data)
+
+    def update_quotas(self, tenant_id, **kwargs):
+        put_body = {'quota': kwargs}
+        body = json.dumps(put_body)
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.put(uri, body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body['quota'])
+
+    def reset_quotas(self, tenant_id):
+        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_router(self, name, admin_state_up=True, **kwargs):
+        post_body = {'router': kwargs}
+        post_body['router']['name'] = name
+        post_body['router']['admin_state_up'] = admin_state_up
+        body = json.dumps(post_body)
+        uri = '%s/routers' % (self.uri_prefix)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def _update_router(self, router_id, set_enable_snat, **kwargs):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        update_body = {}
+        update_body['name'] = kwargs.get('name', body['router']['name'])
+        update_body['admin_state_up'] = kwargs.get(
+            'admin_state_up', body['router']['admin_state_up'])
+        cur_gw_info = body['router']['external_gateway_info']
+        if cur_gw_info:
+            # TODO(kevinbenton): setting the external gateway info is not
+            # allowed for a regular tenant. If the ability to update is also
+            # merged, a test case for this will need to be added similar to
+            # the SNAT case.
+            cur_gw_info.pop('external_fixed_ips', None)
+            if not set_enable_snat:
+                cur_gw_info.pop('enable_snat', None)
+        update_body['external_gateway_info'] = kwargs.get(
+            'external_gateway_info', body['router']['external_gateway_info'])
+        if 'distributed' in kwargs:
+            update_body['distributed'] = kwargs['distributed']
+        update_body = dict(router=update_body)
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_router(self, router_id, **kwargs):
+        """Update a router leaving enable_snat to its default value."""
+        # If external_gateway_info contains enable_snat the request will fail
+        # with 404 unless executed with admin client, and therefore we instruct
+        # _update_router to not set this attribute
+        # NOTE(salv-orlando): The above applies as long as Neutron's default
+        # policy is to restrict enable_snat usage to admins only.
+        return self._update_router(router_id, set_enable_snat=False, **kwargs)
+
+    def update_router_with_snat_gw_info(self, router_id, **kwargs):
+        """Update a router passing also the enable_snat attribute.
+
+        This method must be execute with admin credentials, otherwise the API
+        call will return a 404 error.
+        """
+        return self._update_router(router_id, set_enable_snat=True, **kwargs)
+
+    def add_router_interface_with_subnet_id(self, router_id, subnet_id):
+        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
+                                                      router_id)
+        update_body = {"subnet_id": subnet_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def add_router_interface_with_port_id(self, router_id, port_id):
+        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
+                                                      router_id)
+        update_body = {"port_id": port_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
+        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
+                                                         router_id)
+        update_body = {"subnet_id": subnet_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def remove_router_interface_with_port_id(self, router_id, port_id):
+        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
+                                                         router_id)
+        update_body = {"port_id": port_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def associate_health_monitor_with_pool(self, health_monitor_id,
+                                           pool_id):
+        post_body = {
+            "health_monitor": {
+                "id": health_monitor_id,
+            }
+        }
+        body = json.dumps(post_body)
+        uri = '%s/lb/pools/%s/health_monitors' % (self.uri_prefix,
+                                                  pool_id)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def disassociate_health_monitor_with_pool(self, health_monitor_id,
+                                              pool_id):
+        uri = '%s/lb/pools/%s/health_monitors/%s' % (self.uri_prefix, pool_id,
+                                                     health_monitor_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_router_interfaces(self, uuid):
+        uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_agent(self, agent_id, agent_info):
+        """
+        :param agent_info: Agent update information.
+        E.g {"admin_state_up": True}
+        """
+        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        agent = {"agent": agent_info}
+        body = json.dumps(agent)
+        resp, body = self.put(uri, body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_pools_hosted_by_one_lbaas_agent(self, agent_id):
+        uri = '%s/agents/%s/loadbalancer-pools' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def show_lbaas_agent_hosting_pool(self, pool_id):
+        uri = ('%s/lb/pools/%s/loadbalancer-agent' %
+               (self.uri_prefix, pool_id))
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_routers_on_l3_agent(self, agent_id):
+        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_l3_agents_hosting_router(self, router_id):
+        uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def add_router_to_l3_agent(self, agent_id, router_id):
+        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+        post_body = {"router_id": router_id}
+        body = json.dumps(post_body)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def remove_router_from_l3_agent(self, agent_id, router_id):
+        uri = '%s/agents/%s/l3-routers/%s' % (
+            self.uri_prefix, agent_id, router_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_dhcp_agent_hosting_network(self, network_id):
+        uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
+        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def remove_network_from_dhcp_agent(self, agent_id, network_id):
+        uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
+                                                 network_id)
+        resp, body = self.delete(uri)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def create_ikepolicy(self, name, **kwargs):
+        post_body = {
+            "ikepolicy": {
+                "name": name,
+            }
+        }
+        for key, val in kwargs.items():
+            post_body['ikepolicy'][key] = val
+        body = json.dumps(post_body)
+        uri = '%s/vpn/ikepolicies' % (self.uri_prefix)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_extra_routes(self, router_id, nexthop, destination):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        put_body = {
+            'router': {
+                'routes': [{'nexthop': nexthop,
+                            "destination": destination}]
+            }
+        }
+        body = json.dumps(put_body)
+        resp, body = self.put(uri, body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_extra_routes(self, router_id):
+        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+        null_routes = None
+        put_body = {
+            'router': {
+                'routes': null_routes
+            }
+        }
+        body = json.dumps(put_body)
+        resp, body = self.put(uri, body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_lb_pool_stats(self, pool_id):
+        uri = '%s/lb/pools/%s/stats' % (self.uri_prefix, pool_id)
+        resp, body = self.get(uri)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def add_dhcp_agent_to_network(self, agent_id, network_id):
+        post_body = {'network_id': network_id}
+        body = json.dumps(post_body)
+        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
+        resp, body = self.post(uri, body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def insert_firewall_rule_in_policy(self, firewall_policy_id,
+                                       firewall_rule_id, insert_after="",
+                                       insert_before=""):
+        uri = '%s/fw/firewall_policies/%s/insert_rule' % (self.uri_prefix,
+                                                          firewall_policy_id)
+        body = {
+            "firewall_rule_id": firewall_rule_id,
+            "insert_after": insert_after,
+            "insert_before": insert_before
+        }
+        body = json.dumps(body)
+        resp, body = self.put(uri, body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def remove_firewall_rule_from_policy(self, firewall_policy_id,
+                                         firewall_rule_id):
+        uri = '%s/fw/firewall_policies/%s/remove_rule' % (self.uri_prefix,
+                                                          firewall_policy_id)
+        update_body = {"firewall_rule_id": firewall_rule_id}
+        update_body = json.dumps(update_body)
+        resp, body = self.put(uri, update_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
diff --git a/neutron/tests/tempest/services/network/resources.py b/neutron/tests/tempest/services/network/resources.py
new file mode 100644
index 0000000..4d45515
--- /dev/null
+++ b/neutron/tests/tempest/services/network/resources.py
@@ -0,0 +1,189 @@
+# 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 abc
+
+import six
+
+
+class AttributeDict(dict):
+
+    """
+    Provide attribute access (dict.key) to dictionary values.
+    """
+
+    def __getattr__(self, name):
+        """Allow attribute access for all keys in the dict."""
+        if name in self:
+            return self[name]
+        return super(AttributeDict, self).__getattribute__(name)
+
+
+@six.add_metaclass(abc.ABCMeta)
+class DeletableResource(AttributeDict):
+
+    """
+    Support deletion of neutron resources (networks, subnets) via a
+    delete() method, as is supported by keystone and nova resources.
+    """
+
+    def __init__(self, *args, **kwargs):
+        self.client = kwargs.pop('client', None)
+        super(DeletableResource, self).__init__(*args, **kwargs)
+
+    def __str__(self):
+        return '<%s id="%s" name="%s">' % (self.__class__.__name__,
+                                           self.id, self.name)
+
+    @abc.abstractmethod
+    def delete(self):
+        return
+
+    @abc.abstractmethod
+    def refresh(self):
+        return
+
+    def __hash__(self):
+        return hash(self.id)
+
+    def wait_for_status(self, status):
+        if not hasattr(self, 'status'):
+            return
+
+        def helper_get():
+            self.refresh()
+            return self
+
+        return self.client.wait_for_resource_status(helper_get, status)
+
+
+class DeletableNetwork(DeletableResource):
+
+    def delete(self):
+        self.client.delete_network(self.id)
+
+
+class DeletableSubnet(DeletableResource):
+
+    def __init__(self, *args, **kwargs):
+        super(DeletableSubnet, self).__init__(*args, **kwargs)
+        self._router_ids = set()
+
+    def update(self, *args, **kwargs):
+        result = self.client.update_subnet(self.id,
+                                           *args,
+                                           **kwargs)
+        return super(DeletableSubnet, self).update(**result['subnet'])
+
+    def add_to_router(self, router_id):
+        self._router_ids.add(router_id)
+        self.client.add_router_interface_with_subnet_id(router_id,
+                                                        subnet_id=self.id)
+
+    def delete(self):
+        for router_id in self._router_ids.copy():
+            self.client.remove_router_interface_with_subnet_id(
+                router_id,
+                subnet_id=self.id)
+            self._router_ids.remove(router_id)
+        self.client.delete_subnet(self.id)
+
+
+class DeletableRouter(DeletableResource):
+
+    def set_gateway(self, network_id):
+        return self.update(external_gateway_info=dict(network_id=network_id))
+
+    def unset_gateway(self):
+        return self.update(external_gateway_info=dict())
+
+    def update(self, *args, **kwargs):
+        result = self.client.update_router(self.id,
+                                           *args,
+                                           **kwargs)
+        return super(DeletableRouter, self).update(**result['router'])
+
+    def delete(self):
+        self.unset_gateway()
+        self.client.delete_router(self.id)
+
+
+class DeletableFloatingIp(DeletableResource):
+
+    def refresh(self, *args, **kwargs):
+        result = self.client.show_floatingip(self.id,
+                                             *args,
+                                             **kwargs)
+        super(DeletableFloatingIp, self).update(**result['floatingip'])
+
+    def update(self, *args, **kwargs):
+        result = self.client.update_floatingip(self.id,
+                                               *args,
+                                               **kwargs)
+        super(DeletableFloatingIp, self).update(**result['floatingip'])
+
+    def __repr__(self):
+        return '<%s addr="%s">' % (self.__class__.__name__,
+                                   self.floating_ip_address)
+
+    def __str__(self):
+        return '<"FloatingIP" addr="%s" id="%s">' % (self.floating_ip_address,
+                                                     self.id)
+
+    def delete(self):
+        self.client.delete_floatingip(self.id)
+
+
+class DeletablePort(DeletableResource):
+
+    def delete(self):
+        self.client.delete_port(self.id)
+
+
+class DeletableSecurityGroup(DeletableResource):
+
+    def delete(self):
+        self.client.delete_security_group(self.id)
+
+
+class DeletableSecurityGroupRule(DeletableResource):
+
+    def __repr__(self):
+        return '<%s id="%s">' % (self.__class__.__name__, self.id)
+
+    def delete(self):
+        self.client.delete_security_group_rule(self.id)
+
+
+class DeletablePool(DeletableResource):
+
+    def delete(self):
+        self.client.delete_pool(self.id)
+
+
+class DeletableMember(DeletableResource):
+
+    def delete(self):
+        self.client.delete_member(self.id)
+
+
+class DeletableVip(DeletableResource):
+
+    def delete(self):
+        self.client.delete_vip(self.id)
+
+    def refresh(self):
+        result = self.client.show_vip(self.id)
+        super(DeletableVip, self).update(**result['vip'])