Merge "Change hostname_equals_servername to get_hostname"
diff --git a/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml b/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
new file mode 100644
index 0000000..07e3151
--- /dev/null
+++ b/releasenotes/notes/add-network-versions-client-d90e8334e1443f5c.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Adds a network version client for querying
+ Neutron's API version discovery URL ("GET /").
diff --git a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
index faae7d0..90a5056 100644
--- a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
+++ b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
@@ -6,6 +6,7 @@
so the other projects can use these modules as stable libraries
without any maintenance changes.
+ * image_members_client(v1)
* image_members_client(v2)
* images_client(v2)
* namespaces_client(v2)
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index b18789e..357c907 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -109,9 +109,12 @@
@test.attr(type=['negative'])
@test.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
def test_list_servers_by_limits_greater_than_actual_count(self):
+ # Gather the complete list of servers in the project for reference
+ full_list = self.client.list_servers()['servers']
# List servers by specifying a greater value for limit
- body = self.client.list_servers(limit=100)
- self.assertEqual(len(self.existing_fixtures), len(body['servers']))
+ limit = len(full_list) + 100
+ body = self.client.list_servers(limit=limit)
+ self.assertEqual(len(full_list), len(body['servers']))
@test.attr(type=['negative'])
@test.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index 4493c8e..651a316 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -28,7 +28,8 @@
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
cls.service_data = cls.services_client.create_service(
- s_name, s_type, description=s_description)['OS-KSADM:service']
+ name=s_name, type=s_type,
+ description=s_description)['OS-KSADM:service']
cls.service_id = cls.service_data['id']
cls.service_ids.append(cls.service_id)
# Create endpoints so as to use for LIST and GET test cases
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 5847129..0924619 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -84,28 +84,30 @@
def test_assign_user_role(self):
"""Assign a role to a user on a tenant."""
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- roles = self.roles_client.list_user_roles(tenant['id'],
- user['id'])['roles']
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ roles = self.roles_client.list_user_roles_on_project(
+ tenant['id'], user['id'])['roles']
self.assert_role_in_role_list(role, roles)
@test.idempotent_id('f0b9292c-d3ba-4082-aa6c-440489beef69')
def test_remove_user_role(self):
"""Remove a role assigned to a user on a tenant."""
(user, tenant, role) = self._get_role_params()
- user_role = self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])['role']
- self.roles_client.delete_user_role(tenant['id'], user['id'],
- user_role['id'])
+ user_role = self.roles_client.create_user_role_on_project(
+ tenant['id'], user['id'], role['id'])['role']
+ self.roles_client.delete_role_from_user_on_project(tenant['id'],
+ user['id'],
+ user_role['id'])
@test.idempotent_id('262e1e3e-ed71-4edd-a0e5-d64e83d66d05')
def test_list_user_roles(self):
"""List roles assigned to a user on tenant."""
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- roles = self.roles_client.list_user_roles(tenant['id'],
- user['id'])['roles']
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ roles = self.roles_client.list_user_roles_on_project(
+ tenant['id'], user['id'])['roles']
self.assert_role_in_role_list(role, roles)
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index fd56285..770bb14 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -123,9 +123,10 @@
# Non-administrator user should not be authorized to
# assign a role to user
(user, tenant, role) = self._get_role_params()
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.assign_user_role,
- tenant['id'], user['id'], role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.create_user_role_on_project,
+ tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@test.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
@@ -134,9 +135,10 @@
(user, tenant, role) = self._get_role_params()
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
- self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.assign_user_role, tenant['id'],
- user['id'], role['id'])
+ self.assertRaises(
+ lib_exc.Unauthorized,
+ self.roles_client.create_user_role_on_project, tenant['id'],
+ user['id'], role['id'])
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -145,7 +147,8 @@
# Attempt to assign a non existent role to user should fail
(user, tenant, role) = self._get_role_params()
non_existent_role = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], non_existent_role)
@test.attr(type=['negative'])
@@ -154,7 +157,8 @@
# Attempt to assign a role on a non existent tenant should fail
(user, tenant, role) = self._get_role_params()
non_existent_tenant = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.assign_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.create_user_role_on_project,
non_existent_tenant, user['id'], role['id'])
@test.attr(type=['negative'])
@@ -162,9 +166,11 @@
def test_assign_duplicate_user_role(self):
# Duplicate user role should not get assigned
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- self.assertRaises(lib_exc.Conflict, self.roles_client.assign_user_role,
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(lib_exc.Conflict,
+ self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@@ -173,26 +179,27 @@
# Non-administrator user should not be authorized to
# remove a user's role
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.delete_user_role,
- tenant['id'], user['id'], role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.delete_role_from_user_on_project,
+ tenant['id'], user['id'], role['id'])
@test.attr(type=['negative'])
@test.idempotent_id('cac81cf4-c1d2-47dc-90d3-f2b7eb572286')
def test_remove_user_role_request_without_token(self):
# Request to remove a user's role without a valid token
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.delete_user_role, tenant['id'],
- user['id'], role['id'])
+ self.roles_client.delete_role_from_user_on_project,
+ tenant['id'], user['id'], role['id'])
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -200,11 +207,12 @@
def test_remove_user_role_non_existent_role(self):
# Attempt to delete a non existent role from a user should fail
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
non_existent_role = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.delete_role_from_user_on_project,
tenant['id'], user['id'], non_existent_role)
@test.attr(type=['negative'])
@@ -212,11 +220,12 @@
def test_remove_user_role_non_existent_tenant(self):
# Attempt to remove a role from a non existent tenant should fail
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'],
- user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
non_existent_tenant = data_utils.rand_uuid_hex()
- self.assertRaises(lib_exc.NotFound, self.roles_client.delete_user_role,
+ self.assertRaises(lib_exc.NotFound,
+ self.roles_client.delete_role_from_user_on_project,
non_existent_tenant, user['id'], role['id'])
@test.attr(type=['negative'])
@@ -225,11 +234,13 @@
# Non-administrator user should not be authorized to list
# a user's roles
(user, tenant, role) = self._get_role_params()
- self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])
- self.assertRaises(lib_exc.Forbidden,
- self.non_admin_roles_client.list_user_roles,
- tenant['id'], user['id'])
+ self.roles_client.create_user_role_on_project(tenant['id'],
+ user['id'],
+ role['id'])
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.non_admin_roles_client.list_user_roles_on_project,
+ tenant['id'], user['id'])
@test.attr(type=['negative'])
@test.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
@@ -240,7 +251,8 @@
self.client.delete_token(token)
try:
self.assertRaises(lib_exc.Unauthorized,
- self.roles_client.list_user_roles, tenant['id'],
+ self.roles_client.list_user_roles_on_project,
+ tenant['id'],
user['id'])
finally:
self.client.auth_provider.clear_auth()
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index fe83759..6c9b564 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -35,10 +35,11 @@
# GET Service
# Creating a Service
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
+ s_type = data_utils.rand_name('type')
description = data_utils.rand_name('description')
service_data = self.services_client.create_service(
- name, type, description=description)['OS-KSADM:service']
+ name=name, type=s_type,
+ description=description)['OS-KSADM:service']
self.assertFalse(service_data['id'] is None)
self.addCleanup(self._del_service, service_data['id'])
# Verifying response body of create service
@@ -46,7 +47,7 @@
self.assertIn('name', service_data)
self.assertEqual(name, service_data['name'])
self.assertIn('type', service_data)
- self.assertEqual(type, service_data['type'])
+ self.assertEqual(s_type, service_data['type'])
self.assertIn('description', service_data)
self.assertEqual(description, service_data['description'])
# Get service
@@ -68,15 +69,15 @@
def test_create_service_without_description(self):
# Create a service only with name and type
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
- service = self.services_client.create_service(name,
- type)['OS-KSADM:service']
+ s_type = data_utils.rand_name('type')
+ service = self.services_client.create_service(
+ name=name, type=s_type)['OS-KSADM:service']
self.assertIn('id', service)
self.addCleanup(self._del_service, service['id'])
self.assertIn('name', service)
self.assertEqual(name, service['name'])
self.assertIn('type', service)
- self.assertEqual(type, service['type'])
+ self.assertEqual(s_type, service['type'])
@test.attr(type='smoke')
@test.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
@@ -85,10 +86,12 @@
services = []
for _ in moves.xrange(3):
name = data_utils.rand_name('service')
- type = data_utils.rand_name('type')
+ s_type = data_utils.rand_name('type')
description = data_utils.rand_name('description')
+
service = self.services_client.create_service(
- name, type, description=description)['OS-KSADM:service']
+ name=name, type=s_type,
+ description=description)['OS-KSADM:service']
services.append(service)
service_ids = map(lambda x: x['id'], services)
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 5169dae..d1e0217 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -43,7 +43,7 @@
def test_tenant_delete_by_unauthorized_user(self):
# Non-administrator user should not be able to delete a tenant
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.delete_tenant,
@@ -54,7 +54,7 @@
def test_tenant_delete_request_without_token(self):
# Request to delete a tenant without a valid token should fail
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
@@ -75,7 +75,7 @@
def test_tenant_create_duplicate(self):
# Tenant names should be unique
tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(tenant_name)['tenant']
+ body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
tenant = body
self.data.tenants.append(tenant)
tenant1_id = body.get('id')
@@ -83,7 +83,7 @@
self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
self.addCleanup(self.data.tenants.remove, tenant)
self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
@@ -92,7 +92,7 @@
tenant_name = data_utils.rand_name(name='tenant')
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
@@ -103,7 +103,7 @@
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
self.client.auth_provider.clear_auth()
@test.attr(type=['negative'])
@@ -121,7 +121,7 @@
tenant_name = 'a' * 65
self.assertRaises(lib_exc.BadRequest,
self.tenants_client.create_tenant,
- tenant_name)
+ name=tenant_name)
@test.attr(type=['negative'])
@test.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
@@ -135,7 +135,7 @@
def test_tenant_update_by_unauthorized_user(self):
# Non-administrator user should not be able to update a tenant
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.update_tenant,
@@ -146,7 +146,7 @@
def test_tenant_update_request_without_token(self):
# Request to update a tenant without a valid token should fail
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 8d0b9b1..1aa80df 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -28,7 +28,8 @@
tenants = []
for _ in moves.xrange(3):
tenant_name = data_utils.rand_name(name='tenant-new')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(
+ name=tenant_name)['tenant']
self.data.tenants.append(tenant)
tenants.append(tenant)
tenant_ids = map(lambda x: x['id'], tenants)
@@ -49,7 +50,7 @@
# Create tenant with a description
tenant_name = data_utils.rand_name(name='tenant')
tenant_desc = data_utils.rand_name(name='desc')
- body = self.tenants_client.create_tenant(tenant_name,
+ body = self.tenants_client.create_tenant(name=tenant_name,
description=tenant_desc)
tenant = body['tenant']
self.data.tenants.append(tenant)
@@ -68,7 +69,8 @@
def test_tenant_create_enabled(self):
# Create a tenant that is enabled
tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(tenant_name, enabled=True)
+ body = self.tenants_client.create_tenant(name=tenant_name,
+ enabled=True)
tenant = body['tenant']
self.data.tenants.append(tenant)
tenant_id = tenant['id']
@@ -84,7 +86,8 @@
def test_tenant_create_not_enabled(self):
# Create a tenant that is not enabled
tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(tenant_name, enabled=False)
+ body = self.tenants_client.create_tenant(name=tenant_name,
+ enabled=False)
tenant = body['tenant']
self.data.tenants.append(tenant)
tenant_id = tenant['id']
@@ -102,7 +105,7 @@
def test_tenant_update_name(self):
# Update name attribute of a tenant
t_name1 = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(t_name1)['tenant']
+ body = self.tenants_client.create_tenant(name=t_name1)['tenant']
tenant = body
self.data.tenants.append(tenant)
@@ -129,7 +132,8 @@
# Update description attribute of a tenant
t_name = data_utils.rand_name(name='tenant')
t_desc = data_utils.rand_name(name='desc')
- body = self.tenants_client.create_tenant(t_name, description=t_desc)
+ body = self.tenants_client.create_tenant(name=t_name,
+ description=t_desc)
tenant = body['tenant']
self.data.tenants.append(tenant)
@@ -157,7 +161,7 @@
# Update the enabled attribute of a tenant
t_name = data_utils.rand_name(name='tenant')
t_en = False
- body = self.tenants_client.create_tenant(t_name, enabled=t_en)
+ body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
tenant = body['tenant']
self.data.tenants.append(tenant)
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index ee04420..5cf337b 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -27,11 +27,13 @@
user_password = data_utils.rand_password()
# first:create a tenant
tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(tenant_name)['tenant']
+ tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
self.data.tenants.append(tenant)
# second:create a user
- user = self.users_client.create_user(user_name, user_password,
- tenant['id'], '')['user']
+ user = self.users_client.create_user(name=user_name,
+ password=user_password,
+ tenantId=tenant['id'],
+ email='')['user']
self.data.users.append(user)
# then get a token for the user
body = self.token_client.auth(user_name,
@@ -62,17 +64,21 @@
user_password = data_utils.rand_password()
tenant_id = None # No default tenant so will get unscoped token.
email = ''
- user = self.users_client.create_user(user_name, user_password,
- tenant_id, email)['user']
+ user = self.users_client.create_user(name=user_name,
+ password=user_password,
+ tenantId=tenant_id,
+ email=email)['user']
self.data.users.append(user)
# Create a couple tenants.
tenant1_name = data_utils.rand_name(name='tenant')
- tenant1 = self.tenants_client.create_tenant(tenant1_name)['tenant']
+ tenant1 = self.tenants_client.create_tenant(
+ name=tenant1_name)['tenant']
self.data.tenants.append(tenant1)
tenant2_name = data_utils.rand_name(name='tenant')
- tenant2 = self.tenants_client.create_tenant(tenant2_name)['tenant']
+ tenant2 = self.tenants_client.create_tenant(
+ name=tenant2_name)['tenant']
self.data.tenants.append(tenant2)
# Create a role
@@ -81,11 +87,13 @@
self.data.roles.append(role)
# Grant the user the role on the tenants.
- self.roles_client.assign_user_role(tenant1['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant1['id'],
+ user['id'],
+ role['id'])
- self.roles_client.assign_user_role(tenant2['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(tenant2['id'],
+ user['id'],
+ role['id'])
# Get an unscoped token.
body = self.token_client.auth(user_name, user_password)
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index d860d2f..167cbc7 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -36,9 +36,10 @@
def test_create_user(self):
# Create a user
self.data.setup_test_tenant()
- user = self.users_client.create_user(self.alt_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=self.alt_user,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)['user']
self.data.users.append(user)
self.assertEqual(self.alt_user, user['name'])
@@ -47,9 +48,10 @@
# Create a user with enabled : False
self.data.setup_test_tenant()
name = data_utils.rand_name('test_user')
- user = self.users_client.create_user(name, self.alt_password,
- self.data.tenant['id'],
- self.alt_email,
+ user = self.users_client.create_user(name=name,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email,
enabled=False)['user']
self.data.users.append(user)
self.assertEqual(name, user['name'])
@@ -61,9 +63,10 @@
# Test case to check if updating of user attributes is successful.
test_user = data_utils.rand_name('test_user')
self.data.setup_test_tenant()
- user = self.users_client.create_user(test_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=test_user,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)['user']
# Delete the User at the end of this method
self.addCleanup(self.users_client.delete_user, user['id'])
# Updating user details with new values
@@ -87,9 +90,10 @@
# Delete a user
test_user = data_utils.rand_name('test_user')
self.data.setup_test_tenant()
- user = self.users_client.create_user(test_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)['user']
+ user = self.users_client.create_user(name=test_user,
+ password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)['user']
self.users_client.delete_user(user['id'])
@test.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
@@ -139,16 +143,18 @@
fetched_user_ids = list()
password1 = data_utils.rand_password()
alt_tenant_user1 = data_utils.rand_name('tenant_user1')
- user1 = self.users_client.create_user(alt_tenant_user1, password1,
- self.data.tenant['id'],
- 'user1@123')['user']
+ user1 = self.users_client.create_user(name=alt_tenant_user1,
+ password=password1,
+ tenantId=self.data.tenant['id'],
+ email='user1@123')['user']
user_ids.append(user1['id'])
self.data.users.append(user1)
password2 = data_utils.rand_password()
alt_tenant_user2 = data_utils.rand_name('tenant_user2')
- user2 = self.users_client.create_user(alt_tenant_user2, password2,
- self.data.tenant['id'],
- 'user2@123')['user']
+ user2 = self.users_client.create_user(name=alt_tenant_user2,
+ password=password2,
+ tenantId=self.data.tenant['id'],
+ email='user2@123')['user']
user_ids.append(user2['id'])
self.data.users.append(user2)
# List of users for the respective tenant ID
@@ -175,19 +181,20 @@
user_ids = list()
fetched_user_ids = list()
user_ids.append(user['id'])
- role = self.roles_client.assign_user_role(tenant['id'], user['id'],
- role['id'])['role']
+ role = self.roles_client.create_user_role_on_project(
+ tenant['id'], user['id'], role['id'])['role']
alt_user2 = data_utils.rand_name('second_user')
alt_password2 = data_utils.rand_password()
- second_user = self.users_client.create_user(alt_user2, alt_password2,
- self.data.tenant['id'],
- 'user2@123')['user']
+ second_user = self.users_client.create_user(
+ name=alt_user2,
+ password=alt_password2,
+ tenantId=self.data.tenant['id'],
+ email='user2@123')['user']
user_ids.append(second_user['id'])
self.data.users.append(second_user)
- role = self.roles_client.assign_user_role(tenant['id'],
- second_user['id'],
- role['id'])['role']
+ role = self.roles_client.create_user_role_on_project(
+ tenant['id'], second_user['id'], role['id'])['role']
# List of users with roles for the respective tenant ID
body = (self.tenants_client.list_tenant_users(self.data.tenant['id'])
['users'])
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 5fda4c14..78b89fa 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -35,9 +35,9 @@
self.data.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.create_user,
- self.alt_user, self.alt_password,
- self.data.tenant['id'],
- self.alt_email)
+ name=self.alt_user, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
@@ -45,8 +45,9 @@
# User with an empty name should not be created
self.data.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- '', self.alt_password, self.data.tenant['id'],
- self.alt_email)
+ name='', password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
@@ -54,8 +55,9 @@
# Length of user name filed should be restricted to 255 characters
self.data.setup_test_tenant()
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- 'a' * 256, self.alt_password,
- self.data.tenant['id'], self.alt_email)
+ name='a' * 256, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
@@ -63,16 +65,20 @@
# Duplicate user should not be created
self.data.setup_test_user()
self.assertRaises(lib_exc.Conflict, self.users_client.create_user,
- self.data.user['name'], self.data.user_password,
- self.data.tenant['id'], self.data.user['email'])
+ name=self.data.user['name'],
+ password=self.data.user_password,
+ tenantId=self.data.tenant['id'],
+ email=self.data.user['email'])
@test.attr(type=['negative'])
@test.idempotent_id('0132cc22-7c4f-42e1-9e50-ac6aad31d59a')
def test_create_user_for_non_existent_tenant(self):
# Attempt to create a user in a non-existent tenant should fail
self.assertRaises(lib_exc.NotFound, self.users_client.create_user,
- self.alt_user, self.alt_password, '49ffgg99999',
- self.alt_email)
+ name=self.alt_user,
+ password=self.alt_password,
+ tenantId='49ffgg99999',
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
@@ -88,8 +94,9 @@
self.addCleanup(self.client.auth_provider.clear_auth)
self.assertRaises(lib_exc.Unauthorized, self.users_client.create_user,
- self.alt_user, self.alt_password,
- self.data.tenant['id'], self.alt_email)
+ name=self.alt_user, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email)
@test.attr(type=['negative'])
@test.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
@@ -98,9 +105,9 @@
self.data.setup_test_tenant()
name = data_utils.rand_name('test_user')
self.assertRaises(lib_exc.BadRequest, self.users_client.create_user,
- name, self.alt_password,
- self.data.tenant['id'],
- self.alt_email, enabled=3)
+ name=name, password=self.alt_password,
+ tenantId=self.data.tenant['id'],
+ email=self.alt_email, enabled=3)
@test.attr(type=['negative'])
@test.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 31420d1..bc1b158 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -215,11 +215,10 @@
self.domains = []
def _create_test_user(self, **kwargs):
- username = data_utils.rand_name('test_user')
self.user_password = data_utils.rand_password()
self.user = self.users_client.create_user(
- username, password=self.user_password,
- email=username + '@testmail.tm', **kwargs)['user']
+ password=self.user_password,
+ **kwargs)['user']
self.users.append(self.user)
def setup_test_role(self):
@@ -256,7 +255,10 @@
def setup_test_user(self):
"""Set up a test user."""
self.setup_test_tenant()
- self._create_test_user(tenant_id=self.tenant['id'])
+ username = data_utils.rand_name('test_user')
+ email = username + '@testmail.tm'
+ self._create_test_user(name=username, email=email,
+ tenantId=self.tenant['id'])
def setup_test_tenant(self):
"""Set up a test tenant."""
@@ -271,7 +273,10 @@
def setup_test_user(self):
"""Set up a test user."""
self.setup_test_project()
- self._create_test_user(project_id=self.project['id'])
+ username = data_utils.rand_name('test_user')
+ email = username + '@testmail.tm'
+ self._create_test_user(user_name=username, email=email,
+ project_id=self.project['id'])
def setup_test_project(self):
"""Set up a test project."""
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 6d5559d..59ac646 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -16,6 +16,7 @@
from six import moves
from tempest.api.image import base
+from tempest.common import image as common_image
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
@@ -306,7 +307,8 @@
@test.idempotent_id('01752c1c-0275-4de3-9e5b-876e44541928')
def test_list_image_metadata(self):
# All metadata key/value pairs for an image should be returned
- resp_metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ resp_metadata = common_image.get_image_meta_from_headers(resp)
expected = {'key1': 'value1'}
self.assertEqual(expected, resp_metadata['properties'])
@@ -314,12 +316,14 @@
def test_update_image_metadata(self):
# The metadata for the image should match the updated values
req_metadata = {'key1': 'alt1', 'key2': 'value2'}
- metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ metadata = common_image.get_image_meta_from_headers(resp)
self.assertEqual(metadata['properties'], {'key1': 'value1'})
metadata['properties'].update(req_metadata)
metadata = self.client.update_image(
self.image_id, properties=metadata['properties'])['image']
- resp_metadata = self.client.check_image(self.image_id)
+ resp = self.client.check_image(self.image_id)
+ resp_metadata = common_image.get_image_meta_from_headers(resp)
expected = {'key1': 'alt1', 'key2': 'value2'}
self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 89fa576..9e7c795 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -80,6 +80,7 @@
cls.security_groups_client = cls.os.security_groups_client
cls.security_group_rules_client = (
cls.os.security_group_rules_client)
+ cls.network_versions_client = cls.os.network_versions_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/network/test_versions.py b/tempest/api/network/test_versions.py
new file mode 100644
index 0000000..9cf93f6
--- /dev/null
+++ b/tempest/api/network/test_versions.py
@@ -0,0 +1,40 @@
+# Copyright 2016 VMware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest import test
+
+
+class NetworksApiDiscovery(base.BaseNetworkTest):
+ @test.attr(type='smoke')
+ @test.idempotent_id('cac8a836-c2e0-4304-b556-cd299c7281d1')
+ def test_api_version_resources(self):
+ """Test that GET / returns expected resources.
+
+ The versions document returned by Neutron returns a few other
+ resources other than just available API versions: it also
+ states the status of each API version and provides links to
+ schema.
+ """
+
+ result = self.network_versions_client.list_versions()
+ expected_versions = ('v2.0')
+ expected_resources = ('id', 'links', 'status')
+ received_list = result.values()
+
+ for item in received_list:
+ for version in item:
+ for resource in expected_resources:
+ self.assertIn(resource, version)
+ self.assertIn(version['id'], expected_versions)
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 26a5a45..a17cc69 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -61,8 +61,7 @@
cls.client.wait_for_resource_deletion(cls.snapshot['id'])
# Delete the test volume
- cls.volumes_client.delete_volume(cls.volume['id'])
- cls.volumes_client.wait_for_resource_deletion(cls.volume['id'])
+ cls.delete_volume(cls.volumes_client, cls.volume['id'])
super(SnapshotsActionsV2Test, cls).resource_cleanup()
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index cd24d17..ba17d9c 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -33,11 +33,6 @@
cls.demo_tenant_id = cls.os.credentials.tenant_id
cls.alt_client = cls.os_alt.volumes_client
- def _delete_volume(self, volume_id):
- # Delete the specified volume using admin credentials
- self.admin_volume_client.delete_volume(volume_id)
- self.admin_volume_client.wait_for_resource_deletion(volume_id)
-
@test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
@@ -91,7 +86,8 @@
self.demo_tenant_id)['quota_set']
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, volume['id'])
new_quota_usage = self.admin_quotas_client.show_quota_usage(
self.demo_tenant_id)['quota_set']
@@ -128,7 +124,8 @@
def test_quota_usage_after_volume_transfer(self):
# Create a volume for transfer
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, volume['id'])
# List of tenants quota usage pre-transfer
primary_quota = self.admin_quotas_client.show_quota_usage(
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 587dbd2..9023037 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -24,10 +24,6 @@
class VolumeTypesV2Test(base.BaseVolumeAdminTest):
- def _delete_volume(self, volume_id):
- self.volumes_client.delete_volume(volume_id)
- self.volumes_client.wait_for_resource_deletion(volume_id)
-
@test.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
# List volume types.
@@ -59,7 +55,7 @@
# Create volume
volume = self.volumes_client.create_volume(**params)['volume']
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
self.assertEqual(volume_types[0]['name'], volume["volume_type"])
self.assertEqual(volume[self.name_field], vol_name,
"The created volume name is not equal "
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index bdb313f..5388f7f 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -42,8 +42,7 @@
@classmethod
def resource_cleanup(cls):
# Delete the test volume
- cls.client.delete_volume(cls.volume['id'])
- cls.client.wait_for_resource_deletion(cls.volume['id'])
+ cls.delete_volume(cls.client, cls.volume['id'])
super(VolumesActionsV2Test, cls).resource_cleanup()
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
new file mode 100644
index 0000000..6817eaa
--- /dev/null
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -0,0 +1,30 @@
+#
+# 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 fixtures
+
+from tempest.services.volume.base import base_v3_client
+
+
+class APIMicroversionFixture(fixtures.Fixture):
+
+ def __init__(self, volume_microversion):
+ self.volume_microversion = volume_microversion
+
+ def _setUp(self):
+ super(APIMicroversionFixture, self)._setUp()
+ base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
+ self.addCleanup(self._reset_volume_microversion)
+
+ def _reset_volume_microversion(self):
+ base_v3_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index cd21424..665036b 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -45,6 +45,10 @@
if not CONF.volume_feature_enabled.api_v2:
msg = "Volume API v2 is disabled"
raise cls.skipException(msg)
+ elif cls._api_version == 3:
+ if not CONF.volume_feature_enabled.api_v3:
+ msg = "Volume API v3 is disabled"
+ raise cls.skipException(msg)
else:
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
raise exceptions.InvalidConfiguration(message=msg)
@@ -131,6 +135,12 @@
# only in a single location in the source, and could be more general.
@classmethod
+ def delete_volume(cls, client, volume_id):
+ """Delete volume by the given client"""
+ client.delete_volume(volume_id)
+ client.wait_for_resource_deletion(volume_id)
+
+ @classmethod
def clear_volumes(cls):
for volume in cls.volumes:
try:
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 866db3d..d138490 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -36,16 +36,11 @@
cls.alt_tenant_id = cls.alt_client.tenant_id
cls.adm_client = cls.os_adm.volumes_client
- def _delete_volume(self, volume_id):
- # Delete the specified volume using admin creds
- self.adm_client.delete_volume(volume_id)
- self.adm_client.wait_for_resource_deletion(volume_id)
-
@test.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
def test_create_get_list_accept_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
# Create a volume transfer
transfer = self.client.create_volume_transfer(
@@ -74,7 +69,7 @@
def test_create_list_delete_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
# Create a volume transfer
body = self.client.create_volume_transfer(
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 5d83bb0..e5fcdfe 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -39,10 +39,6 @@
cls.name_field = cls.special_fields['name_field']
cls.descrip_field = cls.special_fields['descrip_field']
- def _delete_volume(self, volume_id):
- self.client.delete_volume(volume_id)
- self.client.wait_for_resource_deletion(volume_id)
-
def _volume_create_get_update_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
volume = {}
@@ -53,7 +49,7 @@
kwargs['metadata'] = metadata
volume = self.client.create_volume(**kwargs)['volume']
self.assertIn('id', volume)
- self.addCleanup(self._delete_volume, volume['id'])
+ self.addCleanup(self.delete_volume, self.client, volume['id'])
waiters.wait_for_volume_status(self.client, volume['id'], 'available')
self.assertIn(self.name_field, volume)
self.assertEqual(volume[self.name_field], v_name,
@@ -113,7 +109,7 @@
'availability_zone': volume['availability_zone']}
new_volume = self.client.create_volume(**params)['volume']
self.assertIn('id', new_volume)
- self.addCleanup(self._delete_volume, new_volume['id'])
+ self.addCleanup(self.delete_volume, self.client, new_volume['id'])
waiters.wait_for_volume_status(self.client,
new_volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 38a5a80..a93025d 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -72,8 +72,7 @@
def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
- cls.client.delete_volume(volid)
- cls.client.wait_for_resource_deletion(volid)
+ cls.delete_volume(cls.client, volid)
super(VolumesV2ListTestJSON, cls).resource_cleanup()
def _list_by_param_value_and_assert(self, params, with_detail=False):
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 6707121..0f7c4f6 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -186,8 +186,7 @@
snapshot_id=snapshot['id'])['volume']
waiters.wait_for_volume_status(self.volumes_client,
volume['id'], 'available')
- self.volumes_client.delete_volume(volume['id'])
- self.volumes_client.wait_for_resource_deletion(volume['id'])
+ self.delete_volume(self.volumes_client, volume['id'])
self.cleanup_snapshot(snapshot)
@test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 1fa54c2..5117e6c 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -55,8 +55,7 @@
def resource_cleanup(cls):
# Delete the created volumes
for volid in cls.volume_id_list:
- cls.client.delete_volume(volid)
- cls.client.wait_for_resource_deletion(volid)
+ cls.delete_volume(cls.client, volid)
super(VolumesV2ListTestJSON, cls).resource_cleanup()
@test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
diff --git a/tempest/api/volume/v3/__init__.py b/tempest/api/volume/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/volume/v3/__init__.py
diff --git a/tempest/api/volume/v3/admin/__init__.py b/tempest/api/volume/v3/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/volume/v3/admin/__init__.py
diff --git a/tempest/api/volume/v3/admin/test_user_messages.py b/tempest/api/volume/v3/admin/test_user_messages.py
new file mode 100644
index 0000000..9d59d1b
--- /dev/null
+++ b/tempest/api/volume/v3/admin/test_user_messages.py
@@ -0,0 +1,94 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.volume.v3 import base
+from tempest.common.utils import data_utils
+from tempest.common import waiters
+from tempest import exceptions
+from tempest import test
+
+MESSAGE_KEYS = [
+ 'created_at',
+ 'event_id',
+ 'guaranteed_until',
+ 'id',
+ 'message_level',
+ 'request_id',
+ 'resource_type',
+ 'resource_uuid',
+ 'user_message',
+ 'links']
+
+
+class UserMessagesTest(base.VolumesV3AdminTest):
+ min_microversion = '3.3'
+ max_microversion = 'latest'
+
+ def _create_user_message(self):
+ """Trigger a 'no valid host' situation to generate a message."""
+ bad_protocol = data_utils.rand_name('storage_protocol')
+ bad_vendor = data_utils.rand_name('vendor_name')
+ extra_specs = {'storage_protocol': bad_protocol,
+ 'vendor_name': bad_vendor}
+ vol_type_name = data_utils.rand_name('volume-type')
+ bogus_type = self.admin_volume_types_client.create_volume_type(
+ name=vol_type_name,
+ extra_specs=extra_specs)['volume_type']
+ self.addCleanup(self.admin_volume_types_client.delete_volume_type,
+ bogus_type['id'])
+ params = {'volume_type': bogus_type['id']}
+ volume = self.volumes_client.create_volume(**params)['volume']
+ self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+ try:
+ waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+ 'error')
+ except exceptions.VolumeBuildErrorException:
+ # Error state is expected and desired
+ pass
+ messages = self.messages_client.list_messages()['messages']
+ message_id = None
+ for message in messages:
+ if message['resource_uuid'] == volume['id']:
+ message_id = message['id']
+ break
+ self.assertIsNotNone(message_id, 'No user message generated for '
+ 'volume %s' % volume['id'])
+ return message_id
+
+ @test.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
+ def test_list_messages(self):
+ self._create_user_message()
+ messages = self.messages_client.list_messages()['messages']
+ self.assertIsInstance(messages, list)
+ for message in messages:
+ for key in MESSAGE_KEYS:
+ self.assertIn(key, message.keys(),
+ 'Missing expected key %s' % key)
+
+ @test.idempotent_id('55a4a61e-c7b2-4ba0-a05d-b914bdef3070')
+ def test_show_message(self):
+ message_id = self._create_user_message()
+ self.addCleanup(self.messages_client.delete_message, message_id)
+
+ message = self.messages_client.show_message(message_id)['message']
+
+ for key in MESSAGE_KEYS:
+ self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
+
+ @test.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
+ def test_delete_message(self):
+ message_id = self._create_user_message()
+ self.messages_client.delete_message(message_id)
+ self.messages_client.wait_for_resource_deletion(message_id)
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
new file mode 100644
index 0000000..c31c83c
--- /dev/null
+++ b/tempest/api/volume/v3/base.py
@@ -0,0 +1,64 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from tempest.api.volume import api_microversion_fixture
+from tempest.api.volume import base
+from tempest import config
+from tempest.lib.common import api_version_utils
+
+CONF = config.CONF
+
+
+class VolumesV3Test(api_version_utils.BaseMicroversionTest,
+ base.BaseVolumeTest):
+ """Base test case class for all v3 Cinder API tests."""
+
+ _api_version = 3
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesV3Test, cls).skip_checks()
+ api_version_utils.check_skip_with_microversion(
+ cls.min_microversion, cls.max_microversion,
+ CONF.volume.min_microversion, CONF.volume.max_microversion)
+
+ @classmethod
+ def resource_setup(cls):
+ super(VolumesV3Test, cls).resource_setup()
+ cls.request_microversion = (
+ api_version_utils.select_request_microversion(
+ cls.min_microversion,
+ CONF.volume.min_microversion))
+
+ @classmethod
+ def setup_clients(cls):
+ super(VolumesV3Test, cls).setup_clients()
+ cls.messages_client = cls.os.volume_messages_client
+
+ def setUp(self):
+ super(VolumesV3Test, self).setUp()
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+ self.request_microversion))
+
+
+class VolumesV3AdminTest(VolumesV3Test):
+ """Base test case class for all v3 Volume Admin API tests."""
+
+ credentials = ['primary', 'admin']
+
+ @classmethod
+ def setup_clients(cls):
+ super(VolumesV3AdminTest, cls).setup_clients()
+ cls.admin_messages_client = cls.os_adm.volume_messages_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/clients.py b/tempest/clients.py
index ccbec4e..8c1b279 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -20,62 +20,12 @@
from tempest.common import negative_rest_client
from tempest import config
from tempest import exceptions
-from tempest.lib.services.compute.agents_client import AgentsClient
-from tempest.lib.services.compute.aggregates_client import AggregatesClient
-from tempest.lib.services.compute.availability_zone_client import \
- AvailabilityZoneClient
-from tempest.lib.services.compute.baremetal_nodes_client import \
- BaremetalNodesClient
-from tempest.lib.services.compute.certificates_client import \
- CertificatesClient
-from tempest.lib.services.compute.extensions_client import \
- ExtensionsClient
-from tempest.lib.services.compute.fixed_ips_client import FixedIPsClient
-from tempest.lib.services.compute.flavors_client import FlavorsClient
-from tempest.lib.services.compute.floating_ip_pools_client import \
- FloatingIPPoolsClient
-from tempest.lib.services.compute.floating_ips_bulk_client import \
- FloatingIPsBulkClient
-from tempest.lib.services.compute.floating_ips_client import \
- FloatingIPsClient as ComputeFloatingIPsClient
-from tempest.lib.services.compute.hosts_client import HostsClient
-from tempest.lib.services.compute.hypervisor_client import \
- HypervisorClient
-from tempest.lib.services.compute.images_client import ImagesClient \
- as ComputeImagesClient
-from tempest.lib.services.compute.instance_usage_audit_log_client import \
- InstanceUsagesAuditLogClient
-from tempest.lib.services.compute.interfaces_client import InterfacesClient
-from tempest.lib.services.compute.keypairs_client import KeyPairsClient
-from tempest.lib.services.compute.limits_client import LimitsClient
-from tempest.lib.services.compute.migrations_client import MigrationsClient
-from tempest.lib.services.compute.networks_client import NetworksClient \
- as ComputeNetworksClient
-from tempest.lib.services.compute.quota_classes_client import \
- QuotaClassesClient
-from tempest.lib.services.compute.quotas_client import QuotasClient
-from tempest.lib.services.compute.security_group_default_rules_client import \
- SecurityGroupDefaultRulesClient
-from tempest.lib.services.compute.security_group_rules_client import \
- SecurityGroupRulesClient as ComputeSecurityGroupRulesClient
-from tempest.lib.services.compute.security_groups_client import \
- SecurityGroupsClient as ComputeSecurityGroupsClient
-from tempest.lib.services.compute.server_groups_client import \
- ServerGroupsClient
-from tempest.lib.services.compute.servers_client import ServersClient
-from tempest.lib.services.compute.services_client import ServicesClient
-from tempest.lib.services.compute.snapshots_client import \
- SnapshotsClient as ComputeSnapshotsClient
-from tempest.lib.services.compute.tenant_networks_client import \
- TenantNetworksClient
-from tempest.lib.services.compute.tenant_usages_client import \
- TenantUsagesClient
-from tempest.lib.services.compute.versions_client import VersionsClient
-from tempest.lib.services.compute.volumes_client import \
- VolumesClient as ComputeVolumesClient
+from tempest.lib.services import compute
from tempest.lib.services.identity.v2.endpoints_client import EndpointsClient
from tempest.lib.services.identity.v2.token_client import TokenClient
from tempest.lib.services.identity.v3.token_client import V3TokenClient
+from tempest.lib.services.image.v1.image_members_client import \
+ ImageMembersClient
from tempest.lib.services.image.v2.image_members_client import \
ImageMembersClient as ImageMembersClientV2
from tempest.lib.services.image.v2.images_client import \
@@ -84,26 +34,7 @@
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
from tempest.lib.services.image.v2.schemas_client import SchemasClient
-from tempest.lib.services.network.agents_client import AgentsClient \
- as NetworkAgentsClient
-from tempest.lib.services.network.extensions_client import \
- ExtensionsClient as NetworkExtensionsClient
-from tempest.lib.services.network.floating_ips_client import FloatingIPsClient
-from tempest.lib.services.network.metering_label_rules_client import \
- MeteringLabelRulesClient
-from tempest.lib.services.network.metering_labels_client import \
- MeteringLabelsClient
-from tempest.lib.services.network.networks_client import NetworksClient
-from tempest.lib.services.network.ports_client import PortsClient
-from tempest.lib.services.network.quotas_client import QuotasClient \
- as NetworkQuotasClient
-from tempest.lib.services.network.routers_client import RoutersClient
-from tempest.lib.services.network.security_group_rules_client import \
- SecurityGroupRulesClient
-from tempest.lib.services.network.security_groups_client import \
- SecurityGroupsClient
-from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
-from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services import network
from tempest import manager
from tempest.services.baremetal.v1.json.baremetal_client import \
BaremetalClient
@@ -139,8 +70,6 @@
from tempest.services.identity.v3.json.trusts_client import TrustsClient
from tempest.services.identity.v3.json.users_clients import \
UsersClient as UsersV3Client
-from tempest.services.image.v1.json.image_members_client import \
- ImageMembersClient
from tempest.services.image.v1.json.images_client import ImagesClient
from tempest.services.object_storage.account_client import AccountClient
from tempest.services.object_storage.container_client import ContainerClient
@@ -183,6 +112,7 @@
SnapshotsClient as SnapshotsV2Client
from tempest.services.volume.v2.json.volumes_client import \
VolumesClient as VolumesV2Client
+from tempest.services.volume.v3.json.messages_client import MessagesClient
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -255,31 +185,33 @@
'build_timeout': CONF.network.build_timeout
}
params.update(self.default_params)
- self.network_agents_client = NetworkAgentsClient(
+ self.network_agents_client = network.AgentsClient(
self.auth_provider, **params)
- self.network_extensions_client = NetworkExtensionsClient(
+ self.network_extensions_client = network.ExtensionsClient(
self.auth_provider, **params)
- self.networks_client = NetworksClient(
+ self.networks_client = network.NetworksClient(
self.auth_provider, **params)
- self.subnetpools_client = SubnetpoolsClient(
+ self.subnetpools_client = network.SubnetpoolsClient(
self.auth_provider, **params)
- self.subnets_client = SubnetsClient(
+ self.subnets_client = network.SubnetsClient(
self.auth_provider, **params)
- self.ports_client = PortsClient(
+ self.ports_client = network.PortsClient(
self.auth_provider, **params)
- self.network_quotas_client = NetworkQuotasClient(
+ self.network_quotas_client = network.QuotasClient(
self.auth_provider, **params)
- self.floating_ips_client = FloatingIPsClient(
+ self.floating_ips_client = network.FloatingIPsClient(
self.auth_provider, **params)
- self.metering_labels_client = MeteringLabelsClient(
+ self.metering_labels_client = network.MeteringLabelsClient(
self.auth_provider, **params)
- self.metering_label_rules_client = MeteringLabelRulesClient(
+ self.metering_label_rules_client = network.MeteringLabelRulesClient(
self.auth_provider, **params)
- self.routers_client = RoutersClient(
+ self.routers_client = network.RoutersClient(
self.auth_provider, **params)
- self.security_group_rules_client = SecurityGroupRulesClient(
+ self.security_group_rules_client = network.SecurityGroupRulesClient(
self.auth_provider, **params)
- self.security_groups_client = SecurityGroupsClient(
+ self.security_groups_client = network.SecurityGroupsClient(
+ self.auth_provider, **params)
+ self.network_versions_client = network.NetworkVersionsClient(
self.auth_provider, **params)
def _set_image_clients(self):
@@ -318,61 +250,65 @@
}
params.update(self.default_params)
- self.agents_client = AgentsClient(self.auth_provider, **params)
- self.compute_networks_client = ComputeNetworksClient(
+ self.agents_client = compute.AgentsClient(self.auth_provider, **params)
+ self.compute_networks_client = compute.NetworksClient(
self.auth_provider, **params)
- self.migrations_client = MigrationsClient(self.auth_provider,
- **params)
+ self.migrations_client = compute.MigrationsClient(self.auth_provider,
+ **params)
self.security_group_default_rules_client = (
- SecurityGroupDefaultRulesClient(self.auth_provider, **params))
- self.certificates_client = CertificatesClient(self.auth_provider,
- **params)
- self.servers_client = ServersClient(
+ compute.SecurityGroupDefaultRulesClient(self.auth_provider,
+ **params))
+ self.certificates_client = compute.CertificatesClient(
+ self.auth_provider, **params)
+ self.servers_client = compute.ServersClient(
self.auth_provider,
enable_instance_password=CONF.compute_feature_enabled
.enable_instance_password,
**params)
- self.server_groups_client = ServerGroupsClient(
+ self.server_groups_client = compute.ServerGroupsClient(
self.auth_provider, **params)
- self.limits_client = LimitsClient(self.auth_provider, **params)
- self.compute_images_client = ComputeImagesClient(self.auth_provider,
- **params)
- self.keypairs_client = KeyPairsClient(self.auth_provider, **params)
- self.quotas_client = QuotasClient(self.auth_provider, **params)
- self.quota_classes_client = QuotaClassesClient(self.auth_provider,
+ self.limits_client = compute.LimitsClient(self.auth_provider, **params)
+ self.compute_images_client = compute.ImagesClient(self.auth_provider,
+ **params)
+ self.keypairs_client = compute.KeyPairsClient(self.auth_provider,
+ **params)
+ self.quotas_client = compute.QuotasClient(self.auth_provider, **params)
+ self.quota_classes_client = compute.QuotaClassesClient(
+ self.auth_provider, **params)
+ self.flavors_client = compute.FlavorsClient(self.auth_provider,
+ **params)
+ self.extensions_client = compute.ExtensionsClient(self.auth_provider,
+ **params)
+ self.floating_ip_pools_client = compute.FloatingIPPoolsClient(
+ self.auth_provider, **params)
+ self.floating_ips_bulk_client = compute.FloatingIPsBulkClient(
+ self.auth_provider, **params)
+ self.compute_floating_ips_client = compute.FloatingIPsClient(
+ self.auth_provider, **params)
+ self.compute_security_group_rules_client = (
+ compute.SecurityGroupRulesClient(self.auth_provider, **params))
+ self.compute_security_groups_client = compute.SecurityGroupsClient(
+ self.auth_provider, **params)
+ self.interfaces_client = compute.InterfacesClient(self.auth_provider,
+ **params)
+ self.fixed_ips_client = compute.FixedIPsClient(self.auth_provider,
**params)
- self.flavors_client = FlavorsClient(self.auth_provider, **params)
- self.extensions_client = ExtensionsClient(self.auth_provider,
- **params)
- self.floating_ip_pools_client = FloatingIPPoolsClient(
+ self.availability_zone_client = compute.AvailabilityZoneClient(
self.auth_provider, **params)
- self.floating_ips_bulk_client = FloatingIPsBulkClient(
+ self.aggregates_client = compute.AggregatesClient(self.auth_provider,
+ **params)
+ self.services_client = compute.ServicesClient(self.auth_provider,
+ **params)
+ self.tenant_usages_client = compute.TenantUsagesClient(
self.auth_provider, **params)
- self.compute_floating_ips_client = ComputeFloatingIPsClient(
+ self.hosts_client = compute.HostsClient(self.auth_provider, **params)
+ self.hypervisor_client = compute.HypervisorClient(self.auth_provider,
+ **params)
+ self.instance_usages_audit_log_client = (
+ compute.InstanceUsagesAuditLogClient(self.auth_provider, **params))
+ self.tenant_networks_client = compute.TenantNetworksClient(
self.auth_provider, **params)
- self.compute_security_group_rules_client = \
- ComputeSecurityGroupRulesClient(self.auth_provider, **params)
- self.compute_security_groups_client = ComputeSecurityGroupsClient(
- self.auth_provider, **params)
- self.interfaces_client = InterfacesClient(self.auth_provider,
- **params)
- self.fixed_ips_client = FixedIPsClient(self.auth_provider,
- **params)
- self.availability_zone_client = AvailabilityZoneClient(
- self.auth_provider, **params)
- self.aggregates_client = AggregatesClient(self.auth_provider,
- **params)
- self.services_client = ServicesClient(self.auth_provider, **params)
- self.tenant_usages_client = TenantUsagesClient(self.auth_provider,
- **params)
- self.hosts_client = HostsClient(self.auth_provider, **params)
- self.hypervisor_client = HypervisorClient(self.auth_provider,
- **params)
- self.instance_usages_audit_log_client = \
- InstanceUsagesAuditLogClient(self.auth_provider, **params)
- self.tenant_networks_client = \
- TenantNetworksClient(self.auth_provider, **params)
- self.baremetal_nodes_client = BaremetalNodesClient(
+ self.baremetal_nodes_client = compute.BaremetalNodesClient(
self.auth_provider, **params)
# NOTE: The following client needs special timeout values because
@@ -382,11 +318,11 @@
'build_interval': CONF.volume.build_interval,
'build_timeout': CONF.volume.build_timeout
})
- self.volumes_extensions_client = ComputeVolumesClient(
+ self.volumes_extensions_client = compute.VolumesClient(
self.auth_provider, **params_volume)
- self.compute_versions_client = VersionsClient(self.auth_provider,
- **params_volume)
- self.snapshots_extensions_client = ComputeSnapshotsClient(
+ self.compute_versions_client = compute.VersionsClient(
+ self.auth_provider, **params_volume)
+ self.snapshots_extensions_client = compute.SnapshotsClient(
self.auth_provider, **params_volume)
def _set_database_clients(self):
@@ -508,6 +444,8 @@
self.volumes_v2_client = VolumesV2Client(
self.auth_provider, default_volume_size=CONF.volume.volume_size,
**params)
+ self.volume_messages_client = MessagesClient(self.auth_provider,
+ **params)
self.volume_types_client = VolumeTypesClient(self.auth_provider,
**params)
self.volume_types_v2_client = VolumeTypesV2Client(
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index db323de..f9d7a9b 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -157,7 +157,8 @@
spec.append([CONF.object_storage.operator_role])
spec.append([CONF.object_storage.reseller_admin_role])
if CONF.service_available.heat:
- spec.append([CONF.orchestration.stack_owner_role])
+ spec.append([CONF.orchestration.stack_owner_role,
+ CONF.object_storage.operator_role])
if admin:
spec.append('admin')
resources = []
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index e8e691e..289650f 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -232,15 +232,16 @@
def _add_admin(self, tenant_id):
rl_cl = self.admin_mgr.roles_client
needs_role = True
- roles = rl_cl.list_user_roles(tenant_id, self.admin_id)['roles']
+ roles = rl_cl.list_user_roles_on_project(tenant_id,
+ self.admin_id)['roles']
for role in roles:
if role['id'] == self.admin_role_id:
needs_role = False
LOG.debug("User already had admin privilege for this tenant")
if needs_role:
LOG.debug("Adding admin privilege for : %s" % tenant_id)
- rl_cl.assign_user_role(tenant_id, self.admin_id,
- self.admin_role_id)
+ rl_cl.create_user_role_on_project(tenant_id, self.admin_id,
+ self.admin_role_id)
self.admin_role_added.append(tenant_id)
def _remove_admin_role(self, tenant_id):
@@ -250,8 +251,9 @@
id_cl = credentials.AdminManager().identity_client
if (self._tenant_exists(tenant_id)):
try:
- id_cl.delete_user_role(tenant_id, self.admin_id,
- self.admin_role_id)
+ id_cl.delete_role_from_user_on_project(tenant_id,
+ self.admin_id,
+ self.admin_role_id)
except Exception as ex:
LOG.exception("Failed removing role from tenant which still"
"exists, exception: %s" % ex)
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 9e86b48..8d2cfdc 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -661,7 +661,7 @@
if self.is_preserve:
secgroups = self._filter_by_conf_networks(secgroups)
- LOG.debug("List count, %s securtiy_groups" % len(secgroups))
+ LOG.debug("List count, %s security_groups" % len(secgroups))
return secgroups
def delete(self):
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 1f433eb..08ad94f 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -319,7 +319,7 @@
existing = [x['name'] for x in body]
for tenant in tenants:
if tenant not in existing:
- admin.tenants.create_tenant(tenant)['tenant']
+ admin.tenants.create_tenant(name=tenant)['tenant']
else:
LOG.warning("Tenant '%s' already exists in this environment"
% tenant)
@@ -361,7 +361,7 @@
role = next(r for r in roles if r['name'] == swift_role)
LOG.debug(USERS[user])
try:
- admin.roles.assign_user_role(
+ admin.roles.create_user_role_on_project(
USERS[user]['tenant_id'],
USERS[user]['id'],
role['id'])
@@ -391,8 +391,9 @@
% u['name'])
except lib_exc.NotFound:
admin.users.create_user(
- u['name'], u['pass'], tenant['id'],
- "%s@%s" % (u['name'], tenant['id']),
+ name=u['name'], password=u['pass'],
+ tenantId=tenant['id'],
+ email="%s@%s" % (u['name'], tenant['id']),
enabled=True)
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index b4b7ebb..e78f6b0 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -70,7 +70,7 @@
def take_action(self, parsed_args):
self._set_env()
- # Local exceution mode
+ # Local execution mode
if os.path.isfile('.testr.conf'):
# If you're running in local execution mode and there is not a
# testrepository dir create one
@@ -80,7 +80,7 @@
if returncode:
sys.exit(returncode)
else:
- print("No .testr.conf file was found for local exceution")
+ print("No .testr.conf file was found for local execution")
sys.exit(2)
regex = self._build_regex(parsed_args)
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index aac036b..2ca9f40 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -40,8 +40,9 @@
self.roles_client = roles_client
def create_user(self, username, password, project, email):
- user = self.users_client.create_user(
- username, password, project['id'], email)
+ params = self._create_user_params(username, password,
+ project['id'], email)
+ user = self.users_client.create_user(**params)
if 'user' in user:
user = user['user']
return user
@@ -101,6 +102,13 @@
users_client,
roles_client)
+ def _create_user_params(self, username, password, project_id, email):
+ params = {'name': username,
+ 'password': password,
+ 'tenantId': project_id,
+ 'email': email}
+ return params
+
def create_project(self, name, description):
tenant = self.projects_client.create_tenant(
name=name, description=description)['tenant']
@@ -121,8 +129,9 @@
password=password)
def _assign_user_role(self, project, user, role):
- self.roles_client.assign_user_role(project['id'], user['id'],
- role['id'])
+ self.roles_client.create_user_role_on_project(project['id'],
+ user['id'],
+ role['id'])
class V3CredsClient(CredsClient):
@@ -143,6 +152,13 @@
msg = "Requested domain %s could not be found" % domain_name
raise lib_exc.InvalidCredentials(msg)
+ def _create_user_params(self, username, password, project_id, email):
+ params = {'user_name': username,
+ 'password': password,
+ 'project_id': project_id,
+ 'email': email}
+ return params
+
def create_project(self, name, description):
project = self.projects_client.create_project(
name=name, description=description,
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 0b92c15..b0c01f5 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -109,22 +109,22 @@
os.subnets_client, os.ports_client,
os.security_groups_client)
- def _create_creds(self, suffix="", admin=False, roles=None):
- """Create random credentials under the following schema.
+ def _create_creds(self, admin=False, roles=None):
+ """Create credentials with random name.
- If the name contains a '.' is the full class path of something, and
- we don't really care. If it isn't, it's probably a meaningful name,
- so use it.
+ Creates project and user. When admin flag is True create user
+ with admin role. Assign user with additional roles (for example
+ _member_) and roles requested by caller.
- For logging purposes, -user and -tenant are long and redundant,
- don't use them. The user# will be sufficient to figure it out.
+ :param admin: Flag if to assign to the user admin role
+ :type admin: bool
+ :param roles: Roles to assign for the user
+ :type roles: list
+ :return: Readonly Credentials with network resources
"""
- if '.' in self.name:
- root = ""
- else:
- root = self.name
+ root = self.name
- project_name = data_utils.rand_name(root) + suffix
+ project_name = data_utils.rand_name(root)
project_desc = project_name + "-desc"
project = self.creds_client.create_project(
name=project_name, description=project_desc)
@@ -133,15 +133,14 @@
# having the same ID in both makes it easier to match them and debug.
username = project_name
user_password = data_utils.rand_password()
- email = data_utils.rand_name(root) + suffix + "@example.com"
+ email = data_utils.rand_name(root) + "@example.com"
user = self.creds_client.create_user(
username, user_password, project, email)
if 'user' in user:
user = user['user']
role_assigned = False
if admin:
- self.creds_client.assign_user_role(user, project,
- self.admin_role)
+ self.creds_client.assign_user_role(user, project, self.admin_role)
role_assigned = True
if (self.identity_version == 'v3' and
CONF.identity.admin_domain_scope):
diff --git a/tempest/common/image.py b/tempest/common/image.py
new file mode 100644
index 0000000..42ce5ac
--- /dev/null
+++ b/tempest/common/image.py
@@ -0,0 +1,38 @@
+# Copyright 2016 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.
+
+
+def get_image_meta_from_headers(resp):
+ meta = {'properties': {}}
+ for key in resp.response:
+ value = resp.response[key]
+ if key.startswith('x-image-meta-property-'):
+ _key = key[22:]
+ meta['properties'][_key] = value
+ elif key.startswith('x-image-meta-'):
+ _key = key[13:]
+ meta[_key] = value
+
+ for key in ['is_public', 'protected', 'deleted']:
+ if key in meta:
+ meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes', '1')
+
+ for key in ['size', 'min_ram', 'min_disk']:
+ if key in meta:
+ try:
+ meta[key] = int(meta[key])
+ except ValueError:
+ pass
+ return meta
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index c3c9a41..a55ee32 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -22,6 +22,26 @@
LOG = logging.getLogger(__name__)
+def _create_neutron_sec_group_rules(os, sec_group):
+ sec_group_rules_client = os.security_group_rules_client
+ ethertype = 'IPv4'
+ if CONF.validation.ip_version_for_ssh == 6:
+ ethertype = 'IPv6'
+
+ sec_group_rules_client.create_security_group_rule(
+ security_group_id=sec_group['id'],
+ protocol='tcp',
+ ethertype=ethertype,
+ port_range_min=22,
+ port_range_max=22,
+ direction='ingress')
+ sec_group_rules_client.create_security_group_rule(
+ security_group_id=sec_group['id'],
+ protocol='icmp',
+ ethertype=ethertype,
+ direction='ingress')
+
+
def create_ssh_security_group(os, add_rule=False):
security_groups_client = os.compute_security_groups_client
security_group_rules_client = os.compute_security_group_rules_client
@@ -30,12 +50,15 @@
security_group = security_groups_client.create_security_group(
name=sg_name, description=sg_description)['security_group']
if add_rule:
- security_group_rules_client.create_security_group_rule(
- parent_group_id=security_group['id'], ip_protocol='tcp',
- from_port=22, to_port=22)
- security_group_rules_client.create_security_group_rule(
- parent_group_id=security_group['id'], ip_protocol='icmp',
- from_port=-1, to_port=-1)
+ if CONF.service_available.neutron:
+ _create_neutron_sec_group_rules(os, security_group)
+ else:
+ security_group_rules_client.create_security_group_rule(
+ parent_group_id=security_group['id'], ip_protocol='tcp',
+ from_port=22, to_port=22)
+ security_group_rules_client.create_security_group_rule(
+ parent_group_id=security_group['id'], ip_protocol='icmp',
+ from_port=-1, to_port=-1)
LOG.debug("SSH Validation resource security group with tcp and icmp "
"rules %s created"
% sg_name)
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 23d7f88..d8dad69 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -15,6 +15,7 @@
from oslo_log import log as logging
+from tempest.common import image as common_image
from tempest import config
from tempest import exceptions
from tempest.lib.common.utils import misc as misc_utils
@@ -127,7 +128,11 @@
# The 'check_image' method is used here because the show_image method
# returns image details plus the image itself which is very expensive.
# The 'check_image' method returns just image details.
- show_image = client.check_image
+ def _show_image_v1(image_id):
+ resp = client.check_image(image_id)
+ return common_image.get_image_meta_from_headers(resp)
+
+ show_image = _show_image_v1
else:
show_image = client.show_image
diff --git a/tempest/config.py b/tempest/config.py
index 1f88871..a9cf537 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -682,6 +682,24 @@
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
+ cfg.StrOpt('min_microversion',
+ default=None,
+ help="Lower version of the test target microversion range. "
+ "The format is 'X.Y', where 'X' and 'Y' are int values. "
+ "Tempest selects tests based on the range between "
+ "min_microversion and max_microversion. "
+ "If both values are not specified, Tempest avoids tests "
+ "which require a microversion. Valid values are string "
+ "with format 'X.Y' or string 'latest'",),
+ cfg.StrOpt('max_microversion',
+ default=None,
+ help="Upper version of the test target microversion range. "
+ "The format is 'X.Y', where 'X' and 'Y' are int values. "
+ "Tempest selects tests based on the range between "
+ "min_microversion and max_microversion. "
+ "If both values are not specified, Tempest avoids tests "
+ "which require a microversion. Valid values are string "
+ "with format 'X.Y' or string 'latest'",),
]
volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
@@ -711,6 +729,9 @@
cfg.BoolOpt('api_v2',
default=True,
help="Is the v2 volume API enabled"),
+ cfg.BoolOpt('api_v3',
+ default=False,
+ help="Is the v3 volume API enabled"),
cfg.BoolOpt('bootable',
default=True,
help='Update bootable status of a volume '
diff --git a/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
index c6c4deb..15224c5 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
@@ -19,7 +19,23 @@
'type': 'array',
'items': {'type': 'string'}
},
- 'log': {'type': 'object'},
+ 'log': {
+ 'type': 'object',
+ 'patternProperties': {
+ # NOTE: Here is a host name.
+ '^.+$': {
+ 'type': 'object',
+ 'properties': {
+ 'state': {'type': 'string'},
+ 'instances': {'type': 'integer'},
+ 'errors': {'type': 'integer'},
+ 'message': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ 'required': ['state', 'instances', 'errors', 'message']
+ }
+ }
+ },
'num_hosts': {'type': 'integer'},
'num_hosts_done': {'type': 'integer'},
'num_hosts_not_run': {'type': 'integer'},
diff --git a/tempest/lib/services/compute/__init__.py b/tempest/lib/services/compute/__init__.py
index e69de29..91e896a 100644
--- a/tempest/lib/services/compute/__init__.py
+++ b/tempest/lib/services/compute/__init__.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.compute.agents_client import AgentsClient
+from tempest.lib.services.compute.aggregates_client import AggregatesClient
+from tempest.lib.services.compute.availability_zone_client import \
+ AvailabilityZoneClient
+from tempest.lib.services.compute.baremetal_nodes_client import \
+ BaremetalNodesClient
+from tempest.lib.services.compute.certificates_client import \
+ CertificatesClient
+from tempest.lib.services.compute.extensions_client import \
+ ExtensionsClient
+from tempest.lib.services.compute.fixed_ips_client import FixedIPsClient
+from tempest.lib.services.compute.flavors_client import FlavorsClient
+from tempest.lib.services.compute.floating_ip_pools_client import \
+ FloatingIPPoolsClient
+from tempest.lib.services.compute.floating_ips_bulk_client import \
+ FloatingIPsBulkClient
+from tempest.lib.services.compute.floating_ips_client import \
+ FloatingIPsClient
+from tempest.lib.services.compute.hosts_client import HostsClient
+from tempest.lib.services.compute.hypervisor_client import \
+ HypervisorClient
+from tempest.lib.services.compute.images_client import ImagesClient
+from tempest.lib.services.compute.instance_usage_audit_log_client import \
+ InstanceUsagesAuditLogClient
+from tempest.lib.services.compute.interfaces_client import InterfacesClient
+from tempest.lib.services.compute.keypairs_client import KeyPairsClient
+from tempest.lib.services.compute.limits_client import LimitsClient
+from tempest.lib.services.compute.migrations_client import MigrationsClient
+from tempest.lib.services.compute.networks_client import NetworksClient
+from tempest.lib.services.compute.quota_classes_client import \
+ QuotaClassesClient
+from tempest.lib.services.compute.quotas_client import QuotasClient
+from tempest.lib.services.compute.security_group_default_rules_client import \
+ SecurityGroupDefaultRulesClient
+from tempest.lib.services.compute.security_group_rules_client import \
+ SecurityGroupRulesClient
+from tempest.lib.services.compute.security_groups_client import \
+ SecurityGroupsClient
+from tempest.lib.services.compute.server_groups_client import \
+ ServerGroupsClient
+from tempest.lib.services.compute.servers_client import ServersClient
+from tempest.lib.services.compute.services_client import ServicesClient
+from tempest.lib.services.compute.snapshots_client import SnapshotsClient
+from tempest.lib.services.compute.tenant_networks_client import \
+ TenantNetworksClient
+from tempest.lib.services.compute.tenant_usages_client import \
+ TenantUsagesClient
+from tempest.lib.services.compute.versions_client import VersionsClient
+from tempest.lib.services.compute.volumes_client import \
+ VolumesClient
+
+__all__ = ['AgentsClient', 'AggregatesClient', 'AvailabilityZoneClient',
+ 'BaremetalNodesClient', 'CertificatesClient', 'ExtensionsClient',
+ 'FixedIPsClient', 'FlavorsClient', 'FloatingIPPoolsClient',
+ 'FloatingIPsBulkClient', 'FloatingIPsClient', 'HostsClient',
+ 'HypervisorClient', 'ImagesClient', 'InstanceUsagesAuditLogClient',
+ 'InterfacesClient', 'KeyPairsClient', 'LimitsClient',
+ 'MigrationsClient', 'NetworksClient', 'QuotaClassesClient',
+ 'QuotasClient', 'SecurityGroupDefaultRulesClient',
+ 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+ 'ServerGroupsClient', 'ServersClient', 'ServicesClient',
+ 'SnapshotsClient', 'TenantNetworksClient', 'TenantUsagesClient',
+ 'VersionsClient', 'VolumesClient']
diff --git a/tempest/lib/services/image/v1/__init__.py b/tempest/lib/services/image/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/services/image/v1/__init__.py
diff --git a/tempest/services/image/v1/json/image_members_client.py b/tempest/lib/services/image/v1/image_members_client.py
similarity index 100%
rename from tempest/services/image/v1/json/image_members_client.py
rename to tempest/lib/services/image/v1/image_members_client.py
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index e69de29..c466f07 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -0,0 +1,38 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.network.agents_client import AgentsClient
+from tempest.lib.services.network.extensions_client import ExtensionsClient
+from tempest.lib.services.network.floating_ips_client import FloatingIPsClient
+from tempest.lib.services.network.metering_label_rules_client import \
+ MeteringLabelRulesClient
+from tempest.lib.services.network.metering_labels_client import \
+ MeteringLabelsClient
+from tempest.lib.services.network.networks_client import NetworksClient
+from tempest.lib.services.network.ports_client import PortsClient
+from tempest.lib.services.network.quotas_client import QuotasClient
+from tempest.lib.services.network.routers_client import RoutersClient
+from tempest.lib.services.network.security_group_rules_client import \
+ SecurityGroupRulesClient
+from tempest.lib.services.network.security_groups_client import \
+ SecurityGroupsClient
+from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
+from tempest.lib.services.network.subnets_client import SubnetsClient
+from tempest.lib.services.network.versions_client import NetworkVersionsClient
+
+__all__ = ['AgentsClient', 'ExtensionsClient', 'FloatingIPsClient',
+ 'MeteringLabelRulesClient', 'MeteringLabelsClient',
+ 'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
+ 'SecurityGroupRulesClient', 'SecurityGroupsClient',
+ 'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/versions_client.py b/tempest/lib/services/network/versions_client.py
new file mode 100644
index 0000000..0202927
--- /dev/null
+++ b/tempest/lib/services/network/versions_client.py
@@ -0,0 +1,45 @@
+# Copyright 2016 VMware, Inc. 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 time
+
+from oslo_serialization import jsonutils as json
+from six.moves import urllib
+
+from tempest.lib.services.network import base
+
+
+class NetworkVersionsClient(base.BaseNetworkClient):
+
+ def list_versions(self):
+ """Do a GET / to fetch available API version information."""
+
+ endpoint = self.base_url
+ url = urllib.parse.urlparse(endpoint)
+ version_url = '%s://%s/' % (url.scheme, url.netloc)
+
+ # Note: we do a raw_request here because we want to use
+ # an unversioned URL, not "v2/$project_id/".
+ # Since raw_request doesn't log anything, we do that too.
+ start = time.time()
+ self._log_request_start('GET', version_url)
+ response, body = self.raw_request(version_url, 'GET')
+ end = time.time()
+ self._log_request('GET', version_url, response,
+ secs=(end - start), resp_body=body)
+
+ self.response_checker('GET', response, body)
+ self.expected_success(200, response.status)
+ body = json.loads(body)
+ return body
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 2b2f8ac..dd6e0e5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -23,6 +23,7 @@
import six
from tempest.common import compute
+from tempest.common import image as common_image
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
@@ -223,7 +224,7 @@
port = self._create_port(network_id=net_id,
client=clients.ports_client,
**create_port_body)
- ports.append({'port': port.id})
+ ports.append({'port': port['id']})
if ports:
kwargs['networks'] = ports
self.ports = ports
@@ -468,7 +469,8 @@
cleanup_args=[_image_client.delete_image, image_id])
if CONF.image_feature_enabled.api_v1:
# In glance v1 the additional properties are stored in the headers.
- snapshot_image = _image_client.check_image(image_id)
+ resp = _image_client.check_image(image_id)
+ snapshot_image = common_image.get_image_meta_from_headers(resp)
image_props = snapshot_image.get('properties', {})
else:
# In glance v2 the additional properties are flattened.
diff --git a/tempest/services/identity/v2/json/roles_client.py b/tempest/services/identity/v2/json/roles_client.py
index acd97c6..15c8834 100644
--- a/tempest/services/identity/v2/json/roles_client.py
+++ b/tempest/services/identity/v2/json/roles_client.py
@@ -11,6 +11,7 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -30,45 +31,77 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def show_role(self, role_id):
- """Get a role by its id."""
- resp, body = self.get('OS-KSADM/roles/%s' % role_id)
+ def show_role(self, role_id_or_name):
+ """Get a role by its id or name.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#showRoleByID
+ OR
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#showRoleByName
+ """
+ resp, body = self.get('OS-KSADM/roles/%s' % role_id_or_name)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(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_roles(self, **params):
+ """Returns roles.
- 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)
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#listRoles
+ """
+ url = 'OS-KSADM/roles'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def assign_user_role(self, tenant_id, user_id, role_id):
- """Add roles to a user on a tenant."""
+ def delete_role(self, role_id):
+ """Delete a role.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#deleteRole
+ """
+ resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_user_role_on_project(self, tenant_id, user_id, role_id):
+ """Add roles to a user on a tenant.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#grantRoleToUserOnTenant
+ """
resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
(tenant_id, user_id, role_id), "")
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def delete_user_role(self, tenant_id, user_id, role_id):
- """Removes a role assignment for a user on a tenant."""
+ def list_user_roles_on_project(self, tenant_id, user_id, **params):
+ """Returns a list of roles assigned to a user for a tenant."""
+ # TODO(gmann): Need to write API-ref link, Bug# 1592711
+ url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_role_from_user_on_project(self, tenant_id, user_id, role_id):
+ """Removes a role assignment for a user on a tenant.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#revokeRoleFromUserOnTenant
+ """
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 rest_client.ResponseBody(resp, body)
-
- def list_roles(self):
- """Returns roles."""
- resp, body = self.get('OS-KSADM/roles')
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/services_client.py b/tempest/services/identity/v2/json/services_client.py
index d8be6c6..4a63d56 100644
--- a/tempest/services/identity/v2/json/services_client.py
+++ b/tempest/services/identity/v2/json/services_client.py
@@ -13,6 +13,7 @@
# limitations under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -20,14 +21,9 @@
class ServicesClient(rest_client.RestClient):
api_version = "v2.0"
- def create_service(self, name, type, **kwargs):
+ def create_service(self, **kwargs):
"""Create a service."""
- post_body = {
- 'name': name,
- 'type': type,
- 'description': kwargs.get('description')
- }
- post_body = json.dumps({'OS-KSADM:service': post_body})
+ post_body = json.dumps({'OS-KSADM:service': kwargs})
resp, body = self.post('/OS-KSADM/services', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
@@ -41,9 +37,12 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_services(self):
+ def list_services(self, **params):
"""List Service - Returns Services."""
- resp, body = self.get('/OS-KSADM/services')
+ url = '/OS-KSADM/services'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/tenants_client.py b/tempest/services/identity/v2/json/tenants_client.py
index 97e5c11..77ddaa5 100644
--- a/tempest/services/identity/v2/json/tenants_client.py
+++ b/tempest/services/identity/v2/json/tenants_client.py
@@ -13,6 +13,7 @@
# limitations under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -20,40 +21,50 @@
class TenantsClient(rest_client.RestClient):
api_version = "v2.0"
- def create_tenant(self, name, **kwargs):
+ def create_tenant(self, **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)
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#createTenant
"""
- post_body = {
- 'name': name,
- 'description': kwargs.get('description', ''),
- 'enabled': kwargs.get('enabled', True),
- }
- post_body = json.dumps({'tenant': post_body})
+ post_body = json.dumps({'tenant': kwargs})
resp, body = self.post('tenants', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_tenant(self, tenant_id):
- """Delete a tenant."""
+ """Delete a tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#deleteTenant
+ """
resp, body = self.delete('tenants/%s' % str(tenant_id))
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
def show_tenant(self, tenant_id):
- """Get tenant details."""
+ """Get tenant details.
+
+ Available params: see
+ http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#admin-showTenantById
+ """
resp, body = self.get('tenants/%s' % str(tenant_id))
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_tenants(self):
- """Returns tenants."""
- resp, body = self.get('tenants')
+ def list_tenants(self, **params):
+ """Returns tenants.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#admin-listTenants
+ """
+ url = 'tenants'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
@@ -64,25 +75,24 @@
Available params: see http://developer.openstack.org/
api-ref-identity-v2-ext.html#updateTenant
"""
- body = self.show_tenant(tenant_id)['tenant']
- 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})
+ if 'id' not in kwargs:
+ kwargs['id'] = tenant_id
+ post_body = json.dumps({'tenant': kwargs})
resp, body = self.post('tenants/%s' % tenant_id, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def list_tenant_users(self, tenant_id):
- """List users for a Tenant."""
- resp, body = self.get('/tenants/%s/users' % tenant_id)
+ def list_tenant_users(self, tenant_id, **params):
+ """List users for a Tenant.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#listUsersForTenant
+ """
+ url = '/tenants/%s/users' % tenant_id
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/users_client.py b/tempest/services/identity/v2/json/users_client.py
index 6573b6e..1048840 100644
--- a/tempest/services/identity/v2/json/users_client.py
+++ b/tempest/services/identity/v2/json/users_client.py
@@ -11,6 +11,7 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -18,18 +19,13 @@
class UsersClient(rest_client.RestClient):
api_version = "v2.0"
- 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})
+ def create_user(self, **kwargs):
+ """Create a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-createUser
+ """
+ post_body = json.dumps({'user': kwargs})
resp, body = self.post('users', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
@@ -48,21 +44,36 @@
return rest_client.ResponseBody(resp, body)
def show_user(self, user_id):
- """GET a user."""
+ """GET a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-showUser
+ """
resp, body = self.get("users/%s" % user_id)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_user(self, user_id):
- """Delete a user."""
+ """Delete a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-deleteUser
+ """
resp, body = self.delete("users/%s" % user_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
- def list_users(self):
- """Get the list of users."""
- resp, body = self.get("users")
+ def list_users(self, **params):
+ """Get the list of users.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-listUsers
+ """
+ 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 rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index 4ffaf3b..5680668 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -16,7 +16,6 @@
import copy
import functools
-from oslo_log import log as logging
from oslo_serialization import jsonutils as json
import six
from six.moves.urllib import parse as urllib
@@ -24,35 +23,12 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
-LOG = logging.getLogger(__name__)
CHUNKSIZE = 1024 * 64 # 64kB
class ImagesClient(rest_client.RestClient):
api_version = "v1"
- def _image_meta_from_headers(self, headers):
- meta = {'properties': {}}
- for key, value in six.iteritems(headers):
- if key.startswith('x-image-meta-property-'):
- _key = key[22:]
- meta['properties'][_key] = value
- elif key.startswith('x-image-meta-'):
- _key = key[13:]
- meta[_key] = value
-
- for key in ['is_public', 'protected', 'deleted']:
- if key in meta:
- meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes',
- '1')
- for key in ['size', 'min_ram', 'min_disk']:
- if key in meta:
- try:
- meta[key] = int(meta[key])
- except ValueError:
- pass
- return meta
-
def _image_meta_to_headers(self, fields):
headers = {}
fields_copy = copy.deepcopy(fields)
@@ -98,9 +74,13 @@
self._http = self._get_http()
return self._http
- def create_image(self, **kwargs):
+ def create_image(self, data=None, **kwargs):
+ """Create an image.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v1.html#createImage-v1
+ """
headers = {}
- data = kwargs.pop('data', None)
headers.update(self._image_meta_to_headers(kwargs))
if data is not None:
@@ -111,9 +91,13 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def update_image(self, image_id, **kwargs):
+ def update_image(self, image_id, data=None, **kwargs):
+ """Update an image.
+
+ Available params: http://developer.openstack.org/
+ api-ref-image-v1.html#updateImage-v1
+ """
headers = {}
- data = kwargs.pop('data', None)
headers.update(self._image_meta_to_headers(kwargs))
if data is not None:
@@ -164,9 +148,8 @@
def check_image(self, image_id):
"""Check image metadata."""
url = 'images/%s' % image_id
- resp, __ = self.head(url)
+ resp, body = self.head(url)
self.expected_success(200, resp.status)
- body = self._image_meta_from_headers(resp)
return rest_client.ResponseBody(resp, body)
def show_image(self, image_id):
@@ -178,7 +161,8 @@
def is_resource_deleted(self, id):
try:
- if self.check_image(id)['status'] == 'deleted':
+ resp = self.check_image(id)
+ if resp.response["x-image-meta-status"] == 'deleted':
return True
except lib_exc.NotFound:
return True
diff --git a/tempest/services/volume/base/base_v3_client.py b/tempest/services/volume/base/base_v3_client.py
new file mode 100644
index 0000000..ad6f760
--- /dev/null
+++ b/tempest/services/volume/base/base_v3_client.py
@@ -0,0 +1,46 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.common import api_version_utils
+from tempest.lib.common import rest_client
+
+VOLUME_MICROVERSION = None
+
+
+class BaseV3Client(rest_client.RestClient):
+ """Base class to handle Cinder v3 client microversion support."""
+ api_version = 'v3'
+ api_microversion_header_name = 'Openstack-Api-Version'
+
+ def get_headers(self, accept_type=None, send_type=None):
+ headers = super(BaseV3Client, self).get_headers(
+ accept_type=accept_type, send_type=send_type)
+ if VOLUME_MICROVERSION:
+ headers[self.api_microversion_header_name] = ('volume %s' %
+ VOLUME_MICROVERSION)
+ return headers
+
+ def request(self, method, url, extra_headers=False, headers=None,
+ body=None, chunked=False):
+
+ resp, resp_body = super(BaseV3Client, self).request(
+ method, url, extra_headers, headers, body, chunked)
+ if (VOLUME_MICROVERSION and
+ VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
+ api_version_utils.assert_version_header_matches_request(
+ self.api_microversion_header_name,
+ 'volume %s' % VOLUME_MICROVERSION,
+ resp)
+ return resp, resp_body
diff --git a/tempest/services/volume/v3/__init__.py b/tempest/services/volume/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/v3/__init__.py
diff --git a/tempest/services/volume/v3/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/v3/json/__init__.py
diff --git a/tempest/services/volume/v3/json/messages_client.py b/tempest/services/volume/v3/json/messages_client.py
new file mode 100644
index 0000000..6be6d59
--- /dev/null
+++ b/tempest/services/volume/v3/json/messages_client.py
@@ -0,0 +1,59 @@
+# Copyright 2016 Andrew Kerr
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.services.volume.base import base_v3_client
+
+
+class MessagesClient(base_v3_client.BaseV3Client):
+ """Client class to send user messages API requests."""
+
+ def show_message(self, message_id):
+ """Show details for a single message."""
+ url = 'messages/%s' % str(message_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_messages(self):
+ """List all messages."""
+ url = 'messages'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_message(self, message_id):
+ """Delete a single message."""
+ url = 'messages/%s' % str(message_id)
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_message(id)
+ except lib_exc.NotFound:
+ return True
+ return False
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'message'
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index f253802..b3931d1 100755
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -234,7 +234,7 @@
self.assertIn('admin', resource_types)
self.assertIn(['fake_operator'], resource_types)
self.assertIn(['fake_reseller'], resource_types)
- self.assertIn(['fake_owner'], resource_types)
+ self.assertIn(['fake_owner', 'fake_operator'], resource_types)
for resource in resources:
self.assertIsNotNone(resource[1].network)
self.assertIsNotNone(resource[1].router)
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index 2d0256a..5ec9720 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -92,7 +92,7 @@
javelin.create_tenants([self.fake_object['name']])
mocked_function = self.fake_client.tenants.create_tenant
- mocked_function.assert_called_once_with(self.fake_object['name'])
+ mocked_function.assert_called_once_with(name=self.fake_object['name'])
def test_create_duplicate_tenant(self):
self.fake_client.tenants.list_tenants.return_value = {'tenants': [
@@ -120,11 +120,12 @@
fake_tenant_id = self.fake_object['tenant']['id']
fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
mocked_function = self.fake_client.users.create_user
- mocked_function.assert_called_once_with(self.fake_object['name'],
- self.fake_object['password'],
- fake_tenant_id,
- fake_email,
- enabled=True)
+ mocked_function.assert_called_once_with(
+ name=self.fake_object['name'],
+ password=self.fake_object['password'],
+ tenantId=fake_tenant_id,
+ email=fake_email,
+ enabled=True)
def test_create_user_missing_tenant(self):
self.useFixture(mockpatch.Patch(
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index c4bd7b2..2639d93 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -41,8 +41,8 @@
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
return_code = process.returncode
- msg = ("%s failled with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
- stdout, stderr))
+ msg = ("%s failed with:\nstdout: %s\nstderr: %s" % (' '.join(cmd),
+ stdout, stderr))
self.assertEqual(return_code, expected, msg)
def test_run_workspace_list(self):
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index a7a3a22..e97f65f 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -54,7 +54,7 @@
users_client = v2_users_client
token_client_class = token_client.TokenClient
fake_response = fake_identity._fake_v2_response
- assign_role_on_project = 'assign_user_role'
+ assign_role_on_project = 'create_user_role_on_project'
tenants_client_class = tenants_client.TenantsClient
delete_tenant = 'delete_tenant'
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
new file mode 100644
index 0000000..fdd0ae8
--- /dev/null
+++ b/tempest/tests/common/test_image.py
@@ -0,0 +1,40 @@
+# Copyright 2016 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.common import image
+from tempest.lib.common import rest_client
+from tempest.tests import base
+
+
+class TestImage(base.TestCase):
+
+ def test_get_image_meta_from_headers(self):
+ resp = {
+ 'x-image-meta-id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+ 'x-image-meta-owner': '8f421f9470e645b1b10f5d2db7804924',
+ 'x-image-meta-status': 'queued',
+ 'x-image-meta-name': 'New Http Image'
+ }
+ respbody = rest_client.ResponseBody(resp)
+ observed = image.get_image_meta_from_headers(respbody)
+
+ expected = {
+ 'properties': {},
+ 'id': 'ea30c926-0629-4400-bb6e-f8a8da6a4e56',
+ 'owner': '8f421f9470e645b1b10f5d2db7804924',
+ 'status': 'queued',
+ 'name': 'New Http Image'
+ }
+ self.assertEqual(expected, observed)
diff --git a/tempest/tests/lib/services/image/v1/__init__.py b/tempest/tests/lib/services/image/v1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/lib/services/image/v1/__init__.py
diff --git a/tempest/tests/lib/services/image/v1/test_image_members_client.py b/tempest/tests/lib/services/image/v1/test_image_members_client.py
new file mode 100644
index 0000000..a5a6128
--- /dev/null
+++ b/tempest/tests/lib/services/image/v1/test_image_members_client.py
@@ -0,0 +1,84 @@
+# Copyright 2016 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.image.v1 import image_members_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImageMembersClient(base.BaseServiceTest):
+ FAKE_LIST_IMAGE_MEMBERS = {
+ "members": [
+ {
+ "created_at": "2013-10-07T17:58:03Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "123456789",
+ "status": "pending",
+ "updated_at": "2013-10-07T17:58:03Z"
+ },
+ {
+ "created_at": "2013-10-07T17:58:55Z",
+ "image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
+ "member_id": "987654321",
+ "status": "accepted",
+ "updated_at": "2013-10-08T12:08:55Z"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestImageMembersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = image_members_client.ImageMembersClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_list_image_members(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_image_members,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_IMAGE_MEMBERS,
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e")
+
+ def _test_create_image_member(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_image_member,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ {},
+ bytes_body,
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ status=204)
+
+ def test_list_image_members_with_str_body(self):
+ self._test_list_image_members()
+
+ def test_list_image_members_with_bytes_body(self):
+ self._test_list_image_members(bytes_body=True)
+
+ def test_create_image_member_with_str_body(self):
+ self._test_create_image_member()
+
+ def test_create_image_member_with_bytes_body(self):
+ self._test_create_image_member(bytes_body=True)
+
+ def test_delete_image_member(self):
+ self.check_service_client_function(
+ self.client.delete_image_member,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
+ member_id="8989447062e04a818baf9e073fd04fa7",
+ status=204)
diff --git a/tempest/tests/lib/services/network/test_versions_client.py b/tempest/tests/lib/services/network/test_versions_client.py
new file mode 100644
index 0000000..715176b
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_versions_client.py
@@ -0,0 +1,77 @@
+# Copyright 2016 VMware, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import copy
+
+from tempest.lib.services.network.versions_client import NetworkVersionsClient
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNetworkVersionsClient(base.BaseServiceTest):
+
+ FAKE_INIT_VERSION = {
+ "version": {
+ "id": "v2.0",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2.0/",
+ "rel": "self"
+ },
+ {
+ "href": "http://docs.openstack.org/",
+ "rel": "describedby",
+ "type": "text/html"
+ }
+ ],
+ "status": "CURRENT",
+ "updated": "2013-07-23T11:33:21Z",
+ "version": "2.0",
+ "min_version": "2.0"
+ }
+ }
+
+ FAKE_VERSIONS_INFO = {
+ "versions": [FAKE_INIT_VERSION["version"]]
+ }
+
+ FAKE_VERSION_INFO = copy.deepcopy(FAKE_INIT_VERSION)
+
+ FAKE_VERSION_INFO["version"]["media-types"] = [
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.network+json;version=2.0"
+ }
+ ]
+
+ def setUp(self):
+ super(TestNetworkVersionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.versions_client = (
+ NetworkVersionsClient
+ (fake_auth, 'compute', 'regionOne'))
+
+ def _test_versions_client(self, bytes_body=False):
+ self.check_service_client_function(
+ self.versions_client.list_versions,
+ 'tempest.lib.common.rest_client.RestClient.raw_request',
+ self.FAKE_VERSIONS_INFO,
+ bytes_body,
+ 200)
+
+ def test_list_versions_client_with_str_body(self):
+ self._test_versions_client()
+
+ def test_list_versions_client_with_bytes_body(self):
+ self._test_versions_client(bytes_body=True)
diff --git a/tempest/tests/services/identity/__init__.py b/tempest/tests/services/identity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/services/identity/__init__.py
diff --git a/tempest/tests/services/identity/v2/__init__.py b/tempest/tests/services/identity/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/services/identity/v2/__init__.py
diff --git a/tempest/tests/services/identity/v2/test_roles_client.py b/tempest/tests/services/identity/v2/test_roles_client.py
new file mode 100644
index 0000000..e36ec18
--- /dev/null
+++ b/tempest/tests/services/identity/v2/test_roles_client.py
@@ -0,0 +1,141 @@
+# Copyright 2016 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.services.identity.v2.json import roles_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRolesClient(base.BaseServiceTest):
+ FAKE_ROLE_INFO = {
+ "role": {
+ "id": "1",
+ "name": "test",
+ "description": "test_description"
+ }
+ }
+
+ FAKE_LIST_ROLES = {
+ "roles": [
+ {
+ "id": "1",
+ "name": "test",
+ "description": "test_description"
+ },
+ {
+ "id": "2",
+ "name": "test2",
+ "description": "test2_description"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestRolesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = roles_client.RolesClient(fake_auth,
+ 'identity', 'regionOne')
+
+ def _test_create_role(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_role,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ id="1",
+ name="test",
+ description="test_description")
+
+ def _test_show_role(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_role,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ role_id_or_name="1")
+
+ def _test_list_roles(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_roles,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ROLES,
+ bytes_body)
+
+ def _test_create_user_role_on_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_user_role_on_project,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_ROLE_INFO,
+ bytes_body,
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123",
+ role_id="1234",
+ status=200)
+
+ def _test_list_user_roles_on_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_user_roles_on_project,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ROLES,
+ bytes_body,
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123")
+
+ def test_create_role_with_str_body(self):
+ self._test_create_role()
+
+ def test_create_role_with_bytes_body(self):
+ self._test_create_role(bytes_body=True)
+
+ def test_show_role_with_str_body(self):
+ self._test_show_role()
+
+ def test_show_role_with_bytes_body(self):
+ self._test_show_role(bytes_body=True)
+
+ def test_list_roles_with_str_body(self):
+ self._test_list_roles()
+
+ def test_list_roles_with_bytes_body(self):
+ self._test_list_roles(bytes_body=True)
+
+ def test_delete_role(self):
+ self.check_service_client_function(
+ self.client.delete_role,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ role_id="1",
+ status=204)
+
+ def test_create_user_role_on_project_with_str_body(self):
+ self._test_create_user_role_on_project()
+
+ def test_create_user_role_on_project_with_bytes_body(self):
+ self._test_create_user_role_on_project(bytes_body=True)
+
+ def test_list_user_roles_on_project_with_str_body(self):
+ self._test_list_user_roles_on_project()
+
+ def test_list_user_roles_on_project_with_bytes_body(self):
+ self._test_list_user_roles_on_project(bytes_body=True)
+
+ def test_delete_role_from_user_on_project(self):
+ self.check_service_client_function(
+ self.client.delete_role_from_user_on_project,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ tenant_id="b344506af7644f6794d9cb316600b020",
+ user_id="123",
+ role_id="1234",
+ status=204)