Merge "Provides a sample resourcefile for Javelin"
diff --git a/.testr.conf b/.testr.conf
index 4f6e0b3..95a4fb4 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -3,7 +3,7 @@
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
OS_TEST_LOCK_PATH=${OS_TEST_LOCK_PATH:-${TMPDIR:-'/tmp'}} \
- ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
+ ${PYTHON:-python} -m subunit.run discover -t ${OS_TOP_LEVEL:-./} ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=([^\.]*\.)*
diff --git a/README.rst b/README.rst
index 94a5352..af24569 100644
--- a/README.rst
+++ b/README.rst
@@ -117,8 +117,8 @@
Tempest suite.
Alternatively, you can use the run_tests.sh script which will create a venv and
-run the unit tests. There are also the py26, py27, or py33 tox jobs which will
-run the unit tests with the corresponding version of python.
+run the unit tests. There are also the py27 and py34 tox jobs which will run
+the unit tests with the corresponding version of python.
Python 2.6
----------
@@ -131,3 +131,16 @@
on an earlier release with python 2.6 you can easily run Tempest against it
from a remote system running python 2.7. (or deploy a cloud guest in your cloud
that has python 2.7)
+
+Python 3.4
+----------
+
+Starting during the Liberty release development cycle work began on enabling
+Tempest to run under both Python 2.7 and Python 3.4. Tempest strives to fully
+support running with Python 3.4. A gating unit test job was added to also run
+Tempest's unit tests under Python 3.4. This means that the Tempest code at
+least imports under Python 3.4 and things that have unit test coverage will
+work on Python 3.4. However, because large parts of Tempest are self verifying
+there might be uncaught issues running on Python 3.4. So until there is a gating
+job which does a full Tempest run using Python 3.4 there isn't any guarantee
+that running Tempest under Python 3.4 is bug free.
diff --git a/doc/source/account_generator.rst b/doc/source/account_generator.rst
new file mode 100644
index 0000000..1c30473
--- /dev/null
+++ b/doc/source/account_generator.rst
@@ -0,0 +1,5 @@
+--------------------------------
+Tempest Test-Account Generator Utility
+--------------------------------
+
+.. automodule:: tempest.cmd.account_generator
\ No newline at end of file
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index b6e00ce..0805544 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -112,9 +112,11 @@
available in the file. (if running serially the worker count is 1)
You can check the sample file packaged in tempest for the yaml format
- #. Provide tempest with the location of you accounts.yaml file with the
+ #. Provide tempest with the location of your accounts.yaml file with the
test_accounts_file option in the auth section
+ #. Set allow_tenant_isolation = False in the auth group
+
It is worth pointing out that each set of credentials in the accounts.yaml
should have a unique tenant. This is required to provide proper isolation
to the tests using the credentials, and failure to do this will likely cause
@@ -140,6 +142,11 @@
#. alt_password
#. alt_tenant_name
+And in the auth secion:
+
+ #. allow_tenant_isolation = False
+ #. comment out 'test_accounts_file' or keep it as empty
+
It only makes sense to use it if parallel execution isn't needed, since tempest
won't be able to properly isolate tests using this. Additionally, using the
traditional config options for credentials is not able to provide credentials to
@@ -303,6 +310,12 @@
will additionally be used as a fallback for server creation. However, unlike
accounts.yaml this should never be triggered.
+However, there is an option *create_isolated_networks* to disable tenant
+isolation's automatic provisioning of network resources. If this option is
+used you will have to either rely on there only being a single/default network
+available for the server creation, or use *fixed_network_name* to inform
+Tempest which network to use.
+
Configuring Available Services
------------------------------
OpenStack is really a constellation of several different projects which
diff --git a/doc/source/index.rst b/doc/source/index.rst
index cb1c504..f925018 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -45,6 +45,7 @@
.. toctree::
:maxdepth: 1
+ account_generator
cleanup
javelin
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 3f9e70e..6424f55 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -40,13 +40,15 @@
#log_dir = <None>
# Use syslog for logging. Existing syslog format is DEPRECATED during
-# I, and will change in J to honor RFC5424. (boolean value)
+# I, and changed in J to honor RFC5424. (boolean value)
#use_syslog = false
# (Optional) Enables or disables syslog rfc5424 format for logging. If
# enabled, prefixes the MSG part of the syslog message with APP-NAME
# (RFC5424). The format without the APP-NAME is deprecated in K, and
-# will be removed in L, along with this option. (boolean value)
+# will be removed in M, along with this option. (boolean value)
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
#use_syslog_rfc_format = true
# Syslog facility to receive log lines. (string value)
@@ -67,7 +69,7 @@
# Prefix each line of exception output with this format. (string
# value)
-#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s
+#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d ERROR %(name)s %(instance)s
# List of logger=LEVEL pairs. (list value)
#default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN
@@ -86,6 +88,9 @@
# (string value)
#instance_uuid_format = "[instance: %(uuid)s] "
+# Enables or disables fatal status of deprecations. (boolean value)
+#fatal_deprecations = false
+
[auth]
@@ -117,6 +122,14 @@
# the domain from theadmin user is used instead. (string value)
#tenant_isolation_domain_name = <None>
+# If allow_tenant_isolation is set to True and Neutron is enabled
+# Tempest will try to create a useable network, subnet, and router
+# when needed for each tenant it creates. However in some neutron
+# configurations, like with VLAN provider networks, this doesn't work.
+# So if set to False the isolated networks will not be created
+# (boolean value)
+#create_isolated_networks = true
+
[baremetal]
@@ -148,8 +161,9 @@
# Timeout for Ironic power transitions. (integer value)
#power_timeout = 60
-# Timeout for unprovisioning an Ironic node. (integer value)
-#unprovision_timeout = 60
+# Timeout for unprovisioning an Ironic node. Takes longer since Kilo
+# as Ironic performs an extra step in Node cleaning. (integer value)
+#unprovision_timeout = 300
[boto]
@@ -571,6 +585,9 @@
# applies to user and project (string value)
#admin_domain_name = <None>
+# ID of the default domain (string value)
+#default_domain_id = default
+
[identity-feature-enabled]
@@ -631,6 +648,10 @@
# Is the v1 image API enabled (boolean value)
#api_v1 = true
+# Is the deactivate-image feature enabled. The feature has been
+# integrated since Kilo. (boolean value)
+#deactivate_image = false
+
[input-scenario]
@@ -935,6 +956,10 @@
# Image container format (string value)
#img_container_format = bare
+# Glance image properties. Use for custom images which require them
+# (dict value)
+#img_properties = <None>
+
# AMI image file name (string value)
#ami_img_file = cirros-0.3.1-x86_64-blank.img
@@ -1171,3 +1196,7 @@
# Is the v2 volume API enabled (boolean value)
#api_v2 = true
+
+# Update bootable status of a volume Not implemented on icehouse
+# (boolean value)
+#bootable = false
diff --git a/setup.cfg b/setup.cfg
index 2de9f34..3a14bba 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,6 +15,8 @@
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3.4
[entry_points]
console_scripts =
@@ -22,6 +24,7 @@
javelin2 = tempest.cmd.javelin:main
run-tempest-stress = tempest.cmd.run_stress:main
tempest-cleanup = tempest.cmd.cleanup:main
+ tempest-account-generator = tempest.cmd.account_generator:main
oslo.config.opts =
tempest.config = tempest.config:list_opts
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index eec4688..a65fda6 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -50,7 +50,7 @@
@test.idempotent_id('16b7d848-2f7c-4709-85a3-2dfb4576cc52')
@test.services('network')
def test_list_fixed_ip_details(self):
- fixed_ip = self.client.get_fixed_ip_details(self.ip)
+ fixed_ip = self.client.show_fixed_ip(self.ip)
self.assertEqual(fixed_ip['address'], self.ip)
@test.idempotent_id('5485077b-7e46-4cec-b402-91dc3173433b')
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index ac8a60d..35c719d 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -54,7 +54,7 @@
@test.services('network')
def test_list_fixed_ip_details_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
- self.non_admin_client.get_fixed_ip_details, self.ip)
+ self.non_admin_client.show_fixed_ip, self.ip)
@test.attr(type=['negative'])
@test.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 8aab8da..f12784e 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -82,7 +82,7 @@
self.assertEqual(flavor['os-flavor-access:is_public'], True)
# Verify flavor is retrieved
- flavor = self.client.get_flavor_details(flavor['id'])
+ flavor = self.client.show_flavor(flavor['id'])
self.assertEqual(flavor['name'], flavor_name)
return flavor['id']
@@ -160,7 +160,7 @@
verify_flavor_response_extension(flavor)
# Verify flavor is retrieved
- flavor = self.client.get_flavor_details(new_flavor_id)
+ flavor = self.client.show_flavor(new_flavor_id)
self.assertEqual(flavor['name'], flavor_name)
verify_flavor_response_extension(flavor)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 2ed1e35..a14a61f 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -75,7 +75,7 @@
self.client.set_flavor_extra_spec(self.flavor['id'], specs)
self.assertEqual(set_body, specs)
# GET extra specs and verify
- get_body = self.client.get_flavor_extra_spec(self.flavor['id'])
+ get_body = self.client.list_flavor_extra_specs(self.flavor['id'])
self.assertEqual(get_body, specs)
# UPDATE the value of the extra specs key1
@@ -87,7 +87,7 @@
# GET extra specs and verify the value of the key2
# is the same as before
- get_body = self.client.get_flavor_extra_spec(self.flavor['id'])
+ get_body = self.client.list_flavor_extra_specs(self.flavor['id'])
self.assertEqual(get_body, {"key1": "value", "key2": "value2"})
# UNSET extra specs that were set in this test
@@ -98,7 +98,7 @@
def test_flavor_non_admin_get_all_keys(self):
specs = {"key1": "value1", "key2": "value2"}
self.client.set_flavor_extra_spec(self.flavor['id'], specs)
- body = self.flavors_client.get_flavor_extra_spec(self.flavor['id'])
+ body = self.flavors_client.list_flavor_extra_specs(self.flavor['id'])
for key in specs:
self.assertEqual(body[key], specs[key])
@@ -109,7 +109,7 @@
body = self.client.set_flavor_extra_spec(self.flavor['id'], specs)
self.assertEqual(body['key1'], 'value1')
self.assertIn('key2', body)
- body = self.flavors_client.get_flavor_extra_spec_with_key(
+ body = self.flavors_client.show_flavor_extra_spec(
self.flavor['id'], 'key1')
self.assertEqual(body['key1'], 'value1')
self.assertNotIn('key2', body)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index df592dd..ba05c7f 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -115,7 +115,7 @@
@test.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
def test_flavor_get_nonexistent_key(self):
self.assertRaises(lib_exc.NotFound,
- self.flavors_client.get_flavor_extra_spec_with_key,
+ self.flavors_client.show_flavor_extra_spec,
self.flavor['id'],
"nonexistent_key")
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index d3b1f5e..a03439a 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -78,7 +78,7 @@
return server_id
def _volume_clean_up(self, server_id, volume_id):
- body = self.volumes_client.get_volume(volume_id)
+ body = self.volumes_client.show_volume(volume_id)
if body['status'] == 'in-use':
self.servers_client.detach_volume(server_id, volume_id)
self.volumes_client.wait_for_volume_status(volume_id, 'available')
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 7f78ce8..d325bd7 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -53,7 +53,7 @@
flavor_id = data_utils.rand_int_id(start=1000)
while True:
try:
- self.flavors_client.get_flavor_details(flavor_id)
+ self.flavors_client.show_flavor(flavor_id)
except lib_exc.NotFound:
break
flavor_id = data_utils.rand_int_id(start=1000)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 26d6661..9aacfa5 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -52,7 +52,6 @@
def setup_credentials(cls):
cls.set_network_resources()
super(BaseComputeTest, cls).setup_credentials()
- cls.multi_user = cls.check_multi_user()
@classmethod
def setup_clients(cls):
@@ -112,14 +111,6 @@
super(BaseComputeTest, cls).resource_cleanup()
@classmethod
- def check_multi_user(cls):
- # We have a list of accounts now, so just checking if the list is gt 2
- if not cls.isolated_creds.is_multi_user():
- msg = "Not enough users available for multi-user testing"
- raise exceptions.InvalidConfiguration(msg)
- return True
-
- @classmethod
def clear_servers(cls):
LOG.debug('Clearing servers: %s', ','.join(
server['id'] for server in cls.servers))
@@ -312,7 +303,7 @@
if 'wait_until' in kwargs:
cls.images_client.wait_for_image_status(image_id,
kwargs['wait_until'])
- image = cls.images_client.get_image(image_id)
+ image = cls.images_client.show_image(image_id)
if kwargs['wait_until'] == 'ACTIVE':
if kwargs.get('wait_for_server', True):
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index c1c87f9..251f4ec 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -33,7 +33,7 @@
def test_list_flavors(self):
# List of all flavors should contain the expected flavor
flavors = self.client.list_flavors()
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
'name': flavor['name']}
self.assertIn(flavor_min_detail, flavors)
@@ -42,14 +42,14 @@
def test_list_flavors_with_detail(self):
# Detailed list of all flavors should contain the expected flavor
flavors = self.client.list_flavors_with_detail()
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
self.assertIn(flavor, flavors)
@test.attr(type='smoke')
@test.idempotent_id('1f12046b-753d-40d2-abb6-d8eb8b30cb2f')
def test_get_flavor(self):
# The expected flavor details should be returned
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
self.assertEqual(self.flavor_ref, flavor['id'])
@test.idempotent_id('8d7691b3-6ed4-411a-abc9-2839a765adab')
@@ -69,7 +69,7 @@
@test.idempotent_id('e800f879-9828-4bd0-8eae-4f17189951fb')
def test_list_flavors_using_marker(self):
# The list of flavors should start from the provided marker
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {'marker': flavor_id}
@@ -80,7 +80,7 @@
@test.idempotent_id('6db2f0c0-ddee-4162-9c84-0703d3dd1107')
def test_list_flavors_detailed_using_marker(self):
# The list of flavors should start from the provided marker
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {'marker': flavor_id}
@@ -91,7 +91,7 @@
@test.idempotent_id('3df2743e-3034-4e57-a4cb-b6527f6eac79')
def test_list_flavors_detailed_filter_by_min_disk(self):
# The detailed list of flavors should be filtered by disk space
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {self._min_disk: flavor['disk'] + 1}
@@ -101,7 +101,7 @@
@test.idempotent_id('09fe7509-b4ee-4b34-bf8b-39532dc47292')
def test_list_flavors_detailed_filter_by_min_ram(self):
# The detailed list of flavors should be filtered by RAM
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {self._min_ram: flavor['ram'] + 1}
@@ -111,7 +111,7 @@
@test.idempotent_id('10645a4d-96f5-443f-831b-730711e11dd4')
def test_list_flavors_filter_by_min_disk(self):
# The list of flavors should be filtered by disk space
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {self._min_disk: flavor['disk'] + 1}
@@ -121,7 +121,7 @@
@test.idempotent_id('935cf550-e7c8-4da6-8002-00f92d5edfaa')
def test_list_flavors_filter_by_min_ram(self):
# The list of flavors should be filtered by RAM
- flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor = self.client.show_flavor(self.flavor_ref)
flavor_id = flavor['id']
params = {self._min_ram: flavor['ram'] + 1}
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 5f438fb..093f16b 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -67,7 +67,7 @@
self.addCleanup(self.client.delete_floating_ip,
floating_ip_id_allocated)
floating_ip_details = \
- self.client.get_floating_ip_details(floating_ip_id_allocated)
+ self.client.show_floating_ip(floating_ip_id_allocated)
# Checking if the details of allocated IP is in list of floating IP
body = self.client.list_floating_ips()
self.assertIn(floating_ip_details, body)
@@ -97,7 +97,7 @@
self.server_id)
# Check instance_id in the floating_ip body
- body = self.client.get_floating_ip_details(self.floating_ip_id)
+ body = self.client.show_floating_ip(self.floating_ip_id)
self.assertEqual(self.server_id, body['instance_id'])
# Disassociation of floating IP that was associated in this method
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 62bc92c..ad52b8c 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -63,7 +63,7 @@
floating_ip_instance_id = body['instance_id']
floating_ip_ip = body['ip']
floating_ip_fixed_ip = body['fixed_ip']
- body = self.client.get_floating_ip_details(floating_ip_id)
+ body = self.client.show_floating_ip(floating_ip_id)
# Comparing the details of floating IP
self.assertEqual(floating_ip_instance_id,
body['instance_id'])
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index e1e30a4..05d3d05 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -44,4 +44,4 @@
else:
non_exist_id = data_utils.rand_int_id(start=999)
self.assertRaises(lib_exc.NotFound,
- self.client.get_floating_ip_details, non_exist_id)
+ self.client.show_floating_ip, non_exist_id)
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index a92048f..1fd165b 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -70,7 +70,7 @@
cls.server_id = server['id']
def _get_default_flavor_disk_size(self, flavor_id):
- flavor = self.flavors_client.get_flavor_details(flavor_id)
+ flavor = self.flavors_client.show_flavor(flavor_id)
return flavor['disk']
@test.idempotent_id('3731d080-d4c5-4872-b41a-64d0d0021314')
@@ -84,11 +84,11 @@
self.client.wait_for_image_status(image_id, 'ACTIVE')
# Verify the image was created correctly
- image = self.client.get_image(image_id)
+ image = self.client.show_image(image_id)
self.assertEqual(name, image['name'])
self.assertEqual('test', image['metadata']['image_type'])
- original_image = self.client.get_image(self.image_ref)
+ original_image = self.client.show_image(self.image_ref)
# Verify minRAM is the same as the original image
self.assertEqual(image['minRam'], original_image['minRam'])
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index aa12044..8a39d90 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -140,4 +140,4 @@
self.client.delete_image(image_id)
self.image_ids.remove(image_id)
- self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+ self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index abe7be5..e2d4bd9 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -62,7 +62,7 @@
image_file = six.StringIO(('*' * 1024))
cls.glance_client.update_image(image_id, data=image_file)
cls.client.wait_for_image_status(image_id, 'ACTIVE')
- body = cls.client.get_image(image_id)
+ body = cls.client.show_image(image_id)
return body
# Create non-snapshot images via glance
@@ -249,7 +249,7 @@
# The detailed list of servers should be filtered by image type
params = {'type': 'snapshot'}
images = self.client.list_images_with_detail(params)
- self.client.get_image(self.image_ref)
+ self.client.show_image(self.image_ref)
self.assertTrue(any([i for i in images
if i['id'] == self.snapshot1_id]))
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index 334516b..4110619 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -41,5 +41,5 @@
def test_get_nonexistent_image(self):
# Check raises a NotFound
nonexistent_image = data_utils.rand_uuid()
- self.assertRaises(lib_exc.NotFound, self.client.get_image,
+ self.assertRaises(lib_exc.NotFound, self.client.show_image,
nonexistent_image)
diff --git a/tempest/api/compute/images/test_list_images.py b/tempest/api/compute/images/test_list_images.py
index 95f49c9..5b80c72 100644
--- a/tempest/api/compute/images/test_list_images.py
+++ b/tempest/api/compute/images/test_list_images.py
@@ -37,7 +37,7 @@
@test.idempotent_id('490d0898-e12a-463f-aef0-c50156b9f789')
def test_get_image(self):
# Returns the correct details for a single image
- image = self.client.get_image(self.image_ref)
+ image = self.client.show_image(self.image_ref)
self.assertEqual(self.image_ref, image['id'])
@test.idempotent_id('fd51b7f4-d4a3-4331-9885-866658112a6f')
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 2cf2e28..06e073d 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -89,15 +89,10 @@
@test.idempotent_id('777b6f14-aca9-4758-9e84-38783cfa58bc')
@test.services('network')
def test_security_group_create_with_invalid_group_description(self):
- # Negative test:Security Group should not be created with description
- # as an empty string/with white spaces/chars more than 255
+ # Negative test: Security Group should not be created with description
+ # longer than 255 chars. Empty description is allowed by the API
+ # reference, however.
s_name = data_utils.rand_name('securitygroup')
- # Create Security Group with empty string as description
- self.assertRaises(lib_exc.BadRequest,
- self.client.create_security_group, s_name, "")
- # Create Security Group with white space in description
- self.assertRaises(lib_exc.BadRequest,
- self.client.create_security_group, s_name, " ")
# Create Security Group with group description longer than 255 chars
s_description = 'description-'.ljust(260, '0')
self.assertRaises(lib_exc.BadRequest,
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 9012d3d..16b1597 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -100,7 +100,7 @@
def test_verify_created_server_vcpus(self):
# Verify that the number of vcpus reported by the instance matches
# the amount stated by the flavor
- flavor = self.flavors_client.get_flavor_details(self.flavor_ref)
+ flavor = self.flavors_client.show_flavor(self.flavor_ref)
linux_client = remote_client.RemoteClient(self.server, self.ssh_user,
self.password)
self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 5374af0..7a91cab 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -14,7 +14,6 @@
# under the License.
from tempest_lib.common.utils import data_utils
-from tempest_lib import decorators
from tempest_lib import exceptions as lib_exc
from tempest.api.compute import base
@@ -56,13 +55,13 @@
# Do some sanity checks here. If one of the images does
# not exist, fail early since the tests won't work...
try:
- cls.images_client.get_image(cls.image_ref)
+ cls.images_client.show_image(cls.image_ref)
except lib_exc.NotFound:
raise RuntimeError("Image %s (image_ref) was not found!" %
cls.image_ref)
try:
- cls.images_client.get_image(cls.image_ref_alt)
+ cls.images_client.show_image(cls.image_ref_alt)
except lib_exc.NotFound:
raise RuntimeError("Image %s (image_ref_alt) was not found!" %
cls.image_ref_alt)
@@ -275,8 +274,14 @@
msg = 'fixed_network_name needs to be configured to run this test'
raise self.skipException(msg)
self.s1 = self.client.get_server(self.s1['id'])
- ip = self.s1['addresses'][self.fixed_network_name][0]['addr']
- params = {'ip': ip}
+ for addr_spec in self.s1['addresses'][self.fixed_network_name]:
+ ip = addr_spec['addr']
+ if addr_spec['version'] == 4:
+ params = {'ip': ip}
+ break
+ else:
+ msg = "Skipped until bug 1450859 is resolved"
+ raise self.skipException(msg)
body = self.client.list_servers(params)
servers = body['servers']
@@ -284,8 +289,6 @@
self.assertNotIn(self.s2_name, map(lambda x: x['name'], servers))
self.assertNotIn(self.s3_name, map(lambda x: x['name'], servers))
- @decorators.skip_because(bug="1182883",
- condition=CONF.service_available.neutron)
@test.idempotent_id('a905e287-c35e-42f2-b132-d02b09f3654a')
def test_list_servers_filtered_by_ip_regex(self):
# Filter servers by regex ip
@@ -295,8 +298,12 @@
msg = 'fixed_network_name needs to be configured to run this test'
raise self.skipException(msg)
self.s1 = self.client.get_server(self.s1['id'])
- ip = self.s1['addresses'][self.fixed_network_name][0]['addr'][0:-3]
- params = {'ip': ip}
+ addr_spec = self.s1['addresses'][self.fixed_network_name][0]
+ ip = addr_spec['addr'][0:-3]
+ if addr_spec['version'] == 4:
+ params = {'ip': ip}
+ else:
+ params = {'ip6': ip}
body = self.client.list_servers(params)
servers = body['servers']
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index eed3be8..11b457b 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -30,7 +30,9 @@
This is the right way to create_multiple servers and manage to get the
created servers into the servers list to be cleaned up after all.
"""
- kwargs['name'] = kwargs.get('name', self._generate_name())
+ kwargs['name'] = name if name else self._generate_name()
+ if wait_until:
+ kwargs['wait_until'] = wait_until
body = self.create_test_server(**kwargs)
return body
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index c4cabaa..10b08a1 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -42,13 +42,15 @@
# Check if the server is in a clean state after test
try:
self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+ except lib_exc.NotFound:
+ # The server was deleted by previous test, create a new one
+ server = self.create_test_server(wait_until='ACTIVE')
+ self.__class__.server_id = server['id']
except Exception:
# Rebuild server if something happened to it during a test
self.__class__.server_id = self.rebuild_server(self.server_id)
def tearDown(self):
- server = self.client.get_server(self.server_id)
- self.assertEqual(self.image_ref, server['image']['id'])
self.server_check_teardown()
super(ServerActionsTestJSON, self).tearDown()
@@ -110,6 +112,14 @@
# The server should be signaled to reboot gracefully
self._test_reboot_server('SOFT')
+ def _rebuild_server_and_check(self, image_ref):
+ rebuilt_server = self.client.rebuild(self.server_id, image_ref)
+ self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+ msg = ('Server was not rebuilt to the original image. '
+ 'The original image: {0}. The current image: {1}'
+ .format(image_ref, rebuilt_server['image']['id']))
+ self.assertEqual(image_ref, rebuilt_server['image']['id'], msg)
+
@test.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
def test_rebuild_server(self):
# The server should be rebuilt using the provided image and data
@@ -129,8 +139,7 @@
# If the server was rebuilt on a different image, restore it to the
# original image once the test ends
if self.image_ref_alt != self.image_ref:
- self.addCleanup(self.client.rebuild,
- (self.server_id, self.image_ref))
+ self.addCleanup(self._rebuild_server_and_check, self.image_ref)
# Verify the properties in the initial response are correct
self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -157,11 +166,15 @@
# image and remain in SHUTOFF state
server = self.client.get_server(self.server_id)
old_image = server['image']['id']
- new_image = self.image_ref_alt \
- if old_image == self.image_ref else self.image_ref
+ new_image = (self.image_ref_alt
+ if old_image == self.image_ref else self.image_ref)
self.client.stop(self.server_id)
self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
rebuilt_server = self.client.rebuild(self.server_id, new_image)
+ # If the server was rebuilt on a different image, restore it to the
+ # original image once the test ends
+ if self.image_ref_alt != self.image_ref:
+ self.addCleanup(self._rebuild_server_and_check, old_image)
# Verify the properties in the initial response are correct
self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -175,10 +188,6 @@
rebuilt_image_id = server['image']['id']
self.assertEqual(new_image, rebuilt_image_id)
- # Restore to the original image (The tearDown will test it again)
- if self.image_ref_alt != self.image_ref:
- self.client.rebuild(self.server_id, old_image)
- self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
self.client.start(self.server_id)
def _test_resize_server_confirm(self, stop=False):
@@ -282,8 +291,9 @@
'backup_type': "daily",
'instance_uuid': self.server_id,
}
- image_list = self.os.image_client.image_list_detail(
- properties,
+ image_list = self.os.image_client.list_images(
+ detail=True,
+ properties=properties,
status='active',
sort_key='created_at',
sort_dir='asc')
@@ -305,8 +315,9 @@
self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
self.os.image_client.wait_for_resource_deletion(image1_id)
oldest_backup_exist = False
- image_list = self.os.image_client.image_list_detail(
- properties,
+ image_list = self.os.image_client.list_images(
+ detail=True,
+ properties=properties,
status='active',
sort_key='created_at',
sort_dir='asc')
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index b333122..31078e3 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -64,6 +64,7 @@
key_name = data_utils.rand_name('key')
self.keypairs_client.create_keypair(key_name)
+ self.addCleanup(self.keypairs_client.delete_keypair, key_name)
self.keypairs_client.list_keypairs()
server = self.create_test_server(key_name=key_name)
self.client.wait_for_server_status(server['id'], 'ACTIVE')
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 258ff1f..63b78a0 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -73,7 +73,7 @@
image_file = six.StringIO(('*' * 1024))
body = cls.glance_client.update_image(image_id, data=image_file)
cls.glance_client.wait_for_image_status(image_id, 'active')
- cls.image = cls.images_client.get_image(image_id)
+ cls.image = cls.images_client.show_image(image_id)
cls.keypairname = data_utils.rand_name('keypair')
cls.keypairs_client.create_keypair(cls.keypairname)
@@ -92,9 +92,11 @@
@classmethod
def resource_cleanup(cls):
- if cls.multi_user:
+ if hasattr(cls, 'image'):
cls.images_client.delete_image(cls.image['id'])
+ if hasattr(cls, 'keypairname'):
cls.keypairs_client.delete_keypair(cls.keypairname)
+ if hasattr(cls, 'security_group'):
cls.security_client.delete_security_group(cls.security_group['id'])
super(AuthorizationTestJSON, cls).resource_cleanup()
@@ -231,7 +233,7 @@
def test_get_image_for_alt_account_fails(self):
# A GET request for an image on another user's account should fail
self.assertRaises(lib_exc.NotFound,
- self.alt_images_client.get_image, self.image['id'])
+ self.alt_images_client.show_image, self.image['id'])
@test.idempotent_id('9facb962-f043-4a9d-b9ee-166a32dea098')
def test_delete_image_for_alt_account_fails(self):
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index bcac853..8eac80c 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -50,7 +50,7 @@
self.alt_email, enabled=False)
self.data.users.append(user)
self.assertEqual(name, user['name'])
- self.assertEqual('false', str(user['enabled']).lower())
+ self.assertEqual(False, user['enabled'])
self.assertEqual(self.alt_email, user['email'])
@test.idempotent_id('39d05857-e8a5-4ed4-ba83-0b52d3ab97ee')
@@ -71,13 +71,13 @@
enabled=False)
self.assertEqual(u_name2, update_user['name'])
self.assertEqual(u_email2, update_user['email'])
- self.assertEqual('false', str(update_user['enabled']).lower())
+ self.assertEqual(False, update_user['enabled'])
# GET by id after updating
updated_user = self.client.get_user(user['id'])
# Assert response body of GET after updating
self.assertEqual(u_name2, updated_user['name'])
self.assertEqual(u_email2, updated_user['email'])
- self.assertEqual('false', str(updated_user['enabled']).lower())
+ self.assertEqual(False, update_user['enabled'])
@test.idempotent_id('29ed26f4-a74e-4425-9a85-fdb49fa269d2')
def test_delete_user(self):
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 79943bb..b1a9d3b 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -13,10 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api.identity import base
+from tempest import config
+from tempest import test
+
from tempest_lib.common.utils import data_utils
-from tempest.api.identity import base
-from tempest import test
+CONF = config.CONF
class DomainsTestJSON(base.BaseIdentityV3AdminTest):
@@ -76,12 +79,12 @@
self.assertIsNotNone(updated_domain['id'])
self.assertEqual(new_name, updated_domain['name'])
self.assertEqual(new_desc, updated_domain['description'])
- self.assertEqual('true', str(updated_domain['enabled']).lower())
+ self.assertEqual(True, updated_domain['enabled'])
fetched_domain = self.client.get_domain(domain['id'])
self.assertEqual(new_name, fetched_domain['name'])
self.assertEqual(new_desc, fetched_domain['description'])
- self.assertEqual('true', str(fetched_domain['enabled']).lower())
+ self.assertEqual(True, fetched_domain['enabled'])
@test.idempotent_id('036df86e-bb5d-42c0-a7c2-66b9db3a6046')
def test_create_domain_with_disabled_status(self):
@@ -105,3 +108,18 @@
expected_data = {'name': d_name, 'enabled': True}
self.assertIsNone(domain['description'])
self.assertDictContainsSubset(expected_data, domain)
+
+
+class DefaultDomainTestJSON(base.BaseIdentityV3AdminTest):
+
+ @classmethod
+ def resource_setup(cls):
+ cls.domain_id = CONF.identity.default_domain_id
+ super(DefaultDomainTestJSON, cls).resource_setup()
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
+ def test_default_domain_exists(self):
+ domain = self.client.get_domain(self.domain_id)
+
+ self.assertTrue(domain['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
new file mode 100644
index 0000000..e2f3ef5
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -0,0 +1,38 @@
+# Copyright 2015 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 tempest.api.identity import base
+from tempest import test
+
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions as lib_exc
+
+
+class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+ _interface = 'json'
+
+ @test.attr(type=['negative', 'gate'])
+ @test.idempotent_id('1f3fbff5-4e44-400d-9ca1-d953f05f609b')
+ def test_delete_active_domain(self):
+ d_name = data_utils.rand_name('domain')
+ d_desc = data_utils.rand_name('domain-desc')
+ domain = self.client.create_domain(d_name, description=d_desc)
+ domain_id = domain['id']
+
+ self.addCleanup(self.delete_domain, domain_id)
+
+ # domain need to be disabled before deleting
+ self.assertRaises(lib_exc.Forbidden, self.client.delete_domain,
+ domain_id)
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 1672d47..9ca10a4 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -126,4 +126,4 @@
self.assertEqual(interface2, endpoint['interface'])
self.assertEqual(url2, endpoint['url'])
self.assertEqual(region2, endpoint['region'])
- self.assertEqual('false', str(endpoint['enabled']).lower())
+ self.assertEqual(False, endpoint['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 7fe369e..c2456c4 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -53,7 +53,7 @@
self.assertEqual(project['id'],
update_user['project_id'])
self.assertEqual(u_email2, update_user['email'])
- self.assertEqual('false', str(update_user['enabled']).lower())
+ self.assertEqual(False, update_user['enabled'])
# GET by id after updation
new_user_get = self.client.get_user(user['id'])
# Assert response body of GET after updation
@@ -62,7 +62,7 @@
self.assertEqual(project['id'],
new_user_get['project_id'])
self.assertEqual(u_email2, new_user_get['email'])
- self.assertEqual('false', str(new_user_get['enabled']).lower())
+ self.assertEqual(False, new_user_get['enabled'])
@test.idempotent_id('2d223a0e-e457-4a70-9fb1-febe027a0ff9')
def test_update_user_password(self):
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 5d66b9c..913e807 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -66,16 +66,9 @@
credentials = ['primary']
- @classmethod
- def setup_credentials(cls):
- super(BaseIdentityV2Test, cls).setup_credentials()
- cls.os = cls.get_client_manager(identity_version='v2')
-
- @classmethod
- def skip_checks(cls):
- super(BaseIdentityV2Test, cls).skip_checks()
- if not CONF.identity_feature_enabled.api_v2:
- raise cls.skipException("Identity api v2 is not enabled")
+ # identity v2 tests should obtain tokens and create accounts via v2
+ # regardless of the configured CONF.identity.auth_version
+ identity_version = 'v2'
@classmethod
def setup_clients(cls):
@@ -94,7 +87,7 @@
class BaseIdentityV2AdminTest(BaseIdentityV2Test):
- credentials = ['admin']
+ credentials = ['primary', 'admin']
@classmethod
def setup_clients(cls):
@@ -117,16 +110,9 @@
credentials = ['primary']
- @classmethod
- def setup_credentials(cls):
- super(BaseIdentityV3Test, cls).setup_credentials()
- cls.os = cls.get_client_manager(identity_version='v3')
-
- @classmethod
- def skip_checks(cls):
- super(BaseIdentityV3Test, cls).skip_checks()
- if not CONF.identity_feature_enabled.api_v3:
- raise cls.skipException("Identity api v3 is not enabled")
+ # identity v3 tests should obtain tokens and create accounts via v3
+ # regardless of the configured CONF.identity.auth_version
+ identity_version = 'v3'
@classmethod
def setup_clients(cls):
@@ -146,7 +132,7 @@
class BaseIdentityV3AdminTest(BaseIdentityV3Test):
- credentials = ['admin']
+ credentials = ['primary', 'admin']
@classmethod
def setup_clients(cls):
@@ -186,6 +172,12 @@
if len(role) > 0:
return role[0]
+ def delete_domain(self, domain_id):
+ # NOTE(mpavlase) It is necessary to disable the domain before deleting
+ # otherwise it raises Forbidden exception
+ self.client.update_domain(domain_id, enabled=False)
+ self.client.delete_domain(domain_id)
+
class DataGenerator(object):
diff --git a/tempest/api/identity/v2/test_api_discovery.py b/tempest/api/identity/v2/test_api_discovery.py
new file mode 100644
index 0000000..8132ee1
--- /dev/null
+++ b/tempest/api/identity/v2/test_api_discovery.py
@@ -0,0 +1,57 @@
+# Copyright 2015 OpenStack Foundation.
+# Copyright 2015, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.identity import base
+from tempest import test
+
+
+class TestApiDiscovery(base.BaseIdentityV2Test):
+ """Tests for API discovery features."""
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('ea889a68-a15f-4166-bfb1-c12456eae853')
+ def test_api_version_resources(self):
+ descr = self.non_admin_client.get_api_description()
+ expected_resources = ('id', 'links', 'media-types', 'status',
+ 'updated')
+
+ keys = descr.keys()
+ for res in expected_resources:
+ self.assertIn(res, keys)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('007a0be0-78fe-4fdb-bbee-e9216cc17bb2')
+ def test_api_media_types(self):
+ descr = self.non_admin_client.get_api_description()
+ # Get MIME type bases and descriptions
+ media_types = [(media_type['base'], media_type['type']) for
+ media_type in descr['media-types']]
+ # These are supported for API version 2
+ supported_types = [('application/json',
+ 'application/vnd.openstack.identity-v2.0+json')]
+
+ # Check if supported types exist in response body
+ for s_type in supported_types:
+ self.assertIn(s_type, media_types)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('77fd6be0-8801-48e6-b9bf-38cdd2f253ec')
+ def test_api_version_statuses(self):
+ descr = self.non_admin_client.get_api_description()
+ status = descr['status'].lower()
+ supported_statuses = ['current', 'stable', 'experimental',
+ 'supported', 'deprecated']
+
+ self.assertIn(status, supported_statuses)
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
new file mode 100644
index 0000000..2ec8ad8
--- /dev/null
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -0,0 +1,57 @@
+# Copyright 2015 OpenStack Foundation.
+# Copyright 2015, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.identity import base
+from tempest import test
+
+
+class TestApiDiscovery(base.BaseIdentityV3Test):
+ """Tests for API discovery features."""
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
+ def test_api_version_resources(self):
+ descr = self.non_admin_client.get_api_description()
+ expected_resources = ('id', 'links', 'media-types', 'status',
+ 'updated')
+
+ keys = descr.keys()
+ for res in expected_resources:
+ self.assertIn(res, keys)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('657c1970-4722-4189-8831-7325f3bc4265')
+ def test_api_media_types(self):
+ descr = self.non_admin_client.get_api_description()
+ # Get MIME type bases and descriptions
+ media_types = [(media_type['base'], media_type['type']) for
+ media_type in descr['media-types']]
+ # These are supported for API version 2
+ supported_types = [('application/json',
+ 'application/vnd.openstack.identity-v3+json')]
+
+ # Check if supported types exist in response body
+ for s_type in supported_types:
+ self.assertIn(s_type, media_types)
+
+ @test.attr(type='smoke')
+ @test.idempotent_id('8879a470-abfb-47bb-bb8d-5a7fd279ad1e')
+ def test_api_version_statuses(self):
+ descr = self.non_admin_client.get_api_description()
+ status = descr['status'].lower()
+ supported_statuses = ['current', 'stable', 'experimental',
+ 'supported', 'deprecated']
+
+ self.assertIn(status, supported_statuses)
diff --git a/tempest/api/image/admin/__init__.py b/tempest/api/image/admin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/image/admin/__init__.py
diff --git a/tempest/api/image/admin/v2/__init__.py b/tempest/api/image/admin/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/image/admin/v2/__init__.py
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
new file mode 100644
index 0000000..83efc7d
--- /dev/null
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -0,0 +1,55 @@
+# Copyright 2015 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 six import moves
+from tempest_lib.common.utils import data_utils
+import testtools
+
+from tempest.api.image import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class BasicAdminOperationsImagesTest(base.BaseV2ImageAdminTest):
+
+ """
+ Here we test admin operations of images
+ """
+ @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
+ 'deactivate-image is not available.')
+ @test.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
+ def test_admin_deactivate_reactivate_image(self):
+ # Create image by non-admin tenant
+ image_name = data_utils.rand_name('image')
+ body = self.client.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='private')
+ image_id = body['id']
+ self.addCleanup(self.client.delete_image, image_id)
+ # upload an image file
+ image_file = moves.cStringIO(data_utils.random_bytes())
+ self.client.store_image(image_id, image_file)
+ # deactivate image
+ self.admin_client.deactivate_image(image_id)
+ body = self.client.show_image(image_id)
+ self.assertEqual("deactivated", body['status'])
+ # reactivate image
+ self.admin_client.reactivate_image(image_id)
+ body = self.client.show_image(image_id)
+ self.assertEqual("active", body['status'])
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index acf8272..dc38cab 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -146,7 +146,7 @@
cls.alt_tenant_id = cls.alt_img_client.tenant_id
def _list_image_ids_as_alt(self):
- image_list = self.alt_img_client.image_list()
+ image_list = self.alt_img_client.list_images()
image_ids = map(lambda x: x['id'], image_list)
return image_ids
@@ -158,3 +158,23 @@
image_id = image['id']
self.addCleanup(self.os_img_client.delete_image, image_id)
return image_id
+
+
+class BaseV1ImageAdminTest(BaseImageTest):
+ credentials = ['admin', 'primary']
+
+ @classmethod
+ def setup_clients(cls):
+ super(BaseV1ImageAdminTest, cls).setup_clients()
+ cls.client = cls.os.image_client
+ cls.admin_client = cls.os_adm.image_client
+
+
+class BaseV2ImageAdminTest(BaseImageTest):
+ credentials = ['admin', 'primary']
+
+ @classmethod
+ def setup_clients(cls):
+ super(BaseV2ImageAdminTest, cls).setup_clients()
+ cls.client = cls.os.image_client_v2
+ cls.admin_client = cls.os_adm.image_client_v2
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index d57cb93..eb6969b 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -23,12 +23,12 @@
def test_add_image_member(self):
image = self._create_image()
self.client.add_member(self.alt_tenant_id, image)
- body = self.client.get_image_membership(image)
+ body = self.client.list_image_members(image)
members = body['members']
members = map(lambda x: x['member_id'], members)
self.assertIn(self.alt_tenant_id, members)
# get image as alt user
- self.alt_img_cli.get_image(image)
+ self.alt_img_cli.show_image(image)
@test.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
def test_get_shared_images(self):
@@ -36,7 +36,7 @@
self.client.add_member(self.alt_tenant_id, image)
share_image = self._create_image()
self.client.add_member(self.alt_tenant_id, share_image)
- body = self.client.get_shared_images(self.alt_tenant_id)
+ body = self.client.list_shared_images(self.alt_tenant_id)
images = body['shared_images']
images = map(lambda x: x['image_id'], images)
self.assertIn(share_image, images)
@@ -47,6 +47,6 @@
image_id = self._create_image()
self.client.add_member(self.alt_tenant_id, image_id)
self.client.delete_member(self.alt_tenant_id, image_id)
- body = self.client.get_image_membership(image_id)
+ body = self.client.list_image_members(image_id)
members = body['members']
self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index b96cf71..df0e88a 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -52,5 +52,5 @@
# Image is hidden from another tenants.
image_id = self._create_image()
self.assertRaises(lib_exc.NotFound,
- self.alt_img_cli.get_image,
+ self.alt_img_cli.show_image,
image_id)
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index ea95059..3f71fcb 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -77,7 +77,7 @@
self.assertEqual('New Http Image', body.get('name'))
self.assertFalse(body.get('is_public'))
self.client.wait_for_image_status(image_id, 'active')
- self.client.get_image(image_id)
+ self.client.show_image(image_id)
@test.idempotent_id('05b19d55-140c-40d0-b36b-fafd774d421b')
def test_register_image_with_min_ram(self):
@@ -168,14 +168,14 @@
@test.idempotent_id('246178ab-3b33-4212-9a4b-a7fe8261794d')
def test_index_no_params(self):
# Simple test to see all fixture images returned
- images_list = self.client.image_list()
+ images_list = self.client.list_images()
image_list = map(lambda x: x['id'], images_list)
for image_id in self.created_images:
self.assertIn(image_id, image_list)
@test.idempotent_id('f1755589-63d6-4468-b098-589820eb4031')
def test_index_disk_format(self):
- images_list = self.client.image_list(disk_format='ami')
+ images_list = self.client.list_images(disk_format='ami')
for image in images_list:
self.assertEqual(image['disk_format'], 'ami')
result_set = set(map(lambda x: x['id'], images_list))
@@ -184,7 +184,7 @@
@test.idempotent_id('2143655d-96d9-4bec-9188-8674206b4b3b')
def test_index_container_format(self):
- images_list = self.client.image_list(container_format='bare')
+ images_list = self.client.list_images(container_format='bare')
for image in images_list:
self.assertEqual(image['container_format'], 'bare')
result_set = set(map(lambda x: x['id'], images_list))
@@ -193,7 +193,7 @@
@test.idempotent_id('feb32ac6-22bb-4a16-afd8-9454bb714b14')
def test_index_max_size(self):
- images_list = self.client.image_list(size_max=42)
+ images_list = self.client.list_images(size_max=42)
for image in images_list:
self.assertTrue(image['size'] <= 42)
result_set = set(map(lambda x: x['id'], images_list))
@@ -202,7 +202,7 @@
@test.idempotent_id('6ffc16d0-4cbf-4401-95c8-4ac63eac34d8')
def test_index_min_size(self):
- images_list = self.client.image_list(size_min=142)
+ images_list = self.client.list_images(size_min=142)
for image in images_list:
self.assertTrue(image['size'] >= 142)
result_set = set(map(lambda x: x['id'], images_list))
@@ -211,9 +211,10 @@
@test.idempotent_id('e5dc26d9-9aa2-48dd-bda5-748e1445da98')
def test_index_status_active_detail(self):
- images_list = self.client.image_list_detail(status='active',
- sort_key='size',
- sort_dir='desc')
+ images_list = self.client.list_images(detail=True,
+ status='active',
+ sort_key='size',
+ sort_dir='desc')
top_size = images_list[0]['size'] # We have non-zero sized images
for image in images_list:
size = image['size']
@@ -223,7 +224,8 @@
@test.idempotent_id('097af10a-bae8-4342-bff4-edf89969ed2a')
def test_index_name(self):
- images_list = self.client.image_list_detail(
+ images_list = self.client.list_images(
+ detail=True,
name='New Remote Image dup')
result_set = set(map(lambda x: x['id'], images_list))
for image in images_list:
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 32f80a2..8cccddd 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -59,7 +59,7 @@
self.client.store_image(image_id, image_file)
# Now try to get image details
- body = self.client.get_image(image_id)
+ body = self.client.show_image(image_id)
self.assertEqual(image_id, body['id'])
self.assertEqual(image_name, body['name'])
self.assertEqual(uuid, body['ramdisk_id'])
@@ -88,7 +88,7 @@
self.client.wait_for_resource_deletion(image_id)
# Verifying deletion
- images = self.client.image_list()
+ images = self.client.list_images()
images_id = [item['id'] for item in images]
self.assertNotIn(image_id, images_id)
@@ -118,7 +118,7 @@
# Verifying updating
- body = self.client.get_image(image_id)
+ body = self.client.show_image(image_id)
self.assertEqual(image_id, body['id'])
self.assertEqual(new_image_name, body['name'])
@@ -164,7 +164,7 @@
"""
Perform list action with given params and validates result.
"""
- images_list = self.client.image_list(params=params)
+ images_list = self.client.list_images(params=params)
# Validating params of fetched images
for image in images_list:
for key in params:
@@ -174,7 +174,7 @@
@test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
def test_index_no_params(self):
# Simple test to see all fixture images returned
- images_list = self.client.image_list()
+ images_list = self.client.list_images()
image_list = map(lambda x: x['id'], images_list)
for image in self.created_images:
@@ -203,7 +203,7 @@
# Test to get all images by size
image_id = self.created_images[1]
# Get image metadata
- image = self.client.get_image(image_id)
+ image = self.client.show_image(image_id)
params = {"size": image['size']}
self._list_by_param_value_and_assert(params)
@@ -213,11 +213,11 @@
# Test to get all images with size between 2000 to 3000
image_id = self.created_images[1]
# Get image metadata
- image = self.client.get_image(image_id)
+ image = self.client.show_image(image_id)
size = image['size']
params = {"size_min": size - 500, "size_max": size + 500}
- images_list = self.client.image_list(params=params)
+ images_list = self.client.list_images(params=params)
image_size_list = map(lambda x: x['size'], images_list)
for image_size in image_size_list:
@@ -235,7 +235,7 @@
def test_list_images_param_limit(self):
# Test to get images by limit
params = {"limit": 2}
- images_list = self.client.image_list(params=params)
+ images_list = self.client.list_images(params=params)
self.assertEqual(len(images_list), params['limit'],
"Failed to get images by limit")
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 26d4197..eb6ffeb 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,17 +19,17 @@
@test.idempotent_id('5934c6ea-27dc-4d6e-9421-eeb5e045494a')
def test_image_share_accept(self):
image_id = self._create_image()
- member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ member = self.os_img_client.add_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')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
- self.alt_img_client.update_member_status(image_id,
- self.alt_tenant_id,
- 'accepted')
+ self.alt_img_client.update_image_member(image_id,
+ self.alt_tenant_id,
+ {'status': 'accepted'})
self.assertIn(image_id, self._list_image_ids_as_alt())
- body = self.os_img_client.get_image_membership(image_id)
+ body = self.os_img_client.list_image_members(image_id)
members = body['members']
member = members[0]
self.assertEqual(len(members), 1, str(members))
@@ -40,29 +40,29 @@
@test.idempotent_id('d9e83e5f-3524-4b38-a900-22abcb26e90e')
def test_image_share_reject(self):
image_id = self._create_image()
- member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ member = self.os_img_client.add_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')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
- self.alt_img_client.update_member_status(image_id,
- self.alt_tenant_id,
- 'rejected')
+ self.alt_img_client.update_image_member(image_id,
+ self.alt_tenant_id,
+ {'status': 'rejected'})
self.assertNotIn(image_id, self._list_image_ids_as_alt())
@test.idempotent_id('a6ee18b9-4378-465e-9ad9-9a6de58a3287')
def test_get_image_member(self):
image_id = self._create_image()
- self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
- self.alt_img_client.update_member_status(image_id,
- self.alt_tenant_id,
- 'accepted')
+ self.os_img_client.add_image_member(image_id,
+ self.alt_tenant_id)
+ self.alt_img_client.update_image_member(image_id,
+ self.alt_tenant_id,
+ {'status': 'accepted'})
self.assertIn(image_id, self._list_image_ids_as_alt())
- member = self.os_img_client.get_member(image_id,
- self.alt_tenant_id)
+ member = self.os_img_client.show_image_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(self.alt_tenant_id, member['member_id'])
self.assertEqual(image_id, member['image_id'])
self.assertEqual('accepted', member['status'])
@@ -70,14 +70,14 @@
@test.idempotent_id('72989bc7-2268-48ed-af22-8821e835c914')
def test_remove_image_member(self):
image_id = self._create_image()
- self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
- self.alt_img_client.update_member_status(image_id,
- self.alt_tenant_id,
- 'accepted')
+ self.os_img_client.add_image_member(image_id,
+ self.alt_tenant_id)
+ self.alt_img_client.update_image_member(image_id,
+ self.alt_tenant_id,
+ {'status': 'accepted'})
self.assertIn(image_id, self._list_image_ids_as_alt())
- self.os_img_client.remove_member(image_id, self.alt_tenant_id)
+ self.os_img_client.remove_image_member(image_id, self.alt_tenant_id)
self.assertNotIn(image_id, self._list_image_ids_as_alt())
@test.idempotent_id('634dcc3f-f6e2-4409-b8fd-354a0bb25d83')
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index d95e5c1..ae8913c 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -22,22 +22,23 @@
@test.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
def test_image_share_invalid_status(self):
image_id = self._create_image()
- member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ member = self.os_img_client.add_image_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertRaises(lib_exc.BadRequest,
- self.alt_img_client.update_member_status,
- image_id, self.alt_tenant_id, 'notavalidstatus')
+ self.alt_img_client.update_image_member,
+ image_id, self.alt_tenant_id,
+ {'status': 'notavalidstatus'})
@test.attr(type=['negative'])
@test.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
def test_image_share_owner_cannot_accept(self):
image_id = self._create_image()
- member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ member = self.os_img_client.add_image_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
self.assertRaises(lib_exc.Forbidden,
- self.os_img_client.update_member_status,
- image_id, self.alt_tenant_id, 'accepted')
+ self.os_img_client.update_image_member,
+ image_id, self.alt_tenant_id, {'status': 'accepted'})
self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 60a10a2..c5c5e8b 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -25,7 +25,7 @@
class ImagesNegativeTest(base.BaseV2ImageTest):
"""
- here we have -ve tests for get_image and delete_image api
+ here we have -ve tests for show_image and delete_image api
Tests
** get non-existent image
@@ -41,7 +41,7 @@
def test_get_non_existent_image(self):
# get the non-existent image
non_existent_id = str(uuid.uuid4())
- self.assertRaises(lib_exc.NotFound, self.client.get_image,
+ self.assertRaises(lib_exc.NotFound, self.client.show_image,
non_existent_id)
@test.attr(type=['negative'])
@@ -49,7 +49,7 @@
def test_get_image_null_id(self):
# get image with image_id = NULL
image_id = ""
- self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+ self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
@test.attr(type=['negative'])
@test.idempotent_id('e57fc127-7ba0-4693-92d7-1d8a05ebcba9')
@@ -64,7 +64,7 @@
self.client.wait_for_resource_deletion(image_id)
# get the deleted image
- self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+ self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
# delete the deleted image
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index 24688dc..e38136e 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -31,10 +31,10 @@
# Creating image tag and verify it.
self.client.add_image_tag(image_id, tag)
- body = self.client.get_image(image_id)
+ body = self.client.show_image(image_id)
self.assertIn(tag, body['tags'])
# Deleting image tag and verify it.
self.client.delete_image_tag(image_id, tag)
- body = self.client.get_image(image_id)
+ body = self.client.show_image(image_id)
self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/messaging/base.py b/tempest/api/messaging/base.py
index 2ddc3fc..34ac860 100644
--- a/tempest/api/messaging/base.py
+++ b/tempest/api/messaging/base.py
@@ -116,20 +116,6 @@
return resp, body
@classmethod
- def get_single_message(cls, message_uri):
- """Wrapper utility that gets a single message."""
- resp, body = cls.client.show_single_message(message_uri)
-
- return resp, body
-
- @classmethod
- def get_multiple_messages(cls, message_uri):
- """Wrapper utility that gets multiple messages."""
- resp, body = cls.client.show_multiple_messages(message_uri)
-
- return resp, body
-
- @classmethod
def delete_messages(cls, message_uri):
"""Wrapper utility that deletes messages."""
resp, body = cls.client.delete_messages(message_uri)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 1cd1386..f864f95 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -123,10 +123,6 @@
for floating_ip in cls.floating_ips:
cls._try_delete_resource(cls.client.delete_floatingip,
floating_ip['id'])
- # Clean up routers
- for router in cls.routers:
- cls._try_delete_resource(cls.delete_router,
- router)
# Clean up health monitors
for health_monitor in cls.health_monitors:
@@ -158,6 +154,10 @@
for port in cls.ports:
cls._try_delete_resource(cls.client.delete_port,
port['id'])
+ # Clean up routers
+ for router in cls.routers:
+ cls._try_delete_resource(cls.delete_router,
+ router)
# Clean up subnets
for subnet in cls.subnets:
cls._try_delete_resource(cls.client.delete_subnet,
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index ca08fbd..5d798e3 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -193,13 +193,10 @@
self.network, **kwargs_dhcp)
subnet_slaac = self.create_subnet(self.network, **kwargs)
port_mac = data_utils.rand_mac_address()
- dhcp_ip = subnet_dhcp["allocation_pools"][0]["start"]
eui_ip = data_utils.get_ipv6_addr_by_EUI64(
subnet_slaac['cidr'],
port_mac
).format()
- # TODO(sergsh): remove this when 1219795 is fixed
- dhcp_ip = [dhcp_ip, (netaddr.IPAddress(dhcp_ip) + 1).format()]
port = self.create_port(self.network, mac_address=port_mac)
real_ips = dict([(k['subnet_id'], k['ip_address'])
for k in port['fixed_ips']])
@@ -217,11 +214,10 @@
'Real IP is {0}, but shall be {1}'.format(
real_eui_ip,
eui_ip))
- self.assertIn(
- real_dhcp_ip, dhcp_ip,
- 'Real IP is {0}, but shall be one from {1}'.format(
- real_dhcp_ip,
- str(dhcp_ip)))
+ msg = ('Real IP address is {0} and it is NOT on '
+ 'subnet {1}'.format(real_dhcp_ip, subnet_dhcp['cidr']))
+ self.assertIn(netaddr.IPAddress(real_dhcp_ip),
+ netaddr.IPNetwork(subnet_dhcp['cidr']), msg)
@test.idempotent_id('4256c61d-c538-41ea-9147-3c450c36669e')
def test_dhcpv6_64_subnets(self):
@@ -246,13 +242,10 @@
self.network, ip_version=4)
subnet_slaac = self.create_subnet(self.network, **kwargs)
port_mac = data_utils.rand_mac_address()
- dhcp_ip = subnet_dhcp["allocation_pools"][0]["start"]
eui_ip = data_utils.get_ipv6_addr_by_EUI64(
subnet_slaac['cidr'],
port_mac
).format()
- # TODO(sergsh): remove this when 1219795 is fixed
- dhcp_ip = [dhcp_ip, (netaddr.IPAddress(dhcp_ip) + 1).format()]
port = self.create_port(self.network, mac_address=port_mac)
real_ips = dict([(k['subnet_id'], k['ip_address'])
for k in port['fixed_ips']])
@@ -260,23 +253,20 @@
for sub in [subnet_dhcp,
subnet_slaac]]
self._clean_network()
- self.assertTrue({real_eui_ip,
- real_dhcp_ip}.issubset([eui_ip] + dhcp_ip))
self.assertEqual(real_eui_ip,
eui_ip,
'Real IP is {0}, but shall be {1}'.format(
real_eui_ip,
eui_ip))
- self.assertIn(
- real_dhcp_ip, dhcp_ip,
- 'Real IP is {0}, but shall be one from {1}'.format(
- real_dhcp_ip,
- str(dhcp_ip)))
+ msg = ('Real IP address is {0} and it is NOT on '
+ 'subnet {1}'.format(real_dhcp_ip, subnet_dhcp['cidr']))
+ self.assertIn(netaddr.IPAddress(real_dhcp_ip),
+ netaddr.IPNetwork(subnet_dhcp['cidr']), msg)
@test.idempotent_id('4ab211a0-276f-4552-9070-51e27f58fecf')
def test_dhcp_stateful(self):
- """With all options below, DHCPv6 shall allocate first
- address from subnet pool to port.
+ """With all options below, DHCPv6 shall allocate address
+ from subnet pool to port.
"""
for ra_mode, add_mode in (
('dhcpv6-stateful', 'dhcpv6-stateful'),
@@ -289,15 +279,11 @@
subnet = self.create_subnet(self.network, **kwargs)
port = self.create_port(self.network)
port_ip = next(iter(port['fixed_ips']), None)['ip_address']
- dhcp_ip = subnet["allocation_pools"][0]["start"]
- # TODO(sergsh): remove this when 1219795 is fixed
- dhcp_ip = [dhcp_ip, (netaddr.IPAddress(dhcp_ip) + 1).format()]
self._clean_network()
- self.assertIn(
- port_ip, dhcp_ip,
- 'Real IP is {0}, but shall be one from {1}'.format(
- port_ip,
- str(dhcp_ip)))
+ msg = ('Real IP address is {0} and it is NOT on '
+ 'subnet {1}'.format(port_ip, subnet['cidr']))
+ self.assertIn(netaddr.IPAddress(port_ip),
+ netaddr.IPNetwork(subnet['cidr']), msg)
@test.idempotent_id('51a5e97f-f02e-4e4e-9a17-a69811d300e3')
def test_dhcp_stateful_fixedips(self):
diff --git a/tempest/api/network/test_fwaas_extensions.py b/tempest/api/network/test_fwaas_extensions.py
index 0622e87..651b4ab 100644
--- a/tempest/api/network/test_fwaas_extensions.py
+++ b/tempest/api/network/test_fwaas_extensions.py
@@ -99,8 +99,13 @@
if not test.call_until_true(_wait, CONF.network.build_timeout,
CONF.network.build_interval):
- m = ("Timed out waiting for firewall %s to reach %s state(s)" %
- (fw_id, target_states))
+ status = self.client.show_firewall(fw_id)['firewall']['status']
+ m = ("Timed out waiting for firewall %s to reach %s state(s) "
+ "after %ss, currently in %s state." %
+ (fw_id,
+ target_states,
+ CONF.network.build_interval,
+ status))
raise exceptions.TimeoutException(m)
@test.idempotent_id('1b84cf01-9c09-4ce7-bc72-b15e39076468')
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 5e3e374..7a96f34 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -393,7 +393,9 @@
" in filtered list (%s)." % nonexternal)
self.assertIn(CONF.network.public_network_id, networks)
- subnets_iter = (network['subnets'] for network in body['networks'])
+ subnets_iter = (network['subnets']
+ for network in body['networks']
+ if not network['shared'])
# subnets_iter is a list (iterator) of lists. This flattens it to a
# list of UUIDs
public_subnets_iter = itertools.chain(*subnets_iter)
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 7b6b25b..29600c5 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -150,6 +150,34 @@
if port['id'] == self.port['id']]
self.assertNotEmpty(ports, "Created port not found in the list")
+ @test.idempotent_id('e7fe260b-1e79-4dd3-86d9-bec6a7959fc5')
+ def test_port_list_filter_by_ip(self):
+ # Create network and subnet
+ network = self.create_network()
+ subnet = self.create_subnet(network)
+ self.addCleanup(self.client.delete_subnet, subnet['id'])
+ # Create two ports specifying a fixed_ips
+ address = self._get_ipaddress_from_tempest_conf()
+ _fixed_ip_1 = str(address + 3)
+ _fixed_ip_2 = str(address + 4)
+ fixed_ips_1 = [{'ip_address': _fixed_ip_1}]
+ port_1 = self.client.create_port(network_id=network['id'],
+ fixed_ips=fixed_ips_1)
+ self.addCleanup(self.client.delete_port, port_1['port']['id'])
+ fixed_ips_2 = [{'ip_address': _fixed_ip_2}]
+ port_2 = self.client.create_port(network_id=network['id'],
+ fixed_ips=fixed_ips_2)
+ self.addCleanup(self.client.delete_port, port_2['port']['id'])
+ # List ports filtered by fixed_ips
+ fixed_ips = 'ip_address=' + _fixed_ip_1
+ port_list = self.client.list_ports(fixed_ips=fixed_ips)
+ ports = port_list['ports']
+ self.assertEqual(len(ports), 1)
+ self.assertEqual(ports[0]['id'], port_1['port']['id'])
+ self.assertEqual(ports[0]['fixed_ips'][0]['ip_address'],
+ _fixed_ip_1)
+ self.assertEqual(ports[0]['network_id'], network['id'])
+
@test.idempotent_id('5ad01ed0-0e6e-4c5d-8194-232801b15c72')
def test_port_list_filter_by_router_id(self):
# Create a router
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 8bc9b12..41a7d65 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -15,8 +15,6 @@
from tempest_lib import exceptions as lib_exc
-from tempest import clients
-from tempest.common import credentials
from tempest.common import custom_matchers
from tempest import config
import tempest.test
@@ -26,6 +24,8 @@
class BaseObjectTest(tempest.test.BaseTestCase):
+ credentials = [['operator', CONF.object_storage.operator_role]]
+
@classmethod
def skip_checks(cls):
super(BaseObjectTest, cls).skip_checks()
@@ -37,20 +37,9 @@
def setup_credentials(cls):
cls.set_network_resources()
super(BaseObjectTest, cls).setup_credentials()
- operator_role = CONF.object_storage.operator_role
- # There are no credentials by type used by object storage tests so
- # isolated_creds must still be initialized
- cls.isolated_creds = credentials.get_isolated_credentials(
- cls.__name__, network_resources=cls.network_resources)
- if not cls.isolated_creds.is_role_available(operator_role):
- skip_msg = ("%s skipped because the configured credential provider"
- " is not able to provide credentials with the %s role "
- "assigned." % (cls.__name__, operator_role))
- raise cls.skipException(skip_msg)
- else:
- # Get isolated creds for normal user
- cls.os = clients.Manager(cls.isolated_creds.get_creds_by_roles(
- [operator_role]))
+ # credentials may be overwritten by children classes
+ if hasattr(cls, 'os_roles_operator'):
+ cls.os = cls.os_roles_operator
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 364d6e1..bbdf367 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -15,7 +15,6 @@
from tempest_lib.common.utils import data_utils
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -24,19 +23,14 @@
class AccountQuotasTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['reseller', CONF.object_storage.reseller_admin_role]]
+
@classmethod
def setup_credentials(cls):
super(AccountQuotasTest, cls).setup_credentials()
- reseller_admin_role = CONF.object_storage.reseller_admin_role
- if not cls.isolated_creds.is_role_available(reseller_admin_role):
- skip_msg = ("%s skipped because the configured credential provider"
- " is not able to provide credentials with the %s role "
- "assigned." % (cls.__name__, reseller_admin_role))
- raise cls.skipException(skip_msg)
- else:
- cls.os_reselleradmin = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[reseller_admin_role]))
+ cls.os = cls.os_roles_operator
+ cls.os_reselleradmin = cls.os_roles_reseller
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index b32943c..e945e1e 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -17,7 +17,6 @@
from tempest_lib import exceptions as lib_exc
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -26,19 +25,14 @@
class AccountQuotasNegativeTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['reseller', CONF.object_storage.reseller_admin_role]]
+
@classmethod
def setup_credentials(cls):
super(AccountQuotasNegativeTest, cls).setup_credentials()
- reseller_admin_role = CONF.object_storage.reseller_admin_role
- if not cls.isolated_creds.is_role_available(reseller_admin_role):
- skip_msg = ("%s skipped because the configured credential provider"
- " is not able to provide credentials with the %s role "
- "assigned." % (cls.__name__, reseller_admin_role))
- raise cls.skipException(skip_msg)
- else:
- cls.os_reselleradmin = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[reseller_admin_role]))
+ cls.os = cls.os_roles_operator
+ cls.os_reselleradmin = cls.os_roles_reseller
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 918bd5a..ac41148 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -20,7 +20,6 @@
import testtools
from tempest.api.object_storage import base
-from tempest import clients
from tempest.common import custom_matchers
from tempest import config
from tempest import test
@@ -30,14 +29,15 @@
class AccountTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
containers = []
@classmethod
def setup_credentials(cls):
super(AccountTest, cls).setup_credentials()
- cls.os_operator = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_operator = cls.os_roles_operator_alt
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index feccf18..998c2bd 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -15,7 +15,6 @@
from tempest_lib import exceptions as lib_exc
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -24,12 +23,14 @@
class AccountNegativeTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
@classmethod
def setup_credentials(cls):
super(AccountNegativeTest, cls).setup_credentials()
- cls.os_operator = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_operator = cls.os_roles_operator_alt
@test.attr(type=['negative'])
@test.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 1003f82..4df813d 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -16,7 +16,6 @@
from tempest_lib.common.utils import data_utils
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -25,12 +24,14 @@
class ObjectTestACLs(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
@classmethod
def setup_credentials(cls):
super(ObjectTestACLs, cls).setup_credentials()
- cls.os_operator = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_operator = cls.os_roles_operator_alt
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 2c6d3cc..1c42e97 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -16,7 +16,6 @@
from tempest_lib import exceptions as lib_exc
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -25,12 +24,14 @@
class ObjectACLsNegativeTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
@classmethod
def setup_credentials(cls):
super(ObjectACLsNegativeTest, cls).setup_credentials()
- cls.os_operator = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_operator = cls.os_roles_operator_alt
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 53bcfa6..06e700b 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -22,7 +22,6 @@
from tempest.api.object_storage import base
-from tempest import clients
from tempest import config
from tempest import test
@@ -38,11 +37,14 @@
class ContainerSyncTest(base.BaseObjectTest):
clients = {}
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
@classmethod
def setup_credentials(cls):
super(ContainerSyncTest, cls).setup_credentials()
- cls.os_alt = clients.Manager(cls.isolated_creds.get_creds_by_roles(
- [CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_alt = cls.os_roles_operator_alt
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 8075e91..627895e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -24,7 +24,6 @@
from tempest_lib.common.utils import data_utils
from tempest.api.object_storage import base
-from tempest import clients
from tempest.common import custom_matchers
from tempest import config
from tempest import test
@@ -990,12 +989,14 @@
class PublicObjectTest(base.BaseObjectTest):
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
@classmethod
def setup_credentials(cls):
super(PublicObjectTest, cls).setup_credentials()
- cls.os_alt = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- roles=[CONF.object_storage.operator_role], force_new=True))
+ cls.os = cls.os_roles_operator
+ cls.os_alt = cls.os_roles_operator_alt
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 7df0dde..6fc7821 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -110,6 +110,7 @@
self.assertHeaders(resp, 'Object', method)
@test.idempotent_id('2c3f24a6-36e8-4711-9aa2-800ee1fc7b5b')
+ @test.requires_ext(extension='slo', service='object')
def test_upload_manifest(self):
# create static large object from multipart manifest
manifest = self._create_manifest()
@@ -124,6 +125,7 @@
self._assertHeadersSLO(resp, 'PUT')
@test.idempotent_id('e69ad766-e1aa-44a2-bdd2-bf62c09c1456')
+ @test.requires_ext(extension='slo', service='object')
def test_list_large_object_metadata(self):
# list static large object metadata using multipart manifest
object_name = self._create_large_object()
@@ -135,6 +137,7 @@
self._assertHeadersSLO(resp, 'HEAD')
@test.idempotent_id('49bc49bc-dd1b-4c0f-904e-d9f10b830ee8')
+ @test.requires_ext(extension='slo', service='object')
def test_retrieve_large_object(self):
# list static large object using multipart manifest
object_name = self._create_large_object()
@@ -149,6 +152,7 @@
self.assertEqual(body, sum_data)
@test.idempotent_id('87b6dfa1-abe9-404d-8bf0-6c3751e6aa77')
+ @test.requires_ext(extension='slo', service='object')
def test_delete_large_object(self):
# delete static large object using multipart manifest
object_name = self._create_large_object()
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 35f51b9..af97794 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -17,7 +17,6 @@
from tempest_lib import exceptions as lib_exc
import yaml
-from tempest import clients
from tempest import config
import tempest.test
@@ -41,14 +40,7 @@
def setup_credentials(cls):
super(BaseOrchestrationTest, cls).setup_credentials()
stack_owner_role = CONF.orchestration.stack_owner_role
- if not cls.isolated_creds.is_role_available(stack_owner_role):
- skip_msg = ("%s skipped because the configured credential provider"
- " is not able to provide credentials with the %s role "
- "assigned." % (cls.__name__, stack_owner_role))
- raise cls.skipException(skip_msg)
- else:
- cls.os = clients.Manager(cls.isolated_creds.get_creds_by_roles(
- [stack_owner_role]))
+ cls.os = cls.get_client_manager(roles=[stack_owner_role])
@classmethod
def setup_clients(cls):
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index d4fd3f9..dadabfa 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -31,6 +31,14 @@
raise cls.skipException("Swift support is required")
@classmethod
+ def setup_credentials(cls):
+ super(SwiftResourcesTestJSON, cls).setup_credentials()
+ stack_owner_role = CONF.orchestration.stack_owner_role
+ operator_role = CONF.object_storage.operator_role
+ cls.os = cls.get_client_manager(
+ roles=[stack_owner_role, operator_role])
+
+ @classmethod
def setup_clients(cls):
super(SwiftResourcesTestJSON, cls).setup_clients()
cls.account_client = cls.os.account_client
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index b8e87f0..641317a 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from testtools import matchers
+
from tempest.api.volume import base
from tempest import test
@@ -50,12 +52,14 @@
metadata)
# Get the metadata of the snapshot
body = self.client.show_snapshot_metadata(self.snapshot_id)
- self.assertEqual(metadata, body)
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+
# Delete one item metadata of the snapshot
self.client.delete_snapshot_metadata_item(
self.snapshot_id, "key1")
body = self.client.show_snapshot_metadata(self.snapshot_id)
- self.assertEqual(expected, body)
+ 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):
@@ -70,7 +74,8 @@
metadata)
# Get the metadata of the snapshot
body = self.client.show_snapshot_metadata(self.snapshot_id)
- self.assertEqual(metadata, body)
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+
# Update metadata item
body = self.client.update_snapshot_metadata(
self.snapshot_id, update)
@@ -93,13 +98,13 @@
metadata)
# Get the metadata of the snapshot
body = self.client.show_snapshot_metadata(self.snapshot_id)
- self.assertEqual(metadata, body)
+ self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
# Update metadata item
body = self.client.update_snapshot_metadata_item(
self.snapshot_id, "key3", update_item)
# Get the metadata of the snapshot
body = self.client.show_snapshot_metadata(self.snapshot_id)
- self.assertEqual(expect, body)
+ self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
class SnapshotV1MetadataTestJSON(SnapshotV2MetadataTestJSON):
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index e89ff9a..8529cfc 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -75,7 +75,7 @@
self.volume_id, update)
# Get the metadata of the volume
body = self.volumes_client.show_volume_metadata(self.volume_id)
- self.assertThat(body.items(), matchers.ContainsAll(update.items()))
+ self.assertEqual(update, body)
@test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
def test_update_volume_metadata_item(self):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index f571f2d..375d34a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -18,6 +18,7 @@
from tempest.api.volume import base
from tempest import config
from tempest import test
+import testtools
CONF = config.CONF
@@ -69,6 +70,18 @@
self.client.detach_volume(self.volume['id'])
self.client.wait_for_volume_status(self.volume['id'], 'available')
+ @test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
+ @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
+ 'Update bootable status of a volume is not enabled.')
+ def test_volume_bootable(self):
+ # Verify that a volume bootable flag is retrieved
+ for bool_bootable in [True, False]:
+ self.client.set_bootable_volume(self.volume['id'], bool_bootable)
+ fetched_volume = self.client.show_volume(self.volume['id'])
+ # Get Volume information
+ bool_flag = self._is_true(fetched_volume['bootable'])
+ self.assertEqual(bool_bootable, bool_flag)
+
@test.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
@test.stresstest(class_setup_per='process')
@test.services('compute')
diff --git a/tempest/api_schema/response/compute/baremetal_nodes.py b/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
similarity index 100%
rename from tempest/api_schema/response/compute/baremetal_nodes.py
rename to tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
diff --git a/tempest/api_schema/response/compute/v2_1/flavors.py b/tempest/api_schema/response/compute/v2_1/flavors.py
index 725d17a..26760ac 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
list_flavors = {
'status_code': [200],
diff --git a/tempest/api_schema/response/compute/flavors_access.py b/tempest/api_schema/response/compute/v2_1/flavors_access.py
similarity index 100%
rename from tempest/api_schema/response/compute/flavors_access.py
rename to tempest/api_schema/response/compute/v2_1/flavors_access.py
diff --git a/tempest/api_schema/response/compute/flavors_extra_specs.py b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
similarity index 94%
rename from tempest/api_schema/response/compute/flavors_extra_specs.py
rename to tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
index 4003d36..faa25d0 100644
--- a/tempest/api_schema/response/compute/flavors_extra_specs.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-flavor_extra_specs = {
+set_get_flavor_extra_specs = {
'status_code': [200],
'response_body': {
'type': 'object',
@@ -28,7 +28,7 @@
}
}
-flavor_extra_specs_key = {
+set_get_flavor_extra_specs_key = {
'status_code': [200],
'response_body': {
'type': 'object',
diff --git a/tempest/api_schema/response/compute/v2_1/floating_ips.py b/tempest/api_schema/response/compute/v2_1/floating_ips.py
index 7369bec..ad1c531 100644
--- a/tempest/api_schema/response/compute/v2_1/floating_ips.py
+++ b/tempest/api_schema/response/compute/v2_1/floating_ips.py
@@ -48,7 +48,7 @@
}
}
-floating_ip = {
+create_get_floating_ip = {
'status_code': [200],
'response_body': {
'type': 'object',
@@ -59,7 +59,7 @@
}
}
-floating_ip_pools = {
+list_floating_ip_pools = {
'status_code': [200],
'response_body': {
'type': 'object',
diff --git a/tempest/api_schema/response/compute/v2_1/images.py b/tempest/api_schema/response/compute/v2_1/images.py
index 3c0b80e..e6f8db6 100644
--- a/tempest/api_schema/response/compute/v2_1/images.py
+++ b/tempest/api_schema/response/compute/v2_1/images.py
@@ -14,7 +14,7 @@
import copy
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
image_links = copy.deepcopy(parameter_types.links)
image_links['items']['properties'].update({'type': {'type': 'string'}})
diff --git a/tempest/api_schema/response/compute/v2_1/interfaces.py b/tempest/api_schema/response/compute/v2_1/interfaces.py
index 4de3309..033f816 100644
--- a/tempest/api_schema/response/compute/v2_1/interfaces.py
+++ b/tempest/api_schema/response/compute/v2_1/interfaces.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
interface_common_info = {
'type': 'object',
diff --git a/tempest/api_schema/response/compute/migrations.py b/tempest/api_schema/response/compute/v2_1/migrations.py
similarity index 100%
rename from tempest/api_schema/response/compute/migrations.py
rename to tempest/api_schema/response/compute/v2_1/migrations.py
diff --git a/tempest/api_schema/response/compute/parameter_types.py b/tempest/api_schema/response/compute/v2_1/parameter_types.py
similarity index 100%
rename from tempest/api_schema/response/compute/parameter_types.py
rename to tempest/api_schema/response/compute/v2_1/parameter_types.py
diff --git a/tempest/api_schema/response/compute/v2_1/servers.py b/tempest/api_schema/response/compute/v2_1/servers.py
index 726f9b1..875f607 100644
--- a/tempest/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/api_schema/response/compute/v2_1/servers.py
@@ -14,7 +14,7 @@
import copy
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
create_server = {
'status_code': [202],
diff --git a/tempest/api_schema/response/compute/services.py b/tempest/api_schema/response/compute/v2_1/services.py
similarity index 100%
rename from tempest/api_schema/response/compute/services.py
rename to tempest/api_schema/response/compute/v2_1/services.py
diff --git a/tempest/api_schema/response/compute/v2_1/tenant_usages.py b/tempest/api_schema/response/compute/v2_1/tenant_usages.py
index 0b824a1..d51ef12 100644
--- a/tempest/api_schema/response/compute/v2_1/tenant_usages.py
+++ b/tempest/api_schema/response/compute/v2_1/tenant_usages.py
@@ -66,7 +66,7 @@
'total_hours', 'total_local_gb_usage',
'total_memory_mb_usage', 'total_vcpus_usage']
-list_tenant = {
+list_tenant_usage = {
'status_code': [200],
'response_body': {
'type': 'object',
@@ -80,7 +80,7 @@
}
}
-get_tenant = {
+get_tenant_usage = {
'status_code': [200],
'response_body': {
'type': 'object',
diff --git a/tempest/api_schema/response/compute/version.py b/tempest/api_schema/response/compute/v2_1/version.py
similarity index 100%
rename from tempest/api_schema/response/compute/version.py
rename to tempest/api_schema/response/compute/v2_1/version.py
diff --git a/tempest/clients.py b/tempest/clients.py
index a0c9471..9f6a9bb 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -22,6 +22,7 @@
from tempest.common import cred_provider
from tempest.common import negative_rest_client
from tempest import config
+from tempest import exceptions
from tempest import manager
from tempest.services.baremetal.v1.json.baremetal_client import \
BaremetalClientJSON
@@ -343,11 +344,22 @@
self.credentials_client = CredentialsClientJSON(self.auth_provider,
**params)
# Token clients do not use the catalog. They only need default_params.
- self.token_client = TokenClientJSON(CONF.identity.uri,
- **self.default_params)
+ # They read auth_url, so they should only be set if the corresponding
+ # API version is marked as enabled
+ if CONF.identity_feature_enabled.api_v2:
+ if CONF.identity.uri:
+ self.token_client = TokenClientJSON(
+ CONF.identity.uri, **self.default_params)
+ else:
+ msg = 'Identity v2 API enabled, but no identity.uri set'
+ raise exceptions.InvalidConfiguration(msg)
if CONF.identity_feature_enabled.api_v3:
- self.token_v3_client = V3TokenClientJSON(CONF.identity.uri_v3,
- **self.default_params)
+ if CONF.identity.uri_v3:
+ self.token_v3_client = V3TokenClientJSON(
+ CONF.identity.uri_v3, **self.default_params)
+ else:
+ msg = 'Identity v3 API enabled, but no identity.uri_v3 set'
+ raise exceptions.InvalidConfiguration(msg)
def _set_volume_clients(self):
params = {
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
new file mode 100755
index 0000000..892e051
--- /dev/null
+++ b/tempest/cmd/account_generator.py
@@ -0,0 +1,408 @@
+#!/usr/bin/env python
+
+# Copyright 2015 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Utility for creating **accounts.yaml** file for concurrent test runs.
+Creates one primary user, one alt user, one swift admin, one stack owner
+and one admin (optionally) for each concurrent thread. The utility creates
+user for each tenant. The **accounts.yaml** file will be valid and contain
+credentials for created users, so each user will be in separate tenant and
+have the username, tenant_name, password and roles.
+
+**Usage:** ``tempest-account-generator [-h] [OPTIONS] accounts_file.yaml``.
+
+Positional Arguments
+-----------------
+**accounts_file.yaml** (Required) Provide an output accounts yaml file. Utility
+creates a .yaml file in the directory where the command is ran. The appropriate
+name for the file is *accounts.yaml* and it should be placed in *tempest/etc*
+directory.
+
+Authentication
+--------------
+
+Account generator creates users and tenants so it needs the admin credentials
+of your cloud to operate properly. The corresponding info can be given either
+through CLI options or environment variables.
+
+You're probably familiar with these, but just to remind::
+
+ +----------+------------------+----------------------+
+ | Param | CLI | Environment Variable |
+ +----------+------------------+----------------------+
+ | Username | --os-username | OS_USERNAME |
+ | Password | --os-password | OS_PASSWORD |
+ | Tenant | --os-tenant-name | OS_TENANT_NAME |
+ +----------+------------------+----------------------+
+
+Optional Arguments
+-----------------
+**-h**, **--help** (Optional) Shows help message with the description of
+utility and its arguments, and exits.
+
+**c /etc/tempest.conf**, **--config-file /etc/tempest.conf** (Optional) Path to
+tempest config file.
+
+**--os-username <auth-user-name>** (Optional) Name used for authentication with
+the OpenStack Identity service. Defaults to env[OS_USERNAME]. Note: User should
+have permissions to create new user accounts and tenants.
+
+**--os-password <auth-password>** (Optional) Password used for authentication
+with the OpenStack Identity service. Defaults to env[OS_PASSWORD].
+
+**--os-tenant-name <auth-tenant-name>** (Optional) Tenant to request
+authorization on. Defaults to env[OS_TENANT_NAME].
+
+**--tag TAG** (Optional) Resources tag. Each created resource (user, project)
+will have the prefix with the given TAG in its name. Using tag is recommended
+for the further using, cleaning resources.
+
+**-r CONCURRENCY**, **--concurrency CONCURRENCY** (Required) Concurrency count
+(default: 1). The number of accounts required can be estimated as
+CONCURRENCY x 2. Each user provided in *accounts.yaml* file will be in
+a different tenant. This is required to provide isolation between test for
+running in parallel.
+
+**--with-admin** (Optional) Creates admin for each concurrent group
+(default: False).
+
+To see help on specific argument, please do: ``tempest-account-generator
+[OPTIONS] <accounts_file.yaml> -h``.
+"""
+import argparse
+import netaddr
+import os
+
+from oslo_log import log as logging
+import yaml
+
+from tempest import config
+from tempest import exceptions
+from tempest.services.identity.v2.json import identity_client
+from tempest.services.network.json import network_client
+import tempest_lib.auth
+from tempest_lib.common.utils import data_utils
+import tempest_lib.exceptions
+
+LOG = None
+CONF = config.CONF
+
+
+def setup_logging():
+ global LOG
+ logging.setup(CONF, __name__)
+ LOG = logging.getLogger(__name__)
+
+
+def get_admin_clients(opts):
+ _creds = tempest_lib.auth.KeystoneV2Credentials(
+ username=opts.os_username,
+ password=opts.os_password,
+ tenant_name=opts.os_tenant_name)
+ auth_params = {
+ 'disable_ssl_certificate_validation':
+ CONF.identity.disable_ssl_certificate_validation,
+ 'ca_certs': CONF.identity.ca_certificates_file,
+ 'trace_requests': CONF.debug.trace_requests
+ }
+ _auth = tempest_lib.auth.KeystoneV2AuthProvider(
+ _creds, CONF.identity.uri, **auth_params)
+ params = {
+ 'disable_ssl_certificate_validation':
+ CONF.identity.disable_ssl_certificate_validation,
+ 'ca_certs': CONF.identity.ca_certificates_file,
+ 'trace_requests': CONF.debug.trace_requests,
+ 'build_interval': CONF.compute.build_interval,
+ 'build_timeout': CONF.compute.build_timeout
+ }
+ identity_admin = identity_client.IdentityClientJSON(
+ _auth,
+ CONF.identity.catalog_type,
+ CONF.identity.region,
+ endpoint_type='adminURL',
+ **params
+ )
+ network_admin = None
+ if (CONF.service_available.neutron and
+ CONF.auth.create_isolated_networks):
+ network_admin = network_client.NetworkClientJSON(
+ _auth,
+ CONF.network.catalog_type,
+ CONF.network.region or CONF.identity.region,
+ endpoint_type='adminURL',
+ **params)
+ return identity_admin, network_admin
+
+
+def create_resources(opts, resources):
+ identity_admin, network_admin = get_admin_clients(opts)
+ roles = identity_admin.list_roles()
+ for u in resources['users']:
+ u['role_ids'] = []
+ for r in u.get('roles', ()):
+ try:
+ role = filter(lambda r_: r_['name'] == r, roles)[0]
+ u['role_ids'] += [role['id']]
+ except IndexError:
+ raise exceptions.TempestException(
+ "Role: %s - doesn't exist" % r
+ )
+ existing = [x['name'] for x in identity_admin.list_tenants()]
+ for tenant in resources['tenants']:
+ if tenant not in existing:
+ identity_admin.create_tenant(tenant)
+ else:
+ LOG.warn("Tenant '%s' already exists in this environment" % tenant)
+ LOG.info('Tenants created')
+ for u in resources['users']:
+ try:
+ tenant = identity_admin.get_tenant_by_name(u['tenant'])
+ except tempest_lib.exceptions.NotFound:
+ LOG.error("Tenant: %s - not found" % u['tenant'])
+ continue
+ while True:
+ try:
+ identity_admin.get_user_by_username(tenant['id'], u['name'])
+ except tempest_lib.exceptions.NotFound:
+ identity_admin.create_user(
+ u['name'], u['pass'], tenant['id'],
+ "%s@%s" % (u['name'], tenant['id']),
+ enabled=True)
+ break
+ else:
+ LOG.warn("User '%s' already exists in this environment. "
+ "New name generated" % u['name'])
+ u['name'] = random_user_name(opts.tag, u['prefix'])
+
+ LOG.info('Users created')
+ if network_admin:
+ for u in resources['users']:
+ tenant = identity_admin.get_tenant_by_name(u['tenant'])
+ network_name = create_network_resources(network_admin,
+ tenant['id'],
+ u['name'])
+ u['network'] = network_name
+ LOG.info('Networks created')
+ for u in resources['users']:
+ try:
+ tenant = identity_admin.get_tenant_by_name(u['tenant'])
+ except tempest_lib.exceptions.NotFound:
+ LOG.error("Tenant: %s - not found" % u['tenant'])
+ continue
+ try:
+ user = identity_admin.get_user_by_username(tenant['id'],
+ u['name'])
+ except tempest_lib.exceptions.NotFound:
+ LOG.error("User: %s - not found" % u['user'])
+ continue
+ for r in u['role_ids']:
+ try:
+ identity_admin.assign_user_role(tenant['id'], user['id'], r)
+ except tempest_lib.exceptions.Conflict:
+ # don't care if it's already assigned
+ pass
+ LOG.info('Roles assigned')
+ LOG.info('Resources deployed successfully!')
+
+
+def create_network_resources(network_admin_client, tenant_id, name):
+
+ def _create_network(name):
+ resp_body = network_admin_client.create_network(
+ name=name, tenant_id=tenant_id)
+ return resp_body['network']
+
+ def _create_subnet(subnet_name, network_id):
+ base_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
+ mask_bits = CONF.network.tenant_network_mask_bits
+ for subnet_cidr in base_cidr.subnet(mask_bits):
+ try:
+ resp_body = network_admin_client.\
+ create_subnet(
+ network_id=network_id, cidr=str(subnet_cidr),
+ name=subnet_name,
+ tenant_id=tenant_id,
+ enable_dhcp=True,
+ ip_version=4)
+ break
+ except tempest_lib.exceptions.BadRequest as e:
+ if 'overlaps with another subnet' not in str(e):
+ raise
+ else:
+ message = 'Available CIDR for subnet creation could not be found'
+ raise Exception(message)
+ return resp_body['subnet']
+
+ def _create_router(router_name):
+ external_net_id = dict(
+ network_id=CONF.network.public_network_id)
+ resp_body = network_admin_client.create_router(
+ router_name,
+ external_gateway_info=external_net_id,
+ tenant_id=tenant_id)
+ return resp_body['router']
+
+ def _add_router_interface(router_id, subnet_id):
+ network_admin_client.add_router_interface_with_subnet_id(
+ router_id, subnet_id)
+
+ network_name = name + "-network"
+ network = _create_network(network_name)
+ subnet_name = name + "-subnet"
+ subnet = _create_subnet(subnet_name, network['id'])
+ router_name = name + "-router"
+ router = _create_router(router_name)
+ _add_router_interface(router['id'], subnet['id'])
+ return network_name
+
+
+def random_user_name(tag, prefix):
+ if tag:
+ return data_utils.rand_name('-'.join((tag, prefix)))
+ else:
+ return data_utils.rand_name(prefix)
+
+
+def generate_resources(opts):
+ spec = [{'number': 1,
+ 'prefix': 'primary',
+ 'roles': (CONF.auth.tempest_roles +
+ [CONF.object_storage.operator_role])},
+ {'number': 1,
+ 'prefix': 'alt',
+ 'roles': (CONF.auth.tempest_roles +
+ [CONF.object_storage.operator_role])},
+ {'number': 1,
+ 'prefix': 'swift_admin',
+ 'roles': (CONF.auth.tempest_roles +
+ [CONF.object_storage.operator_role,
+ CONF.object_storage.reseller_admin_role])},
+ {'number': 1,
+ 'prefix': 'stack_owner',
+ 'roles': (CONF.auth.tempest_roles +
+ [CONF.orchestration.stack_owner_role])},
+ ]
+ if opts.admin:
+ spec.append({
+ 'number': 1,
+ 'prefix': 'admin',
+ 'roles': (CONF.auth.tempest_roles +
+ [CONF.identity.admin_role])
+ })
+ resources = {'tenants': [],
+ 'users': []}
+ for count in range(opts.concurrency):
+ for user_group in spec:
+ users = [random_user_name(opts.tag, user_group['prefix'])
+ for _ in range(user_group['number'])]
+ for user in users:
+ tenant = '-'.join((user, 'tenant'))
+ resources['tenants'].append(tenant)
+ resources['users'].append({
+ 'tenant': tenant,
+ 'name': user,
+ 'pass': data_utils.rand_name(),
+ 'prefix': user_group['prefix'],
+ 'roles': user_group['roles']
+ })
+ return resources
+
+
+def dump_accounts(opts, resources):
+ accounts = []
+ for user in resources['users']:
+ account = {
+ 'username': user['name'],
+ 'tenant_name': user['tenant'],
+ 'password': user['pass'],
+ 'roles': user['roles']
+ }
+ if 'network' in user:
+ account['resources'] = {'network': user['network']}
+ accounts.append(account)
+ if os.path.exists(opts.accounts):
+ os.rename(opts.accounts, '.'.join((opts.accounts, 'bak')))
+ with open(opts.accounts, 'w') as f:
+ yaml.dump(accounts, f, default_flow_style=False)
+ LOG.info('%s generated successfully!' % opts.accounts)
+
+
+def get_options():
+ usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
+ 'To see help on specific argument, do:\n'
+ 'tempest-account-generator <ARG> -h')
+ parser = argparse.ArgumentParser(
+ description='Create accounts.yaml file for concurrent test runs. '
+ 'One primary user, one alt user, '
+ 'one swift admin, one stack owner '
+ 'and one admin (optionally) will be created '
+ 'for each concurrent thread.',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ usage=usage_string
+ )
+
+ parser.add_argument('-c', '--config-file',
+ metavar='/etc/tempest.conf',
+ help='path to tempest config file')
+ parser.add_argument('--os-username',
+ metavar='<auth-user-name>',
+ default=os.environ.get('OS_USERNAME'),
+ help='User should have permissions '
+ 'to create new user accounts and '
+ 'tenants. Defaults to env[OS_USERNAME].')
+ parser.add_argument('--os-password',
+ metavar='<auth-password>',
+ default=os.environ.get('OS_PASSWORD'),
+ help='Defaults to env[OS_PASSWORD].')
+ parser.add_argument('--os-tenant-name',
+ metavar='<auth-tenant-name>',
+ default=os.environ.get('OS_TENANT_NAME'),
+ help='Defaults to env[OS_TENANT_NAME].')
+ parser.add_argument('--tag',
+ default='',
+ required=False,
+ dest='tag',
+ help='Resources tag')
+ parser.add_argument('-r', '--concurrency',
+ default=1,
+ type=int,
+ required=True,
+ dest='concurrency',
+ help='Concurrency count')
+ parser.add_argument('--with-admin',
+ action='store_true',
+ dest='admin',
+ help='Creates admin for each concurrent group')
+ parser.add_argument('accounts',
+ metavar='accounts_file.yaml',
+ help='Output accounts yaml file')
+
+ opts = parser.parse_args()
+ if opts.config_file:
+ config.CONF.set_config_path(opts.config_file)
+ return opts
+
+
+def main(opts=None):
+ if not opts:
+ opts = get_options()
+ setup_logging()
+ resources = generate_resources(opts)
+ create_resources(opts, resources)
+ dump_accounts(opts, resources)
+
+if __name__ == "__main__":
+ main()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 1ad12eb..eb6f143 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -343,6 +343,44 @@
self.data['volumes'] = vols
+class VolumeQuotaService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(VolumeQuotaService, self).__init__(kwargs)
+ self.client = manager.volume_quotas_client
+
+ def delete(self):
+ client = self.client
+ try:
+ client.delete_quota_set(self.tenant_id)
+ except Exception as e:
+ LOG.exception("Delete Volume Quotas exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ quotas = self.client.show_quota_usage(self.tenant_id)
+ self.data['volume_quotas'] = quotas
+
+
+class NovaQuotaService(BaseService):
+ def __init__(self, manager, **kwargs):
+ super(NovaQuotaService, self).__init__(kwargs)
+ self.client = manager.quotas_client
+ self.limits_client = manager.limits_client
+
+ def delete(self):
+ client = self.client
+ try:
+ client.delete_quota_set(self.tenant_id)
+ except Exception as e:
+ LOG.exception("Delete Quotas exception: %s" % e)
+ pass
+
+ def dry_run(self):
+ client = self.limits_client
+ quotas = client.get_absolute_limits()
+ self.data['compute_quotas'] = quotas
+
+
# Begin network service classes
class NetworkService(BaseService):
def __init__(self, manager, **kwargs):
@@ -667,7 +705,7 @@
self.data['pools'] = pools
-class NetworMeteringLabelRuleService(NetworkService):
+class NetworkMeteringLabelRuleService(NetworkService):
def list(self):
client = self.client
@@ -692,7 +730,7 @@
self.data['rules'] = rules
-class NetworMeteringLabelService(NetworkService):
+class NetworkMeteringLabelService(NetworkService):
def list(self):
client = self.client
@@ -1052,6 +1090,7 @@
tenant_services.append(ServerGroupService)
if not IS_NEUTRON:
tenant_services.append(FloatingIpService)
+ tenant_services.append(NovaQuotaService)
if IS_HEAT:
tenant_services.append(StackService)
if IS_NEUTRON:
@@ -1068,8 +1107,8 @@
tenant_services.append(NetworkVipService)
tenant_services.append(NetworkPoolService)
if test.is_extension_enabled('metering', 'network'):
- tenant_services.append(NetworMeteringLabelRuleService)
- tenant_services.append(NetworMeteringLabelService)
+ tenant_services.append(NetworkMeteringLabelRuleService)
+ tenant_services.append(NetworkMeteringLabelService)
tenant_services.append(NetworkRouterService)
tenant_services.append(NetworkFloatingIpService)
tenant_services.append(NetworkPortService)
@@ -1078,6 +1117,7 @@
if IS_CINDER:
tenant_services.append(SnapshotService)
tenant_services.append(VolumeService)
+ tenant_services.append(VolumeQuotaService)
return tenant_services
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 4e2af76..d3426c6 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -617,7 +617,7 @@
def _get_image_by_name(client, name):
- body = client.images.image_list()
+ body = client.images.list_images()
for image in body:
if name == image['name']:
return image
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 70a8594..2269d41 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -57,14 +57,17 @@
change_option(option, group, value)
+def contains_version(prefix, versions):
+ return any([x for x in versions if x.startswith(prefix)])
+
+
def verify_glance_api_versions(os, update):
# Check glance api versions
- versions = os.image_client.get_versions()
- if CONF.image_feature_enabled.api_v1 != ('v1.1' in versions or 'v1.0' in
- versions):
+ _, versions = os.image_client.get_versions()
+ if CONF.image_feature_enabled.api_v1 != contains_version('v1.', versions):
print_and_or_update('api_v1', 'image_feature_enabled',
not CONF.image_feature_enabled.api_v1, update)
- if CONF.image_feature_enabled.api_v2 != ('v2.0' in versions):
+ if CONF.image_feature_enabled.api_v2 != contains_version('v2.', versions):
print_and_or_update('api_v2', 'image_feature_enabled',
not CONF.image_feature_enabled.api_v2, update)
@@ -94,16 +97,18 @@
versions = map(lambda x: x['id'], body['versions']['values'])
else:
versions = map(lambda x: x['id'], body['versions'])
- return versions
+ return list(versions)
def verify_keystone_api_versions(os, update):
# Check keystone api versions
versions = _get_api_versions(os, 'keystone')
- if CONF.identity_feature_enabled.api_v2 != ('v2.0' in versions):
+ if (CONF.identity_feature_enabled.api_v2 !=
+ contains_version('v2.', versions)):
print_and_or_update('api_v2', 'identity_feature_enabled',
not CONF.identity_feature_enabled.api_v2, update)
- if CONF.identity_feature_enabled.api_v3 != ('v3.0' in versions):
+ if (CONF.identity_feature_enabled.api_v3 !=
+ contains_version('v3.', versions)):
print_and_or_update('api_v3', 'identity_feature_enabled',
not CONF.identity_feature_enabled.api_v3, update)
@@ -111,10 +116,12 @@
def verify_cinder_api_versions(os, update):
# Check cinder api versions
versions = _get_api_versions(os, 'cinder')
- if CONF.volume_feature_enabled.api_v1 != ('v1.0' in versions):
+ if (CONF.volume_feature_enabled.api_v1 !=
+ contains_version('v1.', versions)):
print_and_or_update('api_v1', 'volume_feature_enabled',
not CONF.volume_feature_enabled.api_v1, update)
- if CONF.volume_feature_enabled.api_v2 != ('v2.0' in versions):
+ if (CONF.volume_feature_enabled.api_v2 !=
+ contains_version('v2.', versions)):
print_and_or_update('api_v2', 'volume_feature_enabled',
not CONF.volume_feature_enabled.api_v2, update)
@@ -175,6 +182,7 @@
else:
extensions = map(lambda x: x['alias'], resp)
+ extensions = list(extensions)
if not results.get(service):
results[service] = {}
extensions_opt = get_enabled_extensions(service)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index fee1467..5ded3ee 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -311,7 +311,8 @@
LOG.info("Acquired isolated creds:\n credentials: %s"
% credentials)
if (CONF.service_available.neutron and
- not CONF.baremetal.driver_enabled):
+ not CONF.baremetal.driver_enabled and
+ CONF.auth.create_isolated_networks):
network, subnet, router = self._create_network_resources(
credentials.tenant_id)
credentials.set_resources(network=network, subnet=subnet,
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index bedff1f..4b3995b 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -46,6 +46,9 @@
channel_timeout=ssh_channel_timeout)
def exec_command(self, cmd):
+ # Shell options below add more clearness on failures,
+ # path is extended for some non-cirros guest oses (centos7)
+ cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
return self.ssh_client.exec_command(cmd)
def validate_authentication(self):
@@ -95,26 +98,26 @@
return self.exec_command(cmd)
def get_mac_address(self):
- cmd = "/bin/ip addr | awk '/ether/ {print $2}'"
+ cmd = "ip addr | awk '/ether/ {print $2}'"
return self.exec_command(cmd)
def get_nic_name(self, address):
- cmd = "/bin/ip -o addr | awk '/%s/ {print $2}'" % address
+ cmd = "ip -o addr | awk '/%s/ {print $2}'" % address
return self.exec_command(cmd)
def get_ip_list(self):
- cmd = "/bin/ip address"
+ cmd = "ip address"
return self.exec_command(cmd)
def assign_static_ip(self, nic, addr):
- cmd = "sudo /bin/ip addr add {ip}/{mask} dev {nic}".format(
+ cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
ip=addr, mask=CONF.network.tenant_network_mask_bits,
nic=nic
)
return self.exec_command(cmd)
def turn_nic_on(self, nic):
- cmd = "sudo /bin/ip link set {nic} up".format(nic=nic)
+ cmd = "sudo ip link set {nic} up".format(nic=nic)
return self.exec_command(cmd)
def get_pids(self, pr_name):
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 64ff7f2..249bac9 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -99,15 +99,15 @@
def wait_for_image_status(client, image_id, status):
"""Waits for an image to reach a given status.
- The client should have a get_image(image_id) method to get the image.
+ The client should have a show_image(image_id) method to get the image.
The client should also have build_interval and build_timeout attributes.
"""
- image = client.get_image(image_id)
+ image = client.show_image(image_id)
start = int(time.time())
while image['status'] != status:
time.sleep(client.build_interval)
- image = client.get_image(image_id)
+ image = client.show_image(image_id)
status_curr = image['status']
if status_curr == 'ERROR':
raise exceptions.AddImageException(image_id=image_id)
diff --git a/tempest/config.py b/tempest/config.py
index 3f3e7e7..0fa5bf5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -69,7 +69,15 @@
help="Only applicable when identity.auth_version is v3."
"Domain within which isolated credentials are provisioned."
"The default \"None\" means that the domain from the"
- "admin user is used instead.")
+ "admin user is used instead."),
+ cfg.BoolOpt('create_isolated_networks',
+ default=True,
+ help="If allow_tenant_isolation is set to True and Neutron is "
+ "enabled Tempest will try to create a useable network, "
+ "subnet, and router when needed for each tenant it "
+ "creates. However in some neutron configurations, like "
+ "with VLAN provider networks, this doesn't work. So if "
+ "set to False the isolated networks will not be created"),
]
identity_group = cfg.OptGroup(name='identity',
@@ -142,6 +150,9 @@
cfg.StrOpt('admin_domain_name',
help="Admin domain name for authentication (Keystone V3)."
"The same domain applies to user and project"),
+ cfg.StrOpt('default_domain_id',
+ default='default',
+ help="ID of the default domain"),
]
identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -422,6 +433,10 @@
cfg.BoolOpt('api_v1',
default=True,
help="Is the v1 image API enabled"),
+ cfg.BoolOpt('deactivate_image',
+ default=False,
+ help="Is the deactivate-image feature enabled."
+ " The feature has been integrated since Kilo."),
]
network_group = cfg.OptGroup(name='network',
@@ -657,6 +672,10 @@
cfg.BoolOpt('api_v2',
default=True,
help="Is the v2 volume API enabled"),
+ cfg.BoolOpt('bootable',
+ default=False,
+ help='Update bootable status of a volume '
+ 'Not implemented on icehouse ')
]
@@ -943,6 +962,8 @@
cfg.StrOpt('img_container_format',
default='bare',
help='Image container format'),
+ cfg.DictOpt('img_properties', help='Glance image properties. '
+ 'Use for custom images which require them'),
cfg.StrOpt('ami_img_file',
default='cirros-0.3.1-x86_64-blank.img',
help='AMI image file name'),
@@ -1097,8 +1118,10 @@
default=60,
help="Timeout for Ironic power transitions."),
cfg.IntOpt('unprovision_timeout',
- default=60,
- help="Timeout for unprovisioning an Ironic node.")
+ default=300,
+ help="Timeout for unprovisioning an Ironic node. "
+ "Takes longer since Kilo as Ironic performs an extra "
+ "step in Node cleaning.")
]
negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 7e0c3b3..5f8d605 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -20,9 +20,9 @@
from oslo_log import log
import six
from tempest_lib.common.utils import data_utils
+from tempest_lib.common.utils import misc as misc_utils
from tempest_lib import exceptions as lib_exc
-from tempest import clients
from tempest.common import fixed_network
from tempest.common.utils.linux import remote_client
from tempest import config
@@ -308,8 +308,13 @@
password=password)
try:
linux_client.validate_authentication()
- except Exception:
- LOG.exception('Initializing SSH connection to %s failed' % ip)
+ except Exception as e:
+ message = ('Initializing SSH connection to %(ip)s failed. '
+ 'Error: %(error)s' % {'ip': ip, 'error': e})
+ caller = misc_utils.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
+ LOG.exception(message)
# If we don't explicitly set for which servers we want to
# log the console output then all the servers will be logged.
# See the definition of _log_console_output()
@@ -318,7 +323,8 @@
return linux_client
- def _image_create(self, name, fmt, path, properties=None):
+ def _image_create(self, name, fmt, path,
+ disk_format=None, properties=None):
if properties is None:
properties = {}
name = data_utils.rand_name('%s-' % name)
@@ -327,10 +333,10 @@
params = {
'name': name,
'container_format': fmt,
- 'disk_format': fmt,
+ 'disk_format': disk_format or fmt,
'is_public': 'False',
}
- params.update(properties)
+ params['properties'] = properties
image = self.image_client.create_image(**params)
self.addCleanup(self.image_client.delete_image, image['id'])
self.assertEqual("queued", image['status'])
@@ -344,23 +350,22 @@
ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
img_container_format = CONF.scenario.img_container_format
img_disk_format = CONF.scenario.img_disk_format
+ img_properties = CONF.scenario.img_properties
LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
- "ami: %s, ari: %s, aki: %s" %
+ "properties: %s, ami: %s, ari: %s, aki: %s" %
(img_path, img_container_format, img_disk_format,
- ami_img_path, ari_img_path, aki_img_path))
+ img_properties, ami_img_path, ari_img_path, aki_img_path))
try:
self.image = self._image_create('scenario-img',
img_container_format,
img_path,
- properties={'disk_format':
- img_disk_format})
+ disk_format=img_disk_format,
+ properties=img_properties)
except IOError:
LOG.debug("A qcow2 image was not found. Try to get a uec image.")
kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
- properties = {
- 'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
- }
+ properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
self.image = self._image_create('scenario-ami', 'ami',
path=ami_img_path,
properties=properties)
@@ -408,9 +413,9 @@
return snapshot_image
def nova_volume_attach(self):
- # TODO(andreaf) Device should be here CONF.compute.volume_device_name
volume = self.servers_client.attach_volume(
- self.server['id'], self.volume['id'], '/dev/vdb')
+ self.server['id'], self.volume['id'], '/dev/%s'
+ % CONF.compute.volume_device_name)
self.assertEqual(self.volume['id'], volume['id'])
self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
# Refresh the volume after the attachment
@@ -986,7 +991,7 @@
router_id = CONF.network.public_router_id
network_id = CONF.network.public_network_id
if router_id:
- resp, body = client.show_router(router_id)
+ body = client.show_router(router_id)
return net_resources.AttributeDict(**body['router'])
elif network_id:
router = self._create_router(client, tenant_id)
@@ -1341,15 +1346,7 @@
cls.set_network_resources()
super(SwiftScenarioTest, cls).setup_credentials()
operator_role = CONF.object_storage.operator_role
- if not cls.isolated_creds.is_role_available(operator_role):
- skip_msg = ("%s skipped because the configured credential provider"
- " is not able to provide credentials with the %s role "
- "assigned." % (cls.__name__, operator_role))
- raise cls.skipException(skip_msg)
- else:
- cls.os_operator = clients.Manager(
- cls.isolated_creds.get_creds_by_roles(
- [operator_role]))
+ cls.os_operator = cls.get_client_manager(roles=[operator_role])
@classmethod
def setup_clients(cls):
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index e54c25a..9fdba39 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -98,7 +98,7 @@
def get_flavor_ephemeral_size(self):
"""Returns size of the ephemeral partition in GiB."""
f_id = self.instance['flavor']['id']
- flavor = self.flavors_client.get_flavor_details(f_id)
+ flavor = self.flavors_client.show_flavor(f_id)
ephemeral = flavor.get('OS-FLV-EXT-DATA:ephemeral')
if not ephemeral or ephemeral == 'N/A':
return None
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 0789c21..56d4c7d 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -17,6 +17,7 @@
from tempest_lib.common.utils import data_utils
from tempest_lib import exceptions as lib_exc
+from tempest.common import fixed_network
from tempest import config
from tempest.scenario import manager
from tempest import test
@@ -87,13 +88,18 @@
'secgroup-%s' % name, 'secgroup-desc-%s' % name)
self.addCleanupClass(self.security_groups_client.delete_security_group,
secgroup['id'])
-
+ create_kwargs = {
+ 'min_count': CONF.scenario.large_ops_number,
+ 'security_groups': [{'name': secgroup['name']}]
+ }
+ network = self.get_tenant_network()
+ create_kwargs = fixed_network.set_networks_kwarg(network,
+ create_kwargs)
self.servers_client.create_server(
name,
self.image,
flavor_id,
- min_count=CONF.scenario.large_ops_number,
- security_groups=[{'name': secgroup['name']}])
+ **create_kwargs)
# needed because of bug 1199788
params = {'name': name}
server_list = self.servers_client.list_servers(params)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 6e67c4b..fba839a 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
-import netaddr
from oslo_log import log as logging
import six
@@ -28,12 +27,13 @@
class TestGettingAddress(manager.NetworkScenarioTest):
- """Create network with 2 subnets: IPv4 and IPv6 in a given address mode
+ """Create network with subnets: one IPv4 and
+ one or few IPv6 in a given address mode
Boot 2 VMs on this network
Allocate and assign 2 FIP4
- Check that vNIC of server matches port data from OpenStack DB
- Ping4 tenant IPv4 of one VM from another one
- Will do the same with ping6 when available in VM
+ Check that vNICs of all VMs gets all addresses actually assigned
+ Ping4 to one VM from another one
+ If ping6 available in VM, do ping6 to all v6 addresses
"""
@classmethod
@@ -65,41 +65,41 @@
'key_name': self.keypair['name'],
'security_groups': [{'name': self.sec_grp['name']}]}
- def prepare_network(self, address6_mode):
+ def prepare_network(self, address6_mode, n_subnets6=1):
"""Creates network with
- one IPv6 subnet in the given mode and
+ given number of IPv6 subnets in the given mode and
one IPv4 subnet
- Creates router with ports on both subnets
+ Creates router with ports on all subnets
"""
self.network = self._create_network(tenant_id=self.tenant_id)
sub4 = self._create_subnet(network=self.network,
namestart='sub4',
ip_version=4,)
- # since https://bugs.launchpad.net/neutron/+bug/1394112 we need
- # to specify gateway_ip manually
- net_range = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- gateway_ip = (netaddr.IPAddress(net_range) + 1).format()
- sub6 = self._create_subnet(network=self.network,
- namestart='sub6',
- ip_version=6,
- gateway_ip=gateway_ip,
- ipv6_ra_mode=address6_mode,
- ipv6_address_mode=address6_mode)
router = self._get_router(tenant_id=self.tenant_id)
sub4.add_to_router(router_id=router['id'])
- sub6.add_to_router(router_id=router['id'])
self.addCleanup(sub4.delete)
- self.addCleanup(sub6.delete)
+
+ for _ in range(n_subnets6):
+ sub6 = self._create_subnet(network=self.network,
+ namestart='sub6',
+ ip_version=6,
+ ipv6_ra_mode=address6_mode,
+ ipv6_address_mode=address6_mode)
+
+ sub6.add_to_router(router_id=router['id'])
+ self.addCleanup(sub6.delete)
@staticmethod
def define_server_ips(srv):
+ ips = {'4': None, '6': []}
for net_name, nics in six.iteritems(srv['addresses']):
for nic in nics:
if nic['version'] == 6:
- srv['accessIPv6'] = nic['addr']
+ ips['6'].append(nic['addr'])
else:
- srv['accessIPv4'] = nic['addr']
+ ips['4'] = nic['addr']
+ return ips
def prepare_server(self):
username = CONF.compute.image_ssh_user
@@ -109,53 +109,56 @@
srv = self.create_server(create_kwargs=create_kwargs)
fip = self.create_floating_ip(thing=srv)
- self.define_server_ips(srv=srv)
+ ips = self.define_server_ips(srv=srv)
ssh = self.get_remote_client(
server_or_ip=fip.floating_ip_address,
username=username)
- return ssh, srv
+ return ssh, ips
- def _prepare_and_test(self, address6_mode):
- self.prepare_network(address6_mode=address6_mode)
+ def _prepare_and_test(self, address6_mode, n_subnets6=1):
+ self.prepare_network(address6_mode=address6_mode,
+ n_subnets6=n_subnets6)
- ssh1, srv1 = self.prepare_server()
- ssh2, srv2 = self.prepare_server()
+ sshv4_1, ips_from_api_1 = self.prepare_server()
+ sshv4_2, ips_from_api_2 = self.prepare_server()
def guest_has_address(ssh, addr):
return addr in ssh.get_ip_list()
- srv1_v6_addr_assigned = functools.partial(
- guest_has_address, ssh1, srv1['accessIPv6'])
- srv2_v6_addr_assigned = functools.partial(
- guest_has_address, ssh2, srv2['accessIPv6'])
+ # get addresses assigned to vNIC as reported by 'ip address' utility
+ ips_from_ip_1 = sshv4_1.get_ip_list()
+ ips_from_ip_2 = sshv4_2.get_ip_list()
+ self.assertIn(ips_from_api_1['4'], ips_from_ip_1)
+ self.assertIn(ips_from_api_2['4'], ips_from_ip_2)
+ for i in range(n_subnets6):
+ # v6 should be configured since the image supports it
+ # It can take time for ipv6 automatic address to get assigned
+ srv1_v6_addr_assigned = functools.partial(
+ guest_has_address, sshv4_1, ips_from_api_1['6'][i])
- result = ssh1.get_ip_list()
- self.assertIn(srv1['accessIPv4'], result)
- # v6 should be configured since the image supports it
- # It can take time for ipv6 automatic address to get assigned
- self.assertTrue(
- test.call_until_true(srv1_v6_addr_assigned,
- CONF.compute.ping_timeout, 1))
- result = ssh2.get_ip_list()
- self.assertIn(srv2['accessIPv4'], result)
- # v6 should be configured since the image supports it
- # It can take time for ipv6 automatic address to get assigned
- self.assertTrue(
- test.call_until_true(srv2_v6_addr_assigned,
- CONF.compute.ping_timeout, 1))
- result = ssh1.ping_host(srv2['accessIPv4'])
+ srv2_v6_addr_assigned = functools.partial(
+ guest_has_address, sshv4_2, ips_from_api_2['6'][i])
+
+ self.assertTrue(test.call_until_true(srv1_v6_addr_assigned,
+ CONF.compute.ping_timeout, 1))
+
+ self.assertTrue(test.call_until_true(srv2_v6_addr_assigned,
+ CONF.compute.ping_timeout, 1))
+
+ result = sshv4_1.ping_host(ips_from_api_2['4'])
self.assertIn('0% packet loss', result)
- result = ssh2.ping_host(srv1['accessIPv4'])
+ result = sshv4_2.ping_host(ips_from_api_1['4'])
self.assertIn('0% packet loss', result)
# Some VM (like cirros) may not have ping6 utility
- result = ssh1.exec_command('whereis ping6')
+ result = sshv4_1.exec_command('whereis ping6')
is_ping6 = False if result == 'ping6:\n' else True
if is_ping6:
- result = ssh1.ping_host(srv2['accessIPv6'])
- self.assertIn('0% packet loss', result)
- result = ssh2.ping_host(srv1['accessIPv6'])
- self.assertIn('0% packet loss', result)
+ for i in range(n_subnets6):
+ result = sshv4_1.ping_host(ips_from_api_2['6'][i])
+ self.assertIn('0% packet loss', result)
+ result = sshv4_2.ping_host(ips_from_api_1['6'][i])
+ self.assertIn('0% packet loss', result)
else:
LOG.warning('Ping6 is not available, skipping')
@@ -168,3 +171,13 @@
@test.services('compute', 'network')
def test_dhcp6_stateless_from_os(self):
self._prepare_and_test(address6_mode='dhcpv6-stateless')
+
+ @test.idempotent_id('7ab23f41-833b-4a16-a7c9-5b42fe6d4123')
+ @test.services('compute', 'network')
+ def test_multi_prefix_dhcpv6_stateless(self):
+ self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2)
+
+ @test.idempotent_id('dec222b1-180c-4098-b8c5-cc1b8342d611')
+ @test.services('compute', 'network')
+ def test_multi_prefix_slaac(self):
+ self._prepare_and_test(address6_mode='slaac', n_subnets6=2)
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index a73ae1c..8ec10c7 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -440,7 +440,7 @@
@test.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@test.services('compute', 'network')
def test_cross_tenant_traffic(self):
- if not self.isolated_creds.is_multi_tenant():
+ if not self.credentials_provider.is_multi_tenant():
raise self.skipException("No secondary tenant defined")
try:
# deploy new tenant
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index b795775..d9918f3 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -48,7 +48,7 @@
self.image_ref = CONF.compute.image_ref
if not hasattr(self, 'flavor_ref'):
self.flavor_ref = CONF.compute.flavor_ref
- self.image_utils = test_utils.ImageUtils()
+ self.image_utils = test_utils.ImageUtils(self.manager)
if not self.image_utils.is_flavor_enough(self.flavor_ref,
self.image_ref):
raise self.skipException(
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 53b471a..51c4c59 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -103,9 +103,9 @@
return self.create_volume(snapshot_id=snapshot_id)
def _attach_volume(self, server, volume):
- # TODO(andreaf) we should use device from config instead if vdb
attached_volume = self.servers_client.attach_volume(
- server['id'], volume['id'], device='/dev/vdb')
+ server['id'], volume['id'], device='/dev/%s'
+ % CONF.compute.volume_device_name)
self.assertEqual(volume['id'], attached_volume['id'])
self._wait_for_volume_status(attached_volume, 'in-use')
@@ -119,7 +119,7 @@
def _func():
part = ssh.get_partitions()
LOG.debug("Partitions:%s" % part)
- return 'vdb' in part
+ return CONF.compute.volume_device_name in part
if not tempest.test.call_until_true(_func,
CONF.compute.build_timeout,
@@ -128,15 +128,18 @@
def _create_timestamp(self, server_or_ip):
ssh_client = self._ssh_to_server(server_or_ip)
- ssh_client.exec_command('sudo /usr/sbin/mkfs.ext4 /dev/vdb')
- ssh_client.exec_command('sudo mount /dev/vdb /mnt')
+ ssh_client.exec_command('sudo /usr/sbin/mkfs.ext4 /dev/%s'
+ % CONF.compute.volume_device_name)
+ ssh_client.exec_command('sudo mount /dev/%s /mnt'
+ % CONF.compute.volume_device_name)
ssh_client.exec_command('sudo sh -c "date > /mnt/timestamp;sync"')
self.timestamp = ssh_client.exec_command('sudo cat /mnt/timestamp')
ssh_client.exec_command('sudo umount /mnt')
def _check_timestamp(self, server_or_ip):
ssh_client = self._ssh_to_server(server_or_ip)
- ssh_client.exec_command('sudo mount /dev/vdb /mnt')
+ ssh_client.exec_command('sudo mount /dev/%s /mnt'
+ % CONF.compute.volume_device_name)
got_timestamp = ssh_client.exec_command('sudo cat /mnt/timestamp')
self.assertEqual(self.timestamp, got_timestamp)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 177697b..1731c48 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -41,8 +41,6 @@
super(TestVolumeBootPattern, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
- if CONF.volume.storage_protocol == 'ceph':
- raise cls.skipException('Skip until bug 1439371 is fixed.')
def _create_volume_from_image(self):
img_uuid = CONF.compute.image_ref
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index 60eb1c3..cd9d925 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -30,26 +30,21 @@
CONF = config.CONF
-@misc.singleton
class ImageUtils(object):
default_ssh_user = 'root'
- def __init__(self):
+ def __init__(self, os):
# Load configuration items
self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
self.non_ssh_image_pattern = \
CONF.input_scenario.non_ssh_image_regex
# Setup clients
- self.isolated_creds = credentials.get_isolated_credentials(
- name='ScenarioImageUtils',
- identity_version=CONF.identity.auth_version)
- os = clients.Manager(self.isolated_creds.get_primary_creds())
self.images_client = os.images_client
self.flavors_client = os.flavors_client
def ssh_user(self, image_id):
- _image = self.images_client.get_image(image_id)
+ _image = self.images_client.show_image(image_id)
for regex, user in self.ssh_users:
# First match wins
if re.match(regex, _image['name']) is not None:
@@ -62,15 +57,15 @@
string=str(image['name']))
def is_sshable_image(self, image_id):
- _image = self.images_client.get_image(image_id)
+ _image = self.images_client.show_image(image_id)
return self._is_sshable_image(_image)
def _is_flavor_enough(self, flavor, image):
return image['minDisk'] <= flavor['disk']
def is_flavor_enough(self, flavor_id, image_id):
- _image = self.images_client.get_image(image_id)
- _flavor = self.flavors_client.get_flavor_details(flavor_id)
+ _image = self.images_client.show_image(image_id)
+ _flavor = self.flavors_client.show_flavor(flavor_id)
return self._is_flavor_enough(_flavor, _image)
@@ -103,9 +98,16 @@
digit=string.digits)
def __init__(self):
+ network_resources = {
+ 'network': False,
+ 'router': False,
+ 'subnet': False,
+ 'dhcp': False,
+ }
self.isolated_creds = credentials.get_isolated_credentials(
name='InputScenarioUtils',
- identity_version=CONF.identity.auth_version)
+ identity_version=CONF.identity.auth_version,
+ network_resources=network_resources)
os = clients.Manager(self.isolated_creds.get_primary_creds())
self.images_client = os.images_client
self.flavors_client = os.flavors_client
@@ -117,6 +119,9 @@
nname = ''.join(c for c in nname if c in self.validchars)
return nname
+ def clear_creds(self):
+ self.isolated_creds.clear_isolated_creds()
+
@property
def scenario_images(self):
"""
@@ -163,12 +168,19 @@
loader, standard_tests, pattern = args
else:
standard_tests, module, loader = args
+ output = None
+ scenario_utils = None
try:
scenario_utils = InputScenarioUtils()
scenario_flavor = scenario_utils.scenario_flavors
scenario_image = scenario_utils.scenario_images
except (exceptions.InvalidConfiguration, TypeError):
- return standard_tests
+ output = standard_tests
+ finally:
+ if scenario_utils:
+ scenario_utils.clear_creds()
+ if output is not None:
+ return output
for test in testtools.iterate_tests(standard_tests):
setattr(test, 'scenarios', testscenarios.multiply_scenarios(
scenario_image,
diff --git a/tempest/services/compute/json/baremetal_nodes_client.py b/tempest/services/compute/json/baremetal_nodes_client.py
index fa2d7f4..d8f13c4 100644
--- a/tempest/services/compute/json/baremetal_nodes_client.py
+++ b/tempest/services/compute/json/baremetal_nodes_client.py
@@ -16,7 +16,8 @@
from six.moves.urllib import parse as urllib
-from tempest.api_schema.response.compute import baremetal_nodes as schema
+from tempest.api_schema.response.compute.v2_1 import baremetal_nodes \
+ as schema
from tempest.common import service_client
diff --git a/tempest/services/compute/json/fixed_ips_client.py b/tempest/services/compute/json/fixed_ips_client.py
index 7ba424f..769bfdc 100644
--- a/tempest/services/compute/json/fixed_ips_client.py
+++ b/tempest/services/compute/json/fixed_ips_client.py
@@ -21,7 +21,7 @@
class FixedIPsClientJSON(service_client.ServiceClient):
- def get_fixed_ip_details(self, fixed_ip):
+ def show_fixed_ip(self, fixed_ip):
url = "os-fixed-ips/%s" % (fixed_ip)
resp, body = self.get(url)
body = json.loads(body)
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 80cbe4d..dee56ac 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -17,10 +17,11 @@
from six.moves.urllib import parse as urllib
-from tempest.api_schema.response.compute import flavors_access as schema_access
-from tempest.api_schema.response.compute import flavors_extra_specs \
- as schema_extra_specs
from tempest.api_schema.response.compute.v2_1 import flavors as schema
+from tempest.api_schema.response.compute.v2_1 import flavors_access \
+ as schema_access
+from tempest.api_schema.response.compute.v2_1 import flavors_extra_specs \
+ as schema_extra_specs
from tempest.common import service_client
@@ -46,7 +47,7 @@
self.validate_response(schema.list_flavors_details, resp, body)
return service_client.ResponseBodyList(resp, body['flavors'])
- def get_flavor_details(self, flavor_id):
+ def show_flavor(self, flavor_id):
resp, body = self.get("flavors/%s" % str(flavor_id))
body = json.loads(body)
self.validate_response(schema.create_get_flavor_details, resp, body)
@@ -83,7 +84,7 @@
return service_client.ResponseBody(resp, body)
def is_resource_deleted(self, id):
- # Did not use get_flavor_details(id) for verification as it gives
+ # Did not use show_flavor(id) for verification as it gives
# 200 ok even for deleted id. LP #981263
# we can remove the loop here and use get by ID when bug gets sortedout
flavors = self.list_flavors_with_detail()
@@ -103,25 +104,26 @@
resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
post_body)
body = json.loads(body)
- self.validate_response(schema_extra_specs.flavor_extra_specs,
+ self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
resp, body)
return service_client.ResponseBody(resp, body['extra_specs'])
- def get_flavor_extra_spec(self, flavor_id):
+ def list_flavor_extra_specs(self, flavor_id):
"""Gets extra Specs details of the mentioned flavor."""
resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
body = json.loads(body)
- self.validate_response(schema_extra_specs.flavor_extra_specs,
+ self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
resp, body)
return service_client.ResponseBody(resp, body['extra_specs'])
- def get_flavor_extra_spec_with_key(self, flavor_id, key):
+ def show_flavor_extra_spec(self, flavor_id, key):
"""Gets extra Specs key-value of the mentioned flavor and key."""
resp, body = self.get('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
key))
body = json.loads(body)
- self.validate_response(schema_extra_specs.flavor_extra_specs_key,
- resp, body)
+ self.validate_response(
+ schema_extra_specs.set_get_flavor_extra_specs_key,
+ resp, body)
return service_client.ResponseBody(resp, body)
def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
@@ -129,8 +131,9 @@
resp, body = self.put('flavors/%s/os-extra_specs/%s' %
(flavor_id, key), json.dumps(kwargs))
body = json.loads(body)
- self.validate_response(schema_extra_specs.flavor_extra_specs_key,
- resp, body)
+ self.validate_response(
+ schema_extra_specs.set_get_flavor_extra_specs_key,
+ resp, body)
return service_client.ResponseBody(resp, body)
def unset_flavor_extra_spec(self, flavor_id, key):
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 9568a5e..6095dc0 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -35,12 +35,12 @@
self.validate_response(schema.list_floating_ips, resp, body)
return service_client.ResponseBodyList(resp, body['floating_ips'])
- def get_floating_ip_details(self, floating_ip_id):
+ def show_floating_ip(self, floating_ip_id):
"""Get the details of a floating IP."""
url = "os-floating-ips/%s" % str(floating_ip_id)
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.floating_ip, resp, body)
+ self.validate_response(schema.create_get_floating_ip, resp, body)
return service_client.ResponseBody(resp, body['floating_ip'])
def create_floating_ip(self, pool_name=None):
@@ -50,7 +50,7 @@
post_body = json.dumps(post_body)
resp, body = self.post(url, post_body)
body = json.loads(body)
- self.validate_response(schema.floating_ip, resp, body)
+ self.validate_response(schema.create_get_floating_ip, resp, body)
return service_client.ResponseBody(resp, body['floating_ip'])
def delete_floating_ip(self, floating_ip_id):
@@ -90,7 +90,7 @@
def is_resource_deleted(self, id):
try:
- self.get_floating_ip_details(id)
+ self.show_floating_ip(id)
except lib_exc.NotFound:
return True
return False
@@ -108,7 +108,7 @@
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.floating_ip_pools, resp, body)
+ self.validate_response(schema.list_floating_ip_pools, resp, body)
return service_client.ResponseBodyList(resp, body['floating_ip_pools'])
def create_floating_ips_bulk(self, ip_range, pool, interface):
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 5462efc..30aa962 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -65,7 +65,7 @@
self.validate_response(schema.list_images_details, resp, body)
return service_client.ResponseBodyList(resp, body['images'])
- def get_image(self, image_id):
+ def show_image(self, image_id):
"""Returns the details of a single image."""
resp, body = self.get("images/%s" % str(image_id))
self.expected_success(200, resp.status)
@@ -131,7 +131,7 @@
def is_resource_deleted(self, id):
try:
- self.get_image(id)
+ self.show_image(id)
except lib_exc.NotFound:
return True
return False
diff --git a/tempest/services/compute/json/migrations_client.py b/tempest/services/compute/json/migrations_client.py
index 009992c..f708a07 100644
--- a/tempest/services/compute/json/migrations_client.py
+++ b/tempest/services/compute/json/migrations_client.py
@@ -16,7 +16,7 @@
from six.moves.urllib import parse as urllib
-from tempest.api_schema.response.compute import migrations as schema
+from tempest.api_schema.response.compute.v2_1 import migrations as schema
from tempest.common import service_client
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index e2d959b..156ad8d 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -18,7 +18,7 @@
from six.moves.urllib import parse as urllib
-from tempest.api_schema.response.compute import services as schema
+from tempest.api_schema.response.compute.v2_1 import services as schema
from tempest.common import service_client
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index b7e2b2a..52f46e2 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -30,7 +30,7 @@
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.list_tenant, resp, body)
+ self.validate_response(schema.list_tenant_usage, resp, body)
return service_client.ResponseBodyList(resp, body['tenant_usages'][0])
def get_tenant_usage(self, tenant_id, params=None):
@@ -40,5 +40,5 @@
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.get_tenant, resp, body)
+ self.validate_response(schema.get_tenant_usage, resp, body)
return service_client.ResponseBodyList(resp, body['tenant_usage'])
diff --git a/tempest/services/identity/v2/json/identity_client.py b/tempest/services/identity/v2/json/identity_client.py
index 039f9bb..c5f7338 100644
--- a/tempest/services/identity/v2/json/identity_client.py
+++ b/tempest/services/identity/v2/json/identity_client.py
@@ -18,6 +18,13 @@
class IdentityClientJSON(service_client.ServiceClient):
+ def get_api_description(self):
+ """Retrieves info about the v2.0 Identity API"""
+ url = ''
+ resp, body = self.get(url)
+ self.expected_success([200, 203], resp.status)
+ return service_client.ResponseBody(resp, self._parse_resp(body))
+
def has_admin_extensions(self):
"""
Returns True if the KSADM Admin Extensions are supported
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index f3d02a8..30fb3e1 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -23,6 +23,13 @@
class IdentityV3ClientJSON(service_client.ServiceClient):
api_version = "v3"
+ def get_api_description(self):
+ """Retrieves info about the v3 Identity API"""
+ url = ''
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, self._parse_resp(body))
+
def create_user(self, user_name, password=None, project_id=None,
email=None, domain_id='default', **kwargs):
"""Creates a user."""
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 5e442fa..add26a9 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -201,20 +201,12 @@
self.expected_success(200, resp.status)
return service_client.ResponseBody(resp, body)
- def image_list(self, **kwargs):
+ def list_images(self, detail=False, properties=dict(),
+ changes_since=None, **kwargs):
url = 'v1/images'
- if len(kwargs) > 0:
- url += '?%s' % urllib.urlencode(kwargs)
-
- resp, body = self.get(url)
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return service_client.ResponseBodyList(resp, body['images'])
-
- def image_list_detail(self, properties=dict(), changes_since=None,
- **kwargs):
- url = 'v1/images/detail'
+ if detail:
+ url += '/detail'
params = {}
for key, value in properties.items():
@@ -240,7 +232,7 @@
body = self._image_meta_from_headers(resp)
return service_client.ResponseBody(resp, body)
- def get_image(self, image_id):
+ def show_image(self, image_id):
url = 'v1/images/%s' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -258,15 +250,16 @@
"""Returns the primary type of resource this client works with."""
return 'image_meta'
- def get_image_membership(self, image_id):
+ def list_image_members(self, image_id):
url = 'v1/images/%s/members' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return service_client.ResponseBody(resp, body)
- def get_shared_images(self, member_id):
- url = 'v1/shared-images/%s' % member_id
+ def list_shared_images(self, tenant_id):
+ """List shared images with the specified tenant"""
+ url = 'v1/shared-images/%s' % tenant_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index aff8e85..9e37f6e 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -96,13 +96,25 @@
body = json.loads(body)
return service_client.ResponseBody(resp, body)
+ def deactivate_image(self, image_id):
+ url = 'v2/images/%s/actions/deactivate' % image_id
+ resp, body = self.post(url, None)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def reactivate_image(self, image_id):
+ url = 'v2/images/%s/actions/reactivate' % image_id
+ resp, body = self.post(url, None)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def delete_image(self, image_id):
url = 'v2/images/%s' % image_id
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp)
- def image_list(self, params=None):
+ def list_images(self, params=None):
url = 'v2/images'
if params:
@@ -114,7 +126,7 @@
self._validate_schema(body, type='images')
return service_client.ResponseBodyList(resp, body['images'])
- def get_image(self, image_id):
+ def show_image(self, image_id):
url = 'v2/images/%s' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -123,7 +135,7 @@
def is_resource_deleted(self, id):
try:
- self.get_image(id)
+ self.show_image(id)
except lib_exc.NotFound:
return True
return False
@@ -159,14 +171,14 @@
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp)
- def get_image_membership(self, image_id):
+ def list_image_members(self, image_id):
url = 'v2/images/%s/members' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return service_client.ResponseBody(resp, body)
- def add_member(self, image_id, member_id):
+ def add_image_member(self, image_id, member_id):
url = 'v2/images/%s/members' % image_id
data = json.dumps({'member': member_id})
resp, body = self.post(url, data)
@@ -174,22 +186,21 @@
body = json.loads(body)
return service_client.ResponseBody(resp, body)
- def update_member_status(self, image_id, member_id, status):
- """Valid status are: ``pending``, ``accepted``, ``rejected``."""
+ def update_image_member(self, image_id, member_id, body):
url = 'v2/images/%s/members/%s' % (image_id, member_id)
- data = json.dumps({'status': status})
+ data = json.dumps(body)
resp, body = self.put(url, data)
self.expected_success(200, resp.status)
body = json.loads(body)
return service_client.ResponseBody(resp, body)
- def get_member(self, image_id, member_id):
+ def show_image_member(self, image_id, member_id):
url = 'v2/images/%s/members/%s' % (image_id, member_id)
resp, body = self.get(url)
self.expected_success(200, resp.status)
return service_client.ResponseBody(resp, json.loads(body))
- def remove_member(self, image_id, member_id):
+ def remove_image_member(self, image_id, member_id):
url = 'v2/images/%s/members/%s' % (image_id, member_id)
resp, _ = self.delete(url)
self.expected_success(204, resp.status)
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 9a08bbd..65aa0f4 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -123,6 +123,15 @@
self.expected_success(202, resp.status)
return service_client.ResponseBody(resp, body)
+ def set_bootable_volume(self, volume_id, bootable):
+ """set a bootable flag for a volume - true or false."""
+ post_body = {"bootable": bootable}
+ post_body = json.dumps({'os-set_bootable': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
def detach_volume(self, volume_id):
"""Detaches a volume from an instance."""
post_body = {}
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
index 0df2eb1..9fdb394 100644
--- a/tempest/stress/actions/ssh_floating.py
+++ b/tempest/stress/actions/ssh_floating.py
@@ -145,7 +145,7 @@
cli = self.manager.floating_ips_client
def func():
- floating = cli.get_floating_ip_details(self.floating['id'])
+ floating = cli.show_floating_ip(self.floating['id'])
return floating['instance_id'] is None
if not tempest.test.call_until_true(func, self.check_timeout,
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
index c8d9f06..ea53481 100644
--- a/tempest/stress/actions/volume_attach_verify.py
+++ b/tempest/stress/actions/volume_attach_verify.py
@@ -96,7 +96,7 @@
cli = self.manager.floating_ips_client
def func():
- floating = cli.get_floating_ip_details(self.floating['id'])
+ floating = cli.show_floating_ip(self.floating['id'])
return floating['instance_id'] is None
if not tempest.test.call_until_true(func, CONF.compute.build_timeout,
diff --git a/tempest/test.py b/tempest/test.py
index aa21c7a..0d709f6c 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -230,7 +230,9 @@
_service = None
# NOTE(andreaf) credentials holds a list of the credentials to be allocated
- # at class setup time. Credential types can be 'primary', 'alt' or 'admin'
+ # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
+ # a list of roles - the first element of the list being a label, and the
+ # rest the actual roles
credentials = []
# Resources required to validate a server using ssh
validation_resources = {}
@@ -267,7 +269,7 @@
etype, cls.__name__))
cls.tearDownClass()
try:
- raise etype, value, trace
+ six.reraise(etype, value, trace)
finally:
del trace # to avoid circular refs
@@ -305,7 +307,7 @@
# the first one
if re_raise and etype is not None:
try:
- raise etype, value, trace
+ six.reraise(etype, value, trace)
finally:
del trace # to avoid circular refs
@@ -322,9 +324,16 @@
if 'admin' in cls.credentials and not credentials.is_admin_available():
msg = "Missing Identity Admin API credentials in configuration."
raise cls.skipException(msg)
- if 'alt' is cls.credentials and not credentials.is_alt_available():
+ if 'alt' in cls.credentials and not credentials.is_alt_available():
msg = "Missing a 2nd set of API credentials in configuration."
raise cls.skipException(msg)
+ if hasattr(cls, 'identity_version'):
+ if cls.identity_version == 'v2':
+ if not CONF.identity_feature_enabled.api_v2:
+ raise cls.skipException("Identity api v2 is not enabled")
+ elif cls.identity_version == 'v3':
+ if not CONF.identity_feature_enabled.api_v3:
+ raise cls.skipException("Identity api v3 is not enabled")
@classmethod
def setup_credentials(cls):
@@ -337,19 +346,24 @@
# This may raise an exception in case credentials are not available
# In that case we want to let the exception through and the test
# fail accordingly
- manager = cls.get_client_manager(
- credential_type=credentials_type)
- setattr(cls, 'os_%s' % credentials_type, manager)
- # Setup some common aliases
- # TODO(andreaf) The aliases below are a temporary hack
- # to avoid changing too much code in one patch. They should
- # be removed eventually
- if credentials_type == 'primary':
- cls.os = cls.manager = cls.os_primary
- if credentials_type == 'admin':
- cls.os_adm = cls.admin_manager = cls.os_admin
- if credentials_type == 'alt':
- cls.alt_manager = cls.os_alt
+ if isinstance(credentials_type, six.string_types):
+ manager = cls.get_client_manager(
+ credential_type=credentials_type)
+ setattr(cls, 'os_%s' % credentials_type, manager)
+ # Setup some common aliases
+ # TODO(andreaf) The aliases below are a temporary hack
+ # to avoid changing too much code in one patch. They should
+ # be removed eventually
+ if credentials_type == 'primary':
+ cls.os = cls.manager = cls.os_primary
+ if credentials_type == 'admin':
+ cls.os_adm = cls.admin_manager = cls.os_admin
+ if credentials_type == 'alt':
+ cls.alt_manager = cls.os_alt
+ elif isinstance(credentials_type, list):
+ manager = cls.get_client_manager(roles=credentials_type[1:],
+ force_new=True)
+ setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
@classmethod
def setup_clients(cls):
@@ -414,39 +428,78 @@
format=self.log_format,
level=None))
- @classmethod
- def get_client_manager(cls, identity_version=None,
- credential_type='primary'):
- """
- Returns an OpenStack client manager
- """
- force_tenant_isolation = getattr(cls, 'force_tenant_isolation', None)
- identity_version = identity_version or CONF.identity.auth_version
+ @property
+ def credentials_provider(self):
+ return self._get_credentials_provider()
- if (not hasattr(cls, 'isolated_creds') or
- not cls.isolated_creds.name == cls.__name__):
- cls.isolated_creds = credentials.get_isolated_credentials(
+ @classmethod
+ def _get_credentials_provider(cls):
+ """Returns a credentials provider
+
+ If no credential provider exists yet creates one.
+ It uses self.identity_version if defined, or the configuration value
+ """
+ if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
+ not cls._creds_provider.name == cls.__name__):
+ force_tenant_isolation = getattr(cls, 'force_tenant_isolation',
+ False)
+ identity_version = getattr(cls, 'identity_version', None)
+ identity_version = identity_version or CONF.identity.auth_version
+
+ cls._creds_provider = credentials.get_isolated_credentials(
name=cls.__name__, network_resources=cls.network_resources,
force_tenant_isolation=force_tenant_isolation,
- identity_version=identity_version
- )
+ identity_version=identity_version)
+ return cls._creds_provider
- credentials_method = 'get_%s_creds' % credential_type
- if hasattr(cls.isolated_creds, credentials_method):
- creds = getattr(cls.isolated_creds, credentials_method)()
+ @classmethod
+ def get_client_manager(cls, credential_type=None, roles=None,
+ force_new=None):
+ """Returns an OpenStack client manager
+
+ Returns an OpenStack client manager based on either credential_type
+ or a list of roles. If neither is specified, it defaults to
+ credential_type 'primary'
+ :param credential_type: string - primary, alt or admin
+ :param roles: list of roles
+
+ :returns the created client manager
+ :raises skipException: if the requested credentials are not available
+ """
+ if all([roles, credential_type]):
+ msg = "Cannot get credentials by type and roles at the same time"
+ raise ValueError(msg)
+ if not any([roles, credential_type]):
+ credential_type = 'primary'
+ cred_provider = cls._get_credentials_provider()
+ if roles:
+ for role in roles:
+ if not cred_provider.is_role_available(role):
+ skip_msg = (
+ "%s skipped because the configured credential provider"
+ " is not able to provide credentials with the %s role "
+ "assigned." % (cls.__name__, role))
+ raise cls.skipException(skip_msg)
+ params = dict(roles=roles)
+ if force_new is not None:
+ params.update(force_new=force_new)
+ creds = cred_provider.get_creds_by_roles(**params)
else:
- raise exceptions.InvalidCredentials(
- "Invalid credentials type %s" % credential_type)
- os = clients.Manager(credentials=creds, service=cls._service)
- return os
+ credentials_method = 'get_%s_creds' % credential_type
+ if hasattr(cred_provider, credentials_method):
+ creds = getattr(cred_provider, credentials_method)()
+ else:
+ raise exceptions.InvalidCredentials(
+ "Invalid credentials type %s" % credential_type)
+ return clients.Manager(credentials=creds, service=cls._service)
@classmethod
def clear_isolated_creds(cls):
"""
Clears isolated creds if set
"""
- if hasattr(cls, 'isolated_creds'):
- cls.isolated_creds.clear_isolated_creds()
+ if hasattr(cls, '_creds_provider'):
+ cls._creds_provider.clear_isolated_creds()
@classmethod
def set_validation_resources(cls, keypair=None, floating_ip=None,
@@ -516,16 +569,16 @@
"""
# Make sure isolated_creds exists and get a network client
networks_client = cls.get_client_manager().networks_client
- isolated_creds = getattr(cls, 'isolated_creds', None)
+ cred_provider = cls._get_credentials_provider()
# In case of nova network, isolated tenants are not able to list the
# network configured in fixed_network_name, even if the can use it
# for their servers, so using an admin network client to validate
# the network name
if (not CONF.service_available.neutron and
credentials.is_admin_available()):
- admin_creds = isolated_creds.get_admin_creds()
+ admin_creds = cred_provider.get_admin_creds()
networks_client = clients.Manager(admin_creds).networks_client
- return fixed_network.get_tenant_network(isolated_creds,
+ return fixed_network.get_tenant_network(cred_provider,
networks_client)
def assertEmpty(self, list, msg=None):
@@ -675,7 +728,7 @@
msg = ("Missing Identity Admin API credentials in"
"configuration.")
raise self.skipException(msg)
- creds = self.isolated_creds.get_admin_creds()
+ creds = self.credentials_provider.get_admin_creds()
os_adm = clients.Manager(credentials=creds)
client = os_adm.negative_client
else:
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
new file mode 100644
index 0000000..f98f8ba
--- /dev/null
+++ b/tempest/tests/cmd/test_javelin.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+from oslotest import mockpatch
+from tempest_lib import exceptions as lib_exc
+
+from tempest.cmd import javelin
+from tempest.tests import base
+
+
+class JavelinUnitTest(base.TestCase):
+
+ def setUp(self):
+ super(JavelinUnitTest, self).setUp()
+ javelin.setup_logging()
+ self.fake_client = mock.MagicMock()
+ self.fake_object = mock.MagicMock()
+
+ def test_load_resources(self):
+ with mock.patch('six.moves.builtins.open', mock.mock_open(),
+ create=True) as open_mock:
+ with mock.patch('yaml.load', mock.MagicMock(),
+ create=True) as load_mock:
+ javelin.load_resources(self.fake_object)
+ load_mock.assert_called_once_with(open_mock(self.fake_object))
+
+ def test_keystone_admin(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
+ javelin.OPTS = self.fake_object
+ javelin.keystone_admin()
+ javelin.OSClient.assert_called_once_with(
+ self.fake_object.os_username,
+ self.fake_object.os_password,
+ self.fake_object.os_tenant_name)
+
+ def test_client_for_user(self):
+ fake_user = mock.MagicMock()
+ javelin.USERS = {fake_user['name']: fake_user}
+ self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
+ javelin.client_for_user(fake_user['name'])
+ javelin.OSClient.assert_called_once_with(
+ fake_user['name'], fake_user['pass'], fake_user['tenant'])
+
+ def test_client_for_non_existing_user(self):
+ fake_non_existing_user = self.fake_object
+ fake_user = mock.MagicMock()
+ javelin.USERS = {fake_user['name']: fake_user}
+ self.useFixture(mockpatch.PatchObject(javelin, "OSClient"))
+ javelin.client_for_user(fake_non_existing_user['name'])
+ self.assertFalse(javelin.OSClient.called)
+
+
+class TestCreateResources(JavelinUnitTest):
+ def test_create_tenants(self):
+
+ self.fake_client.identity.list_tenants.return_value = []
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=self.fake_client))
+
+ javelin.create_tenants([self.fake_object['name']])
+
+ mocked_function = self.fake_client.identity.create_tenant
+ mocked_function.assert_called_once_with(self.fake_object['name'])
+
+ def test_create_duplicate_tenant(self):
+ self.fake_client.identity.list_tenants.return_value = [
+ {'name': self.fake_object['name']}]
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=self.fake_client))
+
+ javelin.create_tenants([self.fake_object['name']])
+
+ mocked_function = self.fake_client.identity.create_tenant
+ self.assertFalse(mocked_function.called)
+
+ def test_create_users(self):
+ self.fake_client.identity.get_tenant_by_name.return_value = \
+ self.fake_object['tenant']
+ self.fake_client.identity.get_user_by_username.side_effect = \
+ lib_exc.NotFound()
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=self.fake_client))
+
+ javelin.create_users([self.fake_object])
+
+ fake_tenant_id = self.fake_object['tenant']['id']
+ fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
+ mocked_function = self.fake_client.identity.create_user
+ mocked_function.assert_called_once_with(self.fake_object['name'],
+ self.fake_object['password'],
+ fake_tenant_id,
+ fake_email,
+ enabled=True)
+
+ def test_create_user_missing_tenant(self):
+ self.fake_client.identity.get_tenant_by_name.side_effect = \
+ lib_exc.NotFound()
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=self.fake_client))
+
+ javelin.create_users([self.fake_object])
+
+ mocked_function = self.fake_client.identity.create_user
+ self.assertFalse(mocked_function.called)
+
+ def test_create_objects(self):
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_assign_swift_role"))
+ self.useFixture(mockpatch.PatchObject(javelin, "_file_contents",
+ return_value=self.fake_object.content))
+
+ javelin.create_objects([self.fake_object])
+
+ mocked_function = self.fake_client.containers.create_container
+ mocked_function.assert_called_once_with(self.fake_object['container'])
+ mocked_function = self.fake_client.objects.create_object
+ mocked_function.assert_called_once_with(self.fake_object['container'],
+ self.fake_object['name'],
+ self.fake_object.content)
+
+ def test_create_images(self):
+ self.fake_client.images.create_image.return_value = \
+ self.fake_object['body']
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
+ return_value=[]))
+ self.useFixture(mockpatch.PatchObject(javelin, "_resolve_image",
+ return_value=(None, None)))
+
+ with mock.patch('six.moves.builtins.open', mock.mock_open(),
+ create=True) as open_mock:
+ javelin.create_images([self.fake_object])
+
+ mocked_function = self.fake_client.images.create_image
+ mocked_function.assert_called_once_with(self.fake_object['name'],
+ self.fake_object['format'],
+ self.fake_object['format'])
+
+ mocked_function = self.fake_client.images.store_image
+ fake_image_id = self.fake_object['body'].get('id')
+ mocked_function.assert_called_once_with(fake_image_id, open_mock())
+
+ def test_create_networks(self):
+ self.fake_client.networks.list_networks.return_value = {
+ 'networks': []}
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ javelin.create_networks([self.fake_object])
+
+ mocked_function = self.fake_client.networks.create_network
+ mocked_function.assert_called_once_with(name=self.fake_object['name'])
+
+ def test_create_subnet(self):
+
+ fake_network = self.fake_object['network']
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
+ return_value=fake_network))
+
+ fake_netaddr = mock.MagicMock()
+ self.useFixture(mockpatch.PatchObject(javelin, "netaddr",
+ return_value=fake_netaddr))
+ fake_version = javelin.netaddr.IPNetwork().version
+
+ javelin.create_subnets([self.fake_object])
+
+ mocked_function = self.fake_client.networks.create_subnet
+ mocked_function.assert_called_once_with(network_id=fake_network['id'],
+ cidr=self.fake_object['range'],
+ name=self.fake_object['name'],
+ ip_version=fake_version)
+
+
+class TestDestroyResources(JavelinUnitTest):
+
+ def test_destroy_tenants(self):
+
+ fake_tenant = self.fake_object['tenant']
+
+ fake_auth = mock.MagicMock()
+ fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
+
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=fake_auth))
+ javelin.destroy_tenants([fake_tenant])
+
+ mocked_function = fake_auth.identity.delete_tenant
+ mocked_function.assert_called_once_with(fake_tenant['id'])
+
+ def test_destroy_users(self):
+
+ fake_user = self.fake_object['user']
+ fake_tenant = self.fake_object['tenant']
+
+ fake_auth = mock.MagicMock()
+ fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
+ fake_auth.identity.get_user_by_username.return_value = fake_user
+
+ self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+ return_value=fake_auth))
+
+ javelin.destroy_users([fake_user])
+
+ mocked_function = fake_auth.identity.delete_user
+ mocked_function.assert_called_once_with(fake_user['id'])
+
+ def test_destroy_objects(self):
+
+ fake_client = mock.MagicMock()
+ fake_client.objects.delete_object.return_value = {'status': "200"}, ""
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=fake_client))
+ javelin.destroy_objects([self.fake_object])
+
+ mocked_function = fake_client.objects.delete_object
+ mocked_function.asswert_called_once(self.fake_object['container'],
+ self.fake_object['name'])
+
+ def test_destroy_images(self):
+
+ fake_client = mock.MagicMock()
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
+ return_value=self.fake_object['image']))
+
+ javelin.destroy_images([self.fake_object])
+
+ mocked_function = fake_client.images.delete_image
+ mocked_function.assert_called_once_with(
+ self.fake_object['image']['id'])
+
+ def test_destroy_networks(self):
+
+ fake_client = mock.MagicMock()
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=fake_client))
+ self.useFixture(mockpatch.PatchObject(
+ javelin, "_get_resource_by_name",
+ return_value=self.fake_object['resource']))
+
+ javelin.destroy_networks([self.fake_object])
+
+ mocked_function = fake_client.networks.delete_network
+ mocked_function.assert_called_once_with(
+ self.fake_object['resource']['id'])
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index b9afd5e..7286d76 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -164,7 +164,7 @@
def test_verify_glance_version_no_v2_with_v1_1(self):
def fake_get_versions():
- return (['v1.1'])
+ return (None, ['v1.1'])
fake_os = mock.MagicMock()
fake_os.image_client.get_versions = fake_get_versions
with mock.patch.object(verify_tempest_config,
@@ -175,7 +175,7 @@
def test_verify_glance_version_no_v2_with_v1_0(self):
def fake_get_versions():
- return (['v1.0'])
+ return (None, ['v1.0'])
fake_os = mock.MagicMock()
fake_os.image_client.get_versions = fake_get_versions
with mock.patch.object(verify_tempest_config,
@@ -186,7 +186,7 @@
def test_verify_glance_version_no_v1(self):
def fake_get_versions():
- return (['v2.0'])
+ return (None, ['v2.0'])
fake_os = mock.MagicMock()
fake_os.image_client.get_versions = fake_get_versions
with mock.patch.object(verify_tempest_config,
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index d6377e6..3506856 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -73,6 +73,7 @@
# the information using gnu/linux tools.
def _assert_exec_called_with(self, cmd):
+ cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
self.ssh_mock.mock.exec_command.assert_called_with(cmd)
def test_get_number_of_vcpus(self):
@@ -119,7 +120,7 @@
self.assertEqual(self.conn.get_mac_address(), macs)
self._assert_exec_called_with(
- "/bin/ip addr | awk '/ether/ {print $2}'")
+ "ip addr | awk '/ether/ {print $2}'")
def test_get_ip_list(self):
ips = """1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
@@ -136,7 +137,7 @@
valid_lft forever preferred_lft forever"""
self.ssh_mock.mock.exec_command.return_value = ips
self.assertEqual(self.conn.get_ip_list(), ips)
- self._assert_exec_called_with('/bin/ip address')
+ self._assert_exec_called_with('ip address')
def test_assign_static_ip(self):
self.ssh_mock.mock.exec_command.return_value = ''
@@ -144,9 +145,10 @@
nic = 'eth0'
self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
self._assert_exec_called_with(
- "sudo /bin/ip addr add %s/%s dev %s" % (ip, '28', nic))
+ "sudo ip addr add %s/%s dev %s" % (ip, '28', nic))
def test_turn_nic_on(self):
nic = 'eth0'
self.conn.turn_nic_on(nic)
- self._assert_exec_called_with('sudo /bin/ip link set %s up' % nic)
+ self._assert_exec_called_with(
+ 'sudo ip link set %s up' % nic)
diff --git a/tempest/tests/stress/test_stress.py b/tempest/tests/stress/test_stress.py
index 3a7b436..16f1ac7 100644
--- a/tempest/tests/stress/test_stress.py
+++ b/tempest/tests/stress/test_stress.py
@@ -33,7 +33,7 @@
cmd = ' '.join([cmd, param])
LOG.info("running: '%s'" % cmd)
cmd_str = cmd
- cmd = shlex.split(cmd.encode('utf-8'))
+ cmd = shlex.split(cmd)
result = ''
result_err = ''
try:
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index b132f60..7850ee4 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -137,7 +137,7 @@
resp, body = self.client.raw_request('PUT', '/images', body=req_body)
self.assertEqual(200, resp.status)
self.assertEqual('fake_response_body', body.read())
- httplib.HTTPConnection.send.assert_call_count(req_body.len)
+ httplib.HTTPConnection.send.assert_call_count(req_body.tell())
def test_get_connection_class_for_https(self):
conn_class = self.client.get_connection_class('https')
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 19e4c9c..38d4c5c 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -14,6 +14,7 @@
import os
import re
+import six
import subprocess
from tempest.tests import base
@@ -32,7 +33,7 @@
self.assertEqual(0, p.returncode,
"test discovery failed, one or more files cause an "
"error on import %s" % ids)
- ids = ids.split('\n')
+ ids = six.text_type(ids).split('\n')
for test_id in ids:
if re.match('(\w+\.){3}\w+', test_id):
if not test_id.startswith('tempest.'):
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index 47e6abd..72a63c3 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -268,6 +268,36 @@
self.assertEqual(alt_creds.user_id, '1234')
@mock.patch('tempest_lib.common.rest_client.RestClient')
+ def test_no_network_creation_with_config_set(self, MockRestClient):
+ cfg.CONF.set_default('create_isolated_networks', False, group='auth')
+ iso_creds = isolated_creds.IsolatedCreds(name='test class',
+ password='fake_password')
+ self._mock_assign_user_role()
+ self._mock_list_role()
+ self._mock_user_create('1234', 'fake_prim_user')
+ self._mock_tenant_create('1234', 'fake_prim_tenant')
+ net = mock.patch.object(iso_creds.network_admin_client,
+ 'delete_network')
+ net_mock = net.start()
+ subnet = mock.patch.object(iso_creds.network_admin_client,
+ 'delete_subnet')
+ subnet_mock = subnet.start()
+ router = mock.patch.object(iso_creds.network_admin_client,
+ 'delete_router')
+ router_mock = router.start()
+
+ primary_creds = iso_creds.get_primary_creds()
+ self.assertEqual(net_mock.mock_calls, [])
+ self.assertEqual(subnet_mock.mock_calls, [])
+ self.assertEqual(router_mock.mock_calls, [])
+ network = primary_creds.network
+ subnet = primary_creds.subnet
+ router = primary_creds.router
+ self.assertIsNone(network)
+ self.assertIsNone(subnet)
+ self.assertIsNone(router)
+
+ @mock.patch('tempest_lib.common.rest_client.RestClient')
def test_network_creation(self, MockRestClient):
iso_creds = isolated_creds.IsolatedCreds(name='test class',
password='fake_password')
diff --git a/tempest/tests/test_waiters.py b/tempest/tests/test_waiters.py
index cdf5362..329d610 100644
--- a/tempest/tests/test_waiters.py
+++ b/tempest/tests/test_waiters.py
@@ -29,7 +29,7 @@
self.client.build_interval = 1
def test_wait_for_image_status(self):
- self.client.get_image.return_value = ({'status': 'active'})
+ self.client.show_image.return_value = ({'status': 'active'})
start_time = int(time.time())
waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
end_time = int(time.time())
@@ -37,13 +37,13 @@
self.assertTrue((end_time - start_time) < 10)
def test_wait_for_image_status_timeout(self):
- self.client.get_image.return_value = ({'status': 'saving'})
+ self.client.show_image.return_value = ({'status': 'saving'})
self.assertRaises(exceptions.TimeoutException,
waiters.wait_for_image_status,
self.client, 'fake_image_id', 'active')
def test_wait_for_image_status_error_on_image_create(self):
- self.client.get_image.return_value = ({'status': 'ERROR'})
+ self.client.show_image.return_value = ({'status': 'ERROR'})
self.assertRaises(exceptions.AddImageException,
waiters.wait_for_image_status,
self.client, 'fake_image_id', 'active')
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 4485972..1ff4dee 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -252,9 +252,9 @@
except exception.BotoServerError as exc:
error_msg = excMatcher.match(exc)
if error_msg is not None:
- raise self.failureException, error_msg
+ raise self.failureException(error_msg)
else:
- raise self.failureException, "BotoServerError not raised"
+ raise self.failureException("BotoServerError not raised")
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 19a77dc..b7f0e81 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -283,7 +283,17 @@
CONF.compute.ssh_user,
pkey=self.keypair.material)
text = data_utils.rand_name("Pattern text for console output")
- resp = ssh.write_to_console(text)
+ try:
+ resp = ssh.write_to_console(text)
+ except Exception:
+ if not CONF.compute_feature_enabled.console_output:
+ LOG.debug('Console output not supported, cannot log')
+ else:
+ console_output = instance.get_console_output().output
+ LOG.debug('Console output for %s\nbody=\n%s',
+ instance.id, console_output)
+ raise
+
self.assertFalse(resp)
def _output():
diff --git a/tox.ini b/tox.ini
index 88d1302..4bb5df6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py27
+envlist = pep8,py27,py34
minversion = 1.6
skipsdist = True
@@ -59,16 +59,6 @@
find . -type f -name "*.pyc" -delete
bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
-[testenv:heat-slow]
-sitepackages = {[tempestenv]sitepackages}
-setenv = {[tempestenv]setenv}
- OS_TEST_TIMEOUT=1200
-deps = {[tempestenv]deps}
-# The regex below is used to select heat api/scenario tests tagged as slow.
-commands =
- find . -type f -name "*.pyc" -delete
- bash tools/pretty_tox.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
-
[testenv:large-ops]
sitepackages = {[tempestenv]sitepackages}
setenv = {[tempestenv]setenv}