Merge "Remove advanced services tests"
diff --git a/HACKING.rst b/HACKING.rst
index 04b5eb6..c776c49 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -14,6 +14,7 @@
- [T105] Tests cannot use setUpClass/tearDownClass
- [T106] vim configuration should not be kept in source files.
- [T107] Check that a service tag isn't in the module path
+- [T108] Check no hyphen at the end of rand_name() argument
- [N322] Method's default argument shouldn't be mutable
Test Data/Configuration
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 eab7487..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
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 56172f8..4a43282 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -39,8 +39,8 @@
# Deprecated group/name - [DEFAULT]/logdir
#log_dir = <None>
-# Use syslog for logging. Existing syslog format is DEPRECATED during
-# I, and changed in J to honor RFC5424. (boolean value)
+# Use syslog for logging. Existing syslog format is DEPRECATED and
+# will be changed later to honor RFC5424. (boolean value)
#use_syslog = false
# (Optional) Enables or disables syslog rfc5424 format for logging. If
@@ -254,6 +254,10 @@
# value)
#build_timeout = 300
+# Shell fragments to use before executing a command when sshing to a
+# guest. (string value)
+#ssh_shell_prologue = set -eu -o pipefail; PATH=$$PATH:/sbin;
+
# Auth method used for authenticate to the instance. Valid choices
# are: keypair, configured, adminpass and disabled. Keypair: start the
# servers with a ssh keypair. Configured: use the configured user and
@@ -648,6 +652,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]
diff --git a/requirements.txt b/requirements.txt
index 7fe8858..1d512a0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,22 +1,22 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=0.11,<2.0
+pbr<2.0,>=0.11
anyjson>=0.3.3
httplib2>=0.7.5
-jsonschema>=2.0.0,<3.0.0
-testtools>=0.9.36,!=1.2.0
+jsonschema!=2.5.0,<3.0.0,>=2.0.0
+testtools>=1.4.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.config>=1.11.0 # Apache-2.0
-oslo.i18n>=1.5.0 # Apache-2.0
-oslo.log>=1.0.0 # Apache-2.0
-oslo.serialization>=1.4.0 # Apache-2.0
-oslo.utils>=1.4.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.2.0 # Apache-2.0
+oslo.serialization>=1.4.0 # Apache-2.0
+oslo.utils>=1.6.0 # Apache-2.0
six>=1.9.0
iso8601>=0.1.9
fixtures>=0.3.14
diff --git a/setup.py b/setup.py
old mode 100755
new mode 100644
index 7363757..056c16c
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
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..d4b8311 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']
@@ -125,7 +125,7 @@
self.addCleanup(self.flavor_clean_up, flavor['id'])
flag = False
# Verify flavor is retrieved
- flavors = self.client.list_flavors_with_detail()
+ flavors = self.client.list_flavors(detail=True)
for flavor in flavors:
if flavor['name'] == flavor_name:
flag = True
@@ -160,12 +160,12 @@
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)
# Check if flavor is present in list
- flavors = self.user_client.list_flavors_with_detail()
+ flavors = self.user_client.list_flavors(detail=True)
for flavor in flavors:
if flavor['name'] == flavor_name:
verify_flavor_response_extension(flavor)
@@ -190,7 +190,7 @@
self.addCleanup(self.flavor_clean_up, flavor['id'])
# Verify flavor is retrieved
flag = False
- flavors = self.client.list_flavors_with_detail()
+ flavors = self.client.list_flavors(detail=True)
for flavor in flavors:
if flavor['name'] == flavor_name:
flag = True
@@ -198,7 +198,7 @@
# Verify flavor is not retrieved with other user
flag = False
- flavors = self.user_client.list_flavors_with_detail()
+ flavors = self.user_client.list_flavors(detail=True)
for flavor in flavors:
if flavor['name'] == flavor_name:
flag = True
@@ -240,7 +240,7 @@
flag = False
self.new_client = self.flavors_client
# Verify flavor is retrieved with new user
- flavors = self.new_client.list_flavors_with_detail()
+ flavors = self.new_client.list_flavors(detail=True)
for flavor in flavors:
if flavor['name'] == flavor_name:
flag = True
@@ -278,7 +278,7 @@
def _test_string_variations(variations, flavor_name):
for string in variations:
params = {'is_public': string}
- flavors = self.client.list_flavors_with_detail(params)
+ flavors = self.client.list_flavors(detail=True, **params)
flavor = _flavor_lookup(flavors, flavor_name)
self.assertIsNotNone(flavor)
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 61d3dc6..4c743dc 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -87,7 +87,7 @@
self.assertIn(resp_body, add_body)
# The flavor is present in list.
- flavors = self.flavors_client.list_flavors_with_detail()
+ flavors = self.flavors_client.list_flavors(detail=True)
self.assertIn(new_flavor['id'], map(lambda x: x['id'], flavors))
# Remove flavor access from a tenant.
@@ -96,5 +96,5 @@
self.assertNotIn(resp_body, remove_body)
# The flavor is not present in list.
- flavors = self.flavors_client.list_flavors_with_detail()
+ flavors = self.flavors_client.list_flavors(detail=True)
self.assertNotIn(new_flavor['id'], map(lambda x: x['id'], flavors))
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_networks.py b/tempest/api/compute/admin/test_networks.py
index 477dc61..981a5c9 100644
--- a/tempest/api/compute/admin/test_networks.py
+++ b/tempest/api/compute/admin/test_networks.py
@@ -47,7 +47,7 @@
else:
configured_network = networks
configured_network = configured_network[0]
- network = self.client.get_network(configured_network['id'])
+ network = self.client.show_network(configured_network['id'])
self.assertEqual(configured_network['label'], network['label'])
@test.idempotent_id('df3d1046-6fa5-4b2c-ad0c-cfa46a351cb9')
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 01db25c..029e578 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -58,7 +58,7 @@
def test_get_default_quotas(self):
# Admin can get the default resource quota set for a tenant
expected_quota_set = self.default_quota_set | set(['id'])
- quota_set = self.adm_client.get_default_quota_set(
+ quota_set = self.adm_client.show_default_quota_set(
self.demo_tenant_id)
self.assertEqual(quota_set['id'], self.demo_tenant_id)
for quota in expected_quota_set:
@@ -67,7 +67,7 @@
@test.idempotent_id('55fbe2bf-21a9-435b-bbd2-4162b0ed799a')
def test_update_all_quota_resources_for_tenant(self):
# Admin can update all the resource quota limits for a tenant
- default_quota_set = self.adm_client.get_default_quota_set(
+ default_quota_set = self.adm_client.show_default_quota_set(
self.demo_tenant_id)
new_quota_set = {'injected_file_content_bytes': 20480,
'metadata_items': 256, 'injected_files': 10,
@@ -107,7 +107,7 @@
self.addCleanup(identity_client.delete_tenant, tenant_id)
self.adm_client.update_quota_set(tenant_id, ram='5120')
- quota_set = self.adm_client.get_quota_set(tenant_id)
+ quota_set = self.adm_client.show_quota_set(tenant_id)
self.assertEqual(5120, quota_set['ram'])
# Verify that GET shows the updated quota set of user
@@ -124,8 +124,8 @@
self.adm_client.update_quota_set(tenant_id,
user_id=user_id,
ram='2048')
- quota_set = self.adm_client.get_quota_set(tenant_id,
- user_id=user_id)
+ quota_set = self.adm_client.show_quota_set(tenant_id,
+ user_id=user_id)
self.assertEqual(2048, quota_set['ram'])
@test.idempotent_id('389d04f0-3a41-405f-9317-e5f86e3c44f0')
@@ -138,14 +138,14 @@
description=tenant_desc)
tenant_id = tenant['id']
self.addCleanup(identity_client.delete_tenant, tenant_id)
- quota_set_default = self.adm_client.get_quota_set(tenant_id)
+ quota_set_default = self.adm_client.show_quota_set(tenant_id)
ram_default = quota_set_default['ram']
self.adm_client.update_quota_set(tenant_id, ram='5120')
self.adm_client.delete_quota_set(tenant_id)
- quota_set_new = self.adm_client.get_quota_set(tenant_id)
+ quota_set_new = self.adm_client.show_quota_set(tenant_id)
self.assertEqual(ram_default, quota_set_new['ram'])
@@ -176,7 +176,7 @@
@test.idempotent_id('7932ab0f-5136-4075-b201-c0e2338df51a')
def test_update_default_quotas(self):
LOG.debug("get the current 'default' quota class values")
- body = self.adm_client.get_quota_class_set('default')
+ body = self.adm_client.show_quota_class_set('default')
self.assertIn('id', body)
self.assertEqual('default', body.pop('id'))
# restore the defaults when the test is done
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index d20ebf3..c450a1d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -54,7 +54,7 @@
@test.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
def test_create_server_when_cpu_quota_is_full(self):
# Disallow server creation when tenant's vcpu quota is full
- quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
default_vcpu_quota = quota_set['cores']
vcpu_quota = 0 # Set the quota to zero to conserve resources
@@ -71,7 +71,7 @@
@test.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
def test_create_server_when_memory_quota_is_full(self):
# Disallow server creation when tenant's memory quota is full
- quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
default_mem_quota = quota_set['ram']
mem_quota = 0 # Set the quota to zero to conserve resources
@@ -88,7 +88,7 @@
@test.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
def test_create_server_when_instances_quota_is_full(self):
# Once instances quota limit is reached, disallow server creation
- quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
default_instances_quota = quota_set['instances']
instances_quota = 0 # Set quota to zero to disallow server creation
@@ -107,7 +107,7 @@
def test_security_groups_exceed_limit(self):
# Negative test: Creation Security Groups over limit should FAIL
- quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
default_sg_quota = quota_set['security_groups']
sg_quota = 0 # Set the quota to zero to conserve resources
@@ -136,7 +136,7 @@
# Negative test: Creation of Security Group Rules should FAIL
# when we reach limit maxSecurityGroupRules
- quota_set = self.adm_client.get_quota_set(self.demo_tenant_id)
+ quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
default_sg_rules_quota = quota_set['security_group_rules']
sg_rules_quota = 0 # Set the quota to zero to conserve resources
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 7f78ce8..cda4bc4 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)
@@ -68,7 +68,7 @@
self.useFixture(fixtures.LockFixture('compute_quotas'))
flavor_name = data_utils.rand_name("flavor")
flavor_id = self._get_unused_flavor_id()
- quota_set = self.quotas_client.get_default_quota_set(self.tenant_id)
+ quota_set = self.quotas_client.show_default_quota_set(self.tenant_id)
ram = int(quota_set['ram']) + 1
vcpus = 8
disk = 10
@@ -91,7 +91,7 @@
flavor_name = data_utils.rand_name("flavor")
flavor_id = self._get_unused_flavor_id()
ram = 512
- quota_set = self.quotas_client.get_default_quota_set(self.tenant_id)
+ quota_set = self.quotas_client.show_default_quota_set(self.tenant_id)
vcpus = int(quota_set['cores']) + 1
disk = 10
flavor_ref = self.flavors_client.create_flavor(flavor_name,
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 9aacfa5..a1f6f99 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -16,11 +16,10 @@
import time
from oslo_log import log as logging
-from oslo_utils import excutils
from tempest_lib.common.utils import data_utils
from tempest_lib import exceptions as lib_exc
-from tempest.common import fixed_network
+from tempest.common import compute
from tempest import config
from tempest import exceptions
import tempest.test
@@ -192,41 +191,21 @@
server_group_id)
@classmethod
- def create_test_server(cls, **kwargs):
- """Wrapper utility that returns a test server."""
- name = data_utils.rand_name(cls.__name__ + "-instance")
- if 'name' in kwargs:
- name = kwargs.pop('name')
- flavor = kwargs.get('flavor', cls.flavor_ref)
- image_id = kwargs.get('image_id', cls.image_ref)
+ def create_test_server(cls, validatable=False, **kwargs):
+ """Wrapper utility that returns a test server.
- kwargs = fixed_network.set_networks_kwarg(
- cls.get_tenant_network(), kwargs) or {}
- body = cls.servers_client.create_server(
- name, image_id, flavor, **kwargs)
-
- # handle the case of multiple servers
- servers = [body]
- if 'min_count' in kwargs or 'max_count' in kwargs:
- # Get servers created which name match with name param.
- b = cls.servers_client.list_servers()
- servers = [s for s in b['servers'] if s['name'].startswith(name)]
-
- if 'wait_until' in kwargs:
- for server in servers:
- try:
- cls.servers_client.wait_for_server_status(
- server['id'], kwargs['wait_until'])
- except Exception:
- with excutils.save_and_reraise_exception():
- if ('preserve_server_on_error' not in kwargs
- or kwargs['preserve_server_on_error'] is False):
- for server in servers:
- try:
- cls.servers_client.delete_server(
- server['id'])
- except Exception:
- pass
+ This wrapper utility calls the common create test server and
+ returns a test server. The purpose of this wrapper is to minimize
+ the impact on the code of the tests already using this
+ function.
+ """
+ tenant_network = cls.get_tenant_network()
+ body, servers = compute.create_test_server(
+ cls.os,
+ validatable,
+ validation_resources=cls.validation_resources,
+ tenant_network=tenant_network,
+ **kwargs)
cls.servers.extend(servers)
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index c1c87f9..728fefb 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)
@@ -41,89 +41,89 @@
@test.idempotent_id('6e85fde4-b3cd-4137-ab72-ed5f418e8c24')
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)
+ flavors = self.client.list_flavors(detail=True)
+ 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')
def test_list_flavors_limit_results(self):
# Only the expected number of flavors should be returned
params = {'limit': 1}
- flavors = self.client.list_flavors(params)
+ flavors = self.client.list_flavors(**params)
self.assertEqual(1, len(flavors))
@test.idempotent_id('b26f6327-2886-467a-82be-cef7a27709cb')
def test_list_flavors_detailed_limit_results(self):
# Only the expected number of flavors (detailed) should be returned
params = {'limit': 1}
- flavors = self.client.list_flavors_with_detail(params)
+ flavors = self.client.list_flavors(detail=True, **params)
self.assertEqual(1, len(flavors))
@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}
- flavors = self.client.list_flavors(params)
+ flavors = self.client.list_flavors(**params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
'The list of flavors did not start after the marker.')
@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}
- flavors = self.client.list_flavors_with_detail(params)
+ flavors = self.client.list_flavors(detail=True, **params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
'The list of flavors did not start after the marker.')
@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}
- flavors = self.client.list_flavors_with_detail(params)
+ flavors = self.client.list_flavors(detail=True, **params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@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}
- flavors = self.client.list_flavors_with_detail(params)
+ flavors = self.client.list_flavors(detail=True, **params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@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}
- flavors = self.client.list_flavors(params)
+ flavors = self.client.list_flavors(**params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
@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}
- flavors = self.client.list_flavors(params)
+ flavors = self.client.list_flavors(**params)
self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
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 3771a43..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')
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 974814c..0029bb9 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -27,7 +27,8 @@
@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()
+ limits = self.client.show_limits()
+ absolute_limits = limits['absolute']
expected_elements = ['maxImageMeta', 'maxPersonality',
'maxPersonalitySize',
'maxServerMeta', 'maxTotalCores',
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index bdbe3f1..cbd2004 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -38,7 +38,8 @@
def test_max_image_meta_exceed_limit(self):
# We should not create vm with image meta over maxImageMeta limit
# Get max limit value
- max_meta = self.client.get_specific_absolute_limit('maxImageMeta')
+ limits = self.client.show_limits()
+ max_meta = limits['absolute']['maxImageMeta']
# No point in running this test if there is no limit.
if int(max_meta) == -1:
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..408d4ee 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -53,14 +53,16 @@
personality = [{'path': '/test.txt',
'contents': base64.b64encode(file_contents)}]
disk_config = cls.disk_config
- cls.server_initial = cls.create_test_server(name=cls.name,
- meta=cls.meta,
- accessIPv4=cls.accessIPv4,
- accessIPv6=cls.accessIPv6,
- personality=personality,
- disk_config=disk_config)
+ cls.server_initial = cls.create_test_server(
+ validatable=True,
+ wait_until='ACTIVE',
+ name=cls.name,
+ meta=cls.meta,
+ accessIPv4=cls.accessIPv4,
+ accessIPv6=cls.accessIPv6,
+ personality=personality,
+ disk_config=disk_config)
cls.password = cls.server_initial['adminPass']
- cls.client.wait_for_server_status(cls.server_initial['id'], 'ACTIVE')
cls.server = cls.client.get_server(cls.server_initial['id'])
@test.attr(type='smoke')
@@ -100,7 +102,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 d7607ab..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
@@ -290,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
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index c2ed0ce..10b08a1 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -42,6 +42,10 @@
# 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)
@@ -287,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')
@@ -310,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_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 1a977ab..a8f7597 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -137,7 +137,7 @@
# A 403 Forbidden or 413 Overlimit (old behaviour) exception
# will be raised while exceeding metadata items limit for
# tenant.
- quota_set = self.quotas.get_quota_set(self.tenant_id)
+ quota_set = self.quotas.show_quota_set(self.tenant_id)
quota_metadata = quota_set['metadata_items']
if quota_metadata == -1:
raise self.skipException("No limit for metadata_items")
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index 3a019b4..b3cc072 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -34,8 +34,8 @@
# number of files are injected into the server.
file_contents = 'This is a test file.'
personality = []
- max_file_limit = \
- self.user_client.get_specific_absolute_limit("maxPersonality")
+ limits = self.user_client.show_limits()
+ max_file_limit = limits['absolute']['maxPersonality']
if max_file_limit == -1:
raise self.skipException("No limit for personality files")
for i in range(0, int(max_file_limit) + 1):
@@ -52,8 +52,8 @@
# Server should be created successfully if maximum allowed number of
# files is injected into the server during creation.
file_contents = 'This is a test file.'
- max_file_limit = \
- self.user_client.get_specific_absolute_limit("maxPersonality")
+ limits = self.user_client.show_limits()
+ max_file_limit = limits['absolute']['maxPersonality']
if max_file_limit == -1:
raise self.skipException("No limit for personality files")
person = []
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index a6e877c..e302798 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -47,14 +47,14 @@
def test_get_quotas(self):
# User can get the quota set for it's tenant
expected_quota_set = self.default_quota_set | set(['id'])
- quota_set = self.client.get_quota_set(self.tenant_id)
+ quota_set = self.client.show_quota_set(self.tenant_id)
self.assertEqual(quota_set['id'], self.tenant_id)
for quota in expected_quota_set:
self.assertIn(quota, quota_set.keys())
# get the quota set using user id
- quota_set = self.client.get_quota_set(self.tenant_id,
- self.user_id)
+ quota_set = self.client.show_quota_set(self.tenant_id,
+ self.user_id)
self.assertEqual(quota_set['id'], self.tenant_id)
for quota in expected_quota_set:
self.assertIn(quota, quota_set.keys())
@@ -63,7 +63,7 @@
def test_get_default_quotas(self):
# User can get the default quota set for it's tenant
expected_quota_set = self.default_quota_set | set(['id'])
- quota_set = self.client.get_default_quota_set(self.tenant_id)
+ quota_set = self.client.show_default_quota_set(self.tenant_id)
self.assertEqual(quota_set['id'], self.tenant_id)
for quota in expected_quota_set:
self.assertIn(quota, quota_set.keys())
@@ -72,6 +72,6 @@
def test_compare_tenant_quotas_with_default_quotas(self):
# Tenants are created with the default quota values
defualt_quota_set = \
- self.client.get_default_quota_set(self.tenant_id)
- tenant_quota_set = self.client.get_quota_set(self.tenant_id)
+ self.client.show_default_quota_set(self.tenant_id)
+ tenant_quota_set = self.client.show_quota_set(self.tenant_id)
self.assertEqual(defualt_quota_set, tenant_quota_set)
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index eeb472c..e698baa 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -58,7 +58,7 @@
@test.services('compute')
def test_compare_db_flavors_with_os(self):
db_flavors = self.client.list_db_flavors()
- os_flavors = self.os_flavors_client.list_flavors_with_detail()
+ os_flavors = self.os_flavors_client.list_flavors(detail=True)
self.assertEqual(len(os_flavors), len(db_flavors),
"OS flavors %s do not match DB flavors %s" %
(os_flavors, db_flavors))
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 b775e91..94aab5b 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -79,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):
@@ -101,7 +101,7 @@
@test.idempotent_id('2abf8764-309a-4fa9-bc58-201b799817ad')
def test_create_domain_without_description(self):
# Create domain only with name
- d_name = data_utils.rand_name('domain-')
+ d_name = data_utils.rand_name('domain')
domain = self.client.create_domain(d_name)
self.addCleanup(self._delete_domain, domain['id'])
self.assertIn('id', domain)
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_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 3ebb90d..8a2d797 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -46,7 +46,7 @@
def create_trustor_and_roles(self):
# create a project that trusts will be granted on
- self.trustor_project_name = data_utils.rand_name(name='project-')
+ self.trustor_project_name = data_utils.rand_name(name='project')
project = self.client.create_project(self.trustor_project_name,
domain_id='default')
self.trustor_project_id = project['id']
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/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 00959d9..dc38cab 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -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 4969858..eb6969b 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -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)
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 6c25c93..3f71fcb 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -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 8cccddd..2859cfe 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -56,7 +56,7 @@
# 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.show_image(image_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')
@@ -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')
@@ -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
@@ -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 07c2073..d497005 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -19,15 +19,15 @@
@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.list_image_members(image_id)
members = body['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/messaging/test_queues.py b/tempest/api/messaging/test_queues.py
index 2dac346..a3541b3 100644
--- a/tempest/api/messaging/test_queues.py
+++ b/tempest/api/messaging/test_queues.py
@@ -33,7 +33,7 @@
@test.idempotent_id('9f1c4c72-80c5-4dac-acf3-188cef42e36c')
def test_create_delete_queue(self):
# Create & Delete Queue
- queue_name = data_utils.rand_name('test-')
+ queue_name = data_utils.rand_name('test')
_, body = self.create_queue(queue_name)
self.addCleanup(self.client.delete_queue, queue_name)
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 160cc06..38d68c2 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -27,6 +27,7 @@
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
_agent_mode = 'legacy'
+ is_dvr_router = False
"""
Tests the following operations in the Neutron API using the REST client for
@@ -72,15 +73,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/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_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/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index fec6873..b263050 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -56,18 +56,33 @@
# we want to ensure that we will sleep long enough for things to
# actually expire, so figure out how many secs in the future that is.
sleepy_time = int(resp['x-delete-at']) - int(time.time())
-
+ sleepy_time = sleepy_time if sleepy_time > 0 else 0
resp, body = self.object_client.get_object(self.container_name,
self.object_name)
self.assertHeaders(resp, 'Object', 'GET')
self.assertIn('x-delete-at', resp)
- # add a couple of seconds for safety.
- time.sleep(sleepy_time + 3)
+ # add several seconds for safety.
+ time.sleep(sleepy_time)
+
+ # Checking whether object still exists for several seconds:
+ # sometimes object is not deleted immediately, so we are making
+ # get calls for an approximately 1 minute in a total. Get calls
+ # can take 3s each sometimes so we are making the requests in
+ # exponential periodicity
+ for i in range(1, 6):
+ time.sleep(2 ** i)
+ try:
+ self.object_client.get_object(self.container_name,
+ self.object_name)
+ except lib_exc.NotFound:
+ break
# object should not be there anymore
- self.assertRaises(lib_exc.NotFound, self.object_client.get_object,
- self.container_name, self.object_name)
+ self.assertRaises(lib_exc.NotFound,
+ self.object_client.get_object,
+ self.container_name,
+ self.object_name)
@test.idempotent_id('fb024a42-37f3-4ba5-9684-4f40a7910b41')
def test_get_object_after_expiry_time(self):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 627895e..3396d8f 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -827,8 +827,8 @@
resp, _ = self.object_client.create_object(src_container_name,
object_name, data)
# set object metadata
- meta_key = data_utils.rand_name(name='test-')
- meta_value = data_utils.rand_name(name='MetaValue-')
+ meta_key = data_utils.rand_name(name='test')
+ meta_value = data_utils.rand_name(name='MetaValue')
orig_metadata = {meta_key: meta_value}
resp, _ = self.object_client.update_object_metadata(src_container_name,
object_name,
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/telemetry/base.py b/tempest/api/telemetry/base.py
index 43180e5..cce8e2a 100644
--- a/tempest/api/telemetry/base.py
+++ b/tempest/api/telemetry/base.py
@@ -55,8 +55,7 @@
cls.nova_notifications = ['memory', 'vcpus', 'disk.root.size',
'disk.ephemeral.size']
- cls.glance_notifications = ['image.update', 'image.upload',
- 'image.delete']
+ cls.glance_notifications = ['image.size']
cls.glance_v2_notifications = ['image.download', 'image.serve']
diff --git a/tempest/api/telemetry/test_telemetry_notification_api.py b/tempest/api/telemetry/test_telemetry_notification_api.py
index 73a5fd4..52793c8 100644
--- a/tempest/api/telemetry/test_telemetry_notification_api.py
+++ b/tempest/api/telemetry/test_telemetry_notification_api.py
@@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest_lib import decorators
import testtools
from tempest.api.telemetry import base
@@ -46,7 +45,6 @@
@test.services("image")
@testtools.skipIf(not CONF.image_feature_enabled.api_v1,
"Glance api v1 is disabled")
- @decorators.skip_because(bug='1351627')
def test_check_glance_v1_notifications(self):
body = self.create_image(self.image_client)
self.image_client.update_image(body['id'], data='data')
@@ -63,12 +61,11 @@
@test.services("image")
@testtools.skipIf(not CONF.image_feature_enabled.api_v2,
"Glance api v2 is disabled")
- @decorators.skip_because(bug='1351627')
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/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 24c7c63..ec8e040 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -98,7 +98,7 @@
@test.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
def test_delete_quota(self):
# Admin can delete the resource quota set for a tenant
- tenant_name = data_utils.rand_name('quota_tenant_')
+ tenant_name = data_utils.rand_name('quota_tenant')
identity_client = self.os_adm.identity_client
tenant = identity_client.create_tenant(tenant_name)
tenant_id = tenant['id']
diff --git a/tempest/api_schema/response/compute/v2_1/agents.py b/tempest/api_schema/response/compute/v2_1/agents.py
index 84c5fd3..da38198 100644
--- a/tempest/api_schema/response/compute/v2_1/agents.py
+++ b/tempest/api_schema/response/compute/v2_1/agents.py
@@ -23,6 +23,7 @@
'url': {'type': 'string', 'format': 'uri'},
'md5hash': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['agent_id', 'hypervisor', 'os', 'architecture',
'version', 'url', 'md5hash']
}
@@ -37,6 +38,7 @@
'items': common_agent_info
}
},
+ 'additionalProperties': False,
'required': ['agents']
}
}
@@ -48,6 +50,7 @@
'properties': {
'agent': common_agent_info
},
+ 'additionalProperties': False,
'required': ['agent']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/aggregates.py b/tempest/api_schema/response/compute/v2_1/aggregates.py
index c935592..1a9fe41 100644
--- a/tempest/api_schema/response/compute/v2_1/aggregates.py
+++ b/tempest/api_schema/response/compute/v2_1/aggregates.py
@@ -26,6 +26,7 @@
'name': {'type': 'string'},
'updated_at': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': ['availability_zone', 'created_at', 'deleted',
'deleted_at', 'id', 'name', 'updated_at'],
}
@@ -47,6 +48,7 @@
'items': common_aggregate_info
}
},
+ 'additionalProperties': False,
'required': ['aggregates'],
}
}
@@ -58,6 +60,7 @@
'properties': {
'aggregate': common_aggregate_info
},
+ 'additionalProperties': False,
'required': ['aggregate'],
}
}
@@ -81,6 +84,7 @@
'properties': {
'aggregate': aggregate_for_create
},
+ 'additionalProperties': False,
'required': ['aggregate'],
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/availability_zone.py b/tempest/api_schema/response/compute/v2_1/availability_zone.py
index 5c1224e..d9aebce 100644
--- a/tempest/api_schema/response/compute/v2_1/availability_zone.py
+++ b/tempest/api_schema/response/compute/v2_1/availability_zone.py
@@ -31,16 +31,19 @@
'properties': {
'available': {'type': 'boolean'}
},
+ 'additionalProperties': False,
'required': ['available']
},
# NOTE: Here is the difference between detail and
# non-detail.
'hosts': {'type': 'null'}
},
+ 'additionalProperties': False,
'required': ['zoneName', 'zoneState', 'hosts']
}
}
},
+ 'additionalProperties': False,
'required': ['availabilityZoneInfo']
}
}
@@ -60,6 +63,7 @@
'active': {'type': 'boolean'},
'updated_at': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': ['available', 'active', 'updated_at']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py b/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
index 82506e7..d1ee877 100644
--- a/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
+++ b/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
@@ -25,6 +25,7 @@
'memory_mb': {'type': ['integer', 'string']},
'disk_gb': {'type': ['integer', 'string']},
},
+ 'additionalProperties': False,
'required': ['id', 'interfaces', 'host', 'task_state', 'cpus', 'memory_mb',
'disk_gb']
}
@@ -39,6 +40,7 @@
'items': node
}
},
+ 'additionalProperties': False,
'required': ['nodes']
}
}
@@ -50,6 +52,7 @@
'properties': {
'node': node
},
+ 'additionalProperties': False,
'required': ['node']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/certificates.py b/tempest/api_schema/response/compute/v2_1/certificates.py
index 35445d8..4e7cbe4 100644
--- a/tempest/api_schema/response/compute/v2_1/certificates.py
+++ b/tempest/api_schema/response/compute/v2_1/certificates.py
@@ -25,9 +25,11 @@
'data': {'type': 'string'},
'private_key': {'type': 'string'},
},
+ 'additionalProperties': False,
'required': ['data', 'private_key']
}
},
+ 'additionalProperties': False,
'required': ['certificate']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/extensions.py b/tempest/api_schema/response/compute/v2_1/extensions.py
index 570cd03..a6a455c 100644
--- a/tempest/api_schema/response/compute/v2_1/extensions.py
+++ b/tempest/api_schema/response/compute/v2_1/extensions.py
@@ -35,11 +35,13 @@
'alias': {'type': 'string'},
'description': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['updated', 'name', 'links', 'namespace',
'alias', 'description']
}
}
},
+ 'additionalProperties': False,
'required': ['extensions']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/fixed_ips.py b/tempest/api_schema/response/compute/v2_1/fixed_ips.py
index 13e70bf..6d5ba67 100644
--- a/tempest/api_schema/response/compute/v2_1/fixed_ips.py
+++ b/tempest/api_schema/response/compute/v2_1/fixed_ips.py
@@ -28,9 +28,11 @@
'host': {'type': 'string'},
'hostname': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['address', 'cidr', 'host', 'hostname']
}
},
+ 'additionalProperties': False,
'required': ['fixed_ip']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/flavors.py b/tempest/api_schema/response/compute/v2_1/flavors.py
index 26760ac..5f5b2e3 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors.py
@@ -28,11 +28,13 @@
'links': parameter_types.links,
'id': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['name', 'links', 'id']
}
},
'flavors_links': parameter_types.links
},
+ 'additionalProperties': False,
# NOTE(gmann): flavors_links attribute is not necessary
# to be present always So it is not 'required'.
'required': ['flavors']
@@ -56,6 +58,7 @@
'rxtx_factor': {'type': 'number'},
'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}
},
+ 'additionalProperties': False,
# 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
# 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
@@ -74,6 +77,7 @@
# to be present always So it is not 'required'.
'flavors_links': parameter_types.links
},
+ 'additionalProperties': False,
'required': ['flavors']
}
}
@@ -89,6 +93,7 @@
'properties': {
'flavor': common_flavor_info
},
+ 'additionalProperties': False,
'required': ['flavor']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/flavors_access.py b/tempest/api_schema/response/compute/v2_1/flavors_access.py
index cd31b0a..a4d6af0 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors_access.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors_access.py
@@ -25,10 +25,12 @@
'flavor_id': {'type': 'string'},
'tenant_id': {'type': 'string'},
},
+ 'additionalProperties': False,
'required': ['flavor_id', 'tenant_id'],
}
}
},
+ 'additionalProperties': False,
'required': ['flavor_access']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
index faa25d0..a438d48 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -24,6 +24,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['extra_specs']
}
}
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 ad1c531..28dd40a 100644
--- a/tempest/api_schema/response/compute/v2_1/floating_ips.py
+++ b/tempest/api_schema/response/compute/v2_1/floating_ips.py
@@ -30,6 +30,7 @@
'format': 'ip-address'
}
},
+ 'additionalProperties': False,
'required': ['id', 'pool', 'instance_id',
'ip', 'fixed_ip'],
@@ -44,6 +45,7 @@
'items': common_floating_ip_info
},
},
+ 'additionalProperties': False,
'required': ['floating_ips'],
}
}
@@ -55,6 +57,7 @@
'properties': {
'floating_ip': common_floating_ip_info
},
+ 'additionalProperties': False,
'required': ['floating_ip'],
}
}
@@ -71,10 +74,12 @@
'properties': {
'name': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['name'],
}
}
},
+ 'additionalProperties': False,
'required': ['floating_ip_pools'],
}
}
@@ -95,9 +100,11 @@
'ip_range': {'type': 'string'},
'pool': {'type': ['string', 'null']},
},
+ 'additionalProperties': False,
'required': ['interface', 'ip_range', 'pool'],
}
},
+ 'additionalProperties': False,
'required': ['floating_ips_bulk_create'],
}
}
@@ -109,6 +116,7 @@
'properties': {
'floating_ips_bulk_delete': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['floating_ips_bulk_delete'],
}
}
@@ -136,6 +144,7 @@
'format': 'ip-address'
}
},
+ 'additionalProperties': False,
# NOTE: fixed_ip is introduced after JUNO release,
# So it is not defined as 'required'.
'required': ['address', 'instance_uuid', 'interface',
@@ -143,6 +152,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['floating_ip_info'],
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/hosts.py b/tempest/api_schema/response/compute/v2_1/hosts.py
index 72d5a07..ae70ff1 100644
--- a/tempest/api_schema/response/compute/v2_1/hosts.py
+++ b/tempest/api_schema/response/compute/v2_1/hosts.py
@@ -29,10 +29,12 @@
'service': {'type': 'string'},
'zone': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['host_name', 'service', 'zone']
}
}
},
+ 'additionalProperties': False,
'required': ['hosts']
}
}
@@ -56,14 +58,17 @@
'memory_mb': {'type': 'integer'},
'project': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['cpu', 'disk_gb', 'host',
'memory_mb', 'project']
}
},
+ 'additionalProperties': False,
'required': ['resource']
}
}
},
+ 'additionalProperties': False,
'required': ['host']
}
}
@@ -76,6 +81,7 @@
'host': {'type': 'string'},
'power_action': {'enum': ['startup']}
},
+ 'additionalProperties': False,
'required': ['host', 'power_action']
}
}
@@ -104,6 +110,7 @@
'off_maintenance']},
'status': {'enum': ['enabled', 'disabled']}
},
+ 'additionalProperties': False,
'required': ['host', 'maintenance_mode', 'status']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/hypervisors.py b/tempest/api_schema/response/compute/v2_1/hypervisors.py
index 3efa46b..e24389d 100644
--- a/tempest/api_schema/response/compute/v2_1/hypervisors.py
+++ b/tempest/api_schema/response/compute/v2_1/hypervisors.py
@@ -35,6 +35,7 @@
'vcpus': {'type': 'integer'},
'vcpus_used': {'type': 'integer'}
},
+ 'additionalProperties': False,
'required': ['count', 'current_workload',
'disk_available_least', 'free_disk_gb',
'free_ram_mb', 'local_gb', 'local_gb_used',
@@ -42,6 +43,7 @@
'vcpus', 'vcpus_used']
}
},
+ 'additionalProperties': False,
'required': ['hypervisor_statistics']
}
}
@@ -77,11 +79,13 @@
'id': {'type': ['integer', 'string']},
'disabled_reason': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': ['host', 'id']
},
'vcpus': {'type': 'integer'},
'vcpus_used': {'type': 'integer'}
},
+ 'additionalProperties': False,
# NOTE: When loading os-hypervisor-status extension,
# a response contains status and state. So these params
# should not be required.
@@ -104,6 +108,7 @@
'items': hypervisor_detail
}
},
+ 'additionalProperties': False,
'required': ['hypervisors']
}
}
@@ -115,6 +120,7 @@
'properties': {
'hypervisor': hypervisor_detail
},
+ 'additionalProperties': False,
'required': ['hypervisor']
}
}
@@ -134,6 +140,7 @@
'id': {'type': ['integer', 'string']},
'hypervisor_hostname': {'type': 'string'}
},
+ 'additionalProperties': False,
# NOTE: When loading os-hypervisor-status extension,
# a response contains status and state. So these params
# should not be required.
@@ -141,6 +148,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['hypervisors']
}
}
@@ -159,12 +167,14 @@
'hypervisor_hostname': {'type': 'string'},
'uptime': {'type': 'string'}
},
+ 'additionalProperties': False,
# NOTE: When loading os-hypervisor-status extension,
# a response contains status and state. So these params
# should not be required.
'required': ['id', 'hypervisor_hostname', 'uptime']
}
},
+ 'additionalProperties': False,
'required': ['hypervisor']
}
}
@@ -178,7 +188,8 @@
'properties': {
'uuid': {'type': 'string'},
'name': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
}
}
# In V2 API, if there is no servers (VM) on the Hypervisor host then 'servers'
diff --git a/tempest/api_schema/response/compute/v2_1/images.py b/tempest/api_schema/response/compute/v2_1/images.py
index e6f8db6..a513dcb 100644
--- a/tempest/api_schema/response/compute/v2_1/images.py
+++ b/tempest/api_schema/response/compute/v2_1/images.py
@@ -38,11 +38,13 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
+ 'additionalProperties': False,
'required': ['id', 'links']
},
'OS-EXT-IMG-SIZE:size': {'type': 'integer'},
'OS-DCF:diskConfig': {'type': 'string'}
},
+ 'additionalProperties': False,
# 'server' attributes only comes in response body if image is
# associated with any server. 'OS-EXT-IMG-SIZE:size' & 'OS-DCF:diskConfig'
# are API extension, So those are not defined as 'required'.
@@ -58,6 +60,7 @@
'properties': {
'image': common_image_schema
},
+ 'additionalProperties': False,
'required': ['image']
}
}
@@ -76,11 +79,13 @@
'links': image_links,
'name': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['id', 'links', 'name']
}
},
'images_links': parameter_types.links
},
+ 'additionalProperties': False,
# NOTE(gmann): images_links attribute is not necessary to be
# present always So it is not 'required'.
'required': ['images']
@@ -113,6 +118,7 @@
'properties': {
'metadata': {'type': 'object'}
},
+ 'additionalProperties': False,
'required': ['metadata']
}
}
@@ -124,6 +130,7 @@
'properties': {
'meta': {'type': 'object'}
},
+ 'additionalProperties': False,
'required': ['meta']
}
}
@@ -139,6 +146,7 @@
},
'images_links': parameter_types.links
},
+ 'additionalProperties': False,
# NOTE(gmann): images_links attribute is not necessary to be
# present always So it is not 'required'.
'required': ['images']
diff --git a/tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py b/tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
index 658f574..c6c4deb 100644
--- a/tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
+++ b/tempest/api_schema/response/compute/v2_1/instance_usage_audit_logs.py
@@ -30,6 +30,7 @@
'total_errors': {'type': 'integer'},
'total_instances': {'type': 'integer'}
},
+ 'additionalProperties': False,
'required': ['hosts_not_run', 'log', 'num_hosts', 'num_hosts_done',
'num_hosts_not_run', 'num_hosts_running', 'overall_status',
'period_beginning', 'period_ending', 'total_errors',
@@ -43,6 +44,7 @@
'properties': {
'instance_usage_audit_log': common_instance_usage_audit_log
},
+ 'additionalProperties': False,
'required': ['instance_usage_audit_log']
}
}
@@ -54,6 +56,7 @@
'properties': {
'instance_usage_audit_logs': common_instance_usage_audit_log
},
+ 'additionalProperties': False,
'required': ['instance_usage_audit_logs']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/interfaces.py b/tempest/api_schema/response/compute/v2_1/interfaces.py
index 033f816..b18fba6 100644
--- a/tempest/api_schema/response/compute/v2_1/interfaces.py
+++ b/tempest/api_schema/response/compute/v2_1/interfaces.py
@@ -32,6 +32,7 @@
'format': 'ipv4'
}
},
+ 'additionalProperties': False,
'required': ['subnet_id', 'ip_address']
}
},
@@ -39,6 +40,7 @@
'net_id': {'type': 'string', 'format': 'uuid'},
'mac_addr': parameter_types.mac_address
},
+ 'additionalProperties': False,
'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr']
}
@@ -49,6 +51,7 @@
'properties': {
'interfaceAttachment': interface_common_info
},
+ 'additionalProperties': False,
'required': ['interfaceAttachment']
}
}
@@ -63,6 +66,7 @@
'items': interface_common_info
}
},
+ 'additionalProperties': False,
'required': ['interfaceAttachments']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/keypairs.py b/tempest/api_schema/response/compute/v2_1/keypairs.py
index ceae6cf..9c04c79 100644
--- a/tempest/api_schema/response/compute/v2_1/keypairs.py
+++ b/tempest/api_schema/response/compute/v2_1/keypairs.py
@@ -31,6 +31,7 @@
'id': {'type': 'integer'}
},
+ 'additionalProperties': False,
# When we run the get keypair API, response body includes
# all the above mentioned attributes.
# But in Nova API sample file, response body includes only
@@ -39,6 +40,7 @@
'required': ['public_key', 'name', 'fingerprint']
}
},
+ 'additionalProperties': False,
'required': ['keypair']
}
}
@@ -57,12 +59,14 @@
'user_id': {'type': 'string'},
'private_key': {'type': 'string'}
},
+ 'additionalProperties': False,
# When create keypair API is being called with 'Public key'
# (Importing keypair) then, response body does not contain
# 'private_key' So it is not defined as 'required'
'required': ['fingerprint', 'name', 'public_key', 'user_id']
}
},
+ 'additionalProperties': False,
'required': ['keypair']
}
}
@@ -88,13 +92,16 @@
'name': {'type': 'string'},
'fingerprint': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['public_key', 'name', 'fingerprint']
}
},
+ 'additionalProperties': False,
'required': ['keypair']
}
}
},
+ 'additionalProperties': False,
'required': ['keypairs']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/limits.py b/tempest/api_schema/response/compute/v2_1/limits.py
index a7decb7..81f175f 100644
--- a/tempest/api_schema/response/compute/v2_1/limits.py
+++ b/tempest/api_schema/response/compute/v2_1/limits.py
@@ -43,6 +43,7 @@
'maxServerGroups': {'type': 'integer'},
'totalServerGroupsUsed': {'type': 'integer'}
},
+ 'additionalProperties': False,
# NOTE(gmann): maxServerGroupMembers, maxServerGroups
# and totalServerGroupsUsed are API extension,
# and some environments return a response without these
@@ -84,18 +85,22 @@
{'type': 'integer'},
'verb':
{'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
}
},
'regex': {'type': 'string'},
'uri': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
}
}
},
+ 'additionalProperties': False,
'required': ['absolute', 'rate']
}
},
+ 'additionalProperties': False,
'required': ['limits']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/migrations.py b/tempest/api_schema/response/compute/v2_1/migrations.py
index 6549272..722372c 100644
--- a/tempest/api_schema/response/compute/v2_1/migrations.py
+++ b/tempest/api_schema/response/compute/v2_1/migrations.py
@@ -35,6 +35,7 @@
'created_at': {'type': 'string'},
'updated_at': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': [
'id', 'status', 'instance_uuid', 'source_node',
'source_compute', 'dest_node', 'dest_compute',
@@ -44,6 +45,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['migrations']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/parameter_types.py b/tempest/api_schema/response/compute/v2_1/parameter_types.py
index 90d4c8f..7b4264c 100644
--- a/tempest/api_schema/response/compute/v2_1/parameter_types.py
+++ b/tempest/api_schema/response/compute/v2_1/parameter_types.py
@@ -23,6 +23,7 @@
},
'rel': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['href', 'rel']
}
}
@@ -60,6 +61,7 @@
]
}
},
+ 'additionalProperties': False,
'required': ['version', 'addr']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/quotas.py b/tempest/api_schema/response/compute/v2_1/quotas.py
index 9141f7e..7953983 100644
--- a/tempest/api_schema/response/compute/v2_1/quotas.py
+++ b/tempest/api_schema/response/compute/v2_1/quotas.py
@@ -37,6 +37,7 @@
'injected_file_content_bytes': {'type': 'integer'},
'injected_file_path_bytes': {'type': 'integer'}
},
+ 'additionalProperties': False,
# NOTE: server_group_members and server_groups are represented
# when enabling quota_server_group extension. So they should
# not be required.
@@ -48,6 +49,7 @@
'injected_file_path_bytes']
}
},
+ 'additionalProperties': False,
'required': ['quota_set']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/security_group_default_rule.py b/tempest/api_schema/response/compute/v2_1/security_group_default_rule.py
index 9246ab8..2ec2826 100644
--- a/tempest/api_schema/response/compute/v2_1/security_group_default_rule.py
+++ b/tempest/api_schema/response/compute/v2_1/security_group_default_rule.py
@@ -23,10 +23,12 @@
'properties': {
'cidr': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['cidr'],
},
'to_port': {'type': 'integer'},
},
+ 'additionalProperties': False,
'required': ['from_port', 'id', 'ip_protocol', 'ip_range', 'to_port'],
}
@@ -38,6 +40,7 @@
'security_group_default_rule':
common_security_group_default_rule_info
},
+ 'additionalProperties': False,
'required': ['security_group_default_rule']
}
}
@@ -56,6 +59,7 @@
'items': common_security_group_default_rule_info
}
},
+ 'additionalProperties': False,
'required': ['security_group_default_rules']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/security_groups.py b/tempest/api_schema/response/compute/v2_1/security_groups.py
index 9a852e5..5ed5a5c 100644
--- a/tempest/api_schema/response/compute/v2_1/security_groups.py
+++ b/tempest/api_schema/response/compute/v2_1/security_groups.py
@@ -20,7 +20,8 @@
'properties': {
'tenant_id': {'type': 'string'},
'name': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
},
'ip_protocol': {'type': ['string', 'null']},
# 'parent_group_id' can be UUID so defining it as 'string' also.
@@ -29,7 +30,8 @@
'type': 'object',
'properties': {
'cidr': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
# When optional argument is provided in request body
# like 'group_id' then, attribute 'cidr' does not
# comes in response body. So it is not 'required'.
@@ -47,11 +49,13 @@
'type': 'array',
'items': {
'type': ['object', 'null'],
- 'properties': common_security_group_rule
+ 'properties': common_security_group_rule,
+ 'additionalProperties': False,
}
},
'description': {'type': 'string'},
},
+ 'additionalProperties': False,
'required': ['id', 'name', 'tenant_id', 'rules', 'description'],
}
@@ -65,6 +69,7 @@
'items': common_security_group
}
},
+ 'additionalProperties': False,
'required': ['security_groups']
}
}
@@ -76,6 +81,7 @@
'properties': {
'security_group': common_security_group
},
+ 'additionalProperties': False,
'required': ['security_group']
}
}
@@ -92,10 +98,12 @@
'security_group_rule': {
'type': 'object',
'properties': common_security_group_rule,
+ 'additionalProperties': False,
'required': ['from_port', 'to_port', 'group', 'ip_protocol',
'parent_group_id', 'id', 'ip_range']
}
},
+ 'additionalProperties': False,
'required': ['security_group_rule']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/servers.py b/tempest/api_schema/response/compute/v2_1/servers.py
index 875f607..44ab9e9 100644
--- a/tempest/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/api_schema/response/compute/v2_1/servers.py
@@ -29,12 +29,14 @@
'links': parameter_types.links,
'OS-DCF:diskConfig': {'type': 'string'}
},
+ 'additionalProperties': False,
# NOTE: OS-DCF:diskConfig & security_groups are API extension,
# and some environments return a response without these
# attributes.So they are not 'required'.
'required': ['id', 'links']
}
},
+ 'additionalProperties': False,
'required': ['server']
}
}
@@ -59,11 +61,13 @@
'links': parameter_types.links,
'name': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['id', 'links', 'name']
}
},
'servers_links': parameter_types.links
},
+ 'additionalProperties': False,
# NOTE(gmann): servers_links attribute is not necessary to be
# present always So it is not 'required'.
'required': ['servers']
@@ -86,6 +90,7 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
+ 'additionalProperties': False,
'required': ['id', 'links']},
{'type': ['string', 'null']}
]},
@@ -95,6 +100,7 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
+ 'additionalProperties': False,
'required': ['id', 'links']
},
'fault': {
@@ -105,6 +111,7 @@
'message': {'type': 'string'},
'details': {'type': 'string'},
},
+ 'additionalProperties': False,
# NOTE(gmann): 'details' is not necessary to be present
# in the 'fault'. So it is not defined as 'required'.
'required': ['code', 'created', 'message']
@@ -122,6 +129,7 @@
'accessIPv4': parameter_types.access_ip_v4,
'accessIPv6': parameter_types.access_ip_v6
},
+ 'additionalProperties': False,
# NOTE(GMann): 'progress' attribute is present in the response
# only when server's status is one of the progress statuses
# ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE")
@@ -142,6 +150,7 @@
'properties': {
'server': common_show_server
},
+ 'additionalProperties': False,
'required': ['server']
}
}
@@ -184,6 +193,7 @@
'properties': {
'server': server_detail
},
+ 'additionalProperties': False,
'required': ['server']
}
}
@@ -199,6 +209,7 @@
},
'servers_links': parameter_types.links
},
+ 'additionalProperties': False,
# NOTE(gmann): servers_links attribute is not necessary to be
# present always So it is not 'required'.
'required': ['servers']
@@ -221,6 +232,7 @@
'properties': {
'adminPass': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['adminPass']
}
}
@@ -239,12 +251,14 @@
'mac_address': parameter_types.mac_address,
'OS-EXT-VIF-NET:net_id': {'type': 'string'}
},
+ 'additionalProperties': False,
# 'OS-EXT-VIF-NET:net_id' is API extension So it is
# not defined as 'required'
'required': ['id', 'mac_address']
}
}
},
+ 'additionalProperties': False,
'required': ['virtual_interfaces']
}
}
@@ -257,6 +271,7 @@
'volumeId': {'type': 'string'},
'serverId': {'type': ['integer', 'string']}
},
+ 'additionalProperties': False,
'required': ['id', 'device', 'volumeId', 'serverId']
}
@@ -267,6 +282,7 @@
'properties': {
'volumeAttachment': common_attach_volume_info
},
+ 'additionalProperties': False,
'required': ['volumeAttachment']
}
}
@@ -289,6 +305,7 @@
'items': common_attach_volume_info
}
},
+ 'additionalProperties': False,
'required': ['volumeAttachments']
}
}
@@ -308,6 +325,7 @@
'properties': {
'addresses': parameter_types.addresses
},
+ 'additionalProperties': False,
'required': ['addresses']
}
}
@@ -329,6 +347,7 @@
},
'metadata': {'type': 'object'}
},
+ 'additionalProperties': False,
'required': ['id', 'name', 'policies', 'members', 'metadata']
}
@@ -339,6 +358,7 @@
'properties': {
'server_group': common_server_group
},
+ 'additionalProperties': False,
'required': ['server_group']
}
}
@@ -357,6 +377,7 @@
'items': common_server_group
}
},
+ 'additionalProperties': False,
'required': ['server_groups']
}
}
@@ -372,6 +393,7 @@
'message': {'type': ['string', 'null']},
'instance_uuid': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['action', 'request_id', 'user_id', 'project_id',
'start_time', 'message', 'instance_uuid']
}
@@ -387,6 +409,7 @@
'result': {'type': 'string'},
'traceback': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': ['event', 'start_time', 'finish_time', 'result',
'traceback']
}
@@ -402,6 +425,7 @@
'items': instance_actions
}
},
+ 'additionalProperties': False,
'required': ['instanceActions']
}
}
@@ -419,6 +443,7 @@
'properties': {
'instanceAction': instance_actions_with_events
},
+ 'additionalProperties': False,
'required': ['instanceAction']
}
}
@@ -430,6 +455,7 @@
'properties': {
'password': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['password']
}
}
@@ -448,9 +474,11 @@
'format': 'uri'
}
},
+ 'additionalProperties': False,
'required': ['type', 'url']
}
},
+ 'additionalProperties': False,
'required': ['console']
}
}
@@ -462,6 +490,7 @@
'properties': {
'output': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['output']
}
}
@@ -478,6 +507,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['metadata']
}
}
@@ -502,6 +532,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['meta']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/services.py b/tempest/api_schema/response/compute/v2_1/services.py
index 6f361ef..c2c7a51 100644
--- a/tempest/api_schema/response/compute/v2_1/services.py
+++ b/tempest/api_schema/response/compute/v2_1/services.py
@@ -32,11 +32,13 @@
'updated_at': {'type': ['string', 'null']},
'disabled_reason': {'type': ['string', 'null']}
},
+ 'additionalProperties': False,
'required': ['id', 'zone', 'host', 'state', 'binary',
'status', 'updated_at', 'disabled_reason']
}
}
},
+ 'additionalProperties': False,
'required': ['services']
}
}
@@ -53,9 +55,11 @@
'binary': {'type': 'string'},
'host': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['status', 'binary', 'host']
}
},
+ 'additionalProperties': False,
'required': ['service']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/tenant_networks.py b/tempest/api_schema/response/compute/v2_1/tenant_networks.py
index 0b2868a..ddfab96 100644
--- a/tempest/api_schema/response/compute/v2_1/tenant_networks.py
+++ b/tempest/api_schema/response/compute/v2_1/tenant_networks.py
@@ -19,6 +19,7 @@
'cidr': {'type': ['string', 'null']},
'label': {'type': 'string'}
},
+ 'additionalProperties': False,
'required': ['id', 'cidr', 'label']
}
@@ -33,6 +34,7 @@
'items': param_network
}
},
+ 'additionalProperties': False,
'required': ['networks']
}
}
@@ -45,6 +47,7 @@
'properties': {
'network': param_network
},
+ 'additionalProperties': False,
'required': ['network']
}
}
diff --git a/tempest/api_schema/response/compute/v2_1/volumes.py b/tempest/api_schema/response/compute/v2_1/volumes.py
index 541d3ff..bb34acb 100644
--- a/tempest/api_schema/response/compute/v2_1/volumes.py
+++ b/tempest/api_schema/response/compute/v2_1/volumes.py
@@ -39,7 +39,8 @@
'device': {'type': 'string'},
'volumeId': {'type': 'string'},
'serverId': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
# NOTE- If volume is not attached to any server
# then, 'attachments' attributes comes as array
# with empty objects "[{}]" due to that elements
@@ -49,11 +50,13 @@
}
}
},
+ 'additionalProperties': False,
'required': ['id', 'status', 'displayName', 'availabilityZone',
'createdAt', 'displayDescription', 'volumeType',
'snapshotId', 'metadata', 'size', 'attachments']
}
},
+ 'additionalProperties': False,
'required': ['volume']
}
}
@@ -87,7 +90,8 @@
'device': {'type': 'string'},
'volumeId': {'type': 'string'},
'serverId': {'type': 'string'}
- }
+ },
+ 'additionalProperties': False,
# NOTE- If volume is not attached to any server
# then, 'attachments' attributes comes as array
# with empty object "[{}]" due to that elements
@@ -97,6 +101,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['id', 'status', 'displayName',
'availabilityZone', 'createdAt',
'displayDescription', 'volumeType',
@@ -105,6 +110,7 @@
}
}
},
+ 'additionalProperties': False,
'required': ['volumes']
}
}
diff --git a/tempest/clients.py b/tempest/clients.py
index 9f6a9bb..f0aedd5 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -56,7 +56,8 @@
from tempest.services.compute.json.migrations_client import \
MigrationsClientJSON
from tempest.services.compute.json.networks_client import NetworksClientJSON
-from tempest.services.compute.json.quotas_client import QuotaClassesClientJSON
+from tempest.services.compute.json.quota_classes_client import \
+ QuotaClassesClientJSON
from tempest.services.compute.json.quotas_client import QuotasClientJSON
from tempest.services.compute.json.security_group_default_rules_client import \
SecurityGroupDefaultRulesClientJSON
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 0a48b8d..fbfbbb7 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -14,7 +14,76 @@
# 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
@@ -23,6 +92,7 @@
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
@@ -37,7 +107,7 @@
LOG = logging.getLogger(__name__)
-def keystone_admin(opts):
+def get_admin_clients(opts):
_creds = tempest_lib.auth.KeystoneV2Credentials(
username=opts.os_username,
password=opts.os_password,
@@ -58,18 +128,28 @@
'build_interval': CONF.compute.build_interval,
'build_timeout': CONF.compute.build_timeout
}
- return identity_client.IdentityClientJSON(
+ 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):
- admin = keystone_admin(opts)
- roles = admin.list_roles()
+ 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', ()):
@@ -80,24 +160,24 @@
raise exceptions.TempestException(
"Role: %s - doesn't exist" % r
)
- existing = [x['name'] for x in admin.list_tenants()]
+ existing = [x['name'] for x in identity_admin.list_tenants()]
for tenant in resources['tenants']:
if tenant not in existing:
- admin.create_tenant(tenant)
+ 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 = admin.get_tenant_by_name(u['tenant'])
+ 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:
- admin.get_user_by_username(tenant['id'], u['name'])
+ identity_admin.get_user_by_username(tenant['id'], u['name'])
except tempest_lib.exceptions.NotFound:
- admin.create_user(
+ identity_admin.create_user(
u['name'], u['pass'], tenant['id'],
"%s@%s" % (u['name'], tenant['id']),
enabled=True)
@@ -108,21 +188,30 @@
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 = admin.get_tenant_by_name(u['tenant'])
+ 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 = admin.get_user_by_username(tenant['id'],
- u['name'])
+ 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:
- admin.assign_user_role(tenant['id'], user['id'], r)
+ identity_admin.assign_user_role(tenant['id'], user['id'], r)
except tempest_lib.exceptions.Conflict:
# don't care if it's already assigned
pass
@@ -130,6 +219,57 @@
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)))
@@ -185,12 +325,19 @@
def dump_accounts(opts, resources):
accounts = []
for user in resources['users']:
- accounts.append({
+ 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:
@@ -199,9 +346,9 @@
def get_options():
- usage_string = ('account_generator [-h] <ARG> ...\n\n'
+ usage_string = ('tempest-account-generator [-h] <ARG> ...\n\n'
'To see help on specific argument, do:\n'
- 'account_generator <ARG> -h')
+ 'tempest-account-generator <ARG> -h')
parser = argparse.ArgumentParser(
description='Create accounts.yaml file for concurrent test runs. '
'One primary user, one alt user, '
@@ -218,7 +365,7 @@
parser.add_argument('--os-username',
metavar='<auth-user-name>',
default=os.environ.get('OS_USERNAME'),
- help='User should have permitions '
+ help='User should have permissions '
'to create new user accounts and '
'tenants. Defaults to env[OS_USERNAME].')
parser.add_argument('--os-password',
@@ -243,7 +390,7 @@
parser.add_argument('--with-admin',
action='store_true',
dest='admin',
- help='Create admin in every tenant')
+ help='Creates admin for each concurrent group')
parser.add_argument('accounts',
metavar='accounts_file.yaml',
help='Output accounts yaml file')
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 1f71cb8..1de20d6 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -377,8 +377,8 @@
def dry_run(self):
client = self.limits_client
- quotas = client.get_absolute_limits()
- self.data['compute_quotas'] = quotas
+ quotas = client.show_limits()
+ self.data['compute_quotas'] = quotas['absolute']
# Begin network service classes
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index d3426c6..5871628 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -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/compute.py b/tempest/common/compute.py
new file mode 100644
index 0000000..5de4b0e
--- /dev/null
+++ b/tempest/common/compute.py
@@ -0,0 +1,124 @@
+# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_log import log as logging
+from oslo_utils import excutils
+from tempest_lib.common.utils import data_utils
+
+from tempest.common import fixed_network
+from tempest import config
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+def create_test_server(clients, validatable, validation_resources=None,
+ tenant_network=None, **kwargs):
+ """Common wrapper utility returning a test server.
+
+ This method is a common wrapper returning a test server that can be
+ pingable or sshable.
+
+ :param clients: Client manager which provides Openstack Tempest clients.
+ :param validatable: Whether the server will be pingable or sshable.
+ :param validation_resources: Resources created for the connection to the
+ server. Include a keypair, a security group and an IP.
+ :returns a tuple
+ """
+
+ # TODO(jlanoux) add support of wait_until PINGABLE/SSHABLE
+
+ if 'name' in kwargs:
+ name = kwargs.pop('name')
+ else:
+ name = data_utils.rand_name(__name__ + "-instance")
+
+ flavor = kwargs.get('flavor', CONF.compute.flavor_ref)
+ image_id = kwargs.get('image_id', CONF.compute.image_ref)
+
+ kwargs = fixed_network.set_networks_kwarg(
+ tenant_network, kwargs) or {}
+
+ if CONF.validation.run_validation and validatable:
+ # As a first implementation, multiple pingable or sshable servers will
+ # not be supported
+ if 'min_count' in kwargs or 'max_count' in kwargs:
+ msg = ("Multiple pingable or sshable servers not supported at "
+ "this stage.")
+ raise ValueError(msg)
+
+ if 'security_groups' in kwargs:
+ kwargs['security_groups'].append(
+ {'name': validation_resources['security_group']['name']})
+ else:
+ try:
+ kwargs['security_groups'] = [
+ {'name': validation_resources['security_group']['name']}]
+ except KeyError:
+ LOG.debug("No security group provided.")
+
+ if 'key_name' not in kwargs:
+ try:
+ kwargs['key_name'] = validation_resources['keypair']['name']
+ except KeyError:
+ LOG.debug("No key provided.")
+
+ if CONF.validation.connect_method == 'floating':
+ if 'wait_until' not in kwargs:
+ kwargs['wait_until'] = 'ACTIVE'
+
+ body = clients.servers_client.create_server(name, image_id, flavor,
+ **kwargs)
+
+ # handle the case of multiple servers
+ servers = [body]
+ if 'min_count' in kwargs or 'max_count' in kwargs:
+ # Get servers created which name match with name param.
+ body_servers = clients.servers_client.list_servers()
+ servers = \
+ [s for s in body_servers['servers'] if s['name'].startswith(name)]
+
+ # The name of the method to associate a floating IP to as server is too
+ # long for PEP8 compliance so:
+ assoc = clients.floating_ips_client.associate_floating_ip_to_server
+
+ if 'wait_until' in kwargs:
+ for server in servers:
+ try:
+ clients.servers_client.wait_for_server_status(
+ server['id'], kwargs['wait_until'])
+
+ # Multiple validatable servers are not supported for now. Their
+ # creation will fail with the condition above (l.58).
+ if CONF.validation.run_validation and validatable:
+ if CONF.validation.connect_method == 'floating':
+ assoc(floating_ip=validation_resources[
+ 'floating_ip']['ip'],
+ server_id=servers[0]['id'])
+
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ if ('preserve_server_on_error' not in kwargs
+ or kwargs['preserve_server_on_error'] is False):
+ for server in servers:
+ try:
+ clients.servers_client.delete_server(
+ server['id'])
+ except Exception:
+ LOG.exception('Deleting server %s failed'
+ % server['id'])
+
+ return body, servers
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 2c6e334..de44c4d 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,8 @@
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()
+ networks = [n for n in networks if n['label'] == name]
# Check that a network exists, else raise an InvalidConfigurationException
if len(networks) == 1:
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 4b3995b..d4e6eb8 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -14,6 +14,7 @@
import re
import time
+from oslo_log import log as logging
import six
from tempest_lib.common import ssh
@@ -22,6 +23,8 @@
CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
class RemoteClient(object):
@@ -48,7 +51,8 @@
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
+ cmd = CONF.compute.ssh_shell_prologue + " " + cmd
+ LOG.debug("Remote command: %s" % cmd)
return self.ssh_client.exec_command(cmd)
def validate_authentication(self):
diff --git a/tempest/config.py b/tempest/config.py
index bbd6772..fc55723 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -206,6 +206,10 @@
help="Timeout in seconds to wait for an instance to build. "
"Other services that do not define build_timeout will "
"inherit this value."),
+ cfg.StrOpt('ssh_shell_prologue',
+ default="set -eu -o pipefail; PATH=$$PATH:/sbin;",
+ help="Shell fragments to use before executing a command "
+ "when sshing to a guest."),
cfg.StrOpt('ssh_auth_method',
default='keypair',
help="Auth method used for authenticate to the instance. "
@@ -433,6 +437,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',
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 29898a9..db6375f 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -27,6 +27,7 @@
SETUP_TEARDOWN_CLASS_DEFINITION = re.compile(r'^\s+def (setUp|tearDown)Class')
SCENARIO_DECORATOR = re.compile(r'\s*@.*services\((.*)\)')
VI_HEADER_RE = re.compile(r"^#\s+vim?:.+")
+RAND_NAME_HYPHEN_RE = re.compile(r".*rand_name\(.+[\-\_][\"\']\)")
mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
@@ -106,6 +107,21 @@
"T107: service tag should not be in path")
+def no_hyphen_at_end_of_rand_name(logical_line, filename):
+ """Check no hyphen at the end of rand_name() argument
+
+ T108
+ """
+ if './tempest/api/network/' in filename:
+ # Network API tests are migrating from Tempest to Neutron repo now.
+ # So here should avoid network API tests checks.
+ return
+
+ msg = "T108: hyphen should not be specified at the end of rand_name()"
+ if RAND_NAME_HYPHEN_RE.match(logical_line):
+ return 0, msg
+
+
def no_mutable_default_args(logical_line):
"""Check that mutable object isn't used as default argument
@@ -122,4 +138,5 @@
register(no_setup_teardown_class_for_tests)
register(no_vi_headers)
register(service_tags_not_in_module_path)
+ register(no_hyphen_at_end_of_rand_name)
register(no_mutable_default_args)
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_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/utils.py b/tempest/scenario/utils.py
index f8d9dd4..cd9d925 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -65,7 +65,7 @@
def is_flavor_enough(self, flavor_id, image_id):
_image = self.images_client.show_image(image_id)
- _flavor = self.flavors_client.get_flavor_details(flavor_id)
+ _flavor = self.flavors_client.show_flavor(flavor_id)
return self._is_flavor_enough(_flavor, _image)
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 7938d8e..ebe4117 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -27,27 +27,22 @@
class FlavorsClientJSON(service_client.ServiceClient):
- def list_flavors(self, params=None):
+ def list_flavors(self, detail=False, **params):
url = 'flavors'
+ _schema = schema.list_flavors
+
+ if detail:
+ url += '/detail'
+ _schema = schema.list_flavors_details
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.list_flavors, resp, body)
+ self.validate_response(_schema, resp, body)
return service_client.ResponseBodyList(resp, body['flavors'])
- def list_flavors_with_detail(self, params=None):
- url = 'flavors/detail'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- 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)
@@ -84,10 +79,10 @@
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()
+ flavors = self.list_flavors(detail=True)
for flavor in flavors:
if flavor['id'] == id:
return False
@@ -108,7 +103,7 @@
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)
@@ -116,7 +111,7 @@
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))
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index f30bfdb..6095dc0 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -35,7 +35,7 @@
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)
@@ -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
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/limits_client.py b/tempest/services/compute/json/limits_client.py
index d2aaec6..1454b73 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -21,17 +21,8 @@
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)
- return service_client.ResponseBody(resp, body['limits']['absolute'])
-
- def get_specific_absolute_limit(self, absolute_limit):
- resp, body = self.get("limits")
- body = json.loads(body)
- self.validate_response(schema.get_limit, resp, body)
- if absolute_limit not in body['limits']['absolute']:
- return None
- else:
- return body['limits']['absolute'][absolute_limit]
+ return service_client.ResponseBody(resp, body['limits'])
diff --git a/tempest/services/compute/json/networks_client.py b/tempest/services/compute/json/networks_client.py
index 0ae0920..e641787 100644
--- a/tempest/services/compute/json/networks_client.py
+++ b/tempest/services/compute/json/networks_client.py
@@ -20,17 +20,13 @@
class NetworksClientJSON(service_client.ServiceClient):
- def list_networks(self, name=None):
+ def list_networks(self):
resp, body = self.get("os-networks")
body = json.loads(body)
self.expected_success(200, resp.status)
- if name:
- networks = [n for n in body['networks'] if n['label'] == name]
- else:
- networks = body['networks']
- return service_client.ResponseBodyList(resp, networks)
+ return service_client.ResponseBodyList(resp, body['networks'])
- def get_network(self, network_id):
+ def show_network(self, network_id):
resp, body = self.get("os-networks/%s" % str(network_id))
body = json.loads(body)
self.expected_success(200, resp.status)
diff --git a/tempest/services/compute/json/quota_classes_client.py b/tempest/services/compute/json/quota_classes_client.py
new file mode 100644
index 0000000..ad35e3f
--- /dev/null
+++ b/tempest/services/compute/json/quota_classes_client.py
@@ -0,0 +1,46 @@
+# Copyright 2012 NTT Data
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.api_schema.response.compute.v2_1\
+ import quota_classes as classes_schema
+from tempest.common import service_client
+
+
+class QuotaClassesClientJSON(service_client.ServiceClient):
+
+ def show_quota_class_set(self, quota_class_id):
+ """List the quota class set for a quota class."""
+
+ url = 'os-quota-class-sets/%s' % quota_class_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.validate_response(classes_schema.get_quota_class_set, resp, body)
+ return service_client.ResponseBody(resp, body['quota_class_set'])
+
+ def update_quota_class_set(self, quota_class_id, **kwargs):
+ """
+ Updates the quota class's limits for one or more resources.
+ """
+ post_body = json.dumps({'quota_class_set': kwargs})
+
+ resp, body = self.put('os-quota-class-sets/%s' % quota_class_id,
+ post_body)
+
+ body = json.loads(body)
+ self.validate_response(classes_schema.update_quota_class_set,
+ resp, body)
+ return service_client.ResponseBody(resp, body['quota_class_set'])
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 6e38c47..f488817 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -15,29 +15,27 @@
import json
-from tempest.api_schema.response.compute.v2_1\
- import quota_classes as classes_schema
from tempest.api_schema.response.compute.v2_1 import quotas as schema
from tempest.common import service_client
class QuotasClientJSON(service_client.ServiceClient):
- def get_quota_set(self, tenant_id, user_id=None):
+ def show_quota_set(self, tenant_id, user_id=None):
"""List the quota set for a tenant."""
- url = 'os-quota-sets/%s' % str(tenant_id)
+ url = 'os-quota-sets/%s' % tenant_id
if user_id:
- url += '?user_id=%s' % str(user_id)
+ url += '?user_id=%s' % user_id
resp, body = self.get(url)
body = json.loads(body)
self.validate_response(schema.get_quota_set, resp, body)
return service_client.ResponseBody(resp, body['quota_set'])
- def get_default_quota_set(self, tenant_id):
+ def show_default_quota_set(self, tenant_id):
"""List the default quota set for a tenant."""
- url = 'os-quota-sets/%s/defaults' % str(tenant_id)
+ url = 'os-quota-sets/%s/defaults' % tenant_id
resp, body = self.get(url)
body = json.loads(body)
self.validate_response(schema.get_quota_set, resp, body)
@@ -99,9 +97,9 @@
if user_id:
resp, body = self.put('os-quota-sets/%s?user_id=%s' %
- (str(tenant_id), str(user_id)), post_body)
+ (tenant_id, user_id), post_body)
else:
- resp, body = self.put('os-quota-sets/%s' % str(tenant_id),
+ resp, body = self.put('os-quota-sets/%s' % tenant_id,
post_body)
body = json.loads(body)
@@ -110,32 +108,6 @@
def delete_quota_set(self, tenant_id):
"""Delete the tenant's quota set."""
- resp, body = self.delete('os-quota-sets/%s' % str(tenant_id))
+ resp, body = self.delete('os-quota-sets/%s' % tenant_id)
self.validate_response(schema.delete_quota, resp, body)
return service_client.ResponseBody(resp, body)
-
-
-class QuotaClassesClientJSON(service_client.ServiceClient):
-
- def get_quota_class_set(self, quota_class_id):
- """List the quota class set for a quota class."""
-
- url = 'os-quota-class-sets/%s' % str(quota_class_id)
- resp, body = self.get(url)
- body = json.loads(body)
- self.validate_response(classes_schema.get_quota_class_set, resp, body)
- return service_client.ResponseBody(resp, body['quota_class_set'])
-
- def update_quota_class_set(self, quota_class_id, **kwargs):
- """
- Updates the quota class's limits for one or more resources.
- """
- post_body = json.dumps({'quota_class_set': kwargs})
-
- resp, body = self.put('os-quota-class-sets/%s' % str(quota_class_id),
- post_body)
-
- body = json.loads(body)
- self.validate_response(classes_schema.update_quota_class_set,
- resp, body)
- return service_client.ResponseBody(resp, body['quota_class_set'])
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 7c58f01..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 list_images(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():
@@ -265,8 +257,9 @@
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 a4cb48c..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,6 +96,18 @@
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)
@@ -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)
@@ -166,7 +178,7 @@
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/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
index 0df2eb1..b2a30e9 100644
--- a/tempest/stress/actions/ssh_floating.py
+++ b/tempest/stress/actions/ssh_floating.py
@@ -91,8 +91,8 @@
def _create_sec_group(self):
sec_grp_cli = self.manager.security_groups_client
- s_name = data_utils.rand_name('sec_grp-')
- s_description = data_utils.rand_name('desc-')
+ s_name = data_utils.rand_name('sec_grp')
+ s_description = data_utils.rand_name('desc')
self.sec_grp = sec_grp_cli.create_security_group(s_name,
s_description)
create_rule = sec_grp_cli.create_security_group_rule
@@ -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/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
index f98f8ba..f0921d5 100644
--- a/tempest/tests/cmd/test_javelin.py
+++ b/tempest/tests/cmd/test_javelin.py
@@ -61,6 +61,26 @@
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):
@@ -89,7 +109,7 @@
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()
+ lib_exc.NotFound("user is not found")
self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
return_value=self.fake_client))
@@ -106,7 +126,7 @@
def test_create_user_missing_tenant(self):
self.fake_client.identity.get_tenant_by_name.side_effect = \
- lib_exc.NotFound()
+ lib_exc.NotFound("tenant is not found")
self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
return_value=self.fake_client))
@@ -152,7 +172,7 @@
self.fake_object['format'],
self.fake_object['format'])
- mocked_function = self.fake_client.images.store_image
+ 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())
@@ -190,14 +210,84 @@
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)
+
+ def test_create_router(self):
+
+ self.fake_client.networks.list_routers.return_value = {'routers': []}
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ javelin.create_routers([self.fake_object])
+
+ mocked_function = self.fake_client.networks.create_router
+ mocked_function.assert_called_once_with(self.fake_object['name'])
+
+ def test_create_router_existing(self):
+ self.fake_client.networks.list_routers.return_value = {
+ 'routers': [self.fake_object]}
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ javelin.create_routers([self.fake_object])
+
+ mocked_function = self.fake_client.networks.create_router
+ self.assertFalse(mocked_function.called)
+
+ def test_create_secgroup(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ self.fake_client.secgroups.list_security_groups.return_value = []
+ self.fake_client.secgroups.create_security_group.return_value = \
+ {'id': self.fake_object['secgroup_id']}
+
+ javelin.create_secgroups([self.fake_object])
+
+ mocked_function = self.fake_client.secgroups.create_security_group
+ mocked_function.assert_called_once_with(
+ self.fake_object['name'],
+ self.fake_object['description'])
+
class TestDestroyResources(JavelinUnitTest):
def test_destroy_tenants(self):
fake_tenant = self.fake_object['tenant']
-
- fake_auth = mock.MagicMock()
+ fake_auth = self.fake_client
fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
@@ -212,7 +302,7 @@
fake_user = self.fake_object['user']
fake_tenant = self.fake_object['tenant']
- fake_auth = mock.MagicMock()
+ fake_auth = self.fake_client
fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
fake_auth.identity.get_user_by_username.return_value = fake_user
@@ -226,41 +316,98 @@
def test_destroy_objects(self):
- fake_client = mock.MagicMock()
- fake_client.objects.delete_object.return_value = {'status': "200"}, ""
+ self.fake_client.objects.delete_object.return_value = \
+ {'status': "200"}, ""
self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
- return_value=fake_client))
+ return_value=self.fake_client))
javelin.destroy_objects([self.fake_object])
- mocked_function = fake_client.objects.delete_object
+ mocked_function = self.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))
+ return_value=self.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 = self.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))
+ return_value=self.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 = self.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'])
+
+ def test_destroy_subnets(self):
+
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ fake_subnet_id = self.fake_object['subnet_id']
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
+ return_value={
+ 'id': fake_subnet_id}))
+
+ javelin.destroy_subnets([self.fake_object])
+
+ mocked_function = self.fake_client.networks.delete_subnet
+ mocked_function.assert_called_once_with(fake_subnet_id)
+
+ def test_destroy_routers(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+
+ # this function is used on 2 different occasions in the code
+ def _fake_get_resource_by_name(*args):
+ if args[1] == "routers":
+ return {"id": self.fake_object['router_id']}
+ elif args[1] == "subnets":
+ return {"id": self.fake_object['subnet_id']}
+ javelin._get_resource_by_name = _fake_get_resource_by_name
+
+ javelin.destroy_routers([self.fake_object])
+
+ mocked_function = self.fake_client.networks.delete_router
+ mocked_function.assert_called_once_with(
+ self.fake_object['router_id'])
+
+ def test_destroy_secgroup(self):
+ self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+ return_value=self.fake_client))
+ fake_secgroup = {'id': self.fake_object['id']}
+ self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
+ return_value=fake_secgroup))
+
+ javelin.destroy_secgroups([self.fake_object])
+
+ mocked_function = self.fake_client.secgroups.delete_security_group
+ mocked_function.assert_called_once_with(self.fake_object['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/test_accounts.py b/tempest/tests/common/test_accounts.py
index 596e811..e713969 100644
--- a/tempest/tests/common/test_accounts.py
+++ b/tempest/tests/common/test_accounts.py
@@ -299,7 +299,8 @@
test_accounts_class = accounts.Accounts('v2', 'test_name')
with mock.patch('tempest.services.compute.json.networks_client.'
'NetworksClientJSON.list_networks',
- return_value=[{'name': 'network-2', 'id': 'fake-id'}]):
+ return_value=[{'name': 'network-2', 'id': 'fake-id',
+ 'label': 'network-2'}]):
creds = test_accounts_class.get_creds_by_roles(['role-7'])
self.assertTrue(isinstance(creds, cred_provider.TestResources))
network = creds.network
diff --git a/tempest/tests/common/test_service_clients.py b/tempest/tests/common/test_service_clients.py
index 9bb58b0..c6c1053 100644
--- a/tempest/tests/common/test_service_clients.py
+++ b/tempest/tests/common/test_service_clients.py
@@ -34,6 +34,7 @@
from tempest.services.compute.json import limits_client
from tempest.services.compute.json import migrations_client
from tempest.services.compute.json import networks_client as nova_net_client
+from tempest.services.compute.json import quota_classes_client
from tempest.services.compute.json import quotas_client
from tempest.services.compute.json import security_group_default_rules_client \
as nova_secgrop_default_client
@@ -122,7 +123,7 @@
migrations_client.MigrationsClientJSON,
nova_net_client.NetworksClientJSON,
quotas_client.QuotasClientJSON,
- quotas_client.QuotaClassesClientJSON,
+ quota_classes_client.QuotaClassesClientJSON,
nova_secgrop_default_client.SecurityGroupDefaultRulesClientJSON,
security_groups_client.SecurityGroupsClientJSON,
servers_client.ServersClientJSON,
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index fd01887..9bc9cfe 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -119,6 +119,13 @@
self.assertFalse(checks.service_tags_not_in_module_path(
"@test.services('compute')", './tempest/api/image/fake_test.py'))
+ def test_no_hyphen_at_end_of_rand_name(self):
+ self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
+ 'data_utils.rand_name("fake-resource")', './tempest/test_foo.py'))
+ self.assertEqual(2, len(list(checks.no_hyphen_at_end_of_rand_name(
+ 'data_utils.rand_name("fake-resource-")', './tempest/test_foo.py')
+ )))
+
def test_no_mutable_default_args(self):
self.assertEqual(1, len(list(checks.no_mutable_default_args(
" def function1(para={}):"))))
diff --git a/test-requirements.txt b/test-requirements.txt
index 32f33bc..9bd3f46 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,11 +3,11 @@
# process, which may cause wedges in the gate later.
hacking<0.11,>=0.10.0
# needed for doc build
-sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
+sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
python-subunit>=0.0.18
-oslosphinx>=2.5.0 # Apache-2.0
+oslosphinx>=2.5.0 # Apache-2.0
mox>=0.5.3
mock>=1.0
coverage>=3.6
-oslotest>=1.5.1 # Apache-2.0
-stevedore>=1.3.0 # Apache-2.0
+oslotest>=1.5.1 # Apache-2.0
+stevedore>=1.5.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 4bb5df6..b495fd6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,6 +13,13 @@
[testenv]
setenv = VIRTUAL_ENV={envdir}
OS_TEST_PATH=./tempest/tests
+passenv = OS_STDOUT_CAPTURE
+ OS_STDERR_CAPTURE
+ OS_TEST_TIMEOUT
+ OS_TEST_LOCK_PATH
+ OS_TEST_PATH
+ TEMPEST_CONFIG
+ TEMPEST_CONFIG_DIR
usedevelop = True
install_command = pip install -U {opts} {packages}
whitelist_externals = *