Merge "Added --rmdir flag to workspace remove"
diff --git a/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml b/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml
new file mode 100644
index 0000000..9115f03
--- /dev/null
+++ b/releasenotes/notes/add-OAUTH-Token-Client-tempest-tests-6351eda451b95a86.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Add a new client to handle the OAUTH token feature from the identity API.
diff --git a/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml b/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml
new file mode 100644
index 0000000..46f3b49
--- /dev/null
+++ b/releasenotes/notes/add-params-to-identity-v3-list-endpoints-958a155be4e17e5b.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ The ``list_endpoints`` method of the v3 ``EndPointsClient`` class now has
+ an additional ``**params`` argument that enables passing additional
+ information in the query string of the HTTP request.
diff --git a/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml b/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml
new file mode 100644
index 0000000..898d366
--- /dev/null
+++ b/releasenotes/notes/add-volume-groups-tempest-tests-dd7b2abfe2b48427.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add groups and group_types clients for the volume service as library.
+ Add tempest tests for create group, delete group, show group, and
+ list group volume APIs.
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index bf4c958..d5bb45a 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -68,7 +68,7 @@
params = {'marker': flavor_id}
flavors = self.flavors_client.list_flavors(**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id],
'The list of flavors did not start after the marker.')
@decorators.idempotent_id('6db2f0c0-ddee-4162-9c84-0703d3dd1107')
@@ -80,7 +80,7 @@
params = {'marker': flavor_id}
flavors = self.flavors_client.list_flavors(detail=True,
**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id],
'The list of flavors did not start after the marker.')
@decorators.idempotent_id('3df2743e-3034-4e57-a4cb-b6527f6eac79')
@@ -92,7 +92,7 @@
params = {self._min_disk: flavor['disk'] + 1}
flavors = self.flavors_client.list_flavors(detail=True,
**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
@decorators.idempotent_id('09fe7509-b4ee-4b34-bf8b-39532dc47292')
def test_list_flavors_detailed_filter_by_min_ram(self):
@@ -103,7 +103,7 @@
params = {self._min_ram: flavor['ram'] + 1}
flavors = self.flavors_client.list_flavors(detail=True,
**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
@decorators.idempotent_id('10645a4d-96f5-443f-831b-730711e11dd4')
def test_list_flavors_filter_by_min_disk(self):
@@ -113,7 +113,7 @@
params = {self._min_disk: flavor['disk'] + 1}
flavors = self.flavors_client.list_flavors(**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
@decorators.idempotent_id('935cf550-e7c8-4da6-8002-00f92d5edfaa')
def test_list_flavors_filter_by_min_ram(self):
@@ -123,4 +123,4 @@
params = {self._min_ram: flavor['ram'] + 1}
flavors = self.flavors_client.list_flavors(**params)['flavors']
- self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+ self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 6677aa2..acc8b3e 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -129,9 +129,9 @@
params = {'status': 'ACTIVE'}
images = self.client.list_images(**params)['images']
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+ self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+ self.assertNotEmpty([i for i in images if i['id'] == self.image2_id])
+ self.assertNotEmpty([i for i in images if i['id'] == self.image3_id])
@decorators.idempotent_id('33163b73-79f5-4d07-a7ea-9213bcc468ff')
def test_list_images_filter_by_name(self):
@@ -140,9 +140,9 @@
params = {'name': self.image1['name']}
images = self.client.list_images(**params)['images']
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+ self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image2_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image3_id])
@decorators.idempotent_id('9f238683-c763-45aa-b848-232ec3ce3105')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -152,14 +152,13 @@
params = {'server': self.server1['id']}
images = self.client.list_images(**params)['images']
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot1_id]),
- "Failed to find image %s in images. Got images %s" %
- (self.image1_id, images))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot2_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.snapshot3_id]))
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot1_id],
+ "Failed to find image %s in images. "
+ "Got images %s" % (self.image1_id, images))
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot2_id])
+ self.assertEmpty([i for i in images if i['id'] == self.snapshot3_id])
@decorators.idempotent_id('05a377b8-28cf-4734-a1e6-2ab5c38bf606')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -173,12 +172,12 @@
params = {'server': link['href']}
images = self.client.list_images(**params)['images']
- self.assertFalse(any([i for i in images
- if i['id'] == self.snapshot1_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.snapshot2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot3_id]))
+ self.assertEmpty([i for i in images
+ if i['id'] == self.snapshot1_id])
+ self.assertEmpty([i for i in images
+ if i['id'] == self.snapshot2_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot3_id])
@decorators.idempotent_id('e3356918-4d3e-4756-81d5-abc4524ba29f')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -188,14 +187,13 @@
params = {'type': 'snapshot'}
images = self.client.list_images(**params)['images']
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot1_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot3_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.image_ref]))
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot1_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot2_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot3_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image_ref])
@decorators.idempotent_id('3a484ca9-67ba-451e-b494-7fcf28d32d62')
def test_list_images_limit_results(self):
@@ -212,8 +210,8 @@
# Filter by the image's created time
params = {'changes-since': self.image3['created']}
images = self.client.list_images(**params)['images']
- found = any([i for i in images if i['id'] == self.image3_id])
- self.assertTrue(found)
+ found = [i for i in images if i['id'] == self.image3_id]
+ self.assertNotEmpty(found)
@decorators.idempotent_id('9b0ea018-6185-4f71-948a-a123a107988e')
def test_list_images_with_detail_filter_by_status(self):
@@ -222,9 +220,9 @@
params = {'status': 'ACTIVE'}
images = self.client.list_images(detail=True, **params)['images']
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
+ self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+ self.assertNotEmpty([i for i in images if i['id'] == self.image2_id])
+ self.assertNotEmpty([i for i in images if i['id'] == self.image3_id])
@decorators.idempotent_id('644ea267-9bd9-4f3b-af9f-dffa02396a17')
def test_list_images_with_detail_filter_by_name(self):
@@ -233,9 +231,9 @@
params = {'name': self.image1['name']}
images = self.client.list_images(detail=True, **params)['images']
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
+ self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image2_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image3_id])
@decorators.idempotent_id('ba2fa9a9-b672-47cc-b354-3b4c0600e2cb')
def test_list_images_with_detail_limit_results(self):
@@ -257,12 +255,12 @@
params = {'server': link['href']}
images = self.client.list_images(detail=True, **params)['images']
- self.assertFalse(any([i for i in images
- if i['id'] == self.snapshot1_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.snapshot2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot3_id]))
+ self.assertEmpty([i for i in images
+ if i['id'] == self.snapshot1_id])
+ self.assertEmpty([i for i in images
+ if i['id'] == self.snapshot2_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot3_id])
@decorators.idempotent_id('888c0cc0-7223-43c5-9db0-b125fd0a393b')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
@@ -273,14 +271,13 @@
images = self.client.list_images(detail=True, **params)['images']
self.client.show_image(self.image_ref)
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot1_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.snapshot3_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.image_ref]))
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot1_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot2_id])
+ self.assertNotEmpty([i for i in images
+ if i['id'] == self.snapshot3_id])
+ self.assertEmpty([i for i in images if i['id'] == self.image_ref])
@decorators.idempotent_id('7d439e18-ac2e-4827-b049-7e18004712c4')
def test_list_images_with_detail_filter_by_changes_since(self):
@@ -290,4 +287,4 @@
# Filter by the image's created time
params = {'changes-since': self.image1['created']}
images = self.client.list_images(detail=True, **params)['images']
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
+ self.assertNotEmpty([i for i in images if i['id'] == self.image1_id])
diff --git a/tempest/api/compute/images/test_list_images.py b/tempest/api/compute/images/test_list_images.py
index 5d3cbf3..e2dbd72 100644
--- a/tempest/api/compute/images/test_list_images.py
+++ b/tempest/api/compute/images/test_list_images.py
@@ -44,12 +44,12 @@
def test_list_images(self):
# The list of all images should contain the image
images = self.client.list_images()['images']
- found = any([i for i in images if i['id'] == self.image_ref])
- self.assertTrue(found)
+ found = [i for i in images if i['id'] == self.image_ref]
+ self.assertNotEmpty(found)
@decorators.idempotent_id('9f94cb6b-7f10-48c5-b911-a0b84d7d4cd6')
def test_list_images_with_detail(self):
# Detailed list of all images should contain the expected images
images = self.client.list_images(detail=True)['images']
- found = any([i for i in images if i['id'] == self.image_ref])
- self.assertTrue(found)
+ found = [i for i in images if i['id'] == self.image_ref]
+ self.assertNotEmpty(found)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index b568f7d..124db0e 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -159,8 +159,8 @@
# Get rules of the created Security Group
rules = self.security_groups_client.show_security_group(
securitygroup_id)['security_group']['rules']
- self.assertTrue(any([i for i in rules if i['id'] == rule1_id]))
- self.assertTrue(any([i for i in rules if i['id'] == rule2_id]))
+ self.assertNotEmpty([i for i in rules if i['id'] == rule1_id])
+ self.assertNotEmpty([i for i in rules if i['id'] == rule2_id])
@decorators.idempotent_id('fc5c5acf-2091-43a6-a6ae-e42760e9ffaf')
@test.services('network')
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index db42c6c..ffadd96 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -97,16 +97,16 @@
# The created server should be in the list of all servers
body = self.client.list_servers()
servers = body['servers']
- found = any([i for i in servers if i['id'] == self.server['id']])
- self.assertTrue(found)
+ found = [i for i in servers if i['id'] == self.server['id']]
+ self.assertNotEmpty(found)
@decorators.idempotent_id('585e934c-448e-43c4-acbf-d06a9b899997')
def test_list_servers_with_detail(self):
# The created server should be in the detailed list of all servers
body = self.client.list_servers(detail=True)
servers = body['servers']
- found = any([i for i in servers if i['id'] == self.server['id']])
- self.assertTrue(found)
+ found = [i for i in servers if i['id'] == self.server['id']]
+ self.assertNotEmpty(found)
@decorators.idempotent_id('cbc0f52f-05aa-492b-bdc1-84b575ca294b')
@testtools.skipUnless(CONF.validation.run_validation,
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 9ab508d..7ee1b02 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -84,10 +84,14 @@
if d['mac'] == self.net_2_200_mac:
self.assertEqual(d['tags'], ['net-2-200'])
- found_devices = [d['tags'][0] for d in md_dict['devices']]
- self.assertItemsEqual(found_devices, ['port-1', 'port-2', 'net-1',
- 'net-2-100', 'net-2-200',
- 'boot', 'other'])
+ # A hypervisor may present multiple paths to a tagged disk, so
+ # there may be duplicated tags in the metadata, use set() to
+ # remove duplicated tags.
+ found_devices = [d['tags'][0] for d in md_dict['devices']]
+ self.assertEqual(set(found_devices), set(['port-1', 'port-2',
+ 'net-1', 'net-2-100',
+ 'net-2-200', 'boot',
+ 'other']))
@decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
@test.services('network', 'volume', 'image')
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 479663c..124bb5f 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -54,7 +54,7 @@
"""Return a list of all roles."""
body = self.roles_client.list_roles()['roles']
found = [role for role in body if role in self.roles]
- self.assertTrue(any(found))
+ self.assertNotEmpty(found)
self.assertEqual(len(found), len(self.roles))
@decorators.idempotent_id('c62d909d-6c21-48c0-ae40-0a0760e6db5e')
@@ -68,13 +68,13 @@
body = self.roles_client.list_roles()['roles']
found = [role for role in body if role['name'] == role_name]
- self.assertTrue(any(found))
+ self.assertNotEmpty(found)
body = self.roles_client.delete_role(found[0]['id'])
body = self.roles_client.list_roles()['roles']
found = [role for role in body if role['name'] == role_name]
- self.assertFalse(any(found))
+ self.assertEmpty(found)
@decorators.idempotent_id('db6870bd-a6ed-43be-a9b1-2f10a5c9994f')
def test_get_role_by_id(self):
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index ad9b983..0f955bf 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -37,7 +37,7 @@
body = self.tenants_client.list_tenants()['tenants']
found = [tenant for tenant in body if tenant['id'] in tenant_ids]
- self.assertFalse(any(found), 'Tenants failed to delete')
+ self.assertEmpty(found, 'Tenants failed to delete')
@decorators.idempotent_id('d25e9f24-1310-4d29-b61b-d91299c21d6d')
def test_tenant_create_with_description(self):
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index b1ae2aa..c9faa9a 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -29,56 +29,93 @@
def resource_setup(cls):
super(EndPointsTestJSON, cls).resource_setup()
cls.service_ids = list()
- s_name = data_utils.rand_name('service')
- s_type = data_utils.rand_name('type')
- s_description = data_utils.rand_name('description')
+
+ # Create endpoints so as to use for LIST and GET test cases
+ interfaces = ['public', 'internal']
+ cls.setup_endpoint_ids = list()
+ for i in range(2):
+ cls._create_service()
+ region = data_utils.rand_name('region')
+ url = data_utils.rand_url()
+ endpoint = cls.client.create_endpoint(
+ service_id=cls.service_ids[i], interface=interfaces[i],
+ url=url, region=region, enabled=True)['endpoint']
+ cls.setup_endpoint_ids.append(endpoint['id'])
+
+ @classmethod
+ def _create_service(cls, s_name=None, s_type=None, s_description=None):
+ if s_name is None:
+ s_name = data_utils.rand_name('service')
+ if s_type is None:
+ s_type = data_utils.rand_name('type')
+ if s_description is None:
+ s_description = data_utils.rand_name('description')
service_data = (
cls.services_client.create_service(name=s_name, type=s_type,
description=s_description))
- cls.service_id = service_data['service']['id']
- cls.service_ids.append(cls.service_id)
- # Create endpoints so as to use for LIST and GET test cases
- cls.setup_endpoints = list()
- for _ in range(2):
- region = data_utils.rand_name('region')
- url = data_utils.rand_url()
- interface = 'public'
- endpoint = cls.client.create_endpoint(service_id=cls.service_id,
- interface=interface,
- url=url, region=region,
- enabled=True)['endpoint']
- cls.setup_endpoints.append(endpoint)
+ service = service_data['service']
+ cls.service_ids.append(service['id'])
+ return service
@classmethod
def resource_cleanup(cls):
- for e in cls.setup_endpoints:
- cls.client.delete_endpoint(e['id'])
+ for e in cls.setup_endpoint_ids:
+ cls.client.delete_endpoint(e)
for s in cls.service_ids:
cls.services_client.delete_service(s)
super(EndPointsTestJSON, cls).resource_cleanup()
@decorators.idempotent_id('c19ecf90-240e-4e23-9966-21cee3f6a618')
def test_list_endpoints(self):
- # Get a list of endpoints
+ # Get the list of all the endpoints.
fetched_endpoints = self.client.list_endpoints()['endpoints']
- # Asserting LIST endpoints
+ fetched_endpoint_ids = [e['id'] for e in fetched_endpoints]
+ # Check that all the created endpoints are present in
+ # "fetched_endpoints".
missing_endpoints =\
- [e for e in self.setup_endpoints if e not in fetched_endpoints]
- self.assertEmpty(missing_endpoints,
+ [e for e in self.setup_endpoint_ids
+ if e not in fetched_endpoint_ids]
+ self.assertEqual(0, len(missing_endpoints),
"Failed to find endpoint %s in fetched list" %
', '.join(str(e) for e in missing_endpoints))
+ # Check that filtering endpoints by service_id works.
+ fetched_endpoints_for_service = self.client.list_endpoints(
+ service_id=self.service_ids[0])['endpoints']
+ fetched_endpoints_for_alt_service = self.client.list_endpoints(
+ service_id=self.service_ids[1])['endpoints']
+
+ # Assert that both filters returned the correct result.
+ self.assertEqual(1, len(fetched_endpoints_for_service))
+ self.assertEqual(1, len(fetched_endpoints_for_alt_service))
+ self.assertEqual(set(self.setup_endpoint_ids),
+ set([fetched_endpoints_for_service[0]['id'],
+ fetched_endpoints_for_alt_service[0]['id']]))
+
+ # Check that filtering endpoints by interface works.
+ fetched_public_endpoints = self.client.list_endpoints(
+ interface='public')['endpoints']
+ fetched_internal_endpoints = self.client.list_endpoints(
+ interface='internal')['endpoints']
+
+ # Check that the expected endpoint_id is present per filter. [0] is
+ # public and [1] is internal.
+ self.assertIn(self.setup_endpoint_ids[0],
+ [e['id'] for e in fetched_public_endpoints])
+ self.assertIn(self.setup_endpoint_ids[1],
+ [e['id'] for e in fetched_internal_endpoints])
+
@decorators.idempotent_id('0e2446d2-c1fd-461b-a729-b9e73e3e3b37')
def test_create_list_show_delete_endpoint(self):
region = data_utils.rand_name('region')
url = data_utils.rand_url()
interface = 'public'
- endpoint = self.client.create_endpoint(service_id=self.service_id,
+ endpoint = self.client.create_endpoint(service_id=self.service_ids[0],
interface=interface,
url=url, region=region,
enabled=True)['endpoint']
- self.setup_endpoints.append(endpoint)
+ self.setup_endpoint_ids.append(endpoint['id'])
# Asserting Create Endpoint response body
self.assertIn('id', endpoint)
self.assertEqual(region, endpoint['region'])
@@ -93,7 +130,7 @@
fetched_endpoint = (
self.client.show_endpoint(endpoint['id'])['endpoint'])
# Asserting if the attributes of endpoint are the same
- self.assertEqual(self.service_id, fetched_endpoint['service_id'])
+ self.assertEqual(self.service_ids[0], fetched_endpoint['service_id'])
self.assertEqual(interface, fetched_endpoint['interface'])
self.assertEqual(url, fetched_endpoint['url'])
self.assertEqual(region, fetched_endpoint['region'])
@@ -101,7 +138,7 @@
# Deleting the endpoint created in this method
self.client.delete_endpoint(endpoint['id'])
- self.setup_endpoints.remove(endpoint)
+ self.setup_endpoint_ids.remove(endpoint['id'])
# Checking whether endpoint is deleted successfully
fetched_endpoints = self.client.list_endpoints()['endpoints']
@@ -117,7 +154,7 @@
url1 = data_utils.rand_url()
interface1 = 'public'
endpoint_for_update = (
- self.client.create_endpoint(service_id=self.service_id,
+ self.client.create_endpoint(service_id=self.service_ids[0],
interface=interface1,
url=url1, region=region1,
enabled=True)['endpoint'])
@@ -126,11 +163,8 @@
s_name = data_utils.rand_name('service')
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
- service2 = (
- self.services_client.create_service(name=s_name, type=s_type,
- description=s_description))
- service2 = service2['service']
- self.service_ids.append(service2['id'])
+ service2 = self._create_service(s_name=s_name, s_type=s_type,
+ s_description=s_description)
# Updating endpoint with new values
region2 = data_utils.rand_name('region')
url2 = data_utils.rand_url()
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index ad4e549..20c8a44 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -77,17 +77,26 @@
def test_list_services(self):
# Create, List, Verify and Delete Services
service_ids = list()
+ service_types = list()
for _ in range(3):
- name = data_utils.rand_name('service')
- serv_type = data_utils.rand_name('type')
+ name = data_utils.rand_name(self.__class__.__name__ + '-Service')
+ serv_type = data_utils.rand_name(self.__class__.__name__ + '-Type')
create_service = self.services_client.create_service(
type=serv_type, name=name)['service']
self.addCleanup(self.services_client.delete_service,
create_service['id'])
service_ids.append(create_service['id'])
+ service_types.append(serv_type)
# List and Verify Services
services = self.services_client.list_services()['services']
fetched_ids = [service['id'] for service in services]
found = [s for s in fetched_ids if s in service_ids]
self.assertEqual(len(found), len(service_ids))
+
+ # Check that filtering by service type works.
+ for serv_type in service_types:
+ fetched_services = self.services_client.list_services(
+ type=serv_type)['services']
+ self.assertEqual(1, len(fetched_services))
+ self.assertEqual(serv_type, fetched_services[0]['type'])
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 3bc6ce1..4495cbf 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -223,6 +223,7 @@
cls.projects_client = cls.os_admin.projects_client
cls.role_assignments = cls.os_admin.role_assignments_client
cls.oauth_consumers_client = cls.os_admin.oauth_consumers_client
+ cls.oauth_token_client = cls.os_admin.oauth_token_client
cls.domain_config_client = cls.os_admin.domain_config_client
cls.endpoint_filter_client = cls.os_admin.endpoint_filter_client
cls.endpoint_groups_client = cls.os_admin.endpoint_groups_client
diff --git a/tempest/api/network/test_tags.py b/tempest/api/network/test_tags.py
index 1f3a7c4..567a462 100644
--- a/tempest/api/network/test_tags.py
+++ b/tempest/api/network/test_tags.py
@@ -14,11 +14,14 @@
# under the License.
from tempest.api.network import base
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
from tempest import test
+CONF = config.CONF
+
class TagsTest(base.BaseNetworkTest):
"""Tests the following operations in the tags API:
@@ -88,3 +91,112 @@
self.assertRaises(lib_exc.NotFound,
self.tags_client.check_tag_existence, 'networks',
self.network['id'], replace_tags[i])
+
+
+class TagsExtTest(base.BaseNetworkTest):
+ """Tests the following operations in the tags API:
+
+ Update all tags.
+ Delete all tags.
+ Check tag existence.
+ Create a tag.
+ List tags.
+ Remove a tag.
+
+ v2.0 of the Neutron API is assumed. The tag-ext extension allows users to
+ set tags on the following resources: subnets, ports, routers and
+ subnetpools.
+ """
+
+ # NOTE(felipemonteiro): The supported resource names are plural. Use
+ # the singular case for the corresponding class resource object.
+ SUPPORTED_RESOURCES = ['subnets', 'ports', 'routers', 'subnetpools']
+
+ @classmethod
+ def skip_checks(cls):
+ super(TagsExtTest, cls).skip_checks()
+ if not test.is_extension_enabled('tag-ext', 'network'):
+ msg = "tag-ext extension not enabled."
+ raise cls.skipException(msg)
+
+ @classmethod
+ def resource_setup(cls):
+ super(TagsExtTest, cls).resource_setup()
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+ cls.port = cls.create_port(cls.network)
+ cls.router = cls.create_router()
+
+ subnetpool_name = data_utils.rand_name(cls.__name__ + '-Subnetpool')
+ prefix = CONF.network.default_network
+ cls.subnetpool = cls.subnetpools_client.create_subnetpool(
+ name=subnetpool_name, prefixes=prefix)['subnetpool']
+
+ @classmethod
+ def resource_cleanup(cls):
+ cls.subnetpools_client.delete_subnetpool(cls.subnetpool['id'])
+ super(TagsExtTest, cls).resource_cleanup()
+
+ def _create_tags_for_each_resource(self):
+ # Create a tag for each resource in `SUPPORTED_RESOURCES` and return
+ # the list of tag names.
+ tag_names = []
+
+ for resource in self.SUPPORTED_RESOURCES:
+ tag_name = data_utils.rand_name(self.__class__.__name__ + '-Tag')
+ tag_names.append(tag_name)
+ resource_object = getattr(self, resource[:-1])
+
+ self.tags_client.create_tag(resource, resource_object['id'],
+ tag_name)
+ self.addCleanup(self.tags_client.delete_all_tags, resource,
+ resource_object['id'])
+
+ return tag_names
+
+ @decorators.idempotent_id('c6231efa-9a89-4adf-b050-2a3156b8a1d9')
+ def test_create_check_list_and_delete_tags(self):
+ tag_names = self._create_tags_for_each_resource()
+
+ for i, resource in enumerate(self.SUPPORTED_RESOURCES):
+ # Ensure that a tag was created for each resource.
+ resource_object = getattr(self, resource[:-1])
+ retrieved_tags = self.tags_client.list_tags(
+ resource, resource_object['id'])['tags']
+ self.assertEqual(1, len(retrieved_tags))
+ self.assertEqual(tag_names[i], retrieved_tags[0])
+
+ # Check that the expected tag exists for each resource.
+ self.tags_client.check_tag_existence(
+ resource, resource_object['id'], tag_names[i])
+
+ # Delete the tag and ensure it was deleted.
+ self.tags_client.delete_tag(
+ resource, resource_object['id'], tag_names[i])
+ retrieved_tags = self.tags_client.list_tags(
+ resource, resource_object['id'])['tags']
+ self.assertEmpty(retrieved_tags)
+
+ @decorators.idempotent_id('663a90f5-f334-4b44-afe0-c5fc1d408791')
+ def test_update_and_delete_all_tags(self):
+ self._create_tags_for_each_resource()
+
+ for resource in self.SUPPORTED_RESOURCES:
+ # Generate 3 new tag names.
+ replace_tags = [data_utils.rand_name(
+ self.__class__.__name__ + '-Tag') for _ in range(3)]
+
+ # Replace the current tag with the 3 new tags and validate that the
+ # current resource has the 3 new tags.
+ resource_object = getattr(self, resource[:-1])
+ updated_tags = self.tags_client.update_all_tags(
+ resource, resource_object['id'], replace_tags)['tags']
+ self.assertEqual(3, len(updated_tags))
+ self.assertEqual(set(replace_tags), set(updated_tags))
+
+ # Delete all the tags and check that they have been removed.
+ self.tags_client.delete_all_tags(resource, resource_object['id'])
+ for i in range(3):
+ self.assertRaises(
+ lib_exc.NotFound, self.tags_client.check_tag_existence,
+ resource, resource_object['id'], replace_tags[i])
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
new file mode 100644
index 0000000..8609bdb
--- /dev/null
+++ b/tempest/api/volume/admin/test_groups.py
@@ -0,0 +1,109 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# 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 base
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class GroupsTest(base.BaseVolumeAdminTest):
+ _api_version = 3
+ min_microversion = '3.14'
+ max_microversion = 'latest'
+
+ def _delete_group(self, grp_id, delete_volumes=True):
+ self.admin_groups_client.delete_group(grp_id, delete_volumes)
+ vols = self.admin_volume_client.list_volumes(detail=True)['volumes']
+ for vol in vols:
+ if vol['group_id'] == grp_id:
+ self.admin_volume_client.wait_for_resource_deletion(vol['id'])
+ self.admin_groups_client.wait_for_resource_deletion(grp_id)
+
+ @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4')
+ def test_group_create_show_list_delete(self):
+ # Create volume type
+ volume_type = self.create_volume_type()
+
+ # Create group type
+ group_type = self.create_group_type()
+
+ # Create group
+ grp1_name = data_utils.rand_name('Group1')
+ grp1 = self.admin_groups_client.create_group(
+ group_type=group_type['id'],
+ volume_types=[volume_type['id']],
+ name=grp1_name)['group']
+ waiters.wait_for_volume_resource_status(
+ self.admin_groups_client, grp1['id'], 'available')
+ grp1_id = grp1['id']
+
+ grp2_name = data_utils.rand_name('Group2')
+ grp2 = self.admin_groups_client.create_group(
+ group_type=group_type['id'],
+ volume_types=[volume_type['id']],
+ name=grp2_name)['group']
+ waiters.wait_for_volume_resource_status(
+ self.admin_groups_client, grp2['id'], 'available')
+ grp2_id = grp2['id']
+
+ # Create volume
+ vol1_name = data_utils.rand_name("volume")
+ params = {'name': vol1_name,
+ 'volume_type': volume_type['id'],
+ 'group_id': grp1['id'],
+ 'size': CONF.volume.volume_size}
+ vol1 = self.admin_volume_client.create_volume(**params)['volume']
+ self.assertEqual(grp1['id'], vol1['group_id'])
+ waiters.wait_for_volume_resource_status(
+ self.admin_volume_client, vol1['id'], 'available')
+ vol1_id = vol1['id']
+
+ # Get a given group
+ grp1 = self.admin_groups_client.show_group(grp1['id'])['group']
+ self.assertEqual(grp1_name, grp1['name'])
+ self.assertEqual(grp1_id, grp1['id'])
+
+ grp2 = self.admin_groups_client.show_group(grp2['id'])['group']
+ self.assertEqual(grp2_name, grp2['name'])
+ self.assertEqual(grp2_id, grp2['id'])
+
+ # Get all groups with detail
+ grps = self.admin_groups_client.list_groups(
+ detail=True)['groups']
+ filtered_grps = [g for g in grps if g['id'] in [grp1_id, grp2_id]]
+ self.assertEqual(2, len(filtered_grps))
+ for grp in filtered_grps:
+ self.assertEqual([volume_type['id']], grp['volume_types'])
+ self.assertEqual(group_type['id'], grp['group_type'])
+
+ vols = self.admin_volume_client.list_volumes(
+ detail=True)['volumes']
+ filtered_vols = [v for v in vols if v['id'] in [vol1_id]]
+ self.assertEqual(1, len(filtered_vols))
+ for vol in filtered_vols:
+ self.assertEqual(grp1_id, vol['group_id'])
+
+ # Delete group
+ # grp1 has a volume so delete_volumes flag is set to True by default
+ self._delete_group(grp1_id)
+ # grp2 is empty so delete_volumes flag can be set to False
+ self._delete_group(grp2_id, delete_volumes=False)
+ grps = self.admin_groups_client.list_groups(
+ detail=True)['groups']
+ self.assertEmpty(grps)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 8d66156..394c453 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -71,6 +71,8 @@
cls.snapshots_client = cls.os_primary.snapshots_v2_client
cls.volumes_client = cls.os_primary.volumes_v2_client
+ if cls._api_version == 3:
+ cls.volumes_client = cls.os_primary.volumes_v3_client
cls.backups_client = cls.os_primary.backups_v2_client
cls.volumes_extension_client =\
cls.os_primary.volumes_v2_extension_client
@@ -79,9 +81,7 @@
cls.volume_limits_client = cls.os_primary.volume_v2_limits_client
cls.messages_client = cls.os_primary.volume_v3_messages_client
cls.versions_client = cls.os_primary.volume_v3_versions_client
-
- if cls._api_version == 3:
- cls.volumes_client = cls.os_primary.volumes_v3_client
+ cls.groups_client = cls.os_primary.groups_v3_client
def setUp(self):
super(BaseVolumeTest, self).setUp()
@@ -253,6 +253,8 @@
cls.admin_volume_types_client = cls.os_admin.volume_types_v2_client
cls.admin_volume_manage_client = cls.os_admin.volume_manage_v2_client
cls.admin_volume_client = cls.os_admin.volumes_v2_client
+ if cls._api_version == 3:
+ cls.admin_volume_client = cls.os_admin.volumes_v3_client
cls.admin_hosts_client = cls.os_admin.volume_hosts_v2_client
cls.admin_snapshot_manage_client = \
cls.os_admin.snapshot_manage_v2_client
@@ -269,9 +271,8 @@
cls.admin_scheduler_stats_client = \
cls.os_admin.volume_scheduler_stats_v2_client
cls.admin_messages_client = cls.os_admin.volume_v3_messages_client
-
- if cls._api_version == 3:
- cls.admin_volume_client = cls.os_admin.volumes_v3_client
+ cls.admin_groups_client = cls.os_admin.groups_v3_client
+ cls.admin_group_types_client = cls.os_admin.group_types_v3_client
@classmethod
def resource_setup(cls):
@@ -305,6 +306,16 @@
cls.volume_types.append(volume_type['id'])
return volume_type
+ def create_group_type(self, name=None, **kwargs):
+ """Create a test group-type"""
+ name = name or data_utils.rand_name(
+ self.__class__.__name__ + '-group-type')
+ group_type = self.admin_group_types_client.create_group_type(
+ name=name, **kwargs)['group_type']
+ self.addCleanup(self.admin_group_types_client.delete_group_type,
+ group_type['id'])
+ return group_type
+
@classmethod
def clear_qos_specs(cls):
for qos_id in cls.qos_specs:
diff --git a/tempest/clients.py b/tempest/clients.py
index 8ad6e92..c3357bb 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -200,6 +200,8 @@
**params_v3)
self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
**params_v3)
+ self.oauth_token_client = self.identity_v3.OAUTHTokenClient(
+ **params_v3)
self.domain_config_client = self.identity_v3.DomainConfigurationClient(
**params_v3)
self.endpoint_filter_client = \
@@ -255,6 +257,8 @@
self.volume_v2.QuotaClassesClient()
self.volumes_extension_client = self.volume_v1.ExtensionsClient()
self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
+ self.groups_v3_client = self.volume_v3.GroupsClient()
+ self.group_types_v3_client = self.volume_v3.GroupTypesClient()
self.volume_availability_zone_client = \
self.volume_v1.AvailabilityZoneClient()
self.volume_v2_availability_zone_client = \
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 9c83c99..93e6fbf 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -186,8 +186,9 @@
resources. The function extracts the name of the desired resource from
the client class name of the resource.
"""
- resource_name = re.findall(r'(Volume|Snapshot|Backup)',
- client.__class__.__name__)[0].lower()
+ resource_name = re.findall(
+ r'(Volume|Snapshot|Backup|Group)',
+ client.__class__.__name__)[0].lower()
show_resource = getattr(client, 'show_' + resource_name)
resource_status = show_resource(resource_id)[resource_name]['status']
start = int(time.time())
diff --git a/tempest/lib/services/compute/baremetal_nodes_client.py b/tempest/lib/services/compute/baremetal_nodes_client.py
index 06dc369..e44c195 100644
--- a/tempest/lib/services/compute/baremetal_nodes_client.py
+++ b/tempest/lib/services/compute/baremetal_nodes_client.py
@@ -25,7 +25,12 @@
"""Tests Baremetal API"""
def list_baremetal_nodes(self, **params):
- """List all baremetal nodes."""
+ """List all baremetal nodes.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#list-bare-metal-nodes
+ """
url = 'os-baremetal-nodes'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -35,7 +40,11 @@
return rest_client.ResponseBody(resp, body)
def show_baremetal_node(self, baremetal_node_id):
- """Return the details of a single baremetal node."""
+ """Show the details of a single baremetal node.
+
+ For more information, please refer to the official API reference:
+ https://developer.openstack.org/api-ref/compute/#show-bare-metal-node-details
+ """
url = 'os-baremetal-nodes/%s' % baremetal_node_id
resp, body = self.get(url)
body = json.loads(body)
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index ce607ff..e271a58 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -28,6 +28,8 @@
InheritedRolesClient
from tempest.lib.services.identity.v3.oauth_consumers_client import \
OAUTHConsumerClient
+from tempest.lib.services.identity.v3.oauth_token_client import \
+ OAUTHTokenClient
from tempest.lib.services.identity.v3.policies_client import PoliciesClient
from tempest.lib.services.identity.v3.projects_client import ProjectsClient
from tempest.lib.services.identity.v3.regions_client import RegionsClient
@@ -43,7 +45,7 @@
__all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient',
'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
- 'OAUTHConsumerClient', 'PoliciesClient', 'ProjectsClient',
- 'RegionsClient', 'RoleAssignmentsClient', 'RolesClient',
- 'ServicesClient', 'V3TokenClient', 'TrustsClient', 'UsersClient',
- 'VersionsClient']
+ 'OAUTHConsumerClient', 'OAUTHTokenClient', 'PoliciesClient',
+ 'ProjectsClient', 'RegionsClient', 'RoleAssignmentsClient',
+ 'RolesClient', 'ServicesClient', 'V3TokenClient', 'TrustsClient',
+ 'UsersClient', 'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/endpoints_client.py b/tempest/lib/services/identity/v3/endpoints_client.py
index 91592de..e24dca7 100644
--- a/tempest/lib/services/identity/v3/endpoints_client.py
+++ b/tempest/lib/services/identity/v3/endpoints_client.py
@@ -18,6 +18,7 @@
"""
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.common import rest_client
@@ -25,9 +26,17 @@
class EndPointsClient(rest_client.RestClient):
api_version = "v3"
- def list_endpoints(self):
- """GET endpoints."""
- resp, body = self.get('endpoints')
+ def list_endpoints(self, **params):
+ """List endpoints.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/#list-endpoints
+ """
+ url = 'endpoints'
+ 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/lib/services/identity/v3/oauth_token_client.py b/tempest/lib/services/identity/v3/oauth_token_client.py
new file mode 100644
index 0000000..b1d298b
--- /dev/null
+++ b/tempest/lib/services/identity/v3/oauth_token_client.py
@@ -0,0 +1,236 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import binascii
+import hashlib
+import hmac
+import random
+import time
+
+import six
+from six.moves.urllib import parse as urlparse
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class OAUTHTokenClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def _escape(self, s):
+ """Escape a unicode string in an OAuth-compatible fashion."""
+ safe = b'~'
+ s = s.encode('utf-8') if isinstance(s, six.text_type) else s
+ s = urlparse.quote(s, safe)
+ if isinstance(s, six.binary_type):
+ s = s.decode('utf-8')
+ return s
+
+ def _generate_params_with_signature(self, client_key, uri,
+ client_secret=None,
+ resource_owner_key=None,
+ resource_owner_secret=None,
+ callback_uri=None,
+ verifier=None,
+ http_method='GET'):
+ """Generate OAUTH params along with signature."""
+ timestamp = six.text_type(int(time.time()))
+ nonce = six.text_type(random.getrandbits(64)) + timestamp
+ oauth_params = [
+ ('oauth_nonce', nonce),
+ ('oauth_timestamp', timestamp),
+ ('oauth_version', '1.0'),
+ ('oauth_signature_method', 'HMAC-SHA1'),
+ ('oauth_consumer_key', client_key),
+ ]
+ if resource_owner_key:
+ oauth_params.append(('oauth_token', resource_owner_key))
+ if callback_uri:
+ oauth_params.append(('oauth_callback', callback_uri))
+ if verifier:
+ oauth_params.append(('oauth_verifier', verifier))
+
+ # normalize_params
+ key_values = [(self._escape(k), self._escape(v))
+ for k, v in oauth_params]
+ key_values.sort()
+ parameter_parts = ['{0}={1}'.format(k, v) for k, v in key_values]
+ normalized_params = '&'.join(parameter_parts)
+
+ # normalize_uri
+ scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
+ scheme = scheme.lower()
+ netloc = netloc.lower()
+ normalized_uri = urlparse.urlunparse((scheme, netloc, path,
+ params, '', ''))
+
+ # construct base string
+ base_string = self._escape(http_method.upper())
+ base_string += '&'
+ base_string += self._escape(normalized_uri)
+ base_string += '&'
+ base_string += self._escape(normalized_params)
+
+ # sign using hmac-sha1
+ key = self._escape(client_secret or '')
+ key += '&'
+ key += self._escape(resource_owner_secret or '')
+ key_utf8 = key.encode('utf-8')
+ text_utf8 = base_string.encode('utf-8')
+ signature = hmac.new(key_utf8, text_utf8, hashlib.sha1)
+ sig = binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
+
+ oauth_params.append(('oauth_signature', sig))
+ return oauth_params
+
+ def _generate_oauth_header(self, oauth_params):
+ authorization_header = {}
+ authorization_header_parameters_parts = []
+ for oauth_parameter_name, value in oauth_params:
+ escaped_name = self._escape(oauth_parameter_name)
+ escaped_value = self._escape(value)
+ part = '{0}="{1}"'.format(escaped_name, escaped_value)
+ authorization_header_parameters_parts.append(part)
+
+ authorization_header_parameters = ', '.join(
+ authorization_header_parameters_parts)
+ oauth_string = 'OAuth %s' % authorization_header_parameters
+ authorization_header['Authorization'] = oauth_string
+
+ return authorization_header
+
+ def create_request_token(self, consumer_key, consumer_secret, project_id):
+ """Create request token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#create-request-token
+ """
+ endpoint = 'OS-OAUTH1/request_token'
+ headers = {'Requested-Project-Id': project_id}
+ oauth_params = self._generate_params_with_signature(
+ consumer_key,
+ self.base_url + '/' + endpoint,
+ client_secret=consumer_secret,
+ callback_uri='oob',
+ http_method='POST')
+ oauth_header = self._generate_oauth_header(oauth_params)
+ headers.update(oauth_header)
+ resp, body = self.post(endpoint,
+ body=None,
+ headers=headers)
+ self.expected_success(201, resp.status)
+ if not isinstance(body, str):
+ body = body.decode('utf-8')
+ body = dict(item.split("=") for item in body.split("&"))
+ return rest_client.ResponseBody(resp, body)
+
+ def authorize_request_token(self, request_token_id, role_ids):
+ """Authorize request token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#authorize-request-token
+ """
+ roles = [{'id': role_id} for role_id in role_ids]
+ body = {'roles': roles}
+ post_body = json.dumps(body)
+ resp, body = self.put("OS-OAUTH1/authorize/%s" % request_token_id,
+ post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_access_token(self, consumer_key, consumer_secret, request_key,
+ request_secret, oauth_verifier):
+ """Create access token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#create-access-token
+ """
+ endpoint = 'OS-OAUTH1/access_token'
+ oauth_params = self._generate_params_with_signature(
+ consumer_key,
+ self.base_url + '/' + endpoint,
+ client_secret=consumer_secret,
+ resource_owner_key=request_key,
+ resource_owner_secret=request_secret,
+ verifier=oauth_verifier,
+ http_method='POST')
+ headers = self._generate_oauth_header(oauth_params)
+ resp, body = self.post(endpoint, body=None, headers=headers)
+ self.expected_success(201, resp.status)
+ if not isinstance(body, str):
+ body = body.decode('utf-8')
+ body = dict(item.split("=") for item in body.split("&"))
+ return rest_client.ResponseBody(resp, body)
+
+ def get_access_token(self, user_id, access_token_id):
+ """Get access token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#get-access-token
+ """
+ resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s"
+ % (user_id, access_token_id))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def revoke_access_token(self, user_id, access_token_id):
+ """Revoke access token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#revoke-access-token
+ """
+ resp, body = self.delete("users/%s/OS-OAUTH1/access_tokens/%s"
+ % (user_id, access_token_id))
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_access_tokens(self, user_id):
+ """List access tokens.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#list-access-tokens
+ """
+ resp, body = self.get("users/%s/OS-OAUTH1/access_tokens"
+ % (user_id))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_access_token_roles(self, user_id, access_token_id):
+ """List roles for an access token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#list-roles-for-an-access-token
+ """
+ resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s/roles"
+ % (user_id, access_token_id))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def get_access_token_role(self, user_id, access_token_id, role_id):
+ """Show role details for an access token.
+
+ For more information, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#show-role-details-for-an-access-token
+ """
+ resp, body = self.get("users/%s/OS-OAUTH1/access_tokens/%s/roles/%s"
+ % (user_id, access_token_id, role_id))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index b6f9c91..fe8b244 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -54,7 +54,8 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
- def create_resource(self, uri, post_data, expect_empty_body=False):
+ def create_resource(self, uri, post_data, expect_empty_body=False,
+ expect_response_code=201):
req_uri = self.uri_prefix + uri
req_post_data = json.dumps(post_data)
resp, body = self.post(req_uri, req_post_data)
@@ -65,10 +66,11 @@
body = json.loads(body)
else:
body = None
- self.expected_success(201, resp.status)
+ self.expected_success(expect_response_code, resp.status)
return rest_client.ResponseBody(resp, body)
- def update_resource(self, uri, post_data, expect_empty_body=False):
+ def update_resource(self, uri, post_data, expect_empty_body=False,
+ expect_response_code=200):
req_uri = self.uri_prefix + uri
req_post_data = json.dumps(post_data)
resp, body = self.put(req_uri, req_post_data)
@@ -79,5 +81,5 @@
body = json.loads(body)
else:
body = None
- self.expected_success(200, resp.status)
+ self.expected_success(expect_response_code, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/network/tags_client.py b/tempest/lib/services/network/tags_client.py
index 20c2c11..5d49a79 100644
--- a/tempest/lib/services/network/tags_client.py
+++ b/tempest/lib/services/network/tags_client.py
@@ -27,14 +27,10 @@
For more information, please refer to the official API reference:
http://developer.openstack.org/api-ref/networking/v2/index.html#add-a-tag
"""
- # NOTE(felipemonteiro): Cannot use ``update_resource`` method because
- # this API requires self.put but returns 201 instead of 200 expected
- # by ``update_resource``.
- uri = '%s/%s/%s/tags/%s' % (
- self.uri_prefix, resource_type, resource_id, tag)
- resp, _ = self.put(uri, json.dumps({}))
- self.expected_success(201, resp.status)
- return rest_client.ResponseBody(resp)
+ uri = '/%s/%s/tags/%s' % (resource_type, resource_id, tag)
+ return self.update_resource(
+ uri, json.dumps({}), expect_response_code=201,
+ expect_empty_body=True)
def check_tag_existence(self, resource_type, resource_id, tag):
"""Confirm that a given tag is set on the resource.
diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index 07ae917..a351d61 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -13,8 +13,11 @@
# the License.
from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient
+from tempest.lib.services.volume.v3.groups_client import GroupsClient
from tempest.lib.services.volume.v3.messages_client import MessagesClient
from tempest.lib.services.volume.v3.versions_client import VersionsClient
from tempest.lib.services.volume.v3.volumes_client import VolumesClient
-__all__ = ['MessagesClient', 'BaseClient', 'VersionsClient', 'VolumesClient']
+__all__ = ['BaseClient', 'GroupsClient', 'GroupTypesClient',
+ 'MessagesClient', 'VersionsClient', 'VolumesClient']
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
new file mode 100644
index 0000000..390d44d
--- /dev/null
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# 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.services.volume.v3 import base_client
+
+
+class GroupTypesClient(base_client.BaseClient):
+ """Client class to send CRUD Volume V3 Group Types API requests"""
+
+ @property
+ def resource_type(self):
+ """Returns the primary type of resource this client works with."""
+ return 'group-type'
+
+ def create_group_type(self, **kwargs):
+ """Create group_type.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#create-group-type
+ """
+ post_body = json.dumps({'group_type': kwargs})
+ resp, body = self.post('group_types', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_group_type(self, group_type_id):
+ """Deletes the specified group_type."""
+ resp, body = self.delete("group_types/%s" % group_type_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
new file mode 100644
index 0000000..c06997a
--- /dev/null
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v3 import base_client
+
+
+class GroupsClient(base_client.BaseClient):
+ """Client class to send CRUD Volume Group API requests"""
+
+ def create_group(self, **kwargs):
+ """Creates a group.
+
+ group_type and volume_types are required parameters in kwargs.
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#create-group
+ """
+ post_body = json.dumps({'group': kwargs})
+ resp, body = self.post('groups', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_group(self, group_id, delete_volumes=True):
+ """Deletes a group.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#delete-group
+ """
+ post_body = {'delete-volumes': delete_volumes}
+ post_body = json.dumps({'delete': post_body})
+ resp, body = self.post('groups/%s/action' % group_id,
+ post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_group(self, group_id):
+ """Returns the details of a single group.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#show-group-details
+ """
+ url = "groups/%s" % str(group_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_groups(self, detail=False, **params):
+ """Lists information for all the tenant's groups.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#list-groups
+ https://developer.openstack.org/api-ref/block-storage/v3/#list-groups-with-details
+ """
+ url = "groups"
+ if detail:
+ url += "/detail"
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.show_group(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 'group'
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index a857329..86f6ad5 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -235,7 +235,7 @@
server_id=self.server_id
)
- def test_delete_server(self, bytes_body=False):
+ def test_delete_server(self):
self.check_service_client_function(
self.client.delete_server,
'tempest.lib.common.rest_client.RestClient.delete',
@@ -288,6 +288,7 @@
self.client.list_addresses_by_network,
'tempest.lib.common.rest_client.RestClient.get',
self.FAKE_ADDRESS['addresses'],
+ bytes_body,
server_id=self.server_id,
network_id=self.network_id
)
@@ -303,6 +304,7 @@
self.client.action,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
server_id=self.server_id,
action_name='fake-action-name',
schema={'status_code': 200}
@@ -319,6 +321,7 @@
self.client.create_backup,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
status=202,
server_id=self.server_id,
backup_type='fake-backup',
@@ -339,6 +342,7 @@
self.client.evacuate_server,
'tempest.lib.common.rest_client.RestClient.post',
self.FAKE_SERVER_PASSWORD,
+ bytes_body,
**kwargs)
def test_change_password_with_str_body(self):
@@ -352,6 +356,7 @@
self.client.change_password,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
status=202,
server_id=self.server_id,
adminPass='fake-admin-pass'
@@ -368,16 +373,11 @@
self.client.show_password,
'tempest.lib.common.rest_client.RestClient.get',
{'password': 'fake-password'},
+ bytes_body,
server_id=self.server_id
)
- def test_delete_password_with_str_body(self):
- self._test_delete_password()
-
- def test_delete_password_with_bytes_body(self):
- self._test_delete_password(True)
-
- def _test_delete_password(self, bytes_body=False):
+ def test_delete_password(self):
self.check_service_client_function(
self.client.delete_password,
'tempest.lib.common.rest_client.RestClient.delete',
@@ -386,13 +386,7 @@
server_id=self.server_id
)
- def test_reboot_server_with_str_body(self):
- self._test_reboot_server()
-
- def test_reboot_server_with_bytes_body(self):
- self._test_reboot_server(True)
-
- def _test_reboot_server(self, bytes_body=False):
+ def test_reboot_server(self):
self.check_service_client_function(
self.client.reboot_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -413,18 +407,13 @@
self.client.rebuild_server,
'tempest.lib.common.rest_client.RestClient.post',
self.FAKE_REBUILD_SERVER,
+ bytes_body,
status=202,
server_id=self.server_id,
image_ref='fake-image-ref'
)
- def test_resize_server_with_str_body(self):
- self._test_resize_server()
-
- def test_resize_server_with_bytes_body(self):
- self._test_resize_server(True)
-
- def _test_resize_server(self, bytes_body=False):
+ def test_resize_server(self):
self.check_service_client_function(
self.client.resize_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -434,13 +423,7 @@
flavor_ref='fake-flavor-ref'
)
- def test_confirm_resize_server_with_str_body(self):
- self._test_confirm_resize_server()
-
- def test_confirm_resize_server_with_bytes_body(self):
- self._test_confirm_resize_server(True)
-
- def _test_confirm_resize_server(self, bytes_body=False):
+ def test_confirm_resize_server(self):
self.check_service_client_function(
self.client.confirm_resize_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -449,13 +432,7 @@
server_id=self.server_id
)
- def test_revert_resize_server_with_str_body(self):
- self._test_revert_resize()
-
- def test_revert_resize_server_with_bytes_body(self):
- self._test_revert_resize(True)
-
- def _test_revert_resize(self, bytes_body=False):
+ def test_revert_resize(self):
self.check_service_client_function(
self.client.revert_resize_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -475,6 +452,7 @@
self.client.list_server_metadata,
'tempest.lib.common.rest_client.RestClient.get',
{'metadata': {'fake-key': 'fake-meta-data'}},
+ bytes_body,
server_id=self.server_id
)
@@ -489,6 +467,7 @@
self.client.set_server_metadata,
'tempest.lib.common.rest_client.RestClient.put',
{'metadata': {'fake-key': 'fake-meta-data'}},
+ bytes_body,
server_id=self.server_id,
meta='fake-meta'
)
@@ -504,6 +483,7 @@
self.client.update_server_metadata,
'tempest.lib.common.rest_client.RestClient.post',
{'metadata': {'fake-key': 'fake-meta-data'}},
+ bytes_body,
server_id=self.server_id,
meta='fake-meta'
)
@@ -519,6 +499,7 @@
self.client.show_server_metadata_item,
'tempest.lib.common.rest_client.RestClient.get',
{'meta': {'fake-key': 'fake-meta-data'}},
+ bytes_body,
server_id=self.server_id,
key='fake-key'
)
@@ -534,18 +515,13 @@
self.client.set_server_metadata_item,
'tempest.lib.common.rest_client.RestClient.put',
{'meta': {'fake-key': 'fake-meta-data'}},
+ bytes_body,
server_id=self.server_id,
key='fake-key',
meta='fake-meta'
)
- def test_delete_server_metadata_item_with_str_body(self):
- self._test_delete_server_metadata()
-
- def test_delete_server_metadata_item_with_bytes_body(self):
- self._test_delete_server_metadata(True)
-
- def _test_delete_server_metadata(self, bytes_body=False):
+ def test_delete_server_metadata(self):
self.check_service_client_function(
self.client.delete_server_metadata_item,
'tempest.lib.common.rest_client.RestClient.delete',
@@ -555,13 +531,7 @@
key='fake-key'
)
- def test_stop_server_with_str_body(self):
- self._test_stop_server()
-
- def test_stop_server_with_bytes_body(self):
- self._test_stop_server(True)
-
- def _test_stop_server(self, bytes_body=False):
+ def test_stop_server(self):
self.check_service_client_function(
self.client.stop_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -570,13 +540,7 @@
server_id=self.server_id
)
- def test_start_server_with_str_body(self):
- self._test_start_server()
-
- def test_start_server_with_bytes_body(self):
- self._test_start_server(True)
-
- def _test_start_server(self, bytes_body=False):
+ def test_start_server(self):
self.check_service_client_function(
self.client.start_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -596,6 +560,7 @@
self.client.attach_volume,
'tempest.lib.common.rest_client.RestClient.post',
{'volumeAttachment': self.FAKE_COMMON_VOLUME},
+ bytes_body,
server_id=self.server_id
)
@@ -621,6 +586,7 @@
self.client.detach_volume,
'tempest.lib.common.rest_client.RestClient.delete',
{},
+ bytes_body,
status=202,
server_id=self.server_id,
volume_id=self.FAKE_COMMON_VOLUME['volumeId']
@@ -637,6 +603,7 @@
self.client.show_volume_attachment,
'tempest.lib.common.rest_client.RestClient.get',
{'volumeAttachment': self.FAKE_COMMON_VOLUME},
+ bytes_body,
server_id=self.server_id,
volume_id=self.FAKE_COMMON_VOLUME['volumeId']
)
@@ -652,6 +619,7 @@
self.client.list_volume_attachments,
'tempest.lib.common.rest_client.RestClient.get',
{'volumeAttachments': [self.FAKE_COMMON_VOLUME]},
+ bytes_body,
server_id=self.server_id
)
@@ -666,18 +634,13 @@
self.client.add_security_group,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
status=202,
server_id=self.server_id,
name='fake-name'
)
- def test_remove_security_group_with_str_body(self):
- self._test_remove_security_group()
-
- def test_remove_security_group_with_bytes_body(self):
- self._test_remove_security_group(True)
-
- def _test_remove_security_group(self, bytes_body=False):
+ def test_remove_security_group(self):
self.check_service_client_function(
self.client.remove_security_group,
'tempest.lib.common.rest_client.RestClient.post',
@@ -698,6 +661,7 @@
self.client.live_migrate_server,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
status=202,
server_id=self.server_id
)
@@ -713,17 +677,12 @@
self.client.migrate_server,
'tempest.lib.common.rest_client.RestClient.post',
{},
+ bytes_body,
status=202,
server_id=self.server_id
)
- def test_lock_server_with_str_body(self):
- self._test_lock_server()
-
- def test_lock_server_with_bytes_body(self):
- self._test_lock_server(True)
-
- def _test_lock_server(self, bytes_body=False):
+ def test_lock_server(self):
self.check_service_client_function(
self.client.lock_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -732,13 +691,7 @@
server_id=self.server_id
)
- def test_unlock_server_with_str_body(self):
- self._test_unlock_server()
-
- def test_unlock_server_with_bytes_body(self):
- self._test_unlock_server(True)
-
- def _test_unlock_server(self, bytes_body=False):
+ def test_unlock_server(self):
self.check_service_client_function(
self.client.unlock_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -747,13 +700,7 @@
server_id=self.server_id
)
- def test_suspend_server_with_str_body(self):
- self._test_suspend_server()
-
- def test_suspend_server_with_bytes_body(self):
- self._test_suspend_server(True)
-
- def _test_suspend_server(self, bytes_body=False):
+ def test_suspend_server(self):
self.check_service_client_function(
self.client.suspend_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -762,13 +709,7 @@
server_id=self.server_id
)
- def test_resume_server_with_str_body(self):
- self._test_resume_server()
-
- def test_resume_server_with_bytes_body(self):
- self._test_resume_server(True)
-
- def _test_resume_server(self, bytes_body=False):
+ def test_resume_server(self):
self.check_service_client_function(
self.client.resume_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -777,13 +718,7 @@
server_id=self.server_id
)
- def test_pause_server_with_str_body(self):
- self._test_pause_server()
-
- def test_pause_server_with_bytes_body(self):
- self._test_pause_server(True)
-
- def _test_pause_server(self, bytes_body=False):
+ def test_pause_server(self):
self.check_service_client_function(
self.client.pause_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -792,13 +727,7 @@
server_id=self.server_id
)
- def test_unpause_server_with_str_body(self):
- self._test_unpause_server()
-
- def test_unpause_server_with_bytes_body(self):
- self._test_unpause_server(True)
-
- def _test_unpause_server(self, bytes_body=False):
+ def test_unpause_server(self):
self.check_service_client_function(
self.client.unpause_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -807,13 +736,7 @@
server_id=self.server_id
)
- def test_reset_state_with_str_body(self):
- self._test_reset_state()
-
- def test_reset_state_with_bytes_body(self):
- self._test_reset_state(True)
-
- def _test_reset_state(self, bytes_body=False):
+ def test_reset_state(self):
self.check_service_client_function(
self.client.reset_state,
'tempest.lib.common.rest_client.RestClient.post',
@@ -823,13 +746,7 @@
state='fake-state'
)
- def test_shelve_server_with_str_body(self):
- self._test_shelve_server()
-
- def test_shelve_server_with_bytes_body(self):
- self._test_shelve_server(True)
-
- def _test_shelve_server(self, bytes_body=False):
+ def test_shelve_server(self):
self.check_service_client_function(
self.client.shelve_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -838,13 +755,7 @@
server_id=self.server_id
)
- def test_unshelve_server_with_str_body(self):
- self._test_unshelve_server()
-
- def test_unshelve_server_with_bytes_body(self):
- self._test_unshelve_server(True)
-
- def _test_unshelve_server(self, bytes_body=False):
+ def test_unshelve_server(self):
self.check_service_client_function(
self.client.unshelve_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -853,13 +764,7 @@
server_id=self.server_id
)
- def test_shelve_offload_server_with_str_body(self):
- self._test_shelve_offload_server()
-
- def test_shelve_offload_server_with_bytes_body(self):
- self._test_shelve_offload_server(True)
-
- def _test_shelve_offload_server(self, bytes_body=False):
+ def test_shelve_offload_server(self):
self.check_service_client_function(
self.client.shelve_offload_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -879,6 +784,7 @@
self.client.get_console_output,
'tempest.lib.common.rest_client.RestClient.post',
{'output': 'fake-output'},
+ bytes_body,
server_id=self.server_id,
length='fake-length'
)
@@ -894,6 +800,7 @@
self.client.list_virtual_interfaces,
'tempest.lib.common.rest_client.RestClient.get',
{'virtual_interfaces': [self.FAKE_VIRTUAL_INTERFACES]},
+ bytes_body,
server_id=self.server_id
)
@@ -908,16 +815,11 @@
self.client.rescue_server,
'tempest.lib.common.rest_client.RestClient.post',
{'adminPass': 'fake-admin-pass'},
+ bytes_body,
server_id=self.server_id
)
- def test_unrescue_server_with_str_body(self):
- self._test_unrescue_server()
-
- def test_unrescue_server_with_bytes_body(self):
- self._test_unrescue_server(True)
-
- def _test_unrescue_server(self, bytes_body=False):
+ def test_unrescue_server(self):
self.check_service_client_function(
self.client.unrescue_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -937,6 +839,7 @@
self.client.show_server_diagnostics,
'tempest.lib.common.rest_client.RestClient.get',
self.FAKE_SERVER_DIAGNOSTICS,
+ bytes_body,
status=200,
server_id=self.server_id
)
@@ -952,6 +855,7 @@
self.client.list_instance_actions,
'tempest.lib.common.rest_client.RestClient.get',
{'instanceActions': [self.FAKE_INSTANCE_ACTIONS]},
+ bytes_body,
server_id=self.server_id
)
@@ -966,17 +870,12 @@
self.client.show_instance_action,
'tempest.lib.common.rest_client.RestClient.get',
{'instanceAction': self.FAKE_INSTANCE_WITH_EVENTS},
+ bytes_body,
server_id=self.server_id,
request_id='fake-request-id'
)
- def test_force_delete_server_with_str_body(self):
- self._test_force_delete_server()
-
- def test_force_delete_server_with_bytes_body(self):
- self._test_force_delete_server(True)
-
- def _test_force_delete_server(self, bytes_body=False):
+ def test_force_delete_server(self):
self.check_service_client_function(
self.client.force_delete_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -985,13 +884,7 @@
server_id=self.server_id
)
- def test_restore_soft_deleted_server_with_str_body(self):
- self._test_restore_soft_deleted_server()
-
- def test_restore_soft_deleted_server_with_bytes_body(self):
- self._test_restore_soft_deleted_server(True)
-
- def _test_restore_soft_deleted_server(self, bytes_body=False):
+ def test_restore_soft_deleted_server(self):
self.check_service_client_function(
self.client.restore_soft_deleted_server,
'tempest.lib.common.rest_client.RestClient.post',
@@ -1000,13 +893,7 @@
server_id=self.server_id
)
- def test_reset_network_with_str_body(self):
- self._test_reset_network()
-
- def test_reset_network_with_bytes_body(self):
- self._test_reset_network(True)
-
- def _test_reset_network(self, bytes_body=False):
+ def test_reset_network(self):
self.check_service_client_function(
self.client.reset_network,
'tempest.lib.common.rest_client.RestClient.post',
@@ -1015,13 +902,7 @@
server_id=self.server_id
)
- def test_inject_network_info_with_str_body(self):
- self._test_inject_network_info()
-
- def test_inject_network_info_with_bytes_body(self):
- self._test_inject_network_info(True)
-
- def _test_inject_network_info(self, bytes_body=False):
+ def test_inject_network_info(self):
self.check_service_client_function(
self.client.inject_network_info,
'tempest.lib.common.rest_client.RestClient.post',
@@ -1041,6 +922,7 @@
self.client.get_vnc_console,
'tempest.lib.common.rest_client.RestClient.post',
{'console': self.FAKE_VNC_CONSOLE},
+ bytes_body,
server_id=self.server_id,
type='fake-console-type'
)
@@ -1056,7 +938,8 @@
self.client.list_security_groups_by_server,
'tempest.lib.common.rest_client.RestClient.get',
{'security_groups': self.FAKE_SECURITY_GROUPS},
- server_id=self.server_id,
+ bytes_body,
+ server_id=self.server_id
)
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
@@ -1151,15 +1034,7 @@
@mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
new_callable=mock.PropertyMock(return_value='2.26'))
- def test_delete_tag_str_body(self, _):
- self._test_delete_tag()
-
- @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
- new_callable=mock.PropertyMock(return_value='2.26'))
- def test_delete_tag_byte_body(self, _):
- self._test_delete_tag(bytes_body=True)
-
- def _test_delete_tag(self, bytes_body=False):
+ def test_delete_tag(self, _):
self.check_service_client_function(
self.client.delete_tag,
'tempest.lib.common.rest_client.RestClient.delete',
@@ -1167,7 +1042,7 @@
server_id=self.server_id,
tag=self.FAKE_TAGS[0],
status=204,
- to_utf=bytes_body)
+ )
class TestServersClientMinV26(base.BaseServiceTest):
diff --git a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
index f8c553f..ca15dd1 100644
--- a/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_endpoints_client.py
@@ -53,6 +53,8 @@
]
}
+ FAKE_SERVICE_ID = "a4dc5060-f757-4662-b658-edd2aefbb41d"
+
def setUp(self):
super(TestEndpointsClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -72,12 +74,15 @@
adminurl="https://compute.north.internal.com/v1",
internalurl="https://compute.north.internal.com/v1")
- def _test_list_endpoints(self, bytes_body=False):
+ def _test_list_endpoints(self, bytes_body=False, mock_args='endpoints',
+ **params):
self.check_service_client_function(
self.client.list_endpoints,
'tempest.lib.common.rest_client.RestClient.get',
self.FAKE_LIST_ENDPOINTS,
- bytes_body)
+ bytes_body,
+ mock_args=[mock_args],
+ **params)
def test_create_endpoint_with_str_body(self):
self._test_create_endpoint()
@@ -91,6 +96,16 @@
def test_list_endpoints_with_bytes_body(self):
self._test_list_endpoints(bytes_body=True)
+ def test_list_endpoints_with_params(self):
+ # Run the test separately for each param, to avoid assertion error
+ # resulting from randomized params order.
+ mock_args = 'endpoints?service_id=%s' % self.FAKE_SERVICE_ID
+ self._test_list_endpoints(mock_args=mock_args,
+ service_id=self.FAKE_SERVICE_ID)
+
+ mock_args = 'endpoints?interface=public'
+ self._test_list_endpoints(mock_args=mock_args, interface='public')
+
def test_delete_endpoint(self):
self.check_service_client_function(
self.client.delete_endpoint,
diff --git a/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py b/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py
new file mode 100644
index 0000000..b9b9b15
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_oauth_token_client.py
@@ -0,0 +1,215 @@
+# Copyright 2017 AT&T 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 oslotest import mockpatch
+
+from tempest.lib.services.identity.v3 import oauth_token_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
+from tempest.tests.lib.services import base
+
+
+class TestOAUTHTokenClient(base.BaseServiceTest):
+ FAKE_CREATE_REQUEST_TOKEN = {
+ 'oauth_token': '29971f',
+ 'oauth_token_secret': '238eb8',
+ 'oauth_expires_at': '2013-09-11T06:07:51.501805Z'
+ }
+
+ FAKE_AUTHORIZE_REQUEST_TOKEN = {
+ 'token': {
+ 'oauth_verifier': '8171'
+ }
+ }
+
+ FAKE_CREATE_ACCESS_TOKEN = {
+ 'oauth_token': 'accd36',
+ 'oauth_token_secret': 'aa47da',
+ 'oauth_expires_at': '2013-09-11T06:07:51.501805Z'
+ }
+
+ FAKE_ACCESS_TOKEN_INFO = {
+ 'access_token': {
+ 'consumer_id': '7fea2d',
+ 'id': '6be26a',
+ 'expires_at': '2013-09-11T06:07:51.501805Z',
+ 'links': {
+ 'roles': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles',
+ 'self': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens/6be26a'
+ },
+ 'project_id': 'b9fca3',
+ 'authorizing_user_id': 'ce9e07'
+ }
+ }
+
+ FAKE_LIST_ACCESS_TOKENS = {
+ 'access_tokens': [
+ {
+ 'consumer_id': '7fea2d',
+ 'id': '6be26a',
+ 'expires_at': '2013-09-11T06:07:51.501805Z',
+ 'links': {
+ 'roles': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens/' +
+ '6be26a/roles',
+ 'self': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens/6be26a'
+ },
+ 'project_id': 'b9fca3',
+ 'authorizing_user_id': 'ce9e07'
+ }
+ ],
+ 'links': {
+ 'next': None,
+ 'previous': None,
+ 'self': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens'
+ }
+ }
+
+ FAKE_LIST_ACCESS_TOKEN_ROLES = {
+ 'roles': [
+ {
+ 'id': '26b860',
+ 'domain_id': 'fake_domain',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'roles/26b860'
+ },
+ 'name': 'fake_role'
+ }
+ ],
+ 'links': {
+ 'next': None,
+ 'previous': None,
+ 'self': 'http://example.com/identity/v3/' +
+ 'users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles'
+ }
+ }
+
+ FAKE_ACCESS_TOKEN_ROLE_INFO = {
+ 'role': {
+ 'id': '26b860',
+ 'domain_id': 'fake_domain',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'roles/26b860'
+ },
+ 'name': 'fake_role'
+ }
+ }
+
+ def setUp(self):
+ super(TestOAUTHTokenClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = oauth_token_client.OAUTHTokenClient(fake_auth,
+ 'identity',
+ 'regionOne')
+
+ def _mock_token_response(self, body):
+ temp_response = [key + '=' + value for key, value in body.items()]
+ return '&'.join(temp_response)
+
+ def _test_authorize_request_token(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.authorize_request_token,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_AUTHORIZE_REQUEST_TOKEN,
+ bytes_body,
+ request_token_id=self.FAKE_CREATE_REQUEST_TOKEN['oauth_token'],
+ role_ids=['26b860'],
+ status=200)
+
+ def test_create_request_token(self):
+ mock_resp = self._mock_token_response(self.FAKE_CREATE_REQUEST_TOKEN)
+ resp = fake_http.fake_http_response(None, status=201), mock_resp
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.rest_client.RestClient.post',
+ return_value=resp))
+
+ resp = self.client.create_request_token(
+ consumer_key='12345',
+ consumer_secret='23456',
+ project_id='c8f58432c6f00162f04d3250f')
+ self.assertEqual(self.FAKE_CREATE_REQUEST_TOKEN, resp)
+
+ def test_authorize_token_request_with_str_body(self):
+ self._test_authorize_request_token()
+
+ def test_authorize_token_request_with_bytes_body(self):
+ self._test_authorize_request_token(bytes_body=True)
+
+ def test_create_access_token(self):
+ mock_resp = self._mock_token_response(self.FAKE_CREATE_ACCESS_TOKEN)
+ req_secret = self.FAKE_CREATE_REQUEST_TOKEN['oauth_token_secret']
+ resp = fake_http.fake_http_response(None, status=201), mock_resp
+ self.useFixture(mockpatch.Patch(
+ 'tempest.lib.common.rest_client.RestClient.post',
+ return_value=resp))
+
+ resp = self.client.create_access_token(
+ consumer_key='12345',
+ consumer_secret='23456',
+ request_key=self.FAKE_CREATE_REQUEST_TOKEN['oauth_token'],
+ request_secret=req_secret,
+ oauth_verifier='8171')
+ self.assertEqual(self.FAKE_CREATE_ACCESS_TOKEN, resp)
+
+ def test_get_access_token(self):
+ self.check_service_client_function(
+ self.client.get_access_token,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ACCESS_TOKEN_INFO,
+ user_id='ce9e07',
+ access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+ status=200)
+
+ def test_list_access_tokens(self):
+ self.check_service_client_function(
+ self.client.list_access_tokens,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ACCESS_TOKENS,
+ user_id='ce9e07',
+ status=200)
+
+ def test_revoke_access_token(self):
+ self.check_service_client_function(
+ self.client.revoke_access_token,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ user_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['consumer_id'],
+ access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+ status=204)
+
+ def test_list_access_token_roles(self):
+ self.check_service_client_function(
+ self.client.list_access_token_roles,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ACCESS_TOKEN_ROLES,
+ user_id='ce9e07',
+ access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+ status=200)
+
+ def test_get_access_token_role(self):
+ self.check_service_client_function(
+ self.client.get_access_token_role,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_ACCESS_TOKEN_ROLE_INFO,
+ user_id='ce9e07',
+ access_token_id=self.FAKE_ACCESS_TOKEN_INFO['access_token']['id'],
+ role_id=self.FAKE_ACCESS_TOKEN_ROLE_INFO['role']['id'],
+ status=200)
diff --git a/tempest/tests/lib/services/network/test_base_network_client.py b/tempest/tests/lib/services/network/test_base_network_client.py
new file mode 100644
index 0000000..e121cec
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_base_network_client.py
@@ -0,0 +1,96 @@
+# Copyright 2017 AT&T Corporation.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from tempest.lib.services.network import base as base_network_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib import fake_http
+from tempest.tests.lib.services import base
+
+
+class TestBaseNetworkClient(base.BaseServiceTest):
+
+ def setUp(self):
+ super(TestBaseNetworkClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = base_network_client.BaseNetworkClient(
+ fake_auth, 'compute', 'regionOne')
+
+ self.mock_expected_success = mock.patch.object(
+ self.client, 'expected_success').start()
+
+ def _assert_empty(self, resp):
+ self.assertEqual([], list(resp.keys()))
+
+ @mock.patch('tempest.lib.common.rest_client.RestClient.post')
+ def test_create_resource(self, mock_post):
+ response = fake_http.fake_http_response(headers=None, status=201)
+ mock_post.return_value = response, '{"baz": "qux"}'
+
+ post_data = {'foo': 'bar'}
+ resp = self.client.create_resource('/fake_url', post_data)
+
+ self.assertEqual({'status': '201'}, resp.response)
+ self.assertEqual("qux", resp["baz"])
+ mock_post.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+ self.mock_expected_success.assert_called_once_with(
+ 201, 201)
+
+ @mock.patch('tempest.lib.common.rest_client.RestClient.post')
+ def test_create_resource_expect_different_values(self, mock_post):
+ response = fake_http.fake_http_response(headers=None, status=200)
+ mock_post.return_value = response, '{}'
+
+ post_data = {'foo': 'bar'}
+ resp = self.client.create_resource('/fake_url', post_data,
+ expect_response_code=200,
+ expect_empty_body=True)
+
+ self.assertEqual({'status': '200'}, resp.response)
+ self._assert_empty(resp)
+ mock_post.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+ self.mock_expected_success.assert_called_once_with(
+ 200, 200)
+
+ @mock.patch('tempest.lib.common.rest_client.RestClient.put')
+ def test_update_resource(self, mock_put):
+ response = fake_http.fake_http_response(headers=None, status=200)
+ mock_put.return_value = response, '{"baz": "qux"}'
+
+ put_data = {'foo': 'bar'}
+ resp = self.client.update_resource('/fake_url', put_data)
+
+ self.assertEqual({'status': '200'}, resp.response)
+ self.assertEqual("qux", resp["baz"])
+ mock_put.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+ self.mock_expected_success.assert_called_once_with(
+ 200, 200)
+
+ @mock.patch('tempest.lib.common.rest_client.RestClient.put')
+ def test_update_resource_expect_different_values(self, mock_put):
+ response = fake_http.fake_http_response(headers=None, status=201)
+ mock_put.return_value = response, '{}'
+
+ put_data = {'foo': 'bar'}
+ resp = self.client.update_resource('/fake_url', put_data,
+ expect_response_code=201,
+ expect_empty_body=True)
+
+ self.assertEqual({'status': '201'}, resp.response)
+ self._assert_empty(resp)
+ mock_put.assert_called_once_with('v2.0/fake_url', '{"foo": "bar"}')
+ self.mock_expected_success.assert_called_once_with(
+ 201, 201)
diff --git a/tempest/tests/lib/services/network/test_security_group_rules_client.py b/tempest/tests/lib/services/network/test_security_group_rules_client.py
index ebffcbe..b9c17a1 100644
--- a/tempest/tests/lib/services/network/test_security_group_rules_client.py
+++ b/tempest/tests/lib/services/network/test_security_group_rules_client.py
@@ -15,8 +15,10 @@
import copy
+import mock
from oslo_serialization import jsonutils as json
+from tempest.lib.services.network import base as network_base
from tempest.lib.services.network import security_group_rules_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -80,16 +82,22 @@
kwargs = {'direction': 'egress',
'security_group_id': '85cc3048-abc3-43cc-89b3-377341426ac5',
'remote_ip_prefix': None}
+ payload = json.dumps({"security_group_rule": kwargs}, sort_keys=True)
+ json_dumps = json.dumps
- self.check_service_client_function(
- self.client.create_security_group_rule,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_SECURITY_GROUP_RULE,
- bytes_body,
- status=201,
- mock_args=['v2.0/security-group-rules',
- json.dumps({"security_group_rule": kwargs})],
- **kwargs)
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.create_security_group_rule,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SECURITY_GROUP_RULE,
+ bytes_body,
+ status=201,
+ mock_args=['v2.0/security-group-rules', payload],
+ **kwargs)
def _test_show_security_group_rule(self, bytes_body=False):
self.check_service_client_function(
diff --git a/tempest/tests/lib/services/network/test_security_groups_client.py b/tempest/tests/lib/services/network/test_security_groups_client.py
index d066378..f96805f 100644
--- a/tempest/tests/lib/services/network/test_security_groups_client.py
+++ b/tempest/tests/lib/services/network/test_security_groups_client.py
@@ -15,8 +15,10 @@
import copy
+import mock
from oslo_serialization import jsonutils as json
+from tempest.lib.services.network import base as network_base
from tempest.lib.services.network import security_groups_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -89,15 +91,22 @@
def _test_create_security_group(self, bytes_body=False):
kwargs = {'name': 'fake-security-group-name'}
- self.check_service_client_function(
- self.client.create_security_group,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_SECURITY_GROUP,
- bytes_body,
- status=201,
- mock_args=['v2.0/security-groups',
- json.dumps({"security_group": kwargs})],
- **kwargs)
+ payload = json.dumps({"security_group": kwargs}, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.create_security_group,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SECURITY_GROUP,
+ bytes_body,
+ status=201,
+ mock_args=['v2.0/security-groups', payload],
+ **kwargs)
def _test_show_security_group(self, bytes_body=False):
self.check_service_client_function(
@@ -113,15 +122,23 @@
resp_body = copy.deepcopy(self.FAKE_SECURITY_GROUP)
resp_body["security_group"]["name"] = 'updated-security-group-name'
- self.check_service_client_function(
- self.client.update_security_group,
- 'tempest.lib.common.rest_client.RestClient.put',
- resp_body,
- bytes_body,
- security_group_id=self.FAKE_SEC_GROUP_ID,
- mock_args=['v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID,
- json.dumps({'security_group': kwargs})],
- **kwargs)
+ payload = json.dumps({'security_group': kwargs}, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.update_security_group,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ resp_body,
+ bytes_body,
+ security_group_id=self.FAKE_SEC_GROUP_ID,
+ mock_args=['v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID,
+ payload],
+ **kwargs)
def test_list_security_groups_with_str_body(self):
self._test_list_security_groups()
diff --git a/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
new file mode 100644
index 0000000..8a5f25f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_scheduler_stats_client.py
@@ -0,0 +1,83 @@
+# Copyright 2017 AT&T 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.volume.v2 import scheduler_stats_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestSchedulerStatsClient(base.BaseServiceTest):
+ FAKE_POOLS_LIST = {
+ "pools": [
+ {
+ "name": "pool1",
+ "capabilities": {
+ "updated": "2014-10-28T00:00:00-00:00",
+ "total_capacity": 1024,
+ "free_capacity": 100,
+ "volume_backend_name": "pool1",
+ "reserved_percentage": 0,
+ "driver_version": "1.0.0",
+ "storage_protocol": "iSCSI",
+ "QoS_support": False
+ }
+ },
+ {
+ "name": "pool2",
+ "capabilities": {
+ "updated": "2014-10-28T00:00:00-00:00",
+ "total_capacity": 512,
+ "free_capacity": 200,
+ "volume_backend_name": "pool2",
+ "reserved_percentage": 0,
+ "driver_version": "1.0.2",
+ "storage_protocol": "iSER",
+ "QoS_support": True
+ }
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestSchedulerStatsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = scheduler_stats_client.SchedulerStatsClient(
+ fake_auth, 'volume', 'regionOne')
+
+ def _test_list_pools(self, bytes_body=False, detail=False):
+ resp_body = []
+ if detail:
+ resp_body = self.FAKE_POOLS_LIST
+ else:
+ resp_body = {'pools': [{'name': pool['name']}
+ for pool in self.FAKE_POOLS_LIST['pools']]}
+ self.check_service_client_function(
+ self.client.list_pools,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ resp_body,
+ bytes_body,
+ detail=detail)
+
+ def test_list_pools_with_str_body(self):
+ self._test_list_pools()
+
+ def test_list_pools_with_str_body_and_detail(self):
+ self._test_list_pools(detail=True)
+
+ def test_list_pools_with_bytes_body(self):
+ self._test_list_pools(bytes_body=True)
+
+ def test_list_pools_with_bytes_body_and_detail(self):
+ self._test_list_pools(bytes_body=True, detail=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_group_types_client.py b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
new file mode 100644
index 0000000..95498c7
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_group_types_client.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+#
+# 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.volume.v3 import group_types_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupTypesClient(base.BaseServiceTest):
+ FAKE_CREATE_GROUP_TYPE = {
+ "group_type": {
+ "name": "group-type-001",
+ "description": "Test group type 1",
+ "group_specs": {},
+ "is_public": True,
+ }
+ }
+
+ def setUp(self):
+ super(TestGroupTypesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = group_types_client.GroupTypesClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_create_group_type(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_group_type,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_GROUP_TYPE,
+ bytes_body,
+ status=202)
+
+ def test_create_group_type_with_str_body(self):
+ self._test_create_group_type()
+
+ def test_create_group_type_with_bytes_body(self):
+ self._test_create_group_type(bytes_body=True)
+
+ def test_delete_group_type(self):
+ self.check_service_client_function(
+ self.client.delete_group_type,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ group_type_id='0e58433f-d108-4bf3-a22c-34e6b71ef86b',
+ status=202)
diff --git a/tempest/tests/lib/services/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
new file mode 100644
index 0000000..00db5b4
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -0,0 +1,136 @@
+# Copyright (C) 2017 Dell Inc. or its subsidiaries.
+#
+# 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.volume.v3 import groups_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestGroupsClient(base.BaseServiceTest):
+ FAKE_CREATE_GROUP = {
+ "group": {
+ "name": "group-001",
+ "description": "Test group 1",
+ "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+ "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+ "availability_zone": "az1",
+ }
+ }
+
+ FAKE_INFO_GROUP = {
+ "group": {
+ "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+ "name": "group-001",
+ "description": "Test group 1",
+ "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+ "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+ "status": "available",
+ "availability_zone": "az1",
+ "created_at": "20127-06-20T03:50:07Z"
+ }
+ }
+
+ FAKE_LIST_GROUPS = {
+ "groups": [
+ {
+ "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
+ "name": "group-001",
+ "description": "Test group 1",
+ "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+ "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+ "status": "available",
+ "availability_zone": "az1",
+ "created_at": "2017-06-20T03:50:07Z",
+ },
+ {
+ "id": "e479997c-650b-40a4-9dfe-77655818b0d2",
+ "name": "group-002",
+ "description": "Test group 2",
+ "group_snapshot_id": "79c9afdb-7e46-4d71-9249-1f022886963c",
+ "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+ "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+ "status": "available",
+ "availability_zone": "az1",
+ "created_at": "2017-06-19T01:52:47Z",
+ },
+ {
+ "id": "c5c4769e-213c-40a6-a568-8e797bb691d4",
+ "name": "group-003",
+ "description": "Test group 3",
+ "source_group_id": "e92f9dc7-0b20-492d-8ab2-3ad8fdac270e",
+ "group_type": "0e58433f-d108-4bf3-a22c-34e6b71ef86b",
+ "volume_types": ["2103099d-7cc3-4e52-a2f1-23a5284416f3"],
+ "status": "available",
+ "availability_zone": "az1",
+ "created_at": "2017-06-18T06:34:32Z",
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestGroupsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = groups_client.GroupsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_create_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_group,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_GROUP,
+ bytes_body,
+ status=202)
+
+ def _test_show_group(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_group,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_INFO_GROUP,
+ bytes_body,
+ group_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
+
+ def _test_list_groups(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_groups,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_GROUPS,
+ bytes_body,
+ detail=True)
+
+ def test_create_group_with_str_body(self):
+ self._test_create_group()
+
+ def test_create_group_with_bytes_body(self):
+ self._test_create_group(bytes_body=True)
+
+ def test_show_group_with_str_body(self):
+ self._test_show_group()
+
+ def test_show_group_with_bytes_body(self):
+ self._test_show_group(bytes_body=True)
+
+ def test_list_groups_with_str_body(self):
+ self._test_list_groups()
+
+ def test_list_groups_with_bytes_body(self):
+ self._test_list_groups(bytes_body=True)
+
+ def test_delete_group(self):
+ self.check_service_client_function(
+ self.client.delete_group,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ {},
+ group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+ status=202)
diff --git a/test-requirements.txt b/test-requirements.txt
index 19b45ea..f8793be 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,11 +3,11 @@
# process, which may cause wedges in the gate later.
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
# needed for doc build
-sphinx!=1.6.1,>=1.5.1 # BSD
+sphinx>=1.6.2 # BSD
oslosphinx>=4.7.0 # Apache-2.0
reno!=2.3.1,>=1.8.0 # Apache-2.0
mock>=2.0 # BSD
coverage!=4.4,>=4.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
flake8-import-order==0.11 # LGPLv3
-openstackdocstheme>=1.5.0 # Apache-2.0
+openstackdocstheme>=1.11.0 # Apache-2.0
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index a33962b..5e63c0d 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -26,12 +26,8 @@
import json
import re
-try:
- # For Python 3.0 and later
- import urllib
-except ImportError:
- # Fall back to Python 2's urllib2
- import urllib2 as urllib
+from six.moves import urllib
+
url = 'https://review.openstack.org/projects/'
@@ -55,18 +51,18 @@
def has_tempest_plugin(proj):
try:
- r = urllib.urlopen("https://git.openstack.org/cgit/%s/plain/setup.cfg"
- % proj)
- except urllib.HTTPError as err:
+ r = urllib.request.urlopen(
+ "https://git.openstack.org/cgit/%s/plain/setup.cfg" % proj)
+ except urllib.error.HTTPError as err:
if err.code == 404:
return False
p = re.compile('^tempest\.test_plugins', re.M)
- if p.findall(r.read()):
+ if p.findall(r.read().decode('utf-8')):
return True
else:
False
-r = urllib.urlopen(url)
+r = urllib.request.urlopen(url)
# Gerrit prepends 4 garbage octets to the JSON, in order to counter
# cross-site scripting attacks. Therefore we must discard it so the
# json library won't choke.