Merge "Wait for server to be active before deleting"
diff --git a/README.rst b/README.rst
index bc58cc0..281516b 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,7 @@
.. image:: http://governance.openstack.org/badges/tempest.svg
:target: http://governance.openstack.org/reference/tags/index.html
+ :remote:
.. Change things from this point on
diff --git a/bindep.txt b/bindep.txt
new file mode 100644
index 0000000..6a28348
--- /dev/null
+++ b/bindep.txt
@@ -0,0 +1,11 @@
+# This file contains runtime (non-python) dependencies
+# More info at: http://docs.openstack.org/infra/bindep/readme.html
+
+libffi-dev [platform:dpkg]
+libffi-devel [platform:rpm]
+gcc [platform:rpm]
+gcc [platform:dpkg]
+python-dev [platform:dpkg]
+python-devel [platform:rpm]
+openssl-devel [platform:rpm]
+libssl-dev [platform:dpkg]
diff --git a/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml b/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
index 056e199..6babd93 100644
--- a/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
+++ b/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
@@ -8,3 +8,5 @@
* volumes_client(v1)
* volumes_client(v2)
+ * capabilities_client(v2)
+ * scheduler_stats_client(v2)
diff --git a/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
new file mode 100644
index 0000000..b504c78
--- /dev/null
+++ b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - A Neutron Service Providers client is added to deal with resources
+ of the '/service-providers' route.
diff --git a/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
new file mode 100644
index 0000000..a734d15
--- /dev/null
+++ b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+ - The Negative Tests Generator has been removed (it was not used by any
+ Tempest tests).
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 55134b1..1f043dc 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -87,7 +87,7 @@
ironic_only = True
hypers_without_ironic = []
for hyper in hypers:
- details = (self.client.show_hypervisor(hypers[0]['id'])
+ details = (self.client.show_hypervisor(hyper['id'])
['hypervisor'])
if details['hypervisor_type'] != 'ironic':
hypers_without_ironic.append(hyper)
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index c9ba730..2dd2e89 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -31,7 +31,6 @@
super(MigrationsAdminTest, cls).setup_clients()
cls.client = cls.os_adm.migrations_client
cls.flavors_admin_client = cls.os_adm.flavors_client
- cls.admin_hosts_client = cls.os_adm.hosts_client
cls.admin_servers_client = cls.os_adm.servers_client
@test.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index efa55d5..a8a8b83 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -29,7 +29,6 @@
super(ServersAdminTestJSON, cls).setup_clients()
cls.client = cls.os_adm.servers_client
cls.non_admin_client = cls.servers_client
- cls.flavors_client = cls.os_adm.flavors_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 096941f..d7e01f0 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -75,7 +75,6 @@
cls.os.compute_security_group_rules_client)
cls.security_groups_client = cls.os.compute_security_groups_client
cls.quotas_client = cls.os.quotas_client
- cls.quota_classes_client = cls.os.quota_classes_client
cls.compute_networks_client = cls.os.compute_networks_client
cls.limits_client = cls.os.limits_client
cls.volumes_extensions_client = cls.os.volumes_extensions_client
@@ -319,12 +318,7 @@
def rebuild_server(cls, server_id, validatable=False, **kwargs):
# Destroy an existing server and creates a new one
if server_id:
- try:
- cls.servers_client.delete_server(server_id)
- waiters.wait_for_server_termination(cls.servers_client,
- server_id)
- except Exception:
- LOG.exception('Failed to delete server %s' % server_id)
+ cls.delete_server(server_id)
cls.password = data_utils.rand_password()
server = cls.create_test_server(
@@ -382,18 +376,21 @@
self.request_microversion))
@classmethod
- def create_volume(cls, image_ref=None):
+ def create_volume(cls, image_ref=None, **kwargs):
"""Create a volume and wait for it to become 'available'.
:param image_ref: Specify an image id to create a bootable volume.
+ :**kwargs: other parameters to create volume.
:returns: The available volume.
"""
- vol_name = data_utils.rand_name(cls.__name__ + '-volume')
- create_params = dict(size=CONF.volume.volume_size,
- display_name=vol_name)
+ if 'size' not in kwargs:
+ kwargs['size'] = CONF.volume.volume_size
+ if 'display_name' not in kwargs:
+ vol_name = data_utils.rand_name(cls.__name__ + '-volume')
+ kwargs['display_name'] = vol_name
if image_ref is not None:
- create_params['imageRef'] = image_ref
- volume = cls.volumes_client.create_volume(**create_params)['volume']
+ kwargs['imageRef'] = image_ref
+ volume = cls.volumes_client.create_volume(**kwargs)['volume']
cls.volumes.append(volume)
waiters.wait_for_volume_status(cls.volumes_client,
volume['id'], 'available')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index fdf1e93..dcb2d2c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -14,7 +14,6 @@
# under the License.
from tempest.api.compute.floating_ips import base
-from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
@@ -111,9 +110,7 @@
# positive test:Association of an already associated floating IP
# to specific server should change the association of the Floating IP
# Create server so as to use for Multiple association
- body = self.create_test_server()
- waiters.wait_for_server_status(self.servers_client,
- body['id'], 'ACTIVE')
+ body = self.create_test_server(wait_until='ACTIVE')
self.new_server_id = body['id']
self.addCleanup(self.servers_client.delete_server, self.new_server_id)
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 7035401..549262e 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -46,7 +46,7 @@
# An image should not be created if the server instance is removed
server = self.create_test_server(wait_until='ACTIVE')
- # Delete server before trying to create server
+ # Delete server before trying to create image
self.servers_client.delete_server(server['id'])
waiters.wait_for_server_termination(self.servers_client, server['id'])
# Create a new image after server is deleted
@@ -96,7 +96,7 @@
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
- non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+ non_existent_image_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
@@ -110,9 +110,9 @@
@test.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
- image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+ invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- image_id)
+ invalid_image_id)
@test.attr(type=['negative'])
@test.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
@@ -122,7 +122,8 @@
@test.attr(type=['negative'])
@test.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
- def test_delete_image_id_is_over_35_character_limit(self):
+ def test_delete_image_with_id_over_character_limit(self):
# Return an error while trying to delete image with id over limit
+ invalid_image_id = data_utils.rand_uuid() + "1"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+ invalid_image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index d9b80e1..1c9b3f1 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -69,11 +69,6 @@
raise cls.skipException(skip_msg)
@classmethod
- def setup_credentials(cls):
- cls.prepare_instance_network()
- super(ImagesOneServerNegativeTestJSON, cls).setup_credentials()
-
- @classmethod
def setup_clients(cls):
super(ImagesOneServerNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
@@ -98,9 +93,9 @@
@test.attr(type=['negative'])
@test.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
def test_create_image_specify_metadata_over_limits(self):
- # Return an error when creating image with meta data over 256 chars
+ # Return an error when creating image with meta data over 255 chars
snapshot_name = data_utils.rand_name('test-snap')
- meta = {'a' * 260: 'b' * 260}
+ meta = {'a' * 256: 'b' * 256}
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
self.server_id, name=snapshot_name, metadata=meta)
@@ -123,10 +118,10 @@
@test.attr(type=['negative'])
@test.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
- def test_create_image_specify_name_over_256_chars(self):
- # Return an error if snapshot name over 256 characters is passed
+ def test_create_image_specify_name_over_character_limit(self):
+ # Return an error if snapshot name over 255 characters is passed
- snapshot_name = data_utils.rand_name('a' * 260)
+ snapshot_name = ('a' * 256)
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
self.server_id, name=snapshot_name)
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 69811f4..6cc722c 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -35,9 +35,10 @@
'maxTotalFloatingIps', 'maxSecurityGroups',
'maxSecurityGroupRules', 'maxTotalInstances',
'maxTotalKeypairs', 'maxTotalRAMSize',
+ 'maxServerGroups', 'maxServerGroupMembers',
'totalCoresUsed', 'totalFloatingIpsUsed',
'totalSecurityGroupsUsed', 'totalInstancesUsed',
- 'totalRAMUsed']
+ 'totalRAMUsed', 'totalServerGroupsUsed']
# check whether all expected elements exist
missing_elements =\
[ele for ele in expected_elements if ele not in absolute_limits]
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 4f53663..32b3ea3 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -35,7 +35,6 @@
@classmethod
def setup_clients(cls):
super(SecurityGroupRulesNegativeTestJSON, cls).setup_clients()
- cls.client = cls.security_groups_client
cls.rules_client = cls.security_group_rules_client
@test.attr(type=['negative'])
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 755336f..4184afa 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -94,10 +94,8 @@
# Create server and add the security group created
# above to the server we just created
- server = self.create_test_server()
+ server = self.create_test_server(wait_until='ACTIVE')
server_id = server['id']
- waiters.wait_for_server_status(self.servers_client, server_id,
- 'ACTIVE')
self.servers_client.add_security_group(server_id, name=sg['name'])
# Check that we are not able to delete the security
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8342a3e..a21ce94 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -46,7 +46,6 @@
@classmethod
def setup_clients(cls):
super(AttachInterfacesTestJSON, cls).setup_clients()
- cls.networks_client = cls.os.networks_client
cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 2f43157..82ebcd0 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -19,7 +19,6 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
-from tempest.common import waiters
from tempest import config
from tempest import test
@@ -178,12 +177,7 @@
# when trying to delete the subnet. The tear down in the base class
# will try to delete the server and get a 404 but it's ignored so
# we're OK.
- def cleanup_server():
- self.client.delete_server(server_multi_nics['id'])
- waiters.wait_for_server_termination(self.client,
- server_multi_nics['id'])
-
- self.addCleanup(cleanup_server)
+ self.addCleanup(self.delete_server, server_multi_nics['id'])
addresses = (self.client.list_addresses(server_multi_nics['id'])
['addresses'])
@@ -215,13 +209,7 @@
server_multi_nics = self.create_test_server(
networks=networks, wait_until='ACTIVE')
-
- def cleanup_server():
- self.client.delete_server(server_multi_nics['id'])
- waiters.wait_for_server_termination(self.client,
- server_multi_nics['id'])
-
- self.addCleanup(cleanup_server)
+ self.addCleanup(self.delete_server, server_multi_nics['id'])
addresses = (self.client.list_addresses(server_multi_nics['id'])
['addresses'])
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 9acc2b1..b2d5ae7 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -21,7 +21,7 @@
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
+from tempest.lib import exceptions
from tempest import test
@@ -52,9 +52,7 @@
super(DeviceTaggingTest, cls).setup_clients()
cls.networks_client = cls.os.networks_client
cls.ports_client = cls.os.ports_client
- cls.volumes_client = cls.os.volumes_client
cls.subnets_client = cls.os.subnets_client
- cls.routers_client = cls.os.routers_client
cls.interfaces_client = cls.os.interfaces_client
@classmethod
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index e5ad7b4..ab291b4 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -118,10 +118,12 @@
raise self.skipException("No limit for personality files")
person = []
for i in range(0, int(max_file_limit)):
- path = '/etc/test' + str(i) + '.txt'
+ # NOTE(andreaf) The cirros disk image is blank before boot
+ # so we can only inject safely to /
+ path = '/test' + str(i) + '.txt'
person.append({
'path': path,
- 'contents': base64.encode_as_text(file_contents),
+ 'contents': base64.encode_as_text(file_contents + str(i)),
})
password = data_utils.rand_password()
created_server = self.create_test_server(personality=person,
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index bd2b185..f072b81 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -33,7 +33,6 @@
@classmethod
def setup_clients(cls):
super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
- cls.admin_hosts_client = cls.os_adm.hosts_client
cls.admin_servers_client = cls.os_adm.servers_client
def _migrate_server_to(self, server_id, dest_host):
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 122e7cc..b9e0c35 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -48,7 +48,8 @@
'fixed_ips', 'key_pairs',
'injected_file_path_bytes',
'instances', 'security_group_rules',
- 'cores', 'security_groups'))
+ 'cores', 'security_groups',
+ 'server_group_members', 'server_groups'))
@test.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
def test_get_quotas(self):
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index b7fa0fe..1f18bfe 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -29,6 +29,7 @@
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
raise cls.skipException(skip_msg)
+ @test.related_bug('1630783', status_code=500)
@test.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
def test_delete_attached_volume(self):
server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 7d76731..e4e625b 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -16,8 +16,6 @@
from oslo_log import log as logging
from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
from tempest import config
from tempest import test
@@ -51,34 +49,11 @@
cls.volume_list = []
cls.volume_id_list = []
for i in range(3):
- v_name = data_utils.rand_name(cls.__name__ + '-volume')
metadata = {'Type': 'work'}
- try:
- volume = cls.client.create_volume(size=CONF.volume.volume_size,
- display_name=v_name,
- metadata=metadata)['volume']
- waiters.wait_for_volume_status(cls.client,
- volume['id'], 'available')
- volume = cls.client.show_volume(volume['id'])['volume']
- cls.volume_list.append(volume)
- cls.volume_id_list.append(volume['id'])
- except Exception as exc:
- LOG.exception(exc)
- if cls.volume_list:
- # We could not create all the volumes, though we were able
- # to create *some* of the volumes. This is typically
- # because the backing file size of the volume group is
- # too small.
- for volume in cls.volume_list:
- cls.delete_volume(volume['id'])
- raise exc
-
- @classmethod
- def resource_cleanup(cls):
- # Delete the created Volumes
- for volume in cls.volume_list:
- cls.delete_volume(volume['id'])
- super(VolumesTestJSON, cls).resource_cleanup()
+ volume = cls.create_volume(metadata=metadata)
+ volume = cls.client.show_volume(volume['id'])['volume']
+ cls.volume_list.append(volume)
+ cls.volume_id_list.append(volume['id'])
@test.idempotent_id('bc2dd1a0-15af-48e5-9990-f2e75a48325d')
def test_volume_list(self):
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 59ffc19..361ff31 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -36,7 +36,8 @@
def test_default_project_id(self):
# create a domain
dom_name = data_utils.rand_name('dom')
- domain_body = self.domains_client.create_domain(dom_name)['domain']
+ domain_body = self.domains_client.create_domain(
+ name=dom_name)['domain']
dom_id = domain_body['id']
self.addCleanup(self._delete_domain, dom_id)
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index cbf1439..e71341f 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -32,7 +32,7 @@
cls.setup_domains = list()
for i in range(3):
domain = cls.domains_client.create_domain(
- data_utils.rand_name('domain'),
+ name=data_utils.rand_name('domain'),
description=data_utils.rand_name('domain-desc'),
enabled=i < 2)['domain']
cls.setup_domains.append(domain)
@@ -67,7 +67,7 @@
# List domains filtering by name
params = {'name': self.setup_domains[0]['name']}
fetched_domains = self.domains_client.list_domains(
- params=params)['domains']
+ **params)['domains']
# Verify the filtered list is correct, domain names are unique
# so exactly one domain should be found with the provided name
self.assertEqual(1, len(fetched_domains))
@@ -79,7 +79,7 @@
# List domains filtering by enabled domains
params = {'enabled': True}
fetched_domains = self.domains_client.list_domains(
- params=params)['domains']
+ **params)['domains']
# Verify the filtered list is correct
self.assertIn(self.setup_domains[0], fetched_domains)
self.assertIn(self.setup_domains[1], fetched_domains)
@@ -93,7 +93,7 @@
d_name = data_utils.rand_name('domain')
d_desc = data_utils.rand_name('domain-desc')
domain = self.domains_client.create_domain(
- d_name, description=d_desc)['domain']
+ name=d_name, description=d_desc)['domain']
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self._delete_domain, domain['id'])
self.assertIn('id', domain)
@@ -138,7 +138,7 @@
d_name = data_utils.rand_name('domain')
d_desc = data_utils.rand_name('domain-desc')
domain = self.domains_client.create_domain(
- d_name, description=d_desc, enabled=False)['domain']
+ name=d_name, description=d_desc, enabled=False)['domain']
self.addCleanup(self.domains_client.delete_domain, domain['id'])
self.assertEqual(d_name, domain['name'])
self.assertFalse(domain['enabled'])
@@ -148,11 +148,15 @@
def test_create_domain_without_description(self):
# Create domain only with name
d_name = data_utils.rand_name('domain')
- domain = self.domains_client.create_domain(d_name)['domain']
+ domain = self.domains_client.create_domain(name=d_name)['domain']
self.addCleanup(self._delete_domain, domain['id'])
self.assertIn('id', domain)
expected_data = {'name': d_name, 'enabled': True}
- self.assertIsNone(domain['description'])
+ # TODO(gmann): there is bug in keystone liberty version where
+ # description is not being returned if it is not being passed in
+ # request. Bug#1649245. Once bug is fixed then we can enable the below
+ # check.
+ # self.assertEqual('', domain['description'])
self.assertDictContainsSubset(expected_data, domain)
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 4330cab..100a121 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -28,7 +28,7 @@
d_name = data_utils.rand_name('domain')
d_desc = data_utils.rand_name('domain-desc')
domain = self.domains_client.create_domain(
- d_name,
+ name=d_name,
description=d_desc)['domain']
domain_id = domain['id']
@@ -51,7 +51,8 @@
# Domain name length should not ne greater than 64 characters
d_name = 'a' * 65
self.assertRaises(lib_exc.BadRequest,
- self.domains_client.create_domain, d_name)
+ self.domains_client.create_domain,
+ name=d_name)
@test.attr(type=['negative'])
@test.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
@@ -64,9 +65,10 @@
@test.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
def test_domain_create_duplicate(self):
domain_name = data_utils.rand_name('domain-dup')
- domain = self.domains_client.create_domain(domain_name)['domain']
+ domain = self.domains_client.create_domain(name=domain_name)['domain']
domain_id = domain['id']
self.addCleanup(self.delete_domain, domain_id)
# Domain name should be unique
self.assertRaises(
- lib_exc.Conflict, self.domains_client.create_domain, domain_name)
+ lib_exc.Conflict, self.domains_client.create_domain,
+ name=domain_name)
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 50cf258..655e4ef 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -23,7 +23,6 @@
@classmethod
def setup_clients(cls):
super(EndPointsTestJSON, cls).setup_clients()
- cls.identity_client = cls.client
cls.client = cls.endpoints_client
@classmethod
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index b16605e..f0f8707 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -25,7 +25,6 @@
@classmethod
def setup_clients(cls):
super(EndpointsNegativeTestJSON, cls).setup_clients()
- cls.identity_client = cls.client
cls.client = cls.endpoints_client
@classmethod
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index c7e8411..33fce8d 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -31,7 +31,7 @@
u_email = '%s@testmail.tm' % u_name
u_password = data_utils.rand_name('pass-')
cls.domain = cls.domains_client.create_domain(
- data_utils.rand_name('domain-'),
+ name=data_utils.rand_name('domain-'),
description=data_utils.rand_name('domain-desc-'))['domain']
cls.project = cls.projects_client.create_project(
data_utils.rand_name('project-'),
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index f5bf923..670cb2f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -34,7 +34,7 @@
u_email = '%s@testmail.tm' % u_name
cls.u_password = data_utils.rand_password()
cls.domain = cls.domains_client.create_domain(
- data_utils.rand_name('domain'),
+ name=data_utils.rand_name('domain'),
description=data_utils.rand_name('domain-desc'))['domain']
cls.project = cls.projects_client.create_project(
data_utils.rand_name('project'),
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index d5bed96..b410da6 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -34,6 +34,7 @@
# it to be 'default'
token_id, resp = self.non_admin_token.get_token(
user_id=user_id,
+ username=username,
user_domain_id=user_domain_id,
password=password,
auth_data=True)
@@ -49,9 +50,19 @@
self.assertGreater(expires_at, now)
subject_id = resp['user']['id']
- self.assertEqual(subject_id, user_id)
+ if user_id:
+ self.assertEqual(subject_id, user_id)
+ else:
+ # Expect a user ID, but don't know what it will be.
+ self.assertGreaterEqual(len(subject_id), 0,
+ 'Expected user ID in token.')
subject_name = resp['user']['name']
- self.assertEqual(subject_name, username)
+ if username:
+ self.assertEqual(subject_name, username)
+ else:
+ # Expect a user name, but don't know what it will be.
+ self.assertGreaterEqual(len(subject_name), 0,
+ 'Expected user name in token.')
self.assertEqual(resp['methods'][0], 'password')
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index fe8dd65..8a4b334 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -21,6 +21,8 @@
image_id = self._create_image()
member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
@@ -42,6 +44,8 @@
image_id = self._create_image()
member = self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
@@ -56,6 +60,8 @@
image_id = self._create_image()
self.image_member_client.create_image_member(
image_id, member=self.alt_tenant_id)
+ self.addCleanup(self.image_member_client.delete_image_member,
+ image_id, self.alt_tenant_id)
self.alt_image_member_client.update_image_member(image_id,
self.alt_tenant_id,
status='accepted')
diff --git a/tempest/api/image/v2/test_images_metadefs_schema.py b/tempest/api/image/v2/test_images_metadefs_schema.py
new file mode 100644
index 0000000..7edf1af
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_schema.py
@@ -0,0 +1,81 @@
+# Copyright 2016 EasyStack.
+# 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.image import base
+from tempest import test
+
+
+class MetadataSchemaTest(base.BaseV2ImageTest):
+ """Test to get metadata schema"""
+
+ @test.idempotent_id('e9e44891-3cb8-3b40-a532-e0a39fea3dab')
+ def test_get_metadata_namespace_schema(self):
+ # Test to get namespace schema
+ body = self.schemas_client.show_schema("metadefs/namespace")
+ self.assertEqual("namespace", body['name'])
+
+ @test.idempotent_id('ffe44891-678b-3ba0-a3e2-e0a3967b3aeb')
+ def test_get_metadata_namespaces_schema(self):
+ # Test to get namespaces schema
+ body = self.schemas_client.show_schema("metadefs/namespaces")
+ self.assertEqual("namespaces", body['name'])
+
+ @test.idempotent_id('fde34891-678b-3b40-ae32-e0a3e67b6beb')
+ def test_get_metadata_resource_type_schema(self):
+ # Test to get resource_type schema
+ body = self.schemas_client.show_schema("metadefs/resource_type")
+ self.assertEqual("resource_type_association", body['name'])
+
+ @test.idempotent_id('dfe4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+ def test_get_metadata_resources_types_schema(self):
+ # Test to get resource_types schema
+ body = self.schemas_client.show_schema("metadefs/resource_types")
+ self.assertEqual("resource_type_associations", body['name'])
+
+ @test.idempotent_id('dff4a891-b38b-3bf0-a3b2-e03ee67b3a3b')
+ def test_get_metadata_object_schema(self):
+ # Test to get object schema
+ body = self.schemas_client.show_schema("metadefs/object")
+ self.assertEqual("object", body['name'])
+
+ @test.idempotent_id('dee4a891-b38b-3bf0-a3b2-e03ee67b3a3c')
+ def test_get_metadata_objects_schema(self):
+ # Test to get objects schema
+ body = self.schemas_client.show_schema("metadefs/objects")
+ self.assertEqual("objects", body['name'])
+
+ @test.idempotent_id('dae4a891-b38b-3bf0-a3b2-e03ee67b3a3d')
+ def test_get_metadata_property_schema(self):
+ # Test to get property schema
+ body = self.schemas_client.show_schema("metadefs/property")
+ self.assertEqual("property", body['name'])
+
+ @test.idempotent_id('dce4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+ def test_get_metadata_properties_schema(self):
+ # Test to get properties schema
+ body = self.schemas_client.show_schema("metadefs/properties")
+ self.assertEqual("properties", body['name'])
+
+ @test.idempotent_id('dde4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+ def test_get_metadata_tag_schema(self):
+ # Test to get tag schema
+ body = self.schemas_client.show_schema("metadefs/tag")
+ self.assertEqual("tag", body['name'])
+
+ @test.idempotent_id('cde4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+ def test_get_metadata_tags_schema(self):
+ # Test to get tags schema
+ body = self.schemas_client.show_schema("metadefs/tags")
+ self.assertEqual("tags", body['name'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 7b037d5..c256b5b 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -38,11 +38,6 @@
msg = "quotas extension not enabled."
raise cls.skipException(msg)
- @classmethod
- def setup_clients(cls):
- super(QuotasNegativeTest, cls).setup_clients()
- cls.identity_admin_client = cls.os_adm.identity_client
-
@test.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
def test_network_quota_exceeding(self):
# Set the network quota to two
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 3a264ff..978fb8f 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -43,11 +43,6 @@
msg = "quotas extension not enabled."
raise cls.skipException(msg)
- @classmethod
- def setup_clients(cls):
- super(QuotasTest, cls).setup_clients()
- cls.identity_admin_client = cls.os_adm.identity_client
-
def _check_quotas(self, new_quotas):
# Add a project to conduct the test
project = data_utils.rand_name('test_project_')
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index deefbeb..c2c42bb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -81,6 +81,7 @@
cls.security_group_rules_client = (
cls.os.security_group_rules_client)
cls.network_versions_client = cls.os.network_versions_client
+ cls.service_providers_client = cls.os.service_providers_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 15d289d..5b46088 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -360,11 +360,6 @@
class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
@classmethod
- def setup_clients(cls):
- super(PortsAdminExtendedAttrsTestJSON, cls).setup_clients()
- cls.identity_client = cls.os_adm.identity_client
-
- @classmethod
def resource_setup(cls):
super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
cls.network = cls.create_network()
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index e989b69..f2170ad 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -34,11 +34,6 @@
raise cls.skipException(msg)
@classmethod
- def setup_clients(cls):
- super(RoutersTest, cls).setup_clients()
- cls.identity_admin_client = cls.os_adm.identity_client
-
- @classmethod
def resource_setup(cls):
super(RoutersTest, cls).resource_setup()
cls.tenant_cidr = (CONF.network.project_network_cidr
diff --git a/tempest/api/network/test_service_providers.py b/tempest/api/network/test_service_providers.py
new file mode 100644
index 0000000..09fb5fe
--- /dev/null
+++ b/tempest/api/network/test_service_providers.py
@@ -0,0 +1,23 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest import test
+
+
+class ServiceProvidersTest(base.BaseNetworkTest):
+
+ @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
+ def test_service_providers_list(self):
+ body = self.service_providers_client.list_service_providers()
+ self.assertIn('service_providers', body)
+ self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
deleted file mode 100644
index f49f082..0000000
--- a/tempest/api/network/test_service_type_management.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.network import base
-from tempest.lib import decorators
-from tempest import test
-
-
-class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
-
- @classmethod
- def skip_checks(cls):
- super(ServiceTypeManagementTestJSON, cls).skip_checks()
- if not test.is_extension_enabled('service-type', 'network'):
- msg = "Neutron Service Type Management not enabled."
- raise cls.skipException(msg)
-
- @decorators.skip_because(bug="1400370")
- @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
- def test_service_provider_list(self):
- body = self.client.list_service_providers()
- self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 1b1ffd1..eb313d2 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from tempest.common import custom_matchers
from tempest import config
from tempest.lib.common.utils import data_utils
@@ -48,6 +50,7 @@
cls.object_client = cls.os.object_client
cls.container_client = cls.os.container_client
cls.account_client = cls.os.account_client
+ cls.capabilities_client = cls.os.capabilities_client
@classmethod
def resource_setup(cls):
@@ -63,7 +66,7 @@
cls.policies = None
if CONF.object_storage_feature_enabled.discoverability:
- _, body = cls.account_client.list_extensions()
+ _, body = cls.capabilities_client.list_capabilities()
if 'swift' in body and 'policies' in body['swift']:
cls.policies = body['swift']['policies']
@@ -102,6 +105,10 @@
The containers should be visible from the container_client given.
Will not throw any error if the containers don't exist.
Will not check that object and container deletions succeed.
+ After delete all the objects from a container, it will wait 2
+ seconds before delete the container itself, in order to deployments
+ using HA proxy sync the deletion properly, otherwise, the container
+ might fail to be deleted because it's not empty.
:param container_client: if None, use cls.container_client, this means
that the default testing user will be used (see 'username' in
@@ -121,6 +128,9 @@
for obj in objlist:
test_utils.call_and_ignore_notfound_exc(
object_client.delete_object, cont, obj['name'])
+ # sleep 2 seconds to sync the deletion of the objects
+ # in HA deployment
+ time.sleep(2)
container_client.delete_container(cont)
except lib_exc.NotFound:
pass
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index eda4568..59129e5 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -15,6 +15,7 @@
import random
+import six
import testtools
from tempest.api.object_storage import base
@@ -60,8 +61,10 @@
self.assertHeaders(resp, 'Account', 'GET')
self.assertIsNotNone(container_list)
+
for container_name in self.containers:
- self.assertIn(container_name, container_list)
+ self.assertIn(six.text_type(container_name).encode('utf-8'),
+ container_list)
@test.idempotent_id('884ec421-fbad-4fcc-916b-0580f2699565')
def test_list_no_containers(self):
@@ -132,7 +135,7 @@
not CONF.object_storage_feature_enabled.discoverability,
'Discoverability function is disabled')
def test_list_extensions(self):
- resp, extensions = self.account_client.list_extensions()
+ resp, extensions = self.capabilities_client.list_capabilities()
self.assertThat(resp, custom_matchers.AreAllWellFormatted())
@@ -246,7 +249,8 @@
params=params)
self.assertHeaders(resp, 'Account', 'GET')
for container in container_list:
- self.assertEqual(True, container.startswith(prefix))
+ self.assertEqual(True, container.decode(
+ 'utf-8').startswith(prefix))
@test.attr(type='smoke')
@test.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index e6c53ec..f63c518 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -13,11 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.object_storage import base
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from tempest import test
+CONF = config.CONF
+
class ContainerNegativeTest(base.BaseObjectTest):
@@ -25,12 +30,16 @@
def resource_setup(cls):
super(ContainerNegativeTest, cls).resource_setup()
- # use /info to get default constraints
- _, body = cls.account_client.list_extensions()
- cls.constraints = body['swift']
+ if CONF.object_storage_feature_enabled.discoverability:
+ # use /info to get default constraints
+ _, body = cls.capabilities_client.list_capabilities()
+ cls.constraints = body['swift']
@test.attr(type=["negative"])
@test.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_name_exceeds_max_length(self):
# Attempts to create a container name that is longer than max
max_length = self.constraints['max_container_name_length']
@@ -44,6 +53,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_name_exceeds_max_length(self):
# Attempts to create container with metadata name
# that is longer than max.
@@ -58,6 +70,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_value_exceeds_max_length(self):
# Attempts to create container with metadata value
# that is longer than max.
@@ -72,6 +87,9 @@
@test.attr(type=["negative"])
@test.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
+ @testtools.skipUnless(
+ CONF.object_storage_feature_enabled.discoverability,
+ 'Discoverability function is disabled')
def test_create_container_metadata_exceeds_overall_metadata_count(self):
# Attempts to create container with metadata that exceeds the
# default count
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index bffcb64..6d27502 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -77,7 +77,7 @@
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
resources = (cls.client.list_resources(cls.stack_identifier)
['resources'])
- except exceptions.TimeoutException as e:
+ except exceptions.TimeoutException:
if CONF.compute_feature_enabled.console_output:
# attempt to log the server console to help with debugging
# the cause of the server not signalling the waitcondition
@@ -89,7 +89,7 @@
output = cls.servers_client.get_console_output(
server_id)['output']
LOG.debug(output)
- raise e
+ raise
cls.test_resources = {}
for resource in resources:
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 261e652..4bd7637 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -22,19 +22,6 @@
class VolumesActionsV2Test(base.BaseVolumeAdminTest):
- @classmethod
- def resource_setup(cls):
- super(VolumesActionsV2Test, cls).resource_setup()
-
- # Create a test shared volume for tests
- cls.volume = cls.create_volume()
-
- def tearDown(self):
- # Set volume's status to available after test
- self.admin_volume_client.reset_volume_status(
- self.volume['id'], status='available')
- super(VolumesActionsV2Test, self).tearDown()
-
def _create_reset_and_force_delete_temp_volume(self, status=None):
# Create volume, reset volume status, and force delete temp volume
temp_volume = self.create_volume()
@@ -47,11 +34,13 @@
@test.idempotent_id('d063f96e-a2e0-4f34-8b8a-395c42de1845')
def test_volume_reset_status(self):
# test volume reset status : available->error->available
- self.admin_volume_client.reset_volume_status(
- self.volume['id'], status='error')
- volume_get = self.admin_volume_client.show_volume(
- self.volume['id'])['volume']
- self.assertEqual('error', volume_get['status'])
+ volume = self.create_volume()
+ for status in ['error', 'available']:
+ self.admin_volume_client.reset_volume_status(
+ volume['id'], status=status)
+ volume_get = self.admin_volume_client.show_volume(
+ volume['id'])['volume']
+ self.assertEqual(status, volume_get['status'])
@test.idempotent_id('21737d5a-92f2-46d7-b009-a0cc0ee7a570')
def test_volume_force_delete_when_volume_is_creating(self):
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/v2/test_backends_capabilities.py
similarity index 90%
rename from tempest/api/volume/admin/test_backends_capabilities.py
rename to tempest/api/volume/admin/v2/test_backends_capabilities.py
index 8a21853..fc9066c 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/v2/test_backends_capabilities.py
@@ -38,13 +38,13 @@
# Get host list, formation: host@backend-name
cls.hosts = [
pool['name'] for pool in
- cls.admin_volume_client.show_pools()['pools']
+ cls.admin_scheduler_stats_client.list_pools()['pools']
]
@test.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
def test_get_capabilities_backend(self):
# Test backend properties
- backend = self.admin_volume_client.show_backend_capabilities(
+ backend = self.admin_capabilities_client.show_backend_capabilities(
self.hosts[0])
# Verify getting capabilities parameters from a backend
@@ -62,12 +62,12 @@
# Get list backend capabilities using show_pools
cinder_pools = [
pool['capabilities'] for pool in
- self.admin_volume_client.show_pools(detail=True)['pools']
+ self.admin_scheduler_stats_client.list_pools(detail=True)['pools']
]
# Get list backends capabilities using show_backend_capabilities
capabilities = [
- self.admin_volume_client.show_backend_capabilities(
+ self.admin_capabilities_client.show_backend_capabilities(
host=host) for host in self.hosts
]
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/v2/test_volume_pools.py
index c662e8c..e460278 100644
--- a/tempest/api/volume/admin/v2/test_volume_pools.py
+++ b/tempest/api/volume/admin/v2/test_volume_pools.py
@@ -29,7 +29,7 @@
def test_get_pools_without_details(self):
volume_info = self.admin_volume_client. \
show_volume(self.volume['id'])['volume']
- cinder_pools = self.admin_volume_client.show_pools()['pools']
+ cinder_pools = self.admin_scheduler_stats_client.list_pools()['pools']
self.assertIn(volume_info['os-vol-host-attr:host'],
[pool['name'] for pool in cinder_pools])
@@ -37,7 +37,7 @@
def test_get_pools_with_details(self):
volume_info = self.admin_volume_client. \
show_volume(self.volume['id'])['volume']
- cinder_pools = self.admin_volume_client.\
- show_pools(detail=True)['pools']
+ cinder_pools = self.admin_scheduler_stats_client.\
+ list_pools(detail=True)['pools']
self.assertIn(volume_info['os-vol-host-attr:host'],
[pool['name'] for pool in cinder_pools])
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 01e2c82..7cd72e1 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -266,6 +266,10 @@
cls.os_adm.encryption_types_v2_client
cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
+ cls.admin_capabilities_client = \
+ cls.os_adm.volume_capabilities_v2_client
+ cls.admin_scheduler_stats_client = \
+ cls.os_adm.volume_scheduler_stats_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 171e241..d0fa07e 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -36,60 +36,47 @@
cls.volume = cls.create_volume()
# Create a snapshot
cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
- cls.snapshot_id = cls.snapshot['id']
def tearDown(self):
# Update the metadata to {}
self.snapshots_client.update_snapshot_metadata(
- self.snapshot_id, metadata={})
+ self.snapshot['id'], metadata={})
super(SnapshotV2MetadataTestJSON, self).tearDown()
@test.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
- def test_create_get_delete_snapshot_metadata(self):
+ def test_crud_snapshot_metadata(self):
# Create metadata for the snapshot
metadata = {"key1": "value1",
"key2": "value2",
"key3": "value3"}
- expected = {"key2": "value2",
- "key3": "value3"}
- body = self.snapshots_client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
- # Get the metadata of the snapshot
- body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
- # Delete one item metadata of the snapshot
- self.snapshots_client.delete_snapshot_metadata_item(
- self.snapshot_id, "key1")
- body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(expected.items()))
- self.assertNotIn("key1", body)
-
- @test.idempotent_id('bd2363bc-de92-48a4-bc98-28943c6e4be1')
- def test_update_snapshot_metadata(self):
- # Update metadata for the snapshot
- metadata = {"key1": "value1",
- "key2": "value2",
- "key3": "value3"}
update = {"key3": "value3_update",
"key4": "value4"}
- # Create metadata for the snapshot
+ expect = {"key4": "value4"}
+ # Create metadata
body = self.snapshots_client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
- # Get the metadata of the snapshot
- body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ self.snapshot['id'], metadata)['metadata']
- # Update metadata item
- body = self.snapshots_client.update_snapshot_metadata(
- self.snapshot_id, metadata=update)['metadata']
# Get the metadata of the snapshot
body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
- self.assertEqual(update, body)
+ self.snapshot['id'])['metadata']
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+ 'Create snapshot metadata failed')
+
+ # Update metadata
+ body = self.snapshots_client.update_snapshot_metadata(
+ self.snapshot['id'], metadata=update)['metadata']
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
+ self.assertEqual(update, body, 'Update snapshot metadata failed')
+
+ # Delete one item metadata of the snapshot
+ self.snapshots_client.delete_snapshot_metadata_item(
+ self.snapshot['id'], "key3")
+ body = self.snapshots_client.show_snapshot_metadata(
+ self.snapshot['id'])['metadata']
+ self.assertThat(body.items(), matchers.ContainsAll(expect.items()),
+ 'Delete one item metadata of the snapshot failed')
+ self.assertNotIn("key3", body)
@test.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
def test_update_snapshot_metadata_item(self):
@@ -103,17 +90,17 @@
"key3": "value3_update"}
# Create metadata for the snapshot
body = self.snapshots_client.create_snapshot_metadata(
- self.snapshot_id, metadata)['metadata']
+ self.snapshot['id'], metadata)['metadata']
# Get the metadata of the snapshot
body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
+ self.snapshot['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Update metadata item
body = self.snapshots_client.update_snapshot_metadata_item(
- self.snapshot_id, "key3", meta=update_item)['meta']
+ self.snapshot['id'], "key3", meta=update_item)['meta']
# Get the metadata of the snapshot
body = self.snapshots_client.show_snapshot_metadata(
- self.snapshot_id)['metadata']
+ self.snapshot['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index ee1744d..c125bb8 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -33,52 +33,39 @@
super(VolumesV2MetadataTest, self).tearDown()
@test.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
- def test_create_get_delete_volume_metadata(self):
+ def test_crud_volume_metadata(self):
# Create metadata for the volume
metadata = {"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "<value&special_chars>"}
+ update = {"key4": "value4",
+ "key1": "value1_update"}
+ expected = {"key4": "value4"}
body = self.volumes_client.create_volume_metadata(self.volume['id'],
metadata)['metadata']
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(
self.volume['id'])['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+ 'Create metadata for the volume failed')
+
+ # Update metadata
+ body = self.volumes_client.update_volume_metadata(
+ self.volume['id'], update)['metadata']
+ body = self.volumes_client.show_volume_metadata(
+ self.volume['id'])['metadata']
+ self.assertEqual(update, body, 'Update metadata failed')
+
# Delete one item metadata of the volume
self.volumes_client.delete_volume_metadata_item(
self.volume['id'], "key1")
body = self.volumes_client.show_volume_metadata(
self.volume['id'])['metadata']
self.assertNotIn("key1", body)
- del metadata["key1"]
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
- @test.idempotent_id('774d2918-9beb-4f30-b3d1-2a4e8179ec0a')
- def test_update_volume_metadata(self):
- # Update metadata for the volume
- metadata = {"key1": "value1",
- "key2": "value2",
- "key3": "value3"}
-
- update = {"key4": "value4",
- "key1": "value1_update"}
-
- # Create metadata for the volume
- body = self.volumes_client.create_volume_metadata(
- self.volume['id'], metadata)['metadata']
- # Get the metadata of the volume
- body = self.volumes_client.show_volume_metadata(
- self.volume['id'])['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
- # Update metadata
- body = self.volumes_client.update_volume_metadata(
- self.volume['id'], update)['metadata']
- # Get the metadata of the volume
- body = self.volumes_client.show_volume_metadata(
- self.volume['id'])['metadata']
- self.assertEqual(update, body)
+ self.assertThat(body.items(), matchers.ContainsAll(expected.items()),
+ 'Delete one item metadata of the volume failed')
@test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
def test_update_volume_metadata_item(self):
@@ -93,7 +80,8 @@
# Create metadata for the volume
body = self.volumes_client.create_volume_metadata(
self.volume['id'], metadata)['metadata']
- self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+ self.assertThat(body.items(),
+ matchers.ContainsAll(metadata.items()))
# Update metadata item
body = self.volumes_client.update_volume_metadata_item(
self.volume['id'], "key3", update_item)['meta']
diff --git a/tempest/clients.py b/tempest/clients.py
index b3290ac..4a30f6f 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -127,6 +127,7 @@
self.network.SecurityGroupRulesClient())
self.security_groups_client = self.network.SecurityGroupsClient()
self.network_versions_client = self.network.NetworkVersionsClient()
+ self.service_providers_client = self.network.ServiceProvidersClient()
def _set_image_clients(self):
if CONF.service_available.glance:
@@ -303,6 +304,10 @@
self.volume_v2.AvailabilityZoneClient()
self.volume_limits_client = self.volume_v1.LimitsClient()
self.volume_v2_limits_client = self.volume_v2.LimitsClient()
+ self.volume_capabilities_v2_client = \
+ self.volume_v2.CapabilitiesClient()
+ self.volume_scheduler_stats_v2_client = \
+ self.volume_v2.SchedulerStatsClient()
def _set_object_storage_clients(self):
# Mandatory parameters (always defined)
@@ -310,6 +315,8 @@
self.account_client = object_storage.AccountClient(self.auth_provider,
**params)
+ self.capabilities_client = object_storage.CapabilitiesClient(
+ self.auth_provider, **params)
self.container_client = object_storage.ContainerClient(
self.auth_provider, **params)
self.object_client = object_storage.ObjectClient(self.auth_provider,
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 381f3df..0a1881c 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -169,7 +169,7 @@
'nova': os.extensions_client,
'cinder': os.volumes_extension_client,
'neutron': os.network_extensions_client,
- 'swift': os.account_client,
+ 'swift': os.capabilities_client,
}
# NOTE (e0ne): Use Cinder API v2 by default because v1 is deprecated
if CONF.volume_feature_enabled.api_v2:
@@ -201,7 +201,7 @@
if service != 'swift':
resp = extensions_client.list_extensions()
else:
- __, resp = extensions_client.list_extensions()
+ __, resp = extensions_client.list_capabilities()
# For Nova, Cinder and Neutron we use the alias name rather than the
# 'name' field because the alias is considered to be the canonical
# name.
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 318eb10..64543fb 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -169,27 +169,28 @@
return body, servers
-def shelve_server(client, server_id, force_shelve_offload=False):
+def shelve_server(servers_client, server_id, force_shelve_offload=False):
"""Common wrapper utility to shelve server.
This method is a common wrapper to make server in 'SHELVED'
or 'SHELVED_OFFLOADED' state.
+ :param servers_clients: Compute servers client instance.
:param server_id: Server to make in shelve state
:param force_shelve_offload: Forcefully offload shelve server if it
is configured not to offload server
automatically after offload time.
"""
- client.shelve_server(server_id)
+ servers_client.shelve_server(server_id)
offload_time = CONF.compute.shelved_offload_time
if offload_time >= 0:
- waiters.wait_for_server_status(client, server_id,
+ waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED',
extra_timeout=offload_time)
else:
- waiters.wait_for_server_status(client, server_id, 'SHELVED')
+ waiters.wait_for_server_status(servers_client, server_id, 'SHELVED')
if force_shelve_offload:
- client.shelve_offload_server(server_id)
- waiters.wait_for_server_status(client, server_id,
+ servers_client.shelve_offload_server(server_id)
+ waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED')
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index b6ff241..8410541 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -85,7 +85,7 @@
return NonExistentHeader('x-account-container-count')
if 'x-account-object-count' not in actual:
return NonExistentHeader('x-account-object-count')
- if actual['x-account-container-count'] > 0:
+ if int(actual['x-account-container-count']) > 0:
acct_header = "x-account-storage-policy-"
matched_policy_count = 0
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/common/generator/__init__.py
+++ /dev/null
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
deleted file mode 100644
index 0647edb..0000000
--- a/tempest/common/generator/base_generator.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-import functools
-
-import jsonschema
-import six
-
-
-def _check_for_expected_result(name, schema):
- expected_result = None
- if "results" in schema:
- if name in schema["results"]:
- expected_result = schema["results"][name]
- return expected_result
-
-
-def generator_type(*args, **kwargs):
- def wrapper(func):
- func.types = args
- for key in kwargs:
- setattr(func, key, kwargs[key])
- return func
- return wrapper
-
-
-def simple_generator(fn):
- """Decorator for simple generators that return one value"""
- @functools.wraps(fn)
- def wrapped(self, schema):
- result = fn(self, schema)
- if result is not None:
- expected_result = _check_for_expected_result(fn.__name__, schema)
- return (fn.__name__, result, expected_result)
- return
- return wrapped
-
-
-class BasicGeneratorSet(object):
- _instance = None
-
- schema = {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "http-method": {
- "enum": ["GET", "PUT", "HEAD",
- "POST", "PATCH", "DELETE", 'COPY']
- },
- "admin_client": {"type": "boolean"},
- "url": {"type": "string"},
- "default_result_code": {"type": "integer"},
- "json-schema": {},
- "resources": {
- "type": "array",
- "items": {
- "oneOf": [
- {"type": "string"},
- {
- "type": "object",
- "properties": {
- "name": {"type": "string"},
- "expected_result": {"type": "integer"}
- }
- }
- ]
- }
- },
- "results": {
- "type": "object",
- "properties": {}
- }
- },
- "required": ["name", "http-method", "url"],
- "additionalProperties": False,
- }
-
- def __init__(self):
- self.types_dict = {}
- for m in dir(self):
- if callable(getattr(self, m)) and not'__' in m:
- method = getattr(self, m)
- if hasattr(method, "types"):
- for type in method.types:
- if type not in self.types_dict:
- self.types_dict[type] = []
- self.types_dict[type].append(method)
-
- def validate_schema(self, schema):
- if "json-schema" in schema:
- jsonschema.Draft4Validator.check_schema(schema['json-schema'])
- jsonschema.validate(schema, self.schema)
-
- def generate_scenarios(self, schema, path=None):
- """Generate scenario (all possible test cases) out of the given schema
-
- :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
- :param path: the schema path if the given schema is a subschema
- """
- schema_type = schema['type']
- scenarios = []
-
- if schema_type == 'object':
- properties = schema["properties"]
- for attribute, definition in six.iteritems(properties):
- current_path = copy.copy(path)
- if path is not None:
- current_path.append(attribute)
- else:
- current_path = [attribute]
- scenarios.extend(
- self.generate_scenarios(definition, current_path))
- elif isinstance(schema_type, list):
- if "integer" in schema_type:
- schema_type = "integer"
- else:
- raise Exception("non-integer list types not supported")
- for generator in self.types_dict[schema_type]:
- if hasattr(generator, "needed_property"):
- prop = generator.needed_property
- if (prop not in schema or
- schema[prop] is None or
- schema[prop] is False):
- continue
-
- name = generator.__name__
- if ("exclude_tests" in schema and
- name in schema["exclude_tests"]):
- continue
- if path is not None:
- name = "%s_%s" % ("_".join(path), name)
- scenarios.append({
- "_negtest_name": name,
- "_negtest_generator": generator,
- "_negtest_schema": schema,
- "_negtest_path": path})
- return scenarios
-
- def generate_payload(self, test, schema):
- """Generates one jsonschema out of the given test.
-
- It's mandatory to use generate_scenarios before to register all needed
- variables to the test.
-
- :param test: A test object (scenario) with all _negtest variables on it
- :param schema: schema for the test
- """
- generator = test._negtest_generator
- ret = generator(test._negtest_schema)
- path = copy.copy(test._negtest_path)
- expected_result = None
-
- if ret is not None:
- generator_result = generator(test._negtest_schema)
- invalid_snippet = generator_result[1]
- expected_result = generator_result[2]
- element = path.pop()
- if len(path) > 0:
- schema_snip = six.moves.reduce(dict.get, path, schema)
- schema_snip[element] = invalid_snippet
- else:
- schema[element] = invalid_snippet
- return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
deleted file mode 100644
index 67ace54..0000000
--- a/tempest/common/generator/negative_generator.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-
-import tempest.common.generator.base_generator as base
-import tempest.common.generator.valid_generator as valid
-
-
-class NegativeTestGenerator(base.BasicGeneratorSet):
- @base.generator_type("string")
- @base.simple_generator
- def gen_int(self, _):
- return 4
-
- @base.generator_type("integer")
- @base.simple_generator
- def gen_string(self, _):
- return "XXXXXX"
-
- @base.generator_type("integer", "string")
- def gen_none(self, schema):
- # Note(mkoderer): it's not using the decorator otherwise it'd be
- # filtered
- expected_result = base._check_for_expected_result('gen_none', schema)
- return ('gen_none', None, expected_result)
-
- @base.generator_type("string")
- @base.simple_generator
- def gen_str_min_length(self, schema):
- min_length = schema.get("minLength", 0)
- if min_length > 0:
- return "x" * (min_length - 1)
-
- @base.generator_type("string", needed_property="maxLength")
- @base.simple_generator
- def gen_str_max_length(self, schema):
- max_length = schema.get("maxLength", -1)
- return "x" * (max_length + 1)
-
- @base.generator_type("integer", needed_property="minimum")
- @base.simple_generator
- def gen_int_min(self, schema):
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- minimum -= 1
- return minimum
-
- @base.generator_type("integer", needed_property="maximum")
- @base.simple_generator
- def gen_int_max(self, schema):
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- maximum += 1
- return maximum
-
- @base.generator_type("object", needed_property="additionalProperties")
- @base.simple_generator
- def gen_obj_add_attr(self, schema):
- valid_schema = valid.ValidTestGenerator().generate_valid(schema)
- new_valid = copy.deepcopy(valid_schema)
- new_valid["$$$$$$$$$$"] = "xxx"
- return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
deleted file mode 100644
index 3070489..0000000
--- a/tempest/common/generator/valid_generator.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 six
-
-import tempest.common.generator.base_generator as base
-
-
-class ValidTestGenerator(base.BasicGeneratorSet):
- @base.generator_type("string")
- @base.simple_generator
- def generate_valid_string(self, schema):
- size = schema.get("minLength", 1)
- # TODO(dkr mko): handle format and pattern
- return "x" * size
-
- @base.generator_type("integer")
- @base.simple_generator
- def generate_valid_integer(self, schema):
- # TODO(dkr mko): handle multipleOf
- if "minimum" in schema:
- minimum = schema["minimum"]
- if "exclusiveMinimum" not in schema:
- return minimum
- else:
- return minimum + 1
- if "maximum" in schema:
- maximum = schema["maximum"]
- if "exclusiveMaximum" not in schema:
- return maximum
- else:
- return maximum - 1
- return 0
-
- @base.generator_type("object")
- @base.simple_generator
- def generate_valid_object(self, schema):
- obj = {}
- for k, v in six.iteritems(schema["properties"]):
- obj[k] = self.generate_valid(v)
- return obj
-
- def generate(self, schema):
- schema_type = schema["type"]
- if isinstance(schema_type, list):
- if "integer" in schema_type:
- schema_type = "integer"
- else:
- raise Exception("non-integer list types not supported")
- result = []
- if schema_type not in self.types_dict:
- raise TypeError("generator (%s) doesn't support type: %s"
- % (self.__class__.__name__, schema_type))
- for generator in self.types_dict[schema_type]:
- ret = generator(schema)
- if ret is not None:
- if isinstance(ret, list):
- result.extend(ret)
- elif isinstance(ret, tuple):
- result.append(ret)
- else:
- raise Exception("generator (%s) returns invalid result: %s"
- % (generator, ret))
- return result
-
- def generate_valid(self, schema):
- return self.generate(schema)[0][1]
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 5e23696..3f68ae8 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -304,7 +304,9 @@
if exist_creds and not force_new:
return exist_creds
elif exist_creds and force_new:
- new_index = six.text_type(roles).encode('utf-8') + '-' + \
+ # NOTE(andreaf) In py3.x encode returns bytes, and b'' is bytes
+ # In py2.7 encode returns strings, and b'' is still string
+ new_index = six.text_type(roles).encode('utf-8') + b'-' + \
six.text_type(len(self._creds)).encode('utf-8')
self._creds[new_index] = exist_creds
net_creds = self._get_creds(roles=roles)
diff --git a/tempest/config.py b/tempest/config.py
index 7550287..47eb427 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -1075,14 +1075,6 @@
"step in Node cleaning.")
]
-negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
-
-NegativeGroup = [
- cfg.StrOpt('test_generator',
- default='tempest.common.' +
- 'generator.negative_generator.NegativeTestGenerator',
- help="Test generator class for all negative tests"),
-]
DefaultGroup = [
cfg.StrOpt('resources_prefix',
@@ -1115,7 +1107,6 @@
(debug_group, DebugGroup),
(baremetal_group, BaremetalGroup),
(input_scenario_group, InputScenarioGroup),
- (negative_group, NegativeGroup),
(None, DefaultGroup)
]
@@ -1179,7 +1170,6 @@
self.debug = _CONF.debug
self.baremetal = _CONF.baremetal
self.input_scenario = _CONF['input-scenario']
- self.negative = _CONF.negative
logging.tempest_set_log_file('tempest.log')
def __init__(self, parse_conf=True, config_path=None):
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 1239ac5..88ce775 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -23,6 +23,7 @@
import unittest
import uuid
+from oslo_utils import uuidutils
import six.moves.urllib.parse as urlparse
DECORATOR_MODULE = 'test'
@@ -61,7 +62,7 @@
if filename not in self.source_files:
with open(filename) as f:
self.source_files[filename] = self._quote(f.read())
- patch_id = str(uuid.uuid4())
+ patch_id = uuidutils.generate_uuid()
if not patch.endswith('\n'):
patch += '\n'
self.patches[patch_id] = self._quote(patch)
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
index ad968f1..3f10dee 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -140,7 +140,7 @@
# Domain names must be unique, in any case a list is returned,
# selecting the first (and only) element
self.creds_domain = self.domains_client.list_domains(
- params={'name': domain_name})['domains'][0]
+ name=domain_name)['domains'][0]
except lib_exc.NotFound:
# TODO(andrea) we could probably create the domain on the fly
msg = "Requested domain %s could not be found" % domain_name
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 4095c77..75c2e51 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -21,6 +21,7 @@
from debtcollector import removals
from oslo_utils import netutils
+from oslo_utils import uuidutils
import six.moves
@@ -30,7 +31,7 @@
:return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6')
:rtype: string
"""
- return str(uuid.uuid4())
+ return uuidutils.generate_uuid()
def rand_uuid_hex():
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index a6c01bb..108ba70 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -71,54 +71,70 @@
message = "The success code is different than the expected one"
-class NotFound(ClientRestClientException):
- message = "Object not found"
+class BadRequest(ClientRestClientException):
+ status_code = 400
+ message = "Bad request"
class Unauthorized(ClientRestClientException):
+ status_code = 401
message = 'Unauthorized'
class Forbidden(ClientRestClientException):
+ status_code = 403
message = "Forbidden"
-class TimeoutException(OtherRestClientException):
- message = "Request timed out"
-
-
-class BadRequest(ClientRestClientException):
- message = "Bad request"
-
-
-class UnprocessableEntity(ClientRestClientException):
- message = "Unprocessable entity"
-
-
-class RateLimitExceeded(ClientRestClientException):
- message = "Rate limit exceeded"
-
-
-class OverLimit(ClientRestClientException):
- message = "Request entity is too large"
-
-
-class ServerFault(ServerRestClientException):
- message = "Got server fault"
-
-
-class NotImplemented(ServerRestClientException):
- message = "Got NotImplemented error"
+class NotFound(ClientRestClientException):
+ status_code = 404
+ message = "Object not found"
class Conflict(ClientRestClientException):
+ status_code = 409
message = "An object with that identifier already exists"
class Gone(ClientRestClientException):
+ status_code = 410
message = "The requested resource is no longer available"
+class RateLimitExceeded(ClientRestClientException):
+ status_code = 413
+ message = "Rate limit exceeded"
+
+
+class OverLimit(ClientRestClientException):
+ status_code = 413
+ message = "Request entity is too large"
+
+
+class InvalidContentType(ClientRestClientException):
+ status_code = 415
+ message = "Invalid content type provided"
+
+
+class UnprocessableEntity(ClientRestClientException):
+ status_code = 422
+ message = "Unprocessable entity"
+
+
+class ServerFault(ServerRestClientException):
+ status_code = 500
+ message = "Got server fault"
+
+
+class NotImplemented(ServerRestClientException):
+ status_code = 501
+ message = "Got NotImplemented error"
+
+
+class TimeoutException(OtherRestClientException):
+ message = "Request timed out"
+
+
class ResponseWithNonEmptyBody(OtherRestClientException):
message = ("RFC Violation! Response with %(status)d HTTP Status Code "
"MUST NOT have a body")
@@ -137,10 +153,6 @@
message = "HTTP response header is invalid"
-class InvalidContentType(ClientRestClientException):
- message = "Invalid content type provided"
-
-
class UnexpectedContentType(OtherRestClientException):
message = "Unexpected content type provided"
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index c466f07..19e5463 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -27,6 +27,8 @@
SecurityGroupRulesClient
from tempest.lib.services.network.security_groups_client import \
SecurityGroupsClient
+from tempest.lib.services.network.service_providers_client import \
+ ServiceProvidersClient
from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
from tempest.lib.services.network.subnets_client import SubnetsClient
from tempest.lib.services.network.versions_client import NetworkVersionsClient
@@ -35,4 +37,5 @@
'MeteringLabelRulesClient', 'MeteringLabelsClient',
'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
'SecurityGroupRulesClient', 'SecurityGroupsClient',
- 'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
+ 'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+ 'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/service_providers_client.py b/tempest/lib/services/network/service_providers_client.py
new file mode 100644
index 0000000..0ee9bc3
--- /dev/null
+++ b/tempest/lib/services/network/service_providers_client.py
@@ -0,0 +1,21 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.network import base
+
+
+class ServiceProvidersClient(base.BaseNetworkClient):
+
+ def list_service_providers(self, **filters):
+ """Lists service providers."""
+ uri = '/service-providers'
+ return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index eaaafa5..837b4f6 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -15,6 +15,8 @@
from tempest.lib.services.volume.v2.availability_zone_client \
import AvailabilityZoneClient
from tempest.lib.services.volume.v2.backups_client import BackupsClient
+from tempest.lib.services.volume.v2.capabilities_client import \
+ CapabilitiesClient
from tempest.lib.services.volume.v2.encryption_types_client import \
EncryptionTypesClient
from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
@@ -22,6 +24,8 @@
from tempest.lib.services.volume.v2.limits_client import LimitsClient
from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
from tempest.lib.services.volume.v2.quotas_client import QuotasClient
+from tempest.lib.services.volume.v2.scheduler_stats_client import \
+ SchedulerStatsClient
from tempest.lib.services.volume.v2.services_client import ServicesClient
from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
from tempest.lib.services.volume.v2.types_client import TypesClient
@@ -30,4 +34,4 @@
__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
- 'LimitsClient']
+ 'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient']
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
new file mode 100644
index 0000000..b6de5b9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -0,0 +1,34 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+ def show_backend_capabilities(self, host):
+ """Shows capabilities for a storage back end.
+
+ Output params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html
+ #showBackendCapabilities
+ """
+ url = 'capabilities/%s' % host
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
new file mode 100644
index 0000000..637254b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -0,0 +1,35 @@
+# Copyright 2016 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SchedulerStatsClient(rest_client.RestClient):
+
+ def list_pools(self, detail=False):
+ """List all the volumes pools (hosts).
+
+ Output params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#listPools
+ """
+ url = 'scheduler-stats/get_pools'
+ if detail:
+ url += '?detail=True'
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index ce97adb..8b8e249 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from debtcollector import removals
from oslo_serialization import jsonutils as json
import six
from six.moves.urllib import parse as urllib
@@ -323,6 +324,8 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ @removals.remove(message="use show_pools from tempest.lib.services."
+ "volume.v2.scheduler_stats_client")
def show_pools(self, detail=False):
# List all the volumes pools (hosts)
url = 'scheduler-stats/get_pools'
@@ -334,6 +337,8 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ @removals.remove(message="use show_backend_capabilities from tempest.lib."
+ "services.volume.v2.capabilities_client")
def show_backend_capabilities(self, host):
"""Shows capabilities for a storage back end.
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 3ac6759..f2f17d2 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,8 +16,8 @@
from tempest.common import custom_matchers
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
from tempest.scenario import manager
from tempest import test
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 32f5d9f..1360b09 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,7 +15,6 @@
from oslo_log import log
import testtools
-from tempest import clients
from tempest.common.utils import data_utils
from tempest.common.utils import net_info
from tempest import config
@@ -113,9 +112,9 @@
access point
"""
- def __init__(self, credentials):
- self.manager = clients.Manager(credentials)
+ def __init__(self, clients):
# Credentials from manager are filled with both names and IDs
+ self.manager = clients
self.creds = self.manager.credentials
self.network = None
self.subnet = None
@@ -152,11 +151,6 @@
# Create no network resources for these tests.
cls.set_network_resources()
super(TestSecurityGroupsBasicOps, cls).setup_credentials()
- # TODO(mnewby) Consider looking up entities as needed instead
- # of storing them as collections on the class.
-
- # Credentials from the manager are filled with both IDs and Names
- cls.alt_creds = cls.alt_manager.credentials
@classmethod
def resource_setup(cls):
@@ -171,9 +165,8 @@
cls.floating_ips = {}
cls.tenants = {}
- creds = cls.manager.credentials
- cls.primary_tenant = cls.TenantProperties(creds)
- cls.alt_tenant = cls.TenantProperties(cls.alt_creds)
+ cls.primary_tenant = cls.TenantProperties(cls.os)
+ cls.alt_tenant = cls.TenantProperties(cls.os_alt)
for tenant in [cls.primary_tenant, cls.alt_tenant]:
cls.tenants[tenant.creds.tenant_id] = tenant
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 3aab2b8..2d2f7df 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -18,8 +18,8 @@
from tempest.common import waiters
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
from tempest.scenario import manager
from tempest import test
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index db5e009..46aebfe 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -148,10 +148,13 @@
# create a 3rd instance from snapshot
LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
- volume = self.create_volume(snapshot_id=snapshot['id'])
+ volume = self.create_volume(snapshot_id=snapshot['id'],
+ size=snapshot['size'])
+ LOG.info("Booting third instance from snapshot")
server_from_snapshot = (
self._boot_instance_from_volume(volume['id'],
keypair, security_group))
+ LOG.info("Booted third instance %s", server_from_snapshot)
# check the content of written file
LOG.info("Logging into third instance to get timestamp: %s" %
diff --git a/tempest/services/identity/v3/json/domains_client.py b/tempest/services/identity/v3/json/domains_client.py
index fe929a5..43cb62c 100644
--- a/tempest/services/identity/v3/json/domains_client.py
+++ b/tempest/services/identity/v3/json/domains_client.py
@@ -21,29 +21,36 @@
class DomainsClient(rest_client.RestClient):
api_version = "v3"
- def create_domain(self, name, **kwargs):
- """Creates a domain."""
- description = kwargs.get('description', None)
- en = kwargs.get('enabled', True)
- post_body = {
- 'description': description,
- 'enabled': en,
- 'name': name
- }
- post_body = json.dumps({'domain': post_body})
+ def create_domain(self, **kwargs):
+ """Creates a domain.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain
+ """
+ post_body = json.dumps({'domain': kwargs})
resp, body = self.post('domains', post_body)
self.expected_success(201, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_domain(self, domain_id):
- """Deletes a domain."""
+ """Deletes a domain.
+
+ For APi details, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain
+ """
resp, body = self.delete('domains/%s' % domain_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
- def list_domains(self, params=None):
- """List Domains."""
+ def list_domains(self, **params):
+ """List Domains.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#list-domains
+ """
url = 'domains'
if params:
url += '?%s' % urllib.urlencode(params)
@@ -53,24 +60,24 @@
return rest_client.ResponseBody(resp, body)
def update_domain(self, domain_id, **kwargs):
- """Updates a domain."""
- body = self.show_domain(domain_id)['domain']
- description = kwargs.get('description', body['description'])
- en = kwargs.get('enabled', body['enabled'])
- name = kwargs.get('name', body['name'])
- post_body = {
- 'description': description,
- 'enabled': en,
- 'name': name
- }
- post_body = json.dumps({'domain': post_body})
+ """Updates a domain.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain
+ """
+ post_body = json.dumps({'domain': kwargs})
resp, body = self.patch('domains/%s' % domain_id, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def show_domain(self, domain_id):
- """Get Domain details."""
+ """Get Domain details.
+
+ For API details, please refer to the official API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-details
+ """
resp, body = self.get('domains/%s' % domain_id)
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index 96fe4a3..d1a61d6 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -13,7 +13,10 @@
# the License.
from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.capabilities_client import \
+ CapabilitiesClient
from tempest.services.object_storage.container_client import ContainerClient
from tempest.services.object_storage.object_client import ObjectClient
-__all__ = ['AccountClient', 'ContainerClient', 'ObjectClient']
+__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
+ 'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 6012a92..9932b4a 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -144,13 +144,3 @@
body = body.strip().splitlines()
self.expected_success([200, 204], resp.status)
return resp, body
-
- def list_extensions(self):
- self.skip_path()
- try:
- resp, body = self.get('info')
- finally:
- self.reset_path()
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return resp, body
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/services/object_storage/capabilities_client.py
new file mode 100644
index 0000000..0fe437f
--- /dev/null
+++ b/tempest/services/object_storage/capabilities_client.py
@@ -0,0 +1,31 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+ def list_capabilities(self):
+ self.skip_path()
+ try:
+ resp, body = self.get('info')
+ finally:
+ self.reset_path()
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 93fbed3..51f0a6a 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -141,6 +141,28 @@
return False
+def related_bug(bug, status_code=None):
+ """A decorator useful to know solutions from launchpad bug reports
+
+ @param bug: The launchpad bug number causing the test
+ @param status_code: The status code related to the bug report
+ """
+ def decorator(f):
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ try:
+ return f(self, *func_args, **func_kwargs)
+ except Exception as exc:
+ exc_status_code = getattr(exc, 'status_code', None)
+ if status_code is None or status_code == exc_status_code:
+ LOG.error('Hints: This test was made for the bug %s. '
+ 'The failure could be related to '
+ 'https://launchpad.net/bugs/%s' % (bug, bug))
+ raise exc
+ return wrapper
+ return decorator
+
+
def is_scheduler_filter_enabled(filter_name):
"""Check the list of enabled compute scheduler filters from config. """
@@ -504,8 +526,12 @@
else:
raise lib_exc.InvalidCredentials(
"Invalid credentials type %s" % credential_type)
- return cls.client_manager(credentials=creds.credentials,
- service=cls._service)
+ manager = cls.client_manager(credentials=creds.credentials,
+ service=cls._service)
+ # NOTE(andreaf) Ensure credentials have user and project id fields.
+ # It may not be the case when using pre-provisioned credentials.
+ manager.auth_provider.set_auth()
+ return manager
@classmethod
def clear_credentials(cls):
@@ -532,18 +558,17 @@
"""
if not CONF.validation.run_validation:
return
+
if keypair is None:
- if CONF.validation.auth_method.lower() == "keypair":
- keypair = True
- else:
- keypair = False
+ keypair = (CONF.validation.auth_method.lower() == "keypair")
+
if floating_ip is None:
- if CONF.validation.connect_method.lower() == "floating":
- floating_ip = True
- else:
- floating_ip = False
+ floating_ip = (CONF.validation.connect_method.lower() ==
+ "floating")
+
if security_group is None:
security_group = CONF.validation.security_group
+
if security_group_rules is None:
security_group_rules = CONF.validation.security_group_rules
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 00b4542..1af0d95 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -401,7 +401,7 @@
'not_fake': 'metadata',
'swift': 'metadata'})
fake_os = mock.MagicMock()
- fake_os.account_client.list_extensions = fake_list_extensions
+ fake_os.capabilities_client.list_capabilities = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, 'get_enabled_extensions',
return_value=(['fake1', 'fake2', 'fake3'])))
@@ -423,7 +423,7 @@
'not_fake': 'metadata',
'swift': 'metadata'})
fake_os = mock.MagicMock()
- fake_os.account_client.list_extensions = fake_list_extensions
+ fake_os.capabilities_client.list_capabilities = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, 'get_enabled_extensions',
return_value=(['all'])))
diff --git a/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
new file mode 100644
index 0000000..1d56db6
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_namespace_properties_client.py
@@ -0,0 +1,191 @@
+# Copyright 2016 EasyStack. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.image.v2 import namespace_properties_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestNamespacePropertiesClient(base.BaseServiceTest):
+ FAKE_CREATE_SHOW_NAMESPACE_PROPERTY = {
+ "description": "property",
+ "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+ "name": "OS::Glance::Image",
+ "title": "Hypervisor Type",
+ "type": "string"
+ }
+
+ FAKE_LIST_NAMESPACE_PROPERTY = {
+ "properties": {
+ "hw_disk_bus": {
+ "description": "property.",
+ "enum": ["scsi", "virtio", "uml", "xen", "ide", "usb"],
+ "title": "Disk Bus",
+ "type": "string"
+ },
+ "hw_machine_type": {
+ "description": "desc.",
+ "title": "Machine Type",
+ "type": "string"
+ },
+ "hw_qemu_guest_agent": {
+ "description": "desc.",
+ "enum": [
+ "yes",
+ "no"
+ ],
+ "title": "QEMU Guest Agent",
+ "type": "string"
+ },
+ "hw_rng_model": {
+ "default": "virtio",
+ "description": "desc",
+ "title": "Random Number Generator Device",
+ "type": "string"
+ },
+ "hw_scsi_model": {
+ "default": "virtio-scsi",
+ "description": "desc.",
+ "title": "SCSI Model",
+ "type": "string"
+ },
+ "hw_video_model": {
+ "description": "The video image driver used.",
+ "enum": [
+ "vga",
+ "cirrus",
+ "vmvga",
+ "xen",
+ "qxl"
+ ],
+ "title": "Video Model",
+ "type": "string"
+ },
+ "hw_video_ram": {
+ "description": "desc.",
+ "title": "Max Video Ram",
+ "type": "integer"
+ },
+ "hw_vif_model": {
+ "description": "desc.",
+ "enum": ["e1000",
+ "ne2k_pci",
+ "pcnet",
+ "rtl8139",
+ "virtio",
+ "e1000",
+ "e1000e",
+ "VirtualE1000",
+ "VirtualE1000e",
+ "VirtualPCNet32",
+ "VirtualSriovEthernetCard",
+ "VirtualVmxnet",
+ "netfront",
+ "ne2k_pci"
+ ],
+ "title": "Virtual Network Interface",
+ "type": "string"
+ },
+ "os_command_line": {
+ "description": "desc.",
+ "title": "Kernel Command Line",
+ "type": "string"
+ }
+ }
+ }
+
+ FAKE_UPDATE_NAMESPACE_PROPERTY = {
+ "description": "property",
+ "enum": ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+ "name": "OS::Glance::Image",
+ "title": "update Hypervisor Type",
+ "type": "string"
+ }
+
+ def setUp(self):
+ super(TestNamespacePropertiesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = namespace_properties_client.NamespacePropertiesClient(
+ fake_auth, 'image', 'regionOne')
+
+ def _test_create_namespace_property(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_namespace_property,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+ bytes_body, status=201,
+ namespace="OS::Compute::Hypervisor",
+ title="Hypervisor Type", name="OS::Glance::Image",
+ type="string",
+ enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"])
+
+ def _test_list_namespace_property(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_namespace_properties,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_NAMESPACE_PROPERTY,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor")
+
+ def _test_show_namespace_property(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_namespace_properties,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CREATE_SHOW_NAMESPACE_PROPERTY,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor",
+ property_name="OS::Glance::Image")
+
+ def _test_update_namespace_property(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_namespace_properties,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_UPDATE_NAMESPACE_PROPERTY,
+ bytes_body,
+ namespace="OS::Compute::Hypervisor",
+ property_name="OS::Glance::Image",
+ title="update Hypervisor Type", type="string",
+ enum=["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"],
+ name="OS::Glance::Image")
+
+ def test_create_namespace_property_with_str_body(self):
+ self._test_create_namespace_property()
+
+ def test_create_namespace_property_with_bytes_body(self):
+ self._test_create_namespace_property(bytes_body=True)
+
+ def test_list_namespace_property_with_str_body(self):
+ self._test_list_namespace_property()
+
+ def test_list_namespace_property_with_bytes_body(self):
+ self._test_list_namespace_property(bytes_body=True)
+
+ def test_show_namespace_property_with_str_body(self):
+ self._test_show_namespace_property()
+
+ def test_show_namespace_property_with_bytes_body(self):
+ self._test_show_namespace_property(bytes_body=True)
+
+ def test_update_namespace_property_with_str_body(self):
+ self._test_update_namespace_property()
+
+ def test_update_namespace_property_with_bytes_body(self):
+ self._test_update_namespace_property(bytes_body=True)
+
+ def test_delete_namespace(self):
+ self.check_service_client_function(
+ self.client.delete_namespace_property,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {}, namespace="OS::Compute::Hypervisor",
+ property_name="OS::Glance::Image", status=204)
diff --git a/tempest/tests/lib/services/network/test_service_providers_client.py b/tempest/tests/lib/services/network/test_service_providers_client.py
new file mode 100644
index 0000000..ae11ef0
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_service_providers_client.py
@@ -0,0 +1,37 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.lib.services.network import service_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServiceProvidersClient(base.BaseServiceTest):
+ def setUp(self):
+ super(TestServiceProvidersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = service_providers_client.ServiceProvidersClient(
+ fake_auth, 'network', 'regionOne')
+
+ def _test_list_service_providers(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_service_providers,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ {"service_providers": []},
+ bytes_body)
+
+ def test_list_service_providers_with_str_body(self):
+ self._test_list_service_providers()
+
+ def test_list_service_providers_with_bytes_body(self):
+ self._test_list_service_providers(bytes_body=True)
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 6da7e41..2f975d2 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -458,6 +458,56 @@
expected = 'http://fake_url/v2.0'
self._test_base_url_helper(expected, filters, ('token', auth_data))
+ def test_base_url_with_extra_path_endpoint(self):
+ auth_data = {
+ 'serviceCatalog': [
+ {
+ 'type': 'compute',
+ 'endpoints': [
+ {
+ 'region': 'FakeRegion',
+ 'publicURL': 'http://fake_url/some_path/v2.0'
+ }
+ ]
+ }
+ ]
+ }
+
+ filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'api_version': 'v2.0'
+ }
+
+ expected = 'http://fake_url/some_path/v2.0'
+ self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+ def test_base_url_with_unversioned_extra_path_endpoint(self):
+ auth_data = {
+ 'serviceCatalog': [
+ {
+ 'type': 'compute',
+ 'endpoints': [
+ {
+ 'region': 'FakeRegion',
+ 'publicURL': 'http://fake_url/some_path'
+ }
+ ]
+ }
+ ]
+ }
+
+ filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'api_version': 'v2.0'
+ }
+
+ expected = 'http://fake_url/some_path/v2.0'
+ self._test_base_url_helper(expected, filters, ('token', auth_data))
+
def test_token_not_expired(self):
expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1)
self._verify_expiry(expiry_data=expiry_data, should_be_expired=False)
@@ -592,6 +642,58 @@
expected = 'http://fake_url/v3'
self._test_base_url_helper(expected, filters, ('token', auth_data))
+ def test_base_url_with_extra_path_endpoint(self):
+ auth_data = {
+ 'catalog': [
+ {
+ 'type': 'compute',
+ 'endpoints': [
+ {
+ 'region': 'FakeRegion',
+ 'url': 'http://fake_url/some_path/v2.0',
+ 'interface': 'public'
+ }
+ ]
+ }
+ ]
+ }
+
+ filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'api_version': 'v2.0'
+ }
+
+ expected = 'http://fake_url/some_path/v2.0'
+ self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+ def test_base_url_with_unversioned_extra_path_endpoint(self):
+ auth_data = {
+ 'catalog': [
+ {
+ 'type': 'compute',
+ 'endpoints': [
+ {
+ 'region': 'FakeRegion',
+ 'url': 'http://fake_url/some_path',
+ 'interface': 'public'
+ }
+ ]
+ }
+ ]
+ }
+
+ filters = {
+ 'service': 'compute',
+ 'endpoint_type': 'publicURL',
+ 'region': 'FakeRegion',
+ 'api_version': 'v2.0'
+ }
+
+ expected = 'http://fake_url/some_path/v2.0'
+ self._test_base_url_helper(expected, filters, ('token', auth_data))
+
# Base URL test with scope only for V3
def test_base_url_scope_project(self):
self.auth_provider.scope = 'project'
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
deleted file mode 100644
index 7e1ee2c..0000000
--- a/tempest/tests/negative/test_negative_generators.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import copy
-
-import jsonschema
-import mock
-import six
-
-from tempest.common.generator import base_generator
-from tempest.common.generator import negative_generator
-from tempest.common.generator import valid_generator
-from tempest.tests import base
-
-
-class TestNegativeBasicGenerator(base.TestCase):
- valid_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "json-schema": {
- "type": "object",
- "properties": {
- "minRam": {"type": "integer"},
- "minDisk": {"type": "integer"}
- }
- },
- "resources": ["flavor", "volume", "image"]
- }
-
- minimal_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- }
-
- add_prop_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "unknown_field": [12]
- }
-
- invalid_json_schema_desc = {
- "name": "list-flavors-with-detail",
- "http-method": "GET",
- "url": "flavors/detail",
- "json-schema": {"type": "NotExistingType"}
- }
-
- def setUp(self):
- super(TestNegativeBasicGenerator, self).setUp()
- self.generator = base_generator.BasicGeneratorSet()
-
- def _assert_valid_jsonschema_call(self, jsonschema_mock, desc):
- self.assertEqual(jsonschema_mock.call_count, 1)
- jsonschema_mock.assert_called_with(desc, self.generator.schema)
-
- @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
- def test_validate_schema_with_valid_input(self, jsonschema_mock):
- self.generator.validate_schema(self.valid_desc)
- self._assert_valid_jsonschema_call(jsonschema_mock, self.valid_desc)
-
- @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
- def test_validate_schema_with_minimal_input(self, jsonschema_mock):
- self.generator.validate_schema(self.minimal_desc)
- self._assert_valid_jsonschema_call(jsonschema_mock, self.minimal_desc)
-
- def test_validate_schema_with_invalid_input(self):
- self.assertRaises(jsonschema.ValidationError,
- self.generator.validate_schema, self.add_prop_desc)
- self.assertRaises(jsonschema.SchemaError,
- self.generator.validate_schema,
- self.invalid_json_schema_desc)
-
-
-class BaseNegativeGenerator(object):
- types = ['string', 'integer', 'object']
-
- fake_input_obj = {"type": "object",
- "properties": {"minRam": {"type": "integer"},
- "diskName": {"type": "string"},
- "maxRam": {"type": "integer", }
- }
- }
-
- unknown_type_schema = {
- "type": "not_defined"
- }
-
- class fake_test_class(object):
- def __init__(self, scenario):
- for k, v in six.iteritems(scenario):
- setattr(self, k, v)
-
- def _validate_result(self, valid_schema, invalid_schema):
- for k, v in six.iteritems(valid_schema):
- self.assertIn(k, invalid_schema)
-
- def test_generator_mandatory_functions(self):
- for data_type in self.types:
- self.assertIn(data_type, self.generator.types_dict)
-
- def test_generate_with_unknown_type(self):
- self.assertRaises(TypeError, self.generator.generate_payload,
- self.unknown_type_schema)
-
-
-class TestNegativeValidGenerator(base.TestCase, BaseNegativeGenerator):
- def setUp(self):
- super(TestNegativeValidGenerator, self).setUp()
- self.generator = valid_generator.ValidTestGenerator()
-
- def test_generate_valid(self):
- result = self.generator.generate_valid(self.fake_input_obj)
- self.assertIn("minRam", result)
- self.assertIsInstance(result["minRam"], int)
- self.assertIn("diskName", result)
- self.assertIsInstance(result["diskName"], str)
-
-
-class TestNegativeNegativeGenerator(base.TestCase, BaseNegativeGenerator):
- def setUp(self):
- super(TestNegativeNegativeGenerator, self).setUp()
- self.generator = negative_generator.NegativeTestGenerator()
-
- def test_generate_obj(self):
- schema = self.fake_input_obj
- scenarios = self.generator.generate_scenarios(schema)
- for scenario in scenarios:
- test = self.fake_test_class(scenario)
- valid_schema = \
- valid_generator.ValidTestGenerator().generate_valid(schema)
- schema_under_test = copy.copy(valid_schema)
- expected_result = \
- self.generator.generate_payload(test, schema_under_test)
- self.assertIsNone(expected_result)
- self._validate_result(valid_schema, schema_under_test)
diff --git a/tox.ini b/tox.ini
index 82dba92..7a36e84 100644
--- a/tox.ini
+++ b/tox.ini
@@ -143,10 +143,10 @@
# E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved. For further detail see https://review.openstack.org/#/c/36788/
# E123 skipped because it is ignored by default in the default pep8
# E129 skipped because it is too limiting when combined with other rules
-# Skipped because of new hacking 0.9: H405
ignore = E125,E123,E129
show-source = True
exclude = .git,.venv,.tox,dist,doc,*egg
+enable-extensions = H106,H203
[testenv:releasenotes]
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -161,3 +161,12 @@
commands=
pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
pip-missing-reqs -d --ignore-file=tempest/tests/* tempest
+
+
+[testenv:bindep]
+# Do not install any requirements. We want this to be fast and work even if
+# system dependencies are missing, since it's used to tell you what system
+# dependencies are missing! This also means that bindep must be installed
+# separately, outside of the requirements files.
+deps = bindep
+commands = bindep test