Merge "Add passenv option to tox.ini"
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 b434fdc..0805544 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -112,7 +112,7 @@
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
@@ -310,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/accounts.yaml.sample b/etc/accounts.yaml.sample
index 3f57eb7..decc659 100644
--- a/etc/accounts.yaml.sample
+++ b/etc/accounts.yaml.sample
@@ -41,3 +41,4 @@
- 'admin'
resources:
network: 'public'
+ router: 'admin_tenant_1-router'
diff --git a/etc/javelin-resources.yaml.sample b/etc/javelin-resources.yaml.sample
new file mode 100644
index 0000000..fb270a4
--- /dev/null
+++ b/etc/javelin-resources.yaml.sample
@@ -0,0 +1,65 @@
+tenants:
+ - javelin
+ - discuss
+
+users:
+ - name: javelin
+ pass: gungnir
+ tenant: javelin
+ - name: javelin2
+ pass: gungnir2
+ tenant: discuss
+
+secgroups:
+ - name: secgroup1
+ owner: javelin
+ description: SecurityGroup1
+ rules:
+ - 'icmp -1 -1 0.0.0.0/0'
+ - 'tcp 22 22 0.0.0.0/0'
+ - name: secgroup2
+ owner: javelin2
+ description: SecurityGroup2
+ rules:
+ - 'tcp 80 80 0.0.0.0/0'
+
+images:
+ - name: cirros1
+ owner: javelin
+ imgdir: images
+ file: cirros.img
+ container_format: bare
+ disk_format: qcow2
+ - name: cirros2
+ owner: javelin2
+ imgdir: files/images/cirros-0.3.2-x86_64-uec
+ file: cirros-0.3.2-x86_64-blank.img
+ container_format: ami
+ disk_format: ami
+ aki: cirros-0.3.2-x86_64-vmlinuz
+ ari: cirros-0.3.2-x86_64-initrd
+
+networks:
+ - name: network1
+ owner: javelin
+ - name: network2
+ owner: javelin2
+
+subnets:
+ - name: net1-subnet1
+ range: 10.1.0.0/24
+ network: network1
+ owner: javelin
+ - name: net2-subnet2
+ range: 192.168.1.0/24
+ network: network2
+ owner: javelin2
+
+objects:
+ - container: container1
+ name: object1
+ owner: javelin
+ file: /etc/hosts
+ swift_role: Member
+
+telemetry: true
\ No newline at end of file
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/requirements.txt b/requirements.txt
index 7fe8858..cb0031d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,17 +4,17 @@
pbr>=0.11,<2.0
anyjson>=0.3.3
httplib2>=0.7.5
-jsonschema>=2.0.0,<3.0.0
+jsonschema>=2.0.0,<3.0.0,!=2.5.0
testtools>=0.9.36,!=1.2.0
boto>=2.32.1
paramiko>=1.13.0
netaddr>=0.7.12
testrepository>=0.0.18
pyOpenSSL>=0.11
-oslo.concurrency>=1.8.0 # Apache-2.0
+oslo.concurrency>=2.0.0 # Apache-2.0
oslo.config>=1.11.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
-oslo.log>=1.0.0 # Apache-2.0
+oslo.log>=1.2.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0
oslo.utils>=1.4.0 # Apache-2.0
six>=1.9.0
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_hosts.py b/tempest/api/compute/admin/test_hosts.py
index a91c9bf..9fee2a1 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -69,7 +69,7 @@
for host in hosts:
hostname = host['host_name']
- resources = self.client.show_host_detail(hostname)
+ resources = self.client.show_host(hostname)
self.assertTrue(len(resources) >= 1)
host_resource = resources[0]['resource']
self.assertIsNotNone(host_resource)
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 042d1fb..930d686 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -48,7 +48,7 @@
def test_show_host_detail_with_nonexistent_hostname(self):
nonexitent_hostname = data_utils.rand_name('rand_hostname')
self.assertRaises(lib_exc.NotFound,
- self.client.show_host_detail, nonexitent_hostname)
+ self.client.show_host, nonexitent_hostname)
@test.attr(type=['negative'])
@test.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
@@ -56,7 +56,7 @@
hostname = self._get_host_name()
self.assertRaises(lib_exc.Forbidden,
- self.non_admin_client.show_host_detail,
+ self.non_admin_client.show_host,
hostname)
@test.attr(type=['negative'])
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 9483f52..47f66af 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -30,7 +30,7 @@
def _list_hypervisors(self):
# List of hypervisors
- hypers = self.client.get_hypervisor_list()
+ hypers = self.client.list_hypervisors()
return hypers
def assertHypervisors(self, hypers):
@@ -45,7 +45,7 @@
@test.idempotent_id('1e7fdac2-b672-4ad1-97a4-bad0e3030118')
def test_get_hypervisor_list_details(self):
# Display the details of the all hypervisor
- hypers = self.client.get_hypervisor_list_details()
+ hypers = self.client.list_hypervisors(detail=True)
self.assertHypervisors(hypers)
@test.idempotent_id('94ff9eae-a183-428e-9cdb-79fde71211cc')
@@ -54,7 +54,7 @@
hypers = self._list_hypervisors()
self.assertHypervisors(hypers)
- details = self.client.get_hypervisor_show_details(hypers[0]['id'])
+ details = self.client.show_hypervisor(hypers[0]['id'])
self.assertTrue(len(details) > 0)
self.assertEqual(details['hypervisor_hostname'],
hypers[0]['hypervisor_hostname'])
@@ -66,13 +66,13 @@
self.assertHypervisors(hypers)
hostname = hypers[0]['hypervisor_hostname']
- hypervisors = self.client.get_hypervisor_servers(hostname)
+ hypervisors = self.client.list_servers_on_hypervisor(hostname)
self.assertTrue(len(hypervisors) > 0)
@test.idempotent_id('797e4f28-b6e0-454d-a548-80cc77c00816')
def test_get_hypervisor_stats(self):
# Verify the stats of the all hypervisor
- stats = self.client.get_hypervisor_stats()
+ stats = self.client.show_hypervisor_statistics()
self.assertTrue(len(stats) > 0)
@test.idempotent_id('91a50d7d-1c2b-4f24-b55a-a1fe20efca70')
@@ -88,7 +88,7 @@
ironic_only = True
hypers_without_ironic = []
for hyper in hypers:
- details = self.client.get_hypervisor_show_details(hypers[0]['id'])
+ details = self.client.show_hypervisor(hypers[0]['id'])
if details['hypervisor_type'] != 'ironic':
hypers_without_ironic.append(hyper)
ironic_only = False
@@ -102,7 +102,7 @@
# because hypervisors might be disabled, this loops looking
# for any good hit.
try:
- uptime = self.client.get_hypervisor_uptime(hyper['id'])
+ uptime = self.client.show_hypervisor_uptime(hyper['id'])
if len(uptime) > 0:
has_valid_uptime = True
break
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 24b0090..a5c2f0d 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -36,7 +36,7 @@
def _list_hypervisors(self):
# List of hypervisors
- hypers = self.client.get_hypervisor_list()
+ hypers = self.client.list_hypervisors()
return hypers
@test.attr(type=['negative'])
@@ -46,7 +46,7 @@
self.assertRaises(
lib_exc.NotFound,
- self.client.get_hypervisor_show_details,
+ self.client.show_hypervisor,
nonexistent_hypervisor_id)
@test.attr(type=['negative'])
@@ -57,7 +57,7 @@
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_show_details,
+ self.non_adm_client.show_hypervisor,
hypers[0]['id'])
@test.attr(type=['negative'])
@@ -68,7 +68,7 @@
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_servers,
+ self.non_adm_client.list_servers_on_hypervisor,
hypers[0]['id'])
@test.attr(type=['negative'])
@@ -78,7 +78,7 @@
self.assertRaises(
lib_exc.NotFound,
- self.client.get_hypervisor_servers,
+ self.client.list_servers_on_hypervisor,
nonexistent_hypervisor_id)
@test.attr(type=['negative'])
@@ -86,7 +86,7 @@
def test_get_hypervisor_stats_with_non_admin_user(self):
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_stats)
+ self.non_adm_client.show_hypervisor_statistics)
@test.attr(type=['negative'])
@test.idempotent_id('f60aa680-9a3a-4c7d-90e1-fae3a4891303')
@@ -95,7 +95,7 @@
self.assertRaises(
lib_exc.NotFound,
- self.client.get_hypervisor_uptime,
+ self.client.show_hypervisor_uptime,
nonexistent_hypervisor_id)
@test.attr(type=['negative'])
@@ -106,7 +106,7 @@
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_uptime,
+ self.non_adm_client.show_hypervisor_uptime,
hypers[0]['id'])
@test.attr(type=['negative'])
@@ -115,7 +115,7 @@
# List of hypervisor and available services with non admin user
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_list)
+ self.non_adm_client.list_hypervisors)
@test.attr(type=['negative'])
@test.idempotent_id('dc02db05-e801-4c5f-bc8e-d915290ab345')
@@ -123,7 +123,7 @@
# List of hypervisor details and available services with non admin user
self.assertRaises(
lib_exc.Forbidden,
- self.non_adm_client.get_hypervisor_list_details)
+ self.non_adm_client.list_hypervisors, detail=True)
@test.attr(type=['negative'])
@test.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
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 c4581e5..9aacfa5 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -303,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/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 974814c..06a77cc 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -27,7 +27,7 @@
@test.idempotent_id('b54c66af-6ab6-4cf0-a9e5-a0cb58d75e0b')
def test_absLimits_get(self):
# To check if all limits are present in the response
- absolute_limits = self.client.get_absolute_limits()
+ absolute_limits = self.client.show_limits()
expected_elements = ['maxImageMeta', 'maxPersonality',
'maxPersonalitySize',
'maxServerMeta', 'maxTotalCores',
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 9f007bf..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)
@@ -233,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..1608b76
--- /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_file(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..2859cfe 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -56,10 +56,10 @@
# Now try uploading an image file
file_content = data_utils.random_bytes()
image_file = moves.cStringIO(file_content)
- self.client.store_image(image_id, image_file)
+ self.client.store_image_file(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'])
@@ -67,7 +67,7 @@
self.assertEqual(1024, body.get('size'))
# Now try get image file
- body = self.client.get_image_file(image_id)
+ body = self.client.load_image_file(image_id)
self.assertEqual(file_content, body.data)
@test.attr(type='smoke')
@@ -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)
@@ -109,7 +109,7 @@
# Now try uploading an image file
image_file = moves.cStringIO(data_utils.random_bytes())
- self.client.store_image(image_id, image_file)
+ self.client.store_image_file(image_id, image_file)
# Update Image
new_image_name = data_utils.rand_name('new-image')
@@ -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'])
@@ -156,7 +156,7 @@
disk_format=disk_format,
visibility='private')
image_id = body['id']
- cls.client.store_image(image_id, data=image_file)
+ cls.client.store_image_file(image_id, data=image_file)
return image_id
@@ -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")
@@ -244,12 +244,12 @@
def test_get_image_schema(self):
# Test to get image schema
schema = "image"
- body = self.client.get_schema(schema)
+ body = self.client.show_schema(schema)
self.assertEqual("image", body['name'])
@test.idempotent_id('25c8d7b2-df21-460f-87ac-93130bcdc684')
def test_get_images_schema(self):
# Test to get images schema
schema = "images"
- body = self.client.get_schema(schema)
+ body = self.client.show_schema(schema)
self.assertEqual("images", body['name'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 26d4197..d497005 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,22 +70,22 @@
@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')
def test_get_image_member_schema(self):
- body = self.os_img_client.get_schema("member")
+ body = self.os_img_client.show_schema("member")
self.assertEqual("member", body['name'])
@test.idempotent_id('6ae916ef-1052-4e11-8d36-b3ae14853cbb')
def test_get_image_members_schema(self):
- body = self.os_img_client.get_schema("members")
+ body = self.os_img_client.show_schema("members")
self.assertEqual("members", body['name'])
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/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 160cc06..2ff8897 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -72,15 +72,22 @@
# query and setup steps are only required if the extension is available
# and only if the router's default type is distributed.
if test.is_extension_enabled('dvr', 'network'):
- is_dvr_router = cls.admin_client.show_router(
+ cls.is_dvr_router = cls.admin_client.show_router(
cls.router['id'])['router'].get('distributed', False)
- if is_dvr_router:
+ if cls.is_dvr_router:
cls.network = cls.create_network()
- cls.create_subnet(cls.network)
+ cls.subnet = cls.create_subnet(cls.network)
cls.port = cls.create_port(cls.network)
cls.client.add_router_interface_with_port_id(
cls.router['id'], cls.port['id'])
+ @classmethod
+ def resource_cleanup(cls):
+ if cls.is_dvr_router:
+ cls.client.remove_router_interface_with_port_id(
+ cls.router['id'], cls.port['id'])
+ super(L3AgentSchedulerTestJSON, cls).resource_cleanup()
+
@test.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
def test_list_routers_on_l3_agent(self):
self.admin_client.list_routers_on_l3_agent(self.agent['id'])
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_extensions.py b/tempest/api/network/test_extensions.py
index e9f1bf4..be7174b 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -57,7 +57,6 @@
self.assertIn('updated', ext_details.keys())
self.assertIn('name', ext_details.keys())
self.assertIn('description', ext_details.keys())
- self.assertIn('namespace', ext_details.keys())
self.assertIn('links', ext_details.keys())
self.assertIn('alias', ext_details.keys())
self.assertEqual(ext_details['name'], ext_name)
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 0c7fa6b..41a7d65 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -24,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()
@@ -35,8 +37,9 @@
def setup_credentials(cls):
cls.set_network_resources()
super(BaseObjectTest, cls).setup_credentials()
- operator_role = CONF.object_storage.operator_role
- cls.os = cls.get_client_manager(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 04bfee4..bbdf367 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -23,12 +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
- cls.os_reselleradmin = cls.get_client_manager(
- 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 a11b407..e945e1e 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -25,12 +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
- cls.os_reselleradmin = cls.get_client_manager(
- 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 c28a3e0..ac41148 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -29,13 +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 = cls.get_client_manager(
- 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 dfc5dfa..998c2bd 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -23,11 +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 = cls.get_client_manager(
- 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 25dac6b..4df813d 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -24,11 +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 = cls.get_client_manager(
- 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 31c301a..1c42e97 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -24,11 +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 = cls.get_client_manager(
- 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 4c0723d..06e700b 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -37,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 = cls.get_client_manager(
- 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 b02f178..627895e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -989,11 +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 = cls.get_client_manager(
- 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/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/telemetry/test_telemetry_notification_api.py b/tempest/api/telemetry/test_telemetry_notification_api.py
index 73a5fd4..d49b2ba 100644
--- a/tempest/api/telemetry/test_telemetry_notification_api.py
+++ b/tempest/api/telemetry/test_telemetry_notification_api.py
@@ -67,8 +67,8 @@
def test_check_glance_v2_notifications(self):
body = self.create_image(self.image_client_v2)
- self.image_client_v2.store_image(body['id'], "file")
- self.image_client_v2.get_image_file(body['id'])
+ self.image_client_v2.store_image_file(body['id'], "file")
+ self.image_client_v2.load_image_file(body['id'])
query = 'resource', 'eq', body['id']
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..fbfbbb7
--- /dev/null
+++ b/tempest/cmd/account_generator.py
@@ -0,0 +1,413 @@
+#!/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, router_name = create_network_resources(network_admin,
+ tenant['id'],
+ u['name'])
+ u['network'] = network_name
+ u['router'] = router_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, router_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' or 'router' in user:
+ account['resources'] = {}
+ if 'network' in user:
+ account['resources']['network'] = user['network']
+ if 'router' in user:
+ account['resources']['router'] = user['router']
+ 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..4dc03f9 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.show_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..5871628 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
@@ -651,13 +651,13 @@
name, fname = _resolve_image(image, 'aki')
aki = client.images.create_image(
'javelin_' + name, 'aki', 'aki')
- client.images.store_image(aki.get('id'), open(fname, 'r'))
+ client.images.store_image_file(aki.get('id'), open(fname, 'r'))
extras['kernel_id'] = aki.get('id')
name, fname = _resolve_image(image, 'ari')
ari = client.images.create_image(
'javelin_' + name, 'ari', 'ari')
- client.images.store_image(ari.get('id'), open(fname, 'r'))
+ client.images.store_image_file(ari.get('id'), open(fname, 'r'))
extras['ramdisk_id'] = ari.get('id')
_, fname = _resolve_image(image, 'file')
@@ -665,7 +665,7 @@
image['name'], image['container_format'],
image['disk_format'], **extras)
image_id = body.get('id')
- client.images.store_image(image_id, open(fname, 'r'))
+ client.images.store_image_file(image_id, open(fname, 'r'))
def destroy_images(images):
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index f51d9aa..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)
@@ -100,10 +103,12 @@
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)
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 2c6e334..18386ce 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -14,7 +14,6 @@
from oslo_log import log as logging
from tempest_lib.common.utils import misc as misc_utils
-from tempest_lib import exceptions as lib_exc
from tempest import config
from tempest import exceptions
@@ -41,19 +40,7 @@
if not name:
raise exceptions.InvalidConfiguration()
- try:
- networks = compute_networks_client.list_networks(name=name)
- except lib_exc.NotFound:
- # In case of nova network, if the fixed_network_name is not
- # owned by the tenant, and the network client is not an admin
- # one, list_networks will not find it
- msg = ('Unable to find network %s. '
- 'Starting instance without specifying a network.' %
- name)
- if caller:
- msg = '(%s) %s' % (caller, msg)
- LOG.info(msg)
- raise exceptions.InvalidConfiguration()
+ networks = compute_networks_client.list_networks(name=name)
# Check that a network exists, else raise an InvalidConfigurationException
if len(networks) == 1:
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 50aa261..5f8d605 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -20,6 +20,7 @@
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.common import fixed_network
@@ -307,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()
@@ -317,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)
@@ -326,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'])
@@ -343,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)
@@ -407,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
@@ -985,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)
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_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/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 287482f..223b80f 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -34,7 +34,7 @@
self.validate_response(schema.list_hosts, resp, body)
return service_client.ResponseBodyList(resp, body['hosts'])
- def show_host_detail(self, hostname):
+ def show_host(self, hostname):
"""Show detail information for the host."""
resp, body = self.get("os-hosts/%s" % str(hostname))
diff --git a/tempest/services/compute/json/hypervisor_client.py b/tempest/services/compute/json/hypervisor_client.py
index 49ac266..2f9f701 100644
--- a/tempest/services/compute/json/hypervisor_client.py
+++ b/tempest/services/compute/json/hypervisor_client.py
@@ -21,42 +21,41 @@
class HypervisorClientJSON(service_client.ServiceClient):
- def get_hypervisor_list(self):
+ def list_hypervisors(self, detail=False):
"""List hypervisors information."""
- resp, body = self.get('os-hypervisors')
+ url = 'os-hypervisors'
+ _schema = schema.list_search_hypervisors
+ if detail:
+ url += '/detail'
+ _schema = schema.list_hypervisors_detail
+
+ resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.list_search_hypervisors, resp, body)
+ self.validate_response(_schema, resp, body)
return service_client.ResponseBodyList(resp, body['hypervisors'])
- def get_hypervisor_list_details(self):
- """Show detailed hypervisors information."""
- resp, body = self.get('os-hypervisors/detail')
- body = json.loads(body)
- self.validate_response(schema.list_hypervisors_detail, resp, body)
- return service_client.ResponseBodyList(resp, body['hypervisors'])
-
- def get_hypervisor_show_details(self, hyper_id):
+ def show_hypervisor(self, hyper_id):
"""Display the details of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s' % hyper_id)
body = json.loads(body)
self.validate_response(schema.get_hypervisor, resp, body)
return service_client.ResponseBody(resp, body['hypervisor'])
- def get_hypervisor_servers(self, hyper_name):
+ def list_servers_on_hypervisor(self, hyper_name):
"""List instances belonging to the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/servers' % hyper_name)
body = json.loads(body)
self.validate_response(schema.get_hypervisors_servers, resp, body)
return service_client.ResponseBodyList(resp, body['hypervisors'])
- def get_hypervisor_stats(self):
+ def show_hypervisor_statistics(self):
"""Get hypervisor statistics over all compute nodes."""
resp, body = self.get('os-hypervisors/statistics')
body = json.loads(body)
self.validate_response(schema.get_hypervisor_statistics, resp, body)
return service_client.ResponseBody(resp, body['hypervisor_statistics'])
- def get_hypervisor_uptime(self, hyper_id):
+ def show_hypervisor_uptime(self, hyper_id):
"""Display the uptime of the specified hypervisor."""
resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id)
body = json.loads(body)
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/limits_client.py b/tempest/services/compute/json/limits_client.py
index d2aaec6..347467d 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -21,7 +21,7 @@
class LimitsClientJSON(service_client.ServiceClient):
- def get_absolute_limits(self):
+ def show_limits(self):
resp, body = self.get("limits")
body = json.loads(body)
self.validate_response(schema.get_limit, resp, body)
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..383c72b 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -52,7 +52,7 @@
def _validate_schema(self, body, type='image'):
if type in ['image', 'images']:
- schema = self.get_schema(type)
+ schema = self.show_schema(type)
else:
raise ValueError("%s is not a valid schema type" % type)
@@ -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
@@ -133,7 +145,7 @@
"""Returns the primary type of resource this client works with."""
return 'image'
- def store_image(self, image_id, data):
+ def store_image_file(self, image_id, data):
url = 'v2/images/%s/file' % image_id
headers = {'Content-Type': 'application/octet-stream'}
resp, body = self.http.raw_request('PUT', url, headers=headers,
@@ -141,7 +153,7 @@
self.expected_success(204, resp.status)
return service_client.ResponseBody(resp, body)
- def get_image_file(self, image_id):
+ def load_image_file(self, image_id):
url = 'v2/images/%s/file' % image_id
resp, body = self.get(url)
self.expected_success(200, resp.status)
@@ -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,28 +186,27 @@
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)
return service_client.ResponseBody(resp)
- def get_schema(self, schema):
+ def show_schema(self, schema):
url = 'v2/schemas/%s' % schema
resp, body = self.get(url)
self.expected_success(200, 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 d80e478..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 = {}
@@ -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):
@@ -439,14 +453,13 @@
return cls._creds_provider
@classmethod
- def get_client_manager(cls, identity_version=None,
- credential_type=None, roles=None, force_new=None):
+ 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 identity_version: string - v2 or v3
:param credential_type: string - primary, alt or admin
:param roles: list of roles
@@ -458,7 +471,6 @@
raise ValueError(msg)
if not any([roles, credential_type]):
credential_type = 'primary'
- cls.identity_version = identity_version
cred_provider = cls._get_credentials_provider()
if roles:
for role in roles:
@@ -486,7 +498,7 @@
"""
Clears isolated creds if set
"""
- if hasattr(cls, '_cred_provider'):
+ if hasattr(cls, '_creds_provider'):
cls._creds_provider.clear_isolated_creds()
@classmethod
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
new file mode 100644
index 0000000..a0b250e
--- /dev/null
+++ b/tempest/tests/cmd/test_javelin.py
@@ -0,0 +1,336 @@
+#!/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)
+
+ def test_attach_volumes(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ self.useFixture(mockpatch.PatchObject(
+ javelin, "_get_volume_by_name",
+ return_value=self.fake_object.volume))
+
+ self.useFixture(mockpatch.PatchObject(
+ javelin, "_get_server_by_name",
+ return_value=self.fake_object.server))
+
+ javelin.attach_volumes([self.fake_object])
+
+ mocked_function = self.fake_client.volumes.attach_volume
+ mocked_function.assert_called_once_with(
+ self.fake_object.volume['id'],
+ self.fake_object.server['id'],
+ self.fake_object['device'])
+
+
+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_file
+ 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)
+
+ def test_create_volumes(self):
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
+ return_value=None))
+ self.fake_client.volumes.create_volume.return_value = \
+ self.fake_object.body
+
+ javelin.create_volumes([self.fake_object])
+
+ mocked_function = self.fake_client.volumes.create_volume
+ mocked_function.assert_called_once_with(
+ size=self.fake_object['gb'],
+ display_name=self.fake_object['name'])
+ mocked_function = self.fake_client.volumes.wait_for_volume_status
+ mocked_function.assert_called_once_with(
+ self.fake_object.body['id'],
+ 'available')
+
+ def test_create_volume_existing(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_volume_by_name",
+ return_value=self.fake_object))
+ self.fake_client.volumes.create_volume.return_value = \
+ self.fake_object.body
+
+ javelin.create_volumes([self.fake_object])
+
+ mocked_function = self.fake_client.volumes.create_volume
+ self.assertFalse(mocked_function.called)
+ mocked_function = self.fake_client.volumes.wait_for_volume_status
+ self.assertFalse(mocked_function.called)
+
+
+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'])
+
+ def test_destroy_volumes(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ self.useFixture(mockpatch.PatchObject(
+ javelin, "_get_volume_by_name",
+ return_value=self.fake_object.volume))
+
+ javelin.destroy_volumes([self.fake_object])
+
+ mocked_function = self.fake_client.volumes.detach_volume
+ mocked_function.assert_called_once_with(self.fake_object.volume['id'])
+ mocked_function = self.fake_client.volumes.delete_volume
+ mocked_function.assert_called_once_with(self.fake_object.volume['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/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_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/test-requirements.txt b/test-requirements.txt
index 32f33bc..7169610 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -10,4 +10,4 @@
mock>=1.0
coverage>=3.6
oslotest>=1.5.1 # Apache-2.0
-stevedore>=1.3.0 # Apache-2.0
+stevedore>=1.5.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index b44d715..b495fd6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py27
+envlist = pep8,py27,py34
minversion = 1.6
skipsdist = True
@@ -66,16 +66,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}