Merge "Add simple test for Neutron GET /"
diff --git a/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
new file mode 100644
index 0000000..b7850d0
--- /dev/null
+++ b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Define identity service clients as libraries
+ The following identity service clients are defined as library interface,
+ so the other projects can use these modules as stable libraries without
+ any maintenance changes.
+
+ * endpoints_client(v2)
diff --git a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
index b50ed38..faae7d0 100644
--- a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
+++ b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
@@ -6,7 +6,8 @@
so the other projects can use these modules as stable libraries
without any maintenance changes.
- * image_members_client
- * namespaces_client
- * resource_types_client
- * schemas_client
+ * image_members_client(v2)
+ * images_client(v2)
+ * namespaces_client(v2)
+ * resource_types_client(v2)
+ * schemas_client(v2)
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index dabc45e..84b00a7 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -39,12 +39,13 @@
cls.az_name_prefix = 'test_az'
cls.host = None
- hypers = cls.os_adm.hypervisor_client.list_hypervisors()['hypervisors']
- hypers_available = [hyper['hypervisor_hostname'] for hyper in hypers
- if (hyper['state'] == 'up' and
- hyper['status'] == 'enabled')]
- if hypers_available:
- cls.host = hypers_available[0]
+ hypers = cls.os_adm.hypervisor_client.list_hypervisors(
+ detail=True)['hypervisors']
+ hosts_available = [hyper['service']['host'] for hyper in hypers
+ if (hyper['state'] == 'up' and
+ hyper['status'] == 'enabled')]
+ if hosts_available:
+ cls.host = hosts_available[0]
else:
raise testtools.TestCase.failureException(
"no available compute node found")
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 df75d0a..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
@@ -37,8 +38,8 @@
region = data_utils.rand_name('region')
url = data_utils.rand_url()
endpoint = cls.endpoints_client.create_endpoint(
- cls.service_id,
- region,
+ service_id=cls.service_id,
+ region=region,
publicurl=url,
adminurl=url,
internalurl=url)['endpoint']
@@ -70,8 +71,8 @@
region = data_utils.rand_name('region')
url = data_utils.rand_url()
endpoint = self.endpoints_client.create_endpoint(
- self.service_id,
- region,
+ service_id=self.service_id,
+ region=region,
publicurl=url,
adminurl=url,
internalurl=url)['endpoint']
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_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index ee04420..2297a9d 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -30,8 +30,10 @@
tenant = self.tenants_client.create_tenant(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,8 +64,10 @@
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.
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index d860d2f..0f783b3 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
@@ -180,9 +186,11 @@
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'],
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/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 27ff15d..24a7a4e 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -16,6 +16,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest import test
CONF = config.CONF
@@ -23,44 +24,78 @@
class DomainsTestJSON(base.BaseIdentityV3AdminTest):
+ @classmethod
+ def resource_setup(cls):
+ super(DomainsTestJSON, cls).resource_setup()
+ # Create some test domains to be used during tests
+ # One of those domains will be disabled
+ cls.setup_domains = list()
+ for i in range(3):
+ domain = cls.domains_client.create_domain(
+ data_utils.rand_name('domain'),
+ description=data_utils.rand_name('domain-desc'),
+ enabled=i < 2)['domain']
+ cls.setup_domains.append(domain)
+
+ @classmethod
+ def resource_cleanup(cls):
+ for domain in cls.setup_domains:
+ cls._delete_domain(domain['id'])
+ super(DomainsTestJSON, cls).resource_cleanup()
+
+ @classmethod
def _delete_domain(self, domain_id):
# It is necessary to disable the domain before deleting,
# or else it would result in unauthorized error
self.domains_client.update_domain(domain_id, enabled=False)
self.domains_client.delete_domain(domain_id)
- # Asserting that the domain is not found in the list
- # after deletion
- body = self.domains_client.list_domains()['domains']
- domains_list = [d['id'] for d in body]
- self.assertNotIn(domain_id, domains_list)
@test.idempotent_id('8cf516ef-2114-48f1-907b-d32726c734d4')
def test_list_domains(self):
# Test to list domains
- domain_ids = list()
fetched_ids = list()
- for _ in range(3):
- domain = self.domains_client.create_domain(
- data_utils.rand_name('domain'),
- description=data_utils.rand_name('domain-desc'))['domain']
- # Delete the domain at the end of this method
- self.addCleanup(self._delete_domain, domain['id'])
- domain_ids.append(domain['id'])
# List and Verify Domains
body = self.domains_client.list_domains()['domains']
for d in body:
fetched_ids.append(d['id'])
- missing_doms = [d for d in domain_ids if d not in fetched_ids]
+ missing_doms = [d for d in self.setup_domains
+ if d['id'] not in fetched_ids]
self.assertEqual(0, len(missing_doms))
+ @test.idempotent_id('c6aee07b-4981-440c-bb0b-eb598f58ffe9')
+ def test_list_domains_filter_by_name(self):
+ # List domains filtering by name
+ params = {'name': self.setup_domains[0]['name']}
+ fetched_domains = self.domains_client.list_domains(
+ params=params)['domains']
+ # Verify the filtered list is correct, domain names are unique
+ # so exactly one domain should be found with the provided name
+ self.assertEqual(1, len(fetched_domains))
+ self.assertEqual(self.setup_domains[0]['name'],
+ fetched_domains[0]['name'])
+
+ @test.idempotent_id('3fd19840-65c1-43f8-b48c-51bdd066dff9')
+ def test_list_domains_filter_by_enabled(self):
+ # List domains filtering by enabled domains
+ params = {'enabled': True}
+ fetched_domains = self.domains_client.list_domains(
+ params=params)['domains']
+ # Verify the filtered list is correct
+ self.assertIn(self.setup_domains[0], fetched_domains)
+ self.assertIn(self.setup_domains[1], fetched_domains)
+ for domain in fetched_domains:
+ self.assertEqual(True, domain['enabled'])
+
@test.attr(type='smoke')
@test.idempotent_id('f2f5b44a-82e8-4dad-8084-0661ea3b18cf')
def test_create_update_delete_domain(self):
+ # Create domain
d_name = data_utils.rand_name('domain')
d_desc = data_utils.rand_name('domain-desc')
domain = self.domains_client.create_domain(
d_name, description=d_desc)['domain']
- self.addCleanup(self._delete_domain, domain['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self._delete_domain, domain['id'])
self.assertIn('id', domain)
self.assertIn('description', domain)
self.assertIn('name', domain)
@@ -70,11 +105,12 @@
self.assertEqual(d_name, domain['name'])
self.assertEqual(d_desc, domain['description'])
self.assertEqual(True, domain['enabled'])
+ # Update domain
new_desc = data_utils.rand_name('new-desc')
new_name = data_utils.rand_name('new-name')
-
updated_domain = self.domains_client.update_domain(
- domain['id'], name=new_name, description=new_desc)['domain']
+ domain['id'], name=new_name, description=new_desc,
+ enabled=False)['domain']
self.assertIn('id', updated_domain)
self.assertIn('description', updated_domain)
self.assertIn('name', updated_domain)
@@ -83,13 +119,18 @@
self.assertIsNotNone(updated_domain['id'])
self.assertEqual(new_name, updated_domain['name'])
self.assertEqual(new_desc, updated_domain['description'])
- self.assertEqual(True, updated_domain['enabled'])
-
+ self.assertEqual(False, updated_domain['enabled'])
+ # Show domain
fetched_domain = self.domains_client.show_domain(
domain['id'])['domain']
self.assertEqual(new_name, fetched_domain['name'])
self.assertEqual(new_desc, fetched_domain['description'])
- self.assertEqual(True, fetched_domain['enabled'])
+ self.assertEqual(False, fetched_domain['enabled'])
+ # Delete domain
+ self.domains_client.delete_domain(domain['id'])
+ body = self.domains_client.list_domains()['domains']
+ domains_list = [d['id'] for d in body]
+ self.assertNotIn(domain['id'], domains_list)
@test.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
def test_create_domain_with_disabled_status(self):
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 928437c..86f6b12 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -37,9 +37,15 @@
cls.p2 = cls.projects_client.create_project(p2_name)['project']
cls.data.projects.append(cls.p2)
cls.project_ids.append(cls.p2['id'])
+ # Create a new project (p3) using p2 as parent project
+ p3_name = data_utils.rand_name('project')
+ cls.p3 = cls.projects_client.create_project(
+ p3_name, parent_id=cls.p2['id'])['project']
+ cls.data.projects.append(cls.p3)
+ cls.project_ids.append(cls.p3['id'])
@test.idempotent_id('1d830662-22ad-427c-8c3e-4ec854b0af44')
- def test_projects_list(self):
+ def test_list_projects(self):
# List projects
list_projects = self.projects_client.list_projects()['projects']
@@ -63,6 +69,16 @@
# List projects with name
self._list_projects_with_params({'name': self.p1_name}, 'name')
+ @test.idempotent_id('6edc66f5-2941-4a17-9526-4073311c1fac')
+ def test_list_projects_with_parent(self):
+ # List projects with parent
+ params = {'parent_id': self.p3['parent_id']}
+ fetched_projects = self.projects_client.list_projects(
+ params)['projects']
+ self.assertNotEmpty(fetched_projects)
+ for project in fetched_projects:
+ self.assertEqual(self.p3['parent_id'], project['parent_id'])
+
def _list_projects_with_params(self, params, key):
body = self.projects_client.list_projects(params)['projects']
self.assertIn(self.p1[key], map(lambda x: x[key], body))
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index ece36b9..95894a6 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -15,7 +15,7 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -42,18 +42,19 @@
cls.client.delete_region(r['id'])
super(RegionsTestJSON, cls).resource_cleanup()
- def _delete_region(self, region_id):
- self.client.delete_region(region_id)
- self.assertRaises(lib_exc.NotFound,
- self.client.show_region, region_id)
-
@test.idempotent_id('56186092-82e4-43f2-b954-91013218ba42')
def test_create_update_get_delete_region(self):
+ # Create region
r_description = data_utils.rand_name('description')
region = self.client.create_region(
description=r_description,
parent_region_id=self.setup_regions[0]['id'])['region']
- self.addCleanup(self._delete_region, region['id'])
+ # This test will delete the region as part of the validation
+ # procedure, so it needs a different cleanup method that
+ # would be useful in case the tests fails at any point before
+ # reaching the deletion part.
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_region, region['id'])
self.assertEqual(r_description, region['description'])
self.assertEqual(self.setup_regions[0]['id'],
region['parent_region_id'])
@@ -71,6 +72,11 @@
self.assertEqual(r_alt_description, region['description'])
self.assertEqual(self.setup_regions[1]['id'],
region['parent_region_id'])
+ # Delete the region
+ self.client.delete_region(region['id'])
+ body = self.client.list_regions()['regions']
+ regions_list = [r['id'] for r in body]
+ self.assertNotIn(region['id'], regions_list)
@test.attr(type='smoke')
@test.idempotent_id('2c12c5b5-efcf-4aa5-90c5-bff1ab0cdbe2')
@@ -80,7 +86,7 @@
r_description = data_utils.rand_name('description')
region = self.client.create_region(
region_id=r_region_id, description=r_description)['region']
- self.addCleanup(self._delete_region, region['id'])
+ self.addCleanup(self.client.delete_region, region['id'])
# Asserting Create Region with specific id response body
self.assertEqual(r_region_id, region['id'])
self.assertEqual(r_description, region['description'])
@@ -95,3 +101,20 @@
self.assertEqual(0, len(missing_regions),
"Failed to find region %s in fetched list" %
', '.join(str(e) for e in missing_regions))
+
+ @test.idempotent_id('2d1057cb-bbde-413a-acdf-e2d265284542')
+ def test_list_regions_filter_by_parent_region_id(self):
+ # Add a sub-region to one of the existing test regions
+ r_description = data_utils.rand_name('description')
+ region = self.client.create_region(
+ description=r_description,
+ parent_region_id=self.setup_regions[0]['id'])['region']
+ self.addCleanup(self.client.delete_region, region['id'])
+ # Get the list of regions filtering with the parent_region_id
+ params = {'parent_region_id': self.setup_regions[0]['id']}
+ fetched_regions = self.client.list_regions(params=params)['regions']
+ # Asserting list regions response
+ self.assertIn(region, fetched_regions)
+ for r in fetched_regions:
+ self.assertEqual(self.setup_regions[0]['id'],
+ r['parent_region_id'])
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_image_members.py b/tempest/api/image/v1/test_image_members.py
index 0bad96a..94edb6c 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -22,7 +22,7 @@
@test.idempotent_id('1d6ef640-3a20-4c84-8710-d95828fdb6ad')
def test_add_image_member(self):
image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image)
+ self.image_member_client.create_image_member(image, self.alt_tenant_id)
body = self.image_member_client.list_image_members(image)
members = body['members']
members = map(lambda x: x['member_id'], members)
@@ -33,9 +33,10 @@
@test.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
def test_get_shared_images(self):
image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image)
+ self.image_member_client.create_image_member(image, self.alt_tenant_id)
share_image = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, share_image)
+ self.image_member_client.create_image_member(share_image,
+ self.alt_tenant_id)
body = self.image_member_client.list_shared_images(
self.alt_tenant_id)
images = body['shared_images']
@@ -46,8 +47,10 @@
@test.idempotent_id('a76a3191-8948-4b44-a9d6-4053e5f2b138')
def test_remove_member(self):
image_id = self._create_image()
- self.image_member_client.add_member(self.alt_tenant_id, image_id)
- self.image_member_client.delete_member(self.alt_tenant_id, image_id)
+ self.image_member_client.create_image_member(image_id,
+ self.alt_tenant_id)
+ self.image_member_client.delete_image_member(image_id,
+ self.alt_tenant_id)
body = self.image_member_client.list_image_members(image_id)
members = body['members']
self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index d46a836..2538781 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -26,8 +26,8 @@
# Add member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.add_member,
- self.alt_tenant_id, non_exist_image)
+ self.image_member_client.create_image_member,
+ non_exist_image, self.alt_tenant_id)
@test.attr(type=['negative'])
@test.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
@@ -35,8 +35,8 @@
# Delete member with non existing image.
non_exist_image = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.delete_member,
- self.alt_tenant_id, non_exist_image)
+ self.image_member_client.delete_image_member,
+ non_exist_image, self.alt_tenant_id)
@test.attr(type=['negative'])
@test.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
@@ -45,8 +45,8 @@
image_id = self._create_image()
non_exist_tenant = data_utils.rand_uuid_hex()
self.assertRaises(lib_exc.NotFound,
- self.image_member_client.delete_member,
- non_exist_tenant, image_id)
+ self.image_member_client.delete_image_member,
+ image_id, non_exist_tenant)
@test.attr(type=['negative'])
@test.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
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/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 27ccae5..cd24d17 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,9 +13,9 @@
# under the License.
import six
-
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest.common import waiters
from tempest import test
QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes']
@@ -25,10 +25,13 @@
class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
+ credentials = ['primary', 'alt', 'admin']
+
@classmethod
def setup_credentials(cls):
super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
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
@@ -121,6 +124,54 @@
['quota_set'])
self.assertEqual(volume_default, quota_set_new['volumes'])
+ @test.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
+ def test_quota_usage_after_volume_transfer(self):
+ # Create a volume for transfer
+ volume = self.create_volume()
+ self.addCleanup(self._delete_volume, volume['id'])
+
+ # List of tenants quota usage pre-transfer
+ primary_quota = self.admin_quotas_client.show_quota_usage(
+ self.demo_tenant_id)['quota_set']
+
+ alt_quota = self.admin_quotas_client.show_quota_usage(
+ self.alt_client.tenant_id)['quota_set']
+
+ # Creates a volume transfer
+ transfer = self.volumes_client.create_volume_transfer(
+ volume_id=volume['id'])['transfer']
+ transfer_id = transfer['id']
+ auth_key = transfer['auth_key']
+
+ # Accepts a volume transfer
+ self.alt_client.accept_volume_transfer(
+ transfer_id, auth_key=auth_key)['transfer']
+
+ # Verify volume transferred is available
+ waiters.wait_for_volume_status(
+ self.alt_client, volume['id'], 'available')
+
+ # List of tenants quota usage post transfer
+ new_primary_quota = self.admin_quotas_client.show_quota_usage(
+ self.demo_tenant_id)['quota_set']
+
+ new_alt_quota = self.admin_quotas_client.show_quota_usage(
+ self.alt_client.tenant_id)['quota_set']
+
+ # Verify tenants quota usage was updated
+ self.assertEqual(primary_quota['volumes']['in_use'] -
+ new_primary_quota['volumes']['in_use'],
+ new_alt_quota['volumes']['in_use'] -
+ alt_quota['volumes']['in_use'])
+
+ self.assertEqual(alt_quota['gigabytes']['in_use'] +
+ volume['size'],
+ new_alt_quota['gigabytes']['in_use'])
+
+ self.assertEqual(primary_quota['gigabytes']['in_use'] -
+ volume['size'],
+ new_primary_quota['gigabytes']['in_use'])
+
class VolumeQuotasAdminV1TestJSON(BaseVolumeQuotasAdminV2TestJSON):
_api_version = 1
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..9010c89 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)
diff --git a/tempest/services/image/v2/__init__.py b/tempest/api/volume/v3/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to tempest/api/volume/v3/__init__.py
diff --git a/tempest/services/image/v2/__init__.py b/tempest/api/volume/v3/admin/__init__.py
similarity index 100%
copy from tempest/services/image/v2/__init__.py
copy to 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..19c37be
--- /dev/null
+++ b/tempest/api/volume/v3/admin/test_user_messages.py
@@ -0,0 +1,98 @@
+# 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 _delete_volume(self, volume_id):
+ self.volumes_client.delete_volume(volume_id)
+ self.volumes_client.wait_for_resource_deletion(volume_id)
+
+ 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, 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 b84fe4d..7a7d15d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -73,10 +73,13 @@
from tempest.lib.services.compute.versions_client import VersionsClient
from tempest.lib.services.compute.volumes_client import \
VolumesClient as ComputeVolumesClient
+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.v2.image_members_client import \
ImageMembersClient as ImageMembersClientV2
+from tempest.lib.services.image.v2.images_client import \
+ ImagesClient as ImagesV2Client
from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
@@ -114,7 +117,6 @@
DatabaseLimitsClient
from tempest.services.database.json.versions_client import \
DatabaseVersionsClient
-from tempest.services.identity.v2.json.endpoints_client import EndpointsClient
from tempest.services.identity.v2.json.identity_client import IdentityClient
from tempest.services.identity.v2.json.roles_client import RolesClient
from tempest.services.identity.v2.json.services_client import \
@@ -142,8 +144,6 @@
from tempest.services.image.v1.json.image_members_client import \
ImageMembersClient
from tempest.services.image.v1.json.images_client import ImagesClient
-from tempest.services.image.v2.json.images_client import \
- ImagesClient as ImagesV2Client
from tempest.services.object_storage.account_client import AccountClient
from tempest.services.object_storage.container_client import ContainerClient
from tempest.services.object_storage.object_client import ObjectClient
@@ -185,6 +185,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__)
@@ -512,6 +513,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 0a5a41b..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 = []
@@ -234,7 +235,7 @@
parser.add_argument('-r', '--concurrency',
default=1,
type=int,
- required=True,
+ required=False,
dest='concurrency',
help='Concurrency count')
parser.add_argument('--with-admin',
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 6a65fcb..7ce6225 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -126,6 +126,7 @@
from tempest.lib.services.compute import security_group_rules_client
from tempest.lib.services.compute import security_groups_client
from tempest.lib.services.compute import servers_client
+from tempest.lib.services.image.v2 import images_client
from tempest.lib.services.network import networks_client
from tempest.lib.services.network import ports_client
from tempest.lib.services.network import routers_client
@@ -134,7 +135,6 @@
from tempest.services.identity.v2.json import roles_client
from tempest.services.identity.v2.json import tenants_client
from tempest.services.identity.v2.json import users_client
-from tempest.services.image.v2.json import images_client
from tempest.services.object_storage import container_client
from tempest.services.object_storage import object_client
from tempest.services.volume.v1.json import volumes_client
@@ -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/common/cred_client.py b/tempest/common/cred_client.py
index aac036b..48d81ca 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']
@@ -143,6 +151,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/cred_provider.py b/tempest/common/cred_provider.py
index 5cce0bb..bf6c537 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -16,8 +16,8 @@
import six
-from tempest import exceptions
from tempest.lib import auth
+from tempest.lib import exceptions
@six.add_metaclass(abc.ABCMeta)
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/exceptions.py b/tempest/exceptions.py
index 92f335f..f534f30 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -13,157 +13,65 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
+
+from tempest.lib import exceptions
-class TempestException(Exception):
- """Base Tempest Exception
-
- To correctly use this class, inherit from it and define
- a 'message' property. That message will get printf'd
- with the keyword arguments provided to the constructor.
- """
- message = "An unknown exception occurred"
-
- def __init__(self, *args, **kwargs):
- super(TempestException, self).__init__()
- try:
- self._error_string = self.message % kwargs
- except Exception:
- # at least get the core message out if something happened
- self._error_string = self.message
- if len(args) > 0:
- # If there is a non-kwarg parameter, assume it's the error
- # message or reason description and tack it on to the end
- # of the exception message
- # Convert all arguments into their string representations...
- args = ["%s" % arg for arg in args]
- self._error_string = (self._error_string +
- "\nDetails: %s" % '\n'.join(args))
-
- def __str__(self):
- return self._error_string
-
-
-class RestClientException(TempestException,
- testtools.TestCase.failureException):
- pass
-
-
-class InvalidConfiguration(TempestException):
+class InvalidConfiguration(exceptions.TempestException):
message = "Invalid Configuration"
-class InvalidCredentials(TempestException):
- message = "Invalid Credentials"
-
-
-class InvalidServiceTag(TempestException):
+class InvalidServiceTag(exceptions.TempestException):
message = "Invalid service tag"
-class InvalidIdentityVersion(TempestException):
- message = "Invalid version %(identity_version)s of the identity service"
-
-
-class TimeoutException(TempestException):
+class TimeoutException(exceptions.TempestException):
message = "Request timed out"
-class BuildErrorException(TempestException):
+class BuildErrorException(exceptions.TempestException):
message = "Server %(server_id)s failed to build and is in ERROR status"
-class ImageKilledException(TempestException):
+class ImageKilledException(exceptions.TempestException):
message = "Image %(image_id)s 'killed' while waiting for '%(status)s'"
-class AddImageException(TempestException):
+class AddImageException(exceptions.TempestException):
message = "Image %(image_id)s failed to become ACTIVE in the allotted time"
-class VolumeBuildErrorException(TempestException):
+class VolumeBuildErrorException(exceptions.TempestException):
message = "Volume %(volume_id)s failed to build and is in ERROR status"
-class VolumeRestoreErrorException(TempestException):
+class VolumeRestoreErrorException(exceptions.TempestException):
message = "Volume %(volume_id)s failed to restore and is in ERROR status"
-class SnapshotBuildErrorException(TempestException):
+class SnapshotBuildErrorException(exceptions.TempestException):
message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
-class VolumeBackupException(TempestException):
+class VolumeBackupException(exceptions.TempestException):
message = "Volume backup %(backup_id)s failed and is in ERROR status"
-class StackBuildErrorException(TempestException):
+class StackBuildErrorException(exceptions.TempestException):
message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
"due to '%(stack_status_reason)s'")
-class EndpointNotFound(TempestException):
- message = "Endpoint not found"
-
-
-class IdentityError(TempestException):
- message = "Got identity error"
-
-
-class ServerUnreachable(TempestException):
+class ServerUnreachable(exceptions.TempestException):
message = "The server is not reachable via the configured network"
# NOTE(andreaf) This exception is added here to facilitate the migration
# of get_network_from_name and preprov_creds to tempest.lib, and it should
# be migrated along with them
-class InvalidTestResource(TempestException):
+class InvalidTestResource(exceptions.TempestException):
message = "%(name) is not a valid %(type), or the name is ambiguous"
-class RFCViolation(RestClientException):
+class RFCViolation(exceptions.RestClientException):
message = "RFC Violation"
-
-
-class InvalidHttpSuccessCode(RestClientException):
- message = "The success code is different than the expected one"
-
-
-class BadRequest(RestClientException):
- message = "Bad request"
-
-
-class ResponseWithNonEmptyBody(RFCViolation):
- message = ("RFC Violation! Response with %(status)d HTTP Status Code "
- "MUST NOT have a body")
-
-
-class ResponseWithEntity(RFCViolation):
- message = ("RFC Violation! Response with 205 HTTP Status Code "
- "MUST NOT have an entity")
-
-
-class InvalidHTTPResponseHeader(RestClientException):
- message = "HTTP response header is invalid"
-
-
-class InvalidStructure(TempestException):
- message = "Invalid structure of table with details"
-
-
-class CommandFailed(Exception):
- def __init__(self, returncode, cmd, output, stderr):
- super(CommandFailed, self).__init__()
- self.returncode = returncode
- self.cmd = cmd
- self.stdout = output
- self.stderr = stderr
-
- def __str__(self):
- return ("Command '%s' returned non-zero exit status %d.\n"
- "stdout:\n%s\n"
- "stderr:\n%s" % (self.cmd,
- self.returncode,
- self.stdout,
- self.stderr))
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 45e5067..f9f0c83 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,6 +19,7 @@
import string
import uuid
+from oslo_utils import netutils
import six.moves
@@ -183,7 +184,7 @@
:rtype: netaddr.IPAddress
"""
# Check if the prefix is IPv4 address
- is_ipv4 = netaddr.valid_ipv4(cidr)
+ is_ipv4 = netutils.is_valid_ipv4(cidr)
if is_ipv4:
msg = "Unable to generate IP address by EUI64 for IPv4 prefix"
raise TypeError(msg)
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index 259bbbb..2a6a788 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -149,6 +149,10 @@
message = "Unexpected response code received"
+class InvalidIdentityVersion(TempestException):
+ message = "Invalid version %(identity_version)s of the identity service"
+
+
class InvalidStructure(TempestException):
message = "Invalid structure of table with details"
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index 8e4eca1..0d31ac7 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -315,7 +315,11 @@
return self.action(server_id, 'os-start', **kwargs)
def attach_volume(self, server_id, **kwargs):
- """Attaches a volume to a server instance."""
+ """Attaches a volume to a server instance.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-compute-v2.1.html#attachVolume
+ """
post_body = json.dumps({'volumeAttachment': kwargs})
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
post_body)
diff --git a/tempest/services/identity/v2/json/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
similarity index 77%
rename from tempest/services/identity/v2/json/endpoints_client.py
rename to tempest/lib/services/identity/v2/endpoints_client.py
index ba9f867..f7b265d 100644
--- a/tempest/services/identity/v2/json/endpoints_client.py
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -20,16 +20,14 @@
class EndpointsClient(rest_client.RestClient):
api_version = "v2.0"
- def create_endpoint(self, service_id, region_id, **kwargs):
- """Create an endpoint for service."""
- post_body = {
- 'service_id': service_id,
- 'region': region_id,
- 'publicurl': kwargs.get('publicurl'),
- 'adminurl': kwargs.get('adminurl'),
- 'internalurl': kwargs.get('internalurl')
- }
- post_body = json.dumps({'endpoint': post_body})
+ def create_endpoint(self, **kwargs):
+ """Create an endpoint for service.
+
+ Available params: http://developer.openstack.org/
+ api-ref-identity-v2-ext.html#createEndpoint
+ """
+
+ post_body = json.dumps({'endpoint': kwargs})
resp, body = self.post('/endpoints', post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/image/v2/json/images_client.py b/tempest/lib/services/image/v2/images_client.py
similarity index 100%
rename from tempest/services/image/v2/json/images_client.py
rename to tempest/lib/services/image/v2/images_client.py
diff --git a/tempest/manager.py b/tempest/manager.py
index 72762ab..f2659a8 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -14,8 +14,8 @@
# under the License.
from tempest import config
-from tempest import exceptions
from tempest.lib import auth
+from tempest.lib import exceptions
CONF = config.CONF
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1dfa86d..dd6e0e5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -19,9 +19,11 @@
import netaddr
from oslo_log import log
from oslo_serialization import jsonutils as json
+from oslo_utils import netutils
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
@@ -29,7 +31,6 @@
from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
-from tempest.scenario import network_resources
import tempest.test
CONF = config.CONF
@@ -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.
@@ -704,12 +706,12 @@
tenant_id = networks_client.tenant_id
name = data_utils.rand_name(namestart)
result = networks_client.create_network(name=name, tenant_id=tenant_id)
- network = network_resources.DeletableNetwork(
- networks_client=networks_client, routers_client=routers_client,
- **result['network'])
- self.assertEqual(network.name, name)
+ network = result['network']
+
+ self.assertEqual(network['name'], name)
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- network.delete)
+ self.networks_client.delete_network,
+ network['id'])
return network
def _list_networks(self, *args, **kwargs):
@@ -779,13 +781,13 @@
# blocks until an unallocated block is found.
for subnet_cidr in tenant_cidr.subnet(num_bits):
str_cidr = str(subnet_cidr)
- if cidr_in_use(str_cidr, tenant_id=network.tenant_id):
+ if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
continue
subnet = dict(
name=data_utils.rand_name(namestart),
- network_id=network.id,
- tenant_id=network.tenant_id,
+ network_id=network['id'],
+ tenant_id=network['tenant_id'],
cidr=str_cidr,
ip_version=ip_version,
**kwargs
@@ -798,11 +800,13 @@
if not is_overlapping_cidr:
raise
self.assertIsNotNone(result, 'Unable to allocate tenant network')
- subnet = network_resources.DeletableSubnet(
- subnets_client=subnets_client,
- routers_client=routers_client, **result['subnet'])
- self.assertEqual(subnet.cidr, str_cidr)
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnet.delete)
+
+ subnet = result['subnet']
+ self.assertEqual(subnet['cidr'], str_cidr)
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ subnets_client.delete_subnet, subnet['id'])
+
return subnet
def _create_port(self, network_id, client=None, namestart='port-quotatest',
@@ -815,9 +819,9 @@
network_id=network_id,
**kwargs)
self.assertIsNotNone(result, 'Unable to allocate port')
- port = network_resources.DeletablePort(ports_client=client,
- **result['port'])
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, port.delete)
+ port = result['port']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ client.delete_port, port['id'])
return port
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
@@ -834,7 +838,7 @@
port_map = [(p["id"], fxip["ip_address"])
for p in ports
for fxip in p["fixed_ips"]
- if netaddr.valid_ipv4(fxip["ip_address"])
+ if netutils.is_valid_ipv4(fxip["ip_address"])
and p['status'] in p_status]
inactive = [p for p in ports if p['status'] != 'ACTIVE']
if inactive:
@@ -852,7 +856,7 @@
net = self._list_networks(name=network_name)
self.assertNotEqual(len(net), 0,
"Unable to get network by name: %s" % network_name)
- return network_resources.AttributeDict(net[0])
+ return net[0]
def create_floating_ip(self, thing, external_network_id=None,
port_id=None, client=None):
@@ -871,44 +875,51 @@
tenant_id=thing['tenant_id'],
fixed_ip_address=ip4
)
- floating_ip = network_resources.DeletableFloatingIp(
- client=client,
- **result['floatingip'])
+ floating_ip = result['floatingip']
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- floating_ip.delete)
+ self.floating_ips_client.delete_floatingip,
+ floating_ip['id'])
return floating_ip
def _associate_floating_ip(self, floating_ip, server):
port_id, _ = self._get_server_port_id_and_ip4(server)
- floating_ip.update(port_id=port_id)
- self.assertEqual(port_id, floating_ip.port_id)
+ kwargs = dict(port_id=port_id)
+ floating_ip = self.floating_ips_client.update_floatingip(
+ floating_ip['id'], **kwargs)['floatingip']
+ self.assertEqual(port_id, floating_ip['port_id'])
return floating_ip
def _disassociate_floating_ip(self, floating_ip):
- """:param floating_ip: type DeletableFloatingIp"""
- floating_ip.update(port_id=None)
- self.assertIsNone(floating_ip.port_id)
+ """:param floating_ip: floating_ips_client.create_floatingip"""
+ kwargs = dict(port_id=None)
+ floating_ip = self.floating_ips_client.update_floatingip(
+ floating_ip['id'], **kwargs)['floatingip']
+ self.assertIsNone(floating_ip['port_id'])
return floating_ip
def check_floating_ip_status(self, floating_ip, status):
"""Verifies floatingip reaches the given status
- :param floating_ip: network_resources.DeletableFloatingIp floating
- IP to check status
+ :param dict floating_ip: floating IP dict to check status
:param status: target status
:raises: AssertionError if status doesn't match
"""
+ floatingip_id = floating_ip['id']
+
def refresh():
- floating_ip.refresh()
- return status == floating_ip.status
+ result = (self.floating_ips_client.
+ show_floatingip(floatingip_id)['floatingip'])
+ return status == result['status']
tempest.test.call_until_true(refresh,
CONF.network.build_timeout,
CONF.network.build_interval)
- self.assertEqual(status, floating_ip.status,
+ floating_ip = self.floating_ips_client.show_floatingip(
+ floatingip_id)['floatingip']
+ self.assertEqual(status, floating_ip['status'],
message="FloatingIP: {fp} is at status: {cst}. "
"failed to reach status: {st}"
- .format(fp=floating_ip, cst=floating_ip.status,
+ .format(fp=floating_ip, cst=floating_ip['status'],
st=status))
LOG.info("FloatingIP: {fp} is at status: {st}"
.format(fp=floating_ip, st=status))
@@ -981,8 +992,8 @@
secgroup=secgroup,
security_groups_client=security_groups_client)
for rule in rules:
- self.assertEqual(tenant_id, rule.tenant_id)
- self.assertEqual(secgroup.id, rule.security_group_id)
+ self.assertEqual(tenant_id, rule['tenant_id'])
+ self.assertEqual(secgroup['id'], rule['security_group_id'])
return secgroup
def _create_empty_security_group(self, client=None, tenant_id=None,
@@ -994,7 +1005,7 @@
- IPv6 egress to any
:param tenant_id: secgroup will be created in this tenant
- :returns: DeletableSecurityGroup -- containing the secgroup created
+ :returns: the created security group
"""
if client is None:
client = self.security_groups_client
@@ -1006,15 +1017,14 @@
description=sg_desc)
sg_dict['tenant_id'] = tenant_id
result = client.create_security_group(**sg_dict)
- secgroup = network_resources.DeletableSecurityGroup(
- client=client, routers_client=self.routers_client,
- **result['security_group']
- )
- self.assertEqual(secgroup.name, sg_name)
- self.assertEqual(tenant_id, secgroup.tenant_id)
- self.assertEqual(secgroup.description, sg_desc)
+
+ secgroup = result['security_group']
+ self.assertEqual(secgroup['name'], sg_name)
+ self.assertEqual(tenant_id, secgroup['tenant_id'])
+ self.assertEqual(secgroup['description'], sg_desc)
+
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- secgroup.delete)
+ client.delete_security_group, secgroup['id'])
return secgroup
def _default_security_group(self, client=None, tenant_id=None):
@@ -1032,8 +1042,7 @@
]
msg = "No default security group for tenant %s." % (tenant_id)
self.assertTrue(len(sgs) > 0, msg)
- return network_resources.DeletableSecurityGroup(client=client,
- **sgs[0])
+ return sgs[0]
def _create_security_group_rule(self, secgroup=None,
sec_group_rules_client=None,
@@ -1044,7 +1053,7 @@
Create a rule in a secgroup. if secgroup not defined will search for
default secgroup in tenant_id.
- :param secgroup: type DeletableSecurityGroup.
+ :param secgroup: the security group.
:param tenant_id: if secgroup not passed -- the tenant in which to
search for default secgroup
:param kwargs: a dictionary containing rule parameters:
@@ -1066,17 +1075,15 @@
secgroup = self._default_security_group(
client=security_groups_client, tenant_id=tenant_id)
- ruleset = dict(security_group_id=secgroup.id,
- tenant_id=secgroup.tenant_id)
+ ruleset = dict(security_group_id=secgroup['id'],
+ tenant_id=secgroup['tenant_id'])
ruleset.update(kwargs)
sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
- sg_rule = network_resources.DeletableSecurityGroupRule(
- client=sec_group_rules_client,
- **sg_rule['security_group_rule']
- )
- self.assertEqual(secgroup.tenant_id, sg_rule.tenant_id)
- self.assertEqual(secgroup.id, sg_rule.security_group_id)
+ sg_rule = sg_rule['security_group_rule']
+
+ self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
+ self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
return sg_rule
@@ -1130,7 +1137,7 @@
if msg not in ex._error_string:
raise ex
else:
- self.assertEqual(r_direction, sg_rule.direction)
+ self.assertEqual(r_direction, sg_rule['direction'])
rules.append(sg_rule)
return rules
@@ -1152,10 +1159,11 @@
network_id = CONF.network.public_network_id
if router_id:
body = client.show_router(router_id)
- return network_resources.AttributeDict(**body['router'])
+ return body['router']
elif network_id:
router = self._create_router(client, tenant_id)
- router.set_gateway(network_id)
+ kwargs = {'external_gateway_info': dict(network_id=network_id)}
+ router = client.update_router(router['id'], **kwargs)['router']
return router
else:
raise Exception("Neither of 'public_router_id' or "
@@ -1171,15 +1179,18 @@
result = client.create_router(name=name,
admin_state_up=True,
tenant_id=tenant_id)
- router = network_resources.DeletableRouter(routers_client=client,
- **result['router'])
- self.assertEqual(router.name, name)
- self.addCleanup(test_utils.call_and_ignore_notfound_exc, router.delete)
+ router = result['router']
+ self.assertEqual(router['name'], name)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ client.delete_router,
+ router['id'])
return router
def _update_router_admin_state(self, router, admin_state_up):
- router.update(admin_state_up=admin_state_up)
- self.assertEqual(admin_state_up, router.admin_state_up)
+ kwargs = dict(admin_state_up=admin_state_up)
+ router = self.routers_client.update_router(
+ router['id'], **kwargs)['router']
+ self.assertEqual(admin_state_up, router['admin_state_up'])
def create_networks(self, networks_client=None,
routers_client=None, subnets_client=None,
@@ -1212,7 +1223,6 @@
tenant_id=tenant_id)
router = self._get_router(client=routers_client,
tenant_id=tenant_id)
-
subnet_kwargs = dict(network=network,
subnets_client=subnets_client,
routers_client=routers_client)
@@ -1220,7 +1230,17 @@
if dns_nameservers is not None:
subnet_kwargs['dns_nameservers'] = dns_nameservers
subnet = self._create_subnet(**subnet_kwargs)
- subnet.add_to_router(router.id)
+ if not routers_client:
+ routers_client = self.routers_client
+ router_id = router['id']
+ routers_client.add_router_interface(router_id,
+ subnet_id=subnet['id'])
+
+ # save a cleanup job to remove this association between
+ # router and subnet
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ routers_client.remove_router_interface, router_id,
+ subnet_id=subnet['id'])
return network, subnet, router
diff --git a/tempest/scenario/network_resources.py b/tempest/scenario/network_resources.py
deleted file mode 100644
index 667476f..0000000
--- a/tempest/scenario/network_resources.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright 2013 Hewlett-Packard Development Company, L.P.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import abc
-import time
-
-import six
-
-from tempest import exceptions
-from tempest.lib.common.utils import misc
-
-
-class AttributeDict(dict):
- """Provide attribute access (dict.key) to dictionary values."""
-
- def __getattr__(self, name):
- """Allow attribute access for all keys in the dict."""
- if name in self:
- return self[name]
- return super(AttributeDict, self).__getattribute__(name)
-
-
-@six.add_metaclass(abc.ABCMeta)
-class DeletableResource(AttributeDict):
- """Support deletion of neutron resources (networks, subnets)
-
- via a delete() method, as is supported by keystone and nova resources.
- """
-
- def __init__(self, *args, **kwargs):
- self.client = kwargs.pop('client', None)
- self.networks_client = kwargs.pop('networks_client', None)
- self.routers_client = kwargs.pop('routers_client', None)
- self.subnets_client = kwargs.pop('subnets_client', None)
- self.ports_client = kwargs.pop('ports_client', None)
- super(DeletableResource, self).__init__(*args, **kwargs)
-
- def __str__(self):
- return '<%s id="%s" name="%s">' % (self.__class__.__name__,
- self.id, self.name)
-
- @abc.abstractmethod
- def delete(self):
- return
-
- @abc.abstractmethod
- def refresh(self):
- return
-
- def __hash__(self):
- return hash(self.id)
-
- def wait_for_status(self, status):
- if not hasattr(self, 'status'):
- return
-
- def helper_get():
- self.refresh()
- return self
-
- return self.wait_for_resource_status(helper_get, status)
-
- def wait_for_resource_status(self, fetch, status):
- """Waits for a network resource to reach a status
-
- @param fetch: the callable to be used to query the resource status
- @type fetch: callable that takes no parameters and returns the resource
- @param status: the status that the resource has to reach
- @type status: String
- """
- interval = self.build_interval
- timeout = self.build_timeout
- start_time = time.time()
-
- while time.time() - start_time <= timeout:
- resource = fetch()
- if resource['status'] == status:
- return
- time.sleep(interval)
-
- # At this point, the wait has timed out
- message = 'Resource %s' % (str(resource))
- message += ' failed to reach status %s' % status
- message += ' (current: %s)' % resource['status']
- message += ' within the required time %s' % timeout
- caller = misc.find_test_caller()
- if caller:
- message = '(%s) %s' % (caller, message)
- raise exceptions.TimeoutException(message)
-
-
-class DeletableNetwork(DeletableResource):
-
- def delete(self):
- self.networks_client.delete_network(self.id)
-
-
-class DeletableSubnet(DeletableResource):
-
- def __init__(self, *args, **kwargs):
- super(DeletableSubnet, self).__init__(*args, **kwargs)
- self._router_ids = set()
-
- def update(self, *args, **kwargs):
- result = self.subnets_client.update_subnet(self.id,
- *args,
- **kwargs)
- return super(DeletableSubnet, self).update(**result['subnet'])
-
- def add_to_router(self, router_id):
- self._router_ids.add(router_id)
- self.routers_client.add_router_interface(router_id,
- subnet_id=self.id)
-
- def delete(self):
- for router_id in self._router_ids.copy():
- self.routers_client.remove_router_interface(router_id,
- subnet_id=self.id)
- self._router_ids.remove(router_id)
- self.subnets_client.delete_subnet(self.id)
-
-
-class DeletableRouter(DeletableResource):
-
- def set_gateway(self, network_id):
- return self.update(external_gateway_info=dict(network_id=network_id))
-
- def unset_gateway(self):
- return self.update(external_gateway_info=dict())
-
- def update(self, *args, **kwargs):
- result = self.routers_client.update_router(self.id,
- *args,
- **kwargs)
- return super(DeletableRouter, self).update(**result['router'])
-
- def delete(self):
- self.unset_gateway()
- self.routers_client.delete_router(self.id)
-
-
-class DeletableFloatingIp(DeletableResource):
-
- def refresh(self, *args, **kwargs):
- result = self.client.show_floatingip(self.id,
- *args,
- **kwargs)
- super(DeletableFloatingIp, self).update(**result['floatingip'])
-
- def update(self, *args, **kwargs):
- result = self.client.update_floatingip(self.id,
- *args,
- **kwargs)
- super(DeletableFloatingIp, self).update(**result['floatingip'])
-
- def __repr__(self):
- return '<%s addr="%s">' % (self.__class__.__name__,
- self.floating_ip_address)
-
- def __str__(self):
- return '<"FloatingIP" addr="%s" id="%s">' % (self.floating_ip_address,
- self.id)
-
- def delete(self):
- self.client.delete_floatingip(self.id)
-
-
-class DeletablePort(DeletableResource):
-
- def delete(self):
- self.ports_client.delete_port(self.id)
-
-
-class DeletableSecurityGroup(DeletableResource):
-
- def delete(self):
- self.client.delete_security_group(self.id)
-
-
-class DeletableSecurityGroupRule(DeletableResource):
-
- def __repr__(self):
- return '<%s id="%s">' % (self.__class__.__name__, self.id)
-
- def delete(self):
- self.client.delete_security_group_rule(self.id)
-
-
-class DeletablePool(DeletableResource):
-
- def delete(self):
- self.client.delete_pool(self.id)
-
-
-class DeletableMember(DeletableResource):
-
- def delete(self):
- self.client.delete_member(self.id)
-
-
-class DeletableVip(DeletableResource):
-
- def delete(self):
- self.client.delete_vip(self.id)
-
- def refresh(self):
- result = self.client.show_vip(self.id)
- super(DeletableVip, self).update(**result['vip'])
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index bfdb0c2..e4b699e 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -61,7 +61,7 @@
server_name = data_utils.rand_name('server-smoke')
server = self.create_server(
name=server_name,
- networks=[{'uuid': network.id}],
+ networks=[{'uuid': network['id']}],
key_name=keypair['name'],
security_groups=security_groups,
wait_until='ACTIVE')
@@ -81,7 +81,7 @@
server, username, private_key,
should_connect=should_connect,
servers_for_debug=[server])
- floating_ip_addr = floating_ip.floating_ip_address
+ floating_ip_addr = floating_ip['floating_ip_address']
# Check FloatingIP status before checking the connectivity
self.check_floating_ip_status(floating_ip, 'ACTIVE')
self.check_public_network_connectivity(floating_ip_addr, username,
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index f5134e5..402a70c 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -25,7 +25,6 @@
from tempest import exceptions
from tempest.lib.common.utils import test_utils
from tempest.scenario import manager
-from tempest.scenario import network_resources
from tempest import test
CONF = config.CONF
@@ -114,7 +113,7 @@
self.port_id = None
if boot_with_port:
# create a port on the network and boot with that
- self.port_id = self._create_port(self.network['id']).id
+ self.port_id = self._create_port(self.network['id'])['id']
self.ports.append({'port': self.port_id})
name = data_utils.rand_name('server-smoke')
@@ -133,30 +132,30 @@
seen_nets = self._list_networks()
seen_names = [n['name'] for n in seen_nets]
seen_ids = [n['id'] for n in seen_nets]
- self.assertIn(self.network.name, seen_names)
- self.assertIn(self.network.id, seen_ids)
+ self.assertIn(self.network['name'], seen_names)
+ self.assertIn(self.network['id'], seen_ids)
if self.subnet:
seen_subnets = self._list_subnets()
seen_net_ids = [n['network_id'] for n in seen_subnets]
seen_subnet_ids = [n['id'] for n in seen_subnets]
- self.assertIn(self.network.id, seen_net_ids)
- self.assertIn(self.subnet.id, seen_subnet_ids)
+ self.assertIn(self.network['id'], seen_net_ids)
+ self.assertIn(self.subnet['id'], seen_subnet_ids)
if self.router:
seen_routers = self._list_routers()
seen_router_ids = [n['id'] for n in seen_routers]
seen_router_names = [n['name'] for n in seen_routers]
- self.assertIn(self.router.name,
+ self.assertIn(self.router['name'],
seen_router_names)
- self.assertIn(self.router.id,
+ self.assertIn(self.router['id'],
seen_router_ids)
def _create_server(self, name, network, port_id=None):
keypair = self.create_keypair()
self.keypairs[keypair['name']] = keypair
security_groups = [{'name': self.security_group['name']}]
- network = {'uuid': network.id}
+ network = {'uuid': network['id']}
if port_id is not None:
network['port'] = port_id
@@ -198,7 +197,7 @@
"""
ssh_login = CONF.validation.image_ssh_user
floating_ip, server = self.floating_ip_tuple
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = None
floatingip_status = 'DOWN'
if should_connect:
@@ -239,7 +238,7 @@
def _hotplug_server(self):
old_floating_ip, server = self.floating_ip_tuple
- ip_address = old_floating_ip.floating_ip_address
+ ip_address = old_floating_ip['floating_ip_address']
private_key = self._get_server_key(server)
ssh_client = self.get_remote_client(
ip_address, private_key=private_key)
@@ -250,7 +249,7 @@
old_port = port_list[0]
interface = self.interface_client.create_interface(
server_id=server['id'],
- net_id=self.new_net.id)['interfaceAttachment']
+ net_id=self.new_net['id'])['interfaceAttachment']
self.addCleanup(self.ports_client.wait_for_resource_deletion,
interface['port_id'])
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
@@ -270,9 +269,7 @@
"Old port: %s. Number of new ports: %d" % (
CONF.network.build_timeout, old_port,
len(self.new_port_list)))
- new_port = network_resources.DeletablePort(
- ports_client=self.ports_client,
- **self.new_port_list[0])
+ new_port = self.new_port_list[0]
def check_new_nic():
new_nic_list = self._get_server_nics(ssh_client)
@@ -287,7 +284,8 @@
num, new_nic = self.diff_list[0]
ssh_client.assign_static_ip(nic=new_nic,
- addr=new_port.fixed_ips[0]['ip_address'])
+ addr=new_port['fixed_ips'][0][
+ 'ip_address'])
ssh_client.set_nic_state(nic=new_nic)
def _get_server_nics(self, ssh_client):
@@ -307,7 +305,7 @@
# get all network ports in the new network
internal_ips = (p['fixed_ips'][0]['ip_address'] for p in
self._list_ports(tenant_id=server['tenant_id'],
- network_id=network.id)
+ network_id=network['id'])
if p['device_owner'].startswith('network'))
self._check_server_connectivity(floating_ip,
@@ -335,7 +333,7 @@
def _check_server_connectivity(self, floating_ip, address_list,
should_connect=True):
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = self._get_server_key(self.floating_ip_tuple.server)
ssh_source = self.get_remote_client(
ip_address, private_key=private_key)
@@ -451,7 +449,13 @@
self._create_server(name, self.new_net)
self._check_network_internal_connectivity(network=self.new_net,
should_connect=False)
- self.new_subnet.add_to_router(self.router.id)
+ router_id = self.router['id']
+ self.routers_client.add_router_interface(
+ router_id, subnet_id=self.new_subnet['id'])
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router_id, subnet_id=self.new_subnet['id'])
self._check_network_internal_connectivity(network=self.new_net,
should_connect=True)
@@ -553,7 +557,7 @@
self.check_public_network_connectivity(should_connect=True)
floating_ip, server = self.floating_ip_tuple
- ip_address = floating_ip.floating_ip_address
+ ip_address = floating_ip['floating_ip_address']
private_key = self._get_server_key(server)
ssh_client = self.get_remote_client(
ip_address, private_key=private_key)
@@ -568,9 +572,11 @@
act_serv=servers,
trgt_serv=dns_servers))
- self.subnet.update(dns_nameservers=[alt_dns_server])
+ self.subnet = self.subnets_client.update_subnet(
+ self.subnet['id'], dns_nameservers=[alt_dns_server])['subnet']
+
# asserts that Neutron DB has updated the nameservers
- self.assertEqual([alt_dns_server], self.subnet.dns_nameservers,
+ self.assertEqual([alt_dns_server], self.subnet['dns_nameservers'],
"Failed to update subnet's nameservers")
def check_new_dns_server():
@@ -695,7 +701,8 @@
# NOTE(kevinbenton): we have to use the admin credentials to check
# for the distributed flag because self.router only has a project view.
- admin = self.admin_manager.routers_client.show_router(self.router.id)
+ admin = self.admin_manager.routers_client.show_router(
+ self.router['id'])
if admin['router'].get('distributed', False):
msg = "Rescheduling test does not apply to distributed routers."
raise self.skipException(msg)
@@ -704,16 +711,16 @@
# remove resource from agents
hosting_agents = set(a["id"] for a in
- list_hosts(self.router.id)['agents'])
+ list_hosts(self.router['id'])['agents'])
no_migration = agent_list_alive == hosting_agents
LOG.info("Router will be assigned to {mig} hosting agent".
format(mig="the same" if no_migration else "a new"))
for hosting_agent in hosting_agents:
- unschedule_router(hosting_agent, self.router.id)
+ unschedule_router(hosting_agent, self.router['id'])
self.assertNotIn(hosting_agent,
[a["id"] for a in
- list_hosts(self.router.id)['agents']],
+ list_hosts(self.router['id'])['agents']],
'unscheduling router failed')
# verify resource is un-functional
@@ -730,7 +737,7 @@
router_id=self.router['id'])
self.assertEqual(
target_agent,
- list_hosts(self.router.id)['agents'][0]['id'],
+ list_hosts(self.router['id'])['agents'][0]['id'],
"Router failed to reschedule. Hosting agent doesn't match "
"target agent")
@@ -776,12 +783,12 @@
network_id=self.new_net["id"])
spoof_port = new_ports[0]
private_key = self._get_server_key(server)
- ssh_client = self.get_remote_client(fip.floating_ip_address,
+ ssh_client = self.get_remote_client(fip['floating_ip_address'],
private_key=private_key)
spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
name = data_utils.rand_name('peer')
peer = self._create_server(name, self.new_net)
- peer_address = peer['addresses'][self.new_net.name][0]['addr']
+ peer_address = peer['addresses'][self.new_net['name']][0]['addr']
self._check_remote_connectivity(ssh_client, dest=peer_address,
nic=spoof_nic, should_succeed=True)
ssh_client.set_mac_address(spoof_nic, spoof_mac)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index a52d8f9..59ebb7a 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -17,6 +17,7 @@
import six
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.scenario import manager
from tempest import test
@@ -82,8 +83,12 @@
ip_version=4)
router = self._get_router(tenant_id=self.tenant_id)
- sub4.add_to_router(router_id=router['id'])
- self.addCleanup(sub4.delete)
+ self.routers_client.add_router_interface(router['id'],
+ subnet_id=sub4['id'])
+
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router['id'], subnet_id=sub4['id'])
self.subnets_v6 = []
for _ in range(n_subnets6):
@@ -94,10 +99,14 @@
ipv6_ra_mode=address6_mode,
ipv6_address_mode=address6_mode)
- sub6.add_to_router(router_id=router['id'])
- self.addCleanup(sub6.delete)
- self.subnets_v6.append(sub6)
+ self.routers_client.add_router_interface(router['id'],
+ subnet_id=sub6['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.routers_client.remove_router_interface,
+ router['id'], subnet_id=sub6['id'])
+
+ self.subnets_v6.append(sub6)
return [self.network, self.network_v6] if dualnet else [self.network]
@staticmethod
@@ -119,12 +128,12 @@
srv = self.create_server(
key_name=self.keypair['name'],
security_groups=[{'name': self.sec_grp['name']}],
- networks=[{'uuid': n.id} for n in networks],
+ networks=[{'uuid': n['id']} for n in networks],
wait_until='ACTIVE')
fip = self.create_floating_ip(thing=srv)
ips = self.define_server_ips(srv=srv)
ssh = self.get_remote_client(
- ip_address=fip.floating_ip_address,
+ ip_address=fip['floating_ip_address'],
username=username)
return ssh, ips, srv["id"]
@@ -139,7 +148,7 @@
"""
ports = [p["mac_address"] for p in
self._list_ports(device_id=sid,
- network_id=self.network_v6.id)]
+ network_id=self.network_v6['id'])]
self.assertEqual(1, len(ports),
message=("Multiple IPv6 ports found on network %s. "
"ports: %s")
@@ -190,11 +199,11 @@
self._check_connectivity(sshv4_1,
ips_from_api_2['6'][i])
self._check_connectivity(sshv4_1,
- self.subnets_v6[i].gateway_ip)
+ self.subnets_v6[i]['gateway_ip'])
self._check_connectivity(sshv4_2,
ips_from_api_1['6'][i])
self._check_connectivity(sshv4_2,
- self.subnets_v6[i].gateway_ip)
+ self.subnets_v6[i]['gateway_ip'])
def _check_connectivity(self, source, dest):
self.assertTrue(
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index adc9008..86185c8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -227,22 +227,23 @@
seen_names = [n['name'] for n in seen_nets]
seen_ids = [n['id'] for n in seen_nets]
- self.assertIn(tenant.network.name, seen_names)
- self.assertIn(tenant.network.id, seen_ids)
+ self.assertIn(tenant.network['name'], seen_names)
+ self.assertIn(tenant.network['id'], seen_ids)
seen_subnets = [(n['id'], n['cidr'], n['network_id'])
for n in self._list_subnets()]
- mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id)
+ mysubnet = (tenant.subnet['id'], tenant.subnet['cidr'],
+ tenant.network['id'])
self.assertIn(mysubnet, seen_subnets)
seen_routers = self._list_routers()
seen_router_ids = [n['id'] for n in seen_routers]
seen_router_names = [n['name'] for n in seen_routers]
- self.assertIn(tenant.router.name, seen_router_names)
- self.assertIn(tenant.router.id, seen_router_ids)
+ self.assertIn(tenant.router['name'], seen_router_names)
+ self.assertIn(tenant.router['id'], seen_router_ids)
- myport = (tenant.router.id, tenant.subnet.id)
+ myport = (tenant.router['id'], tenant.subnet['id'])
router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i
in self._list_ports()
if self._is_router_port(i)]
@@ -270,7 +271,7 @@
kwargs["scheduler_hints"] = {'different_host': self.servers}
server = self.create_server(
name=name,
- networks=[{'uuid': tenant.network.id}],
+ networks=[{'uuid': tenant.network["id"]}],
key_name=tenant.keypair['name'],
security_groups=security_groups_names,
wait_until='ACTIVE',
@@ -353,10 +354,10 @@
def _get_server_ip(self, server, floating=False):
"""returns the ip (floating/internal) of a server"""
if floating:
- server_ip = self.floating_ips[server['id']].floating_ip_address
+ server_ip = self.floating_ips[server['id']]['floating_ip_address']
else:
server_ip = None
- network_name = self.tenants[server['tenant_id']].network.name
+ network_name = self.tenants[server['tenant_id']].network['name']
if network_name in server['addresses']:
server_ip = server['addresses'][network_name][0]['addr']
return server_ip
@@ -364,7 +365,7 @@
def _connect_to_access_point(self, tenant):
"""create ssh connection to tenant access point"""
access_point_ssh = \
- self.floating_ips[tenant.access_point['id']].floating_ip_address
+ self.floating_ips[tenant.access_point['id']]['floating_ip_address']
private_key = tenant.keypair['private_key']
access_point_ssh = self.get_remote_client(
access_point_ssh, private_key=private_key)
@@ -388,7 +389,7 @@
def _test_in_tenant_allow(self, tenant):
ruleset = dict(
protocol='icmp',
- remote_group_id=tenant.security_groups['default'].id,
+ remote_group_id=tenant.security_groups['default']['id'],
direction='ingress'
)
self._create_security_group_rule(
@@ -464,7 +465,7 @@
for port in port_list if port['fixed_ips']
]
server_ip = self._get_server_ip(tenant.access_point)
- subnet_id = tenant.subnet.id
+ subnet_id = tenant.subnet['id']
self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
@test.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@@ -545,7 +546,7 @@
# update port with new security group and check connectivity
self.ports_client.update_port(port_id, security_groups=[
- new_tenant.security_groups['new_sg'].id])
+ new_tenant.security_groups['new_sg']['id']])
self._check_connectivity(
access_point=access_point_ssh,
ip=self._get_server_ip(server))
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 034938e..97e5c11 100644
--- a/tempest/services/identity/v2/json/tenants_client.py
+++ b/tempest/services/identity/v2/json/tenants_client.py
@@ -59,7 +59,11 @@
return rest_client.ResponseBody(resp, body)
def update_tenant(self, tenant_id, **kwargs):
- """Updates a tenant."""
+ """Updates a tenant.
+
+ 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'])
diff --git a/tempest/services/identity/v2/json/users_client.py b/tempest/services/identity/v2/json/users_client.py
index 5f8127f..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,25 +19,24 @@
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)
return rest_client.ResponseBody(resp, body)
def update_user(self, user_id, **kwargs):
- """Updates a user."""
+ """Updates a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-admin-v2.html#admin-updateUser
+ """
put_body = json.dumps({'user': kwargs})
resp, body = self.put('users/%s' % user_id, put_body)
self.expected_success(200, resp.status)
@@ -44,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/identity/v3/json/users_clients.py b/tempest/services/identity/v3/json/users_clients.py
index 3ab8eab..73bd343 100644
--- a/tempest/services/identity/v3/json/users_clients.py
+++ b/tempest/services/identity/v3/json/users_clients.py
@@ -44,7 +44,11 @@
return rest_client.ResponseBody(resp, body)
def update_user(self, user_id, name, **kwargs):
- """Updates a user."""
+ """Updates a user.
+
+ Available params: see http://developer.openstack.org/
+ api-ref-identity-v3.html#updateUser
+ """
body = self.show_user(user_id)['user']
email = kwargs.get('email', body['email'])
en = kwargs.get('enabled', body['enabled'])
diff --git a/tempest/services/image/v1/json/image_members_client.py b/tempest/services/image/v1/json/image_members_client.py
index df16d2fd..e7fa0c9 100644
--- a/tempest/services/image/v1/json/image_members_client.py
+++ b/tempest/services/image/v1/json/image_members_client.py
@@ -39,7 +39,7 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
- def add_member(self, member_id, image_id, **kwargs):
+ def create_image_member(self, image_id, member_id, **kwargs):
"""Add a member to an image.
Available params: see http://developer.openstack.org/
@@ -51,7 +51,7 @@
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp)
- def delete_member(self, member_id, image_id):
+ def delete_image_member(self, image_id, member_id):
"""Removes a membership from the image.
Available params: see http://developer.openstack.org/
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index 4ffaf3b..30325c0 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -31,28 +31,6 @@
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 +76,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 +93,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 +150,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 +163,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/image/v2/__init__.py b/tempest/services/volume/v3/__init__.py
similarity index 100%
rename from tempest/services/image/v2/__init__.py
rename to tempest/services/volume/v3/__init__.py
diff --git a/tempest/services/image/v2/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
similarity index 100%
rename from tempest/services/image/v2/json/__init__.py
rename to 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/test.py b/tempest/test.py
index 4c0d0bb..4e06db8 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -39,6 +39,7 @@
from tempest import exceptions
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
LOG = logging.getLogger(__name__)
@@ -539,7 +540,7 @@
if hasattr(cred_provider, credentials_method):
creds = getattr(cred_provider, credentials_method)()
else:
- raise exceptions.InvalidCredentials(
+ raise lib_exc.InvalidCredentials(
"Invalid credentials type %s" % credential_type)
return cls.client_manager(credentials=creds.credentials,
service=cls._service)
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..50660ff 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -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/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/identity/v2/test_endpoints_client.py b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
new file mode 100644
index 0000000..7d2cac2
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_endpoints_client.py
@@ -0,0 +1,99 @@
+# 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.identity.v2 import endpoints_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestEndpointsClient(base.BaseServiceTest):
+ FAKE_CREATE_ENDPOINT = {
+ "endpoint": {
+ "id": 1,
+ "tenantId": 1,
+ "region": "North",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ }
+ }
+
+ FAKE_LIST_ENDPOINTS = {
+ "endpoints": [
+ {
+ "id": 1,
+ "tenantId": "1",
+ "region": "North",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ },
+ {
+ "id": 2,
+ "tenantId": "1",
+ "region": "South",
+ "type": "compute",
+ "publicURL": "https://compute.north.public.com/v1",
+ "internalURL": "https://compute.north.internal.com/v1",
+ "adminURL": "https://compute.north.internal.com/v1"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestEndpointsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = endpoints_client.EndpointsClient(fake_auth,
+ 'identity', 'regionOne')
+
+ def _test_create_endpoint(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_ENDPOINT,
+ bytes_body,
+ service_id="b344506af7644f6794d9cb316600b020",
+ region="region-demo",
+ publicurl="https://compute.north.public.com/v1",
+ adminurl="https://compute.north.internal.com/v1",
+ internalurl="https://compute.north.internal.com/v1")
+
+ def _test_list_endpoints(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_endpoints,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ENDPOINTS,
+ bytes_body)
+
+ def test_create_endpoint_with_str_body(self):
+ self._test_create_endpoint()
+
+ def test_create_endpoint_with_bytes_body(self):
+ self._test_create_endpoint(bytes_body=True)
+
+ def test_list_endpoints_with_str_body(self):
+ self._test_list_endpoints()
+
+ def test_list_endpoints_with_bytes_body(self):
+ self._test_list_endpoints(bytes_body=True)
+
+ def test_delete_endpoint(self):
+ self.check_service_client_function(
+ self.client.delete_endpoint,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ endpoint_id="b344506af7644f6794d9cb316600b020",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index 7925152..dfce9b3 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -25,9 +25,6 @@
class TestTokenClientV2(base.TestCase):
- def setUp(self):
- super(TestTokenClientV2, self).setUp()
-
def test_init_without_authurl(self):
self.assertRaises(exceptions.IdentityError,
token_client.TokenClient, None)
diff --git a/tempest/tests/lib/services/image/v2/test_images_client.py b/tempest/tests/lib/services/image/v2/test_images_client.py
new file mode 100644
index 0000000..9648985
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -0,0 +1,111 @@
+# 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.v2 import images_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestImagesClient(base.BaseServiceTest):
+ FAKE_CREATE_UPDATE_SHOW_IMAGE = {
+ "id": "e485aab9-0907-4973-921c-bb6da8a8fcf8",
+ "name": u"\u2740(*\xb4\u25e2`*)\u2740",
+ "status": "active",
+ "visibility": "public",
+ "size": 2254249,
+ "checksum": "2cec138d7dae2aa59038ef8c9aec2390",
+ "tags": [
+ "fedora",
+ "beefy"
+ ],
+ "created_at": "2012-08-10T19:23:50Z",
+ "updated_at": "2012-08-12T11:11:33Z",
+ "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea",
+ "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file",
+ "schema": "/v2/schemas/image",
+ "owner": None,
+ "min_ram": None,
+ "min_disk": None,
+ "disk_format": None,
+ "virtual_size": None,
+ "container_format": None
+ }
+
+ def setUp(self):
+ super(TestImagesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = images_client.ImagesClient(fake_auth,
+ 'image', 'regionOne')
+
+ def _test_update_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_image,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8",
+ patch=[{"op": "add", "path": "/a/b/c", "value": ["foo", "bar"]}])
+
+ def _test_create_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ name="virtual machine image",
+ status=201)
+
+ def _test_show_image(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_image,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CREATE_UPDATE_SHOW_IMAGE,
+ bytes_body,
+ image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
+
+ def test_create_image_with_str_body(self):
+ self._test_create_image()
+
+ def test_create_image_with_bytes_body(self):
+ self._test_create_image(bytes_body=True)
+
+ def test_update_image_with_str_body(self):
+ self._test_update_image()
+
+ def test_update_image_with_bytes_body(self):
+ self._test_update_image(bytes_body=True)
+
+ def test_deactivate_image(self):
+ self.check_service_client_function(
+ self.client.deactivate_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_reactivate_image(self):
+ self.check_service_client_function(
+ self.client.reactivate_image,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_delete_image(self):
+ self.check_service_client_function(
+ self.client.delete_image,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
+
+ def test_show_image_with_str_body(self):
+ self._test_show_image()
+
+ def test_show_image_with_bytes_body(self):
+ self._test_show_image(bytes_body=True)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index c08bf6a..12590a3 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -802,3 +802,12 @@
_, auth_data = self.auth_provider.get_auth()
self.assertIn('domain', auth_data)
self.assertNotIn('project', auth_data)
+
+
+class TestGetCredentials(base.TestCase):
+
+ def test_invalid_identity_version(self):
+ with testtools.ExpectedException(exceptions.InvalidIdentityVersion,
+ '.* v1 .*'):
+ auth.get_credentials('http://localhost/identity/v3',
+ identity_version='v1')
diff --git a/tempest/tests/services/image/__init__.py b/tempest/tests/services/image/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/tests/services/image/__init__.py
+++ /dev/null