Merge "Add check_uptodate.sh to run_tests.sh -p"
diff --git a/README.rst b/README.rst
index bff2bf8..9daf873 100644
--- a/README.rst
+++ b/README.rst
@@ -118,3 +118,15 @@
Alternatively, you can use the run_tests.sh script which will create a venv and
run the unit tests. There are also the py26, py27, or py33 tox jobs which will
run the unit tests with the corresponding version of python.
+
+Python 2.6
+----------
+
+Tempest can be run with Python 2.6 however the unit tests and the gate
+currently only run with Python 2.7, so there are no guarantees about the state
+of tempest when running with Python 2.6. Additionally, to enable testr to work
+with tempest using python 2.6 the discover module from the unittest-ext
+project has to be patched to switch the unittest.TestSuite to use
+unittest2.TestSuite instead. See::
+
+https://code.google.com/p/unittest-ext/issues/detail?id=79
diff --git a/doc/source/conf.py b/doc/source/conf.py
index e5444ae..bd4e553 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -30,7 +30,7 @@
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
- 'oslo.sphinx'
+ 'oslosphinx'
]
todo_include_todos = True
diff --git a/etc/schemas/compute/flavors/flavor_details.json b/etc/schemas/compute/flavors/flavor_details.json
index d1c1077..c16075c 100644
--- a/etc/schemas/compute/flavors/flavor_details.json
+++ b/etc/schemas/compute/flavors/flavor_details.json
@@ -2,5 +2,7 @@
"name": "get-flavor-details",
"http-method": "GET",
"url": "flavors/%s",
- "resources": ["flavor"]
+ "resources": [
+ {"name": "flavor", "expected_result": 404}
+ ]
}
diff --git a/etc/schemas/compute/flavors/flavor_details_v3.json b/etc/schemas/compute/flavors/flavor_details_v3.json
new file mode 100644
index 0000000..d1c1077
--- /dev/null
+++ b/etc/schemas/compute/flavors/flavor_details_v3.json
@@ -0,0 +1,6 @@
+{
+ "name": "get-flavor-details",
+ "http-method": "GET",
+ "url": "flavors/%s",
+ "resources": ["flavor"]
+}
diff --git a/etc/schemas/compute/flavors/flavors_list_v3.json b/etc/schemas/compute/flavors/flavors_list_v3.json
new file mode 100644
index 0000000..d5388b3
--- /dev/null
+++ b/etc/schemas/compute/flavors/flavors_list_v3.json
@@ -0,0 +1,24 @@
+{
+ "name": "list-flavors-with-detail",
+ "http-method": "GET",
+ "url": "flavors/detail",
+ "json-schema": {
+ "type": "object",
+ "properties": {
+ "min_ram": {
+ "type": "integer",
+ "results": {
+ "gen_none": 400,
+ "gen_string": 400
+ }
+ },
+ "min_disk": {
+ "type": "integer",
+ "results": {
+ "gen_none": 400,
+ "gen_string": 400
+ }
+ }
+ }
+ }
+}
diff --git a/etc/schemas/compute/servers/get_console_output.json b/etc/schemas/compute/servers/get_console_output.json
index 7c3860f..8d974ba 100644
--- a/etc/schemas/compute/servers/get_console_output.json
+++ b/etc/schemas/compute/servers/get_console_output.json
@@ -2,7 +2,9 @@
"name": "get-console-output",
"http-method": "POST",
"url": "servers/%s/action",
- "resources": ["server"],
+ "resources": [
+ {"name":"server", "expected_result": 404}
+ ],
"json-schema": {
"type": "object",
"properties": {
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 48ca905..025d0f3 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -397,6 +397,10 @@
# (string value)
#uri_v3=<None>
+# Identity API version to be used for authentication for API
+# tests. (string value)
+#auth_version=v2
+
# The identity region name to use. Also used as the other
# services' region name unless they are set explicitly. If no
# such region is found in the service catalog, the first found
@@ -449,6 +453,12 @@
# enabled (boolean value)
#trust=true
+# Is the v2 identity API enabled (boolean value)
+#api_v2=true
+
+# Is the v3 identity API enabled (boolean value)
+#api_v3=true
+
[image]
@@ -520,13 +530,20 @@
# value)
#region=
-# The cidr block to allocate tenant networks from (string
+# The cidr block to allocate tenant ipv4 subnets from (string
# value)
#tenant_network_cidr=10.100.0.0/16
-# The mask bits for tenant networks (integer value)
+# The mask bits for tenant ipv4 subnets (integer value)
#tenant_network_mask_bits=28
+# The cidr block to allocate tenant ipv6 subnets from (string
+# value)
+#tenant_network_v6_cidr=2003::/64
+
+# The mask bits for tenant ipv6 subnets (integer value)
+#tenant_network_v6_mask_bits=96
+
# Whether tenant network connectivity should be evaluated
# directly (boolean value)
#tenant_networks_reachable=false
@@ -748,6 +765,11 @@
# value)
#default_thread_number_per_action=4
+# Prevent the cleaning (tearDownClass()) between each stress
+# test run if an exception occurs during this run. (boolean
+# value)
+#leave_dirty_stack=false
+
[telemetry]
@@ -816,4 +838,7 @@
# Is the v1 volume API enabled (boolean value)
#api_v1=true
+# Is the v2 volume API enabled (boolean value)
+#api_v2=true
+
diff --git a/requirements.txt b/requirements.txt
index 8c0f872..8573a2e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,6 +14,7 @@
python-neutronclient>=2.3.3,<3
python-cinderclient>=1.0.6
python-heatclient>=0.2.3
+python-savannaclient>=0.4.1
python-swiftclient>=1.5
testresources>=0.2.4
keyring>=1.6.1,<2.0,>=2.1
diff --git a/setup.cfg b/setup.cfg
index 23a97ff..79f538f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -21,3 +21,6 @@
all_files = 1
build-dir = doc/build
source-dir = doc/source
+
+[wheel]
+universal = 1
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 252f4be..3e13bf8 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -172,7 +172,6 @@
flag = True
self.assertTrue(flag)
- @test.skip_because(bug="1209101")
@test.attr(type='gate')
def test_list_non_public_flavor(self):
# Create a flavor with os-flavor-access:is_public false should
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 2cee78a..1078847 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -15,8 +15,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
-from tempest.test import skip_because
+from tempest import test
class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -25,6 +24,7 @@
Tests Servers API using admin privileges
"""
+ _host_key = 'OS-EXT-SRV-ATTR:host'
_interface = 'json'
@classmethod
@@ -54,7 +54,7 @@
flavor_id = data_utils.rand_int_id(start=1000)
return flavor_id
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_admin(self):
# Listing servers by admin user returns empty list by default
resp, body = self.client.list_servers_with_detail()
@@ -62,7 +62,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual([], servers)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_filter_by_error_status(self):
# Filter the list of servers by server error status
params = {'status': 'error'}
@@ -78,7 +78,7 @@
self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_admin_with_all_tenants(self):
# Listing servers by admin user with all tenants parameter
# Here should be listed all servers
@@ -90,7 +90,34 @@
self.assertIn(self.s1_name, servers_name)
self.assertIn(self.s2_name, servers_name)
- @attr(type='gate')
+ @test.attr(type='gate')
+ def test_list_servers_filter_by_exist_host(self):
+ # Filter the list of servers by existent host
+ name = data_utils.rand_name('server')
+ flavor = self.flavor_ref
+ image_id = self.image_ref
+ resp, test_server = self.client.create_server(
+ name, image_id, flavor)
+ self.assertEqual('202', resp['status'])
+ self.addCleanup(self.client.delete_server, test_server['id'])
+ self.client.wait_for_server_status(test_server['id'], 'ACTIVE')
+ resp, server = self.client.get_server(test_server['id'])
+ self.assertEqual(server['status'], 'ACTIVE')
+ hostname = server[self._host_key]
+ params = {'host': hostname}
+ resp, body = self.client.list_servers(params)
+ self.assertEqual('200', resp['status'])
+ servers = body['servers']
+ nonexistent_params = {'host': 'nonexistent_host'}
+ resp, nonexistent_body = self.client.list_servers(
+ nonexistent_params)
+ self.assertEqual('200', resp['status'])
+ nonexistent_servers = nonexistent_body['servers']
+ self.assertIn(test_server['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(test_server['id'],
+ map(lambda x: x['id'], nonexistent_servers))
+
+ @test.attr(type='gate')
def test_admin_delete_servers_of_others(self):
# Administrator can delete servers of others
_, server = self.create_test_server()
@@ -98,7 +125,7 @@
self.assertEqual('204', resp['status'])
self.servers_client.wait_for_server_termination(server['id'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_delete_server_while_in_error_state(self):
# Delete a server while it's VM state is error
resp, server = self.create_test_server(wait_until='ACTIVE')
@@ -110,7 +137,7 @@
resp, _ = self.client.delete_server(server['id'])
self.assertEqual('204', resp['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_reset_state_server(self):
# Reset server's state to 'error'
resp, server = self.client.reset_state(self.s1_id)
@@ -128,8 +155,8 @@
resp, server = self.client.get_server(self.s1_id)
self.assertEqual(server['status'], 'ACTIVE')
- @attr(type='gate')
- @skip_because(bug="1240043")
+ @test.attr(type='gate')
+ @test.skip_because(bug="1240043")
def test_get_server_diagnostics_by_admin(self):
# Retrieve server diagnostics by admin user
resp, diagnostic = self.client.get_server_diagnostics(self.s1_id)
@@ -140,12 +167,12 @@
for key in basic_attrs:
self.assertIn(key, str(diagnostic.keys()))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_rebuild_server_in_error_state(self):
# The server in error state should be rebuilt using the provided
# image and changed to ACTIVE state
- # resetting vm state require admin priviledge
+ # resetting vm state require admin privilege
resp, server = self.client.reset_state(self.s1_id, state='error')
self.assertEqual(202, resp.status)
resp, rebuilt_server = self.non_admin_client.rebuild(
@@ -170,4 +197,6 @@
class ServersAdminTestXML(ServersAdminTestJSON):
+ _host_key = (
+ '{http://docs.openstack.org/compute/ext/extended_status/api/v1.1}host')
_interface = 'xml'
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index f8b0051..72bb723 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -51,6 +51,7 @@
cls.servers = []
cls.images = []
cls.multi_user = cls.get_multi_user()
+ cls.security_groups = []
@classmethod
def get_multi_user(cls):
@@ -102,9 +103,25 @@
pass
@classmethod
+ def clear_security_groups(cls):
+ for sg in cls.security_groups:
+ try:
+ resp, body =\
+ cls.security_groups_client.delete_security_group(sg['id'])
+ except exceptions.NotFound:
+ # The security group may have already been deleted which is OK.
+ pass
+ except Exception as exc:
+ LOG.info('Exception raised deleting security group %s',
+ sg['id'])
+ LOG.exception(exc)
+ pass
+
+ @classmethod
def tearDownClass(cls):
cls.clear_images()
cls.clear_servers()
+ cls.clear_security_groups()
cls.clear_isolated_creds()
super(BaseComputeTest, cls).tearDownClass()
@@ -146,6 +163,19 @@
return resp, body
+ @classmethod
+ def create_security_group(cls, name=None, description=None):
+ if name is None:
+ name = data_utils.rand_name(cls.__name__ + "-securitygroup")
+ if description is None:
+ description = data_utils.rand_name('description-')
+ resp, body = \
+ cls.security_groups_client.create_security_group(name,
+ description)
+ cls.security_groups.append(body)
+
+ return resp, body
+
def wait_for(self, condition):
"""Repeatedly calls condition() until a timeout."""
start_time = int(time.time())
@@ -161,6 +191,18 @@
return
time.sleep(self.build_interval)
+ @staticmethod
+ def _delete_volume(volumes_client, volume_id):
+ """Deletes the given volume and waits for it to be gone."""
+ try:
+ resp, _ = volumes_client.delete_volume(volume_id)
+ # TODO(mriedem): We should move the wait_for_resource_deletion
+ # into the delete_volume method as a convenience to the caller.
+ volumes_client.wait_for_resource_deletion(volume_id)
+ except exceptions.NotFound:
+ LOG.warn("Unable to delete volume '%s' since it was not found. "
+ "Maybe it was already deleted?" % volume_id)
+
class BaseV2ComputeTest(BaseComputeTest):
@@ -188,7 +230,6 @@
cls.instance_usages_audit_log_client = \
cls.os.instance_usages_audit_log_client
cls.hypervisor_client = cls.os.hypervisor_client
- cls.servers_client_v3_auth = cls.os.servers_client_v3_auth
cls.certificates_client = cls.os.certificates_client
@classmethod
@@ -232,14 +273,7 @@
@classmethod
def delete_volume(cls, volume_id):
"""Deletes the given volume and waits for it to be gone."""
- try:
- resp, _ = cls.volumes_extensions_client.delete_volume(volume_id)
- # TODO(mriedem): We should move the wait_for_resource_deletion
- # into the delete_volume method as a convenience to the caller.
- cls.volumes_extensions_client.wait_for_resource_deletion(volume_id)
- except exceptions.NotFound:
- LOG.warn("Unable to delete volume '%s' since it was not found. "
- "Maybe it was already deleted?" % volume_id)
+ cls._delete_volume(cls.volumes_extensions_client, volume_id)
class BaseV2ComputeAdminTest(BaseV2ComputeTest):
@@ -292,11 +326,8 @@
cls.extensions_client = cls.os.extensions_v3_client
cls.availability_zone_client = cls.os.availability_zone_v3_client
cls.interfaces_client = cls.os.interfaces_v3_client
- cls.instance_usages_audit_log_client = \
- cls.os.instance_usages_audit_log_v3_client
cls.hypervisor_client = cls.os.hypervisor_v3_client
cls.keypairs_client = cls.os.keypairs_v3_client
- cls.tenant_usages_client = cls.os.tenant_usages_v3_client
cls.volumes_client = cls.os.volumes_client
cls.certificates_client = cls.os.certificates_v3_client
cls.keypairs_client = cls.os.keypairs_v3_client
@@ -337,6 +368,11 @@
cls.password = server['admin_password']
return server['id']
+ @classmethod
+ def delete_volume(cls, volume_id):
+ """Deletes the given volume and waits for it to be gone."""
+ cls._delete_volume(cls.volumes_client, volume_id)
+
class BaseV3ComputeAdminTest(BaseV3ComputeTest):
"""Base test case class for all Compute Admin API V3 tests."""
@@ -363,13 +399,10 @@
cls.os_adm = os_adm
cls.servers_admin_client = cls.os_adm.servers_v3_client
- cls.instance_usages_audit_log_admin_client = \
- cls.os_adm.instance_usages_audit_log_v3_client
cls.services_admin_client = cls.os_adm.services_v3_client
cls.availability_zone_admin_client = \
cls.os_adm.availability_zone_v3_client
cls.hypervisor_admin_client = cls.os_adm.hypervisor_v3_client
- cls.tenant_usages_admin_client = cls.os_adm.tenant_usages_v3_client
cls.flavors_admin_client = cls.os_adm.flavors_v3_client
cls.aggregates_admin_client = cls.os_adm.aggregates_v3_client
cls.hosts_admin_client = cls.os_adm.hosts_v3_client
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index f82143e..6cbf18d 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -15,7 +15,6 @@
from tempest.api.compute import base
from tempest import config
-from tempest import exceptions
from tempest.openstack.common import log as logging
from tempest.test import attr
@@ -64,12 +63,6 @@
cls.tearDownClass()
raise
- @attr(type=['negative', 'gate'])
- def test_get_image_not_existing(self):
- # Check raises a NotFound
- self.assertRaises(exceptions.NotFound, self.client.get_image,
- "nonexistingimageid")
-
@attr(type='gate')
def test_list_images_filter_by_status(self):
# The list of images should contain only images with the
@@ -221,11 +214,6 @@
resp, images = self.client.list_images_with_detail(params)
self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- @attr(type=['negative', 'gate'])
- def test_get_nonexistent_image(self):
- # Negative test: GET on non-existent image should fail
- self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
-
class ListImageFiltersTestXML(ListImageFiltersTestJSON):
_interface = 'xml'
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
new file mode 100644
index 0000000..3b19d3c
--- /dev/null
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -0,0 +1,44 @@
+# Copyright 2014 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import exceptions
+from tempest import test
+
+CONF = config.CONF
+
+
+class ListImageFiltersNegativeTestJSON(base.BaseV2ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ListImageFiltersNegativeTestJSON, cls).setUpClass()
+ if not CONF.service_available.glance:
+ skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
+ cls.client = cls.images_client
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_nonexistent_image(self):
+ # Check raises a NotFound
+ nonexistent_image = data_utils.rand_uuid()
+ self.assertRaises(exceptions.NotFound, self.client.get_image,
+ nonexistent_image)
+
+
+class ListImageFiltersNegativeTestXML(ListImageFiltersNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 375105e..17bb489 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -14,9 +14,8 @@
# under the License.
from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
from tempest import config
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -30,17 +29,13 @@
cls.client = cls.security_groups_client
cls.neutron_available = CONF.service_available.neutron
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_security_group_rules_create(self):
# Positive test: Creation of Security Group rule
# should be successful
# Creating a Security Group to add rules to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- securitygroup_id = securitygroup['id']
- self.addCleanup(self.client.delete_security_group, securitygroup_id)
+ resp, security_group = self.create_security_group()
+ securitygroup_id = security_group['id']
# Adding rules to the created Security Group
ip_protocol = 'tcp'
from_port = 22
@@ -53,7 +48,7 @@
self.addCleanup(self.client.delete_security_group_rule, rule['id'])
self.assertEqual(200, resp.status)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_security_group_rules_create_with_optional_arguments(self):
# Positive test: Creation of Security Group rule
# with optional arguments
@@ -62,19 +57,11 @@
secgroup1 = None
secgroup2 = None
# Creating a Security Group to add rules to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- secgroup1 = securitygroup['id']
- self.addCleanup(self.client.delete_security_group, secgroup1)
+ resp, security_group = self.create_security_group()
+ secgroup1 = security_group['id']
# Creating a Security Group so as to assign group_id to the rule
- s_name2 = data_utils.rand_name('securitygroup-')
- s_description2 = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name2, s_description2)
- secgroup2 = securitygroup['id']
- self.addCleanup(self.client.delete_security_group, secgroup2)
+ resp, security_group = self.create_security_group()
+ secgroup2 = security_group['id']
# Adding rules to the created Security Group with optional arguments
parent_group_id = secgroup1
ip_protocol = 'tcp'
@@ -92,18 +79,13 @@
self.addCleanup(self.client.delete_security_group_rule, rule['id'])
self.assertEqual(200, resp.status)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_security_group_rules_list(self):
# Positive test: Created Security Group rules should be
# in the list of all rules
# Creating a Security Group to add rules to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- securitygroup_id = securitygroup['id']
- # Delete the Security Group at the end of this method
- self.addCleanup(self.client.delete_security_group, securitygroup_id)
+ resp, security_group = self.create_security_group()
+ securitygroup_id = security_group['id']
# Add a first rule to the created Security Group
ip_protocol1 = 'tcp'
@@ -135,29 +117,21 @@
self.assertTrue(any([i for i in rules if i['id'] == rule1_id]))
self.assertTrue(any([i for i in rules if i['id'] == rule2_id]))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_security_group_rules_delete_when_peer_group_deleted(self):
# Positive test:rule will delete when peer group deleting
# Creating a Security Group to add rules to it
- s1_name = data_utils.rand_name('securitygroup1-')
- s1_description = data_utils.rand_name('description1-')
- resp, sg1 = \
- self.client.create_security_group(s1_name, s1_description)
- self.addCleanup(self.client.delete_security_group, sg1['id'])
- self.assertEqual(200, resp.status)
+ resp, security_group = self.create_security_group()
+ sg1_id = security_group['id']
# Creating other Security Group to access to group1
- s2_name = data_utils.rand_name('securitygroup2-')
- s2_description = data_utils.rand_name('description2-')
- resp, sg2 = \
- self.client.create_security_group(s2_name, s2_description)
- self.assertEqual(200, resp.status)
- sg2_id = sg2['id']
+ resp, security_group = self.create_security_group()
+ sg2_id = security_group['id']
# Adding rules to the Group1
ip_protocol = 'tcp'
from_port = 22
to_port = 22
resp, rule = \
- self.client.create_security_group_rule(sg1['id'],
+ self.client.create_security_group_rule(sg1_id,
ip_protocol,
from_port,
to_port,
@@ -169,7 +143,7 @@
self.assertEqual(202, resp.status)
# Get rules of the Group1
resp, rules = \
- self.client.list_security_group_rules(sg1['id'])
+ self.client.list_security_group_rules(sg1_id)
# The group1 has no rules because group2 has deleted
self.assertEqual(0, len(rules))
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 4831939..680bc2f 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -19,8 +19,7 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
-from tempest.test import attr
-from tempest.test import skip_because
+from tempest import test
CONF = config.CONF
@@ -33,9 +32,9 @@
super(SecurityGroupRulesNegativeTestJSON, cls).setUpClass()
cls.client = cls.security_groups_client
- @skip_because(bug="1182384",
- condition=CONF.service_available.neutron)
- @attr(type=['negative', 'smoke'])
+ @test.skip_because(bug="1182384",
+ condition=CONF.service_available.neutron)
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_non_existent_id(self):
# Negative test: Creation of Security Group rule should FAIL
# with non existent Parent group id
@@ -50,7 +49,7 @@
@testtools.skipIf(CONF.service_available.neutron,
"Neutron not check the security_group_id")
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_id(self):
# Negative test: Creation of Security Group rule should FAIL
# with Parent group id which is not integer
@@ -63,21 +62,17 @@
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_duplicate(self):
# Negative test: Create Security Group rule duplicate should fail
# Creating a Security Group to add rule to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, sg = self.client.create_security_group(s_name, s_description)
- self.assertEqual(200, resp.status)
+ resp, sg = self.create_security_group()
# Adding rules to the created Security Group
parent_group_id = sg['id']
ip_protocol = 'tcp'
from_port = 22
to_port = 22
- self.addCleanup(self.client.delete_security_group, sg['id'])
resp, rule = \
self.client.create_security_group_rule(parent_group_id,
ip_protocol,
@@ -90,86 +85,70 @@
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_ip_protocol(self):
# Negative test: Creation of Security Group rule should FAIL
# with invalid ip_protocol
# Creating a Security Group to add rule to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = self.client.create_security_group(s_name,
- s_description)
+ resp, sg = self.create_security_group()
# Adding rules to the created Security Group
- parent_group_id = securitygroup['id']
+ parent_group_id = sg['id']
ip_protocol = data_utils.rand_name('999')
from_port = 22
to_port = 22
- self.addCleanup(self.client.delete_security_group, securitygroup['id'])
self.assertRaises(exceptions.BadRequest,
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_from_port(self):
# Negative test: Creation of Security Group rule should FAIL
# with invalid from_port
# Creating a Security Group to add rule to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = self.client.create_security_group(s_name,
- s_description)
+ resp, sg = self.create_security_group()
# Adding rules to the created Security Group
- parent_group_id = securitygroup['id']
+ parent_group_id = sg['id']
ip_protocol = 'tcp'
from_port = data_utils.rand_int_id(start=65536)
to_port = 22
- self.addCleanup(self.client.delete_security_group, securitygroup['id'])
self.assertRaises(exceptions.BadRequest,
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_to_port(self):
# Negative test: Creation of Security Group rule should FAIL
# with invalid to_port
# Creating a Security Group to add rule to it
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = self.client.create_security_group(s_name,
- s_description)
+ resp, sg = self.create_security_group()
# Adding rules to the created Security Group
- parent_group_id = securitygroup['id']
+ parent_group_id = sg['id']
ip_protocol = 'tcp'
from_port = 22
to_port = data_utils.rand_int_id(start=65536)
- self.addCleanup(self.client.delete_security_group, securitygroup['id'])
self.assertRaises(exceptions.BadRequest,
self.client.create_security_group_rule,
parent_group_id, ip_protocol, from_port, to_port)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_invalid_port_range(self):
# Negative test: Creation of Security Group rule should FAIL
# with invalid port range.
# Creating a Security Group to add rule to it.
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = self.client.create_security_group(s_name,
- s_description)
+ resp, sg = self.create_security_group()
# Adding a rule to the created Security Group
- secgroup_id = securitygroup['id']
+ secgroup_id = sg['id']
ip_protocol = 'tcp'
from_port = 22
to_port = 21
- self.addCleanup(self.client.delete_security_group, securitygroup['id'])
self.assertRaises(exceptions.BadRequest,
self.client.create_security_group_rule,
secgroup_id, ip_protocol, from_port, to_port)
- @skip_because(bug="1182384",
- condition=CONF.service_available.neutron)
- @attr(type=['negative', 'smoke'])
+ @test.skip_because(bug="1182384",
+ condition=CONF.service_available.neutron)
+ @test.attr(type=['negative', 'smoke'])
def test_delete_security_group_rule_with_non_existent_id(self):
# Negative test: Deletion of Security Group rule should be FAIL
# with non existent id
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 2c67581..b376edc 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -27,68 +27,43 @@
super(SecurityGroupsTestJSON, cls).setUpClass()
cls.client = cls.security_groups_client
- def _delete_security_group(self, securitygroup_id):
- resp, _ = self.client.delete_security_group(securitygroup_id)
- self.assertEqual(202, resp.status)
-
@test.attr(type='gate')
def test_security_groups_create_list_delete(self):
# Positive test:Should return the list of Security Groups
# Create 3 Security Groups
- security_group_list = list()
for i in range(3):
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
+ resp, securitygroup = self.create_security_group()
self.assertEqual(200, resp.status)
- self.addCleanup(self._delete_security_group,
- securitygroup['id'])
- security_group_list.append(securitygroup)
# Fetch all Security Groups and verify the list
# has all created Security Groups
resp, fetched_list = self.client.list_security_groups()
self.assertEqual(200, resp.status)
# Now check if all the created Security Groups are in fetched list
missing_sgs = \
- [sg for sg in security_group_list if sg not in fetched_list]
+ [sg for sg in self.security_groups if sg not in fetched_list]
self.assertFalse(missing_sgs,
"Failed to find Security Group %s in fetched "
"list" % ', '.join(m_group['name']
for m_group in missing_sgs))
-
- # TODO(afazekas): scheduled for delete,
- # test_security_group_create_get_delete covers it
- @test.attr(type='gate')
- def test_security_group_create_delete(self):
- # Security Group should be created, verified and deleted
- s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- self.assertIn('id', securitygroup)
- securitygroup_id = securitygroup['id']
- self.addCleanup(self._delete_security_group,
- securitygroup_id)
- self.assertEqual(200, resp.status)
- self.assertFalse(securitygroup_id is None)
- self.assertIn('name', securitygroup)
- securitygroup_name = securitygroup['name']
- self.assertEqual(securitygroup_name, s_name,
- "The created Security Group name is "
- "not equal to the requested name")
+ # Delete all security groups
+ for sg in self.security_groups:
+ resp, _ = self.client.delete_security_group(sg['id'])
+ self.assertEqual(202, resp.status)
+ self.client.wait_for_resource_deletion(sg['id'])
+ # Now check if all the created Security Groups are deleted
+ resp, fetched_list = self.client.list_security_groups()
+ deleted_sgs = \
+ [sg for sg in self.security_groups if sg in fetched_list]
+ self.assertFalse(deleted_sgs,
+ "Failed to delete Security Group %s "
+ "list" % ', '.join(m_group['name']
+ for m_group in deleted_sgs))
@test.attr(type='gate')
def test_security_group_create_get_delete(self):
# Security Group should be created, fetched and deleted
s_name = data_utils.rand_name('securitygroup-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
- self.addCleanup(self._delete_security_group,
- securitygroup['id'])
-
- self.assertEqual(200, resp.status)
+ resp, securitygroup = self.create_security_group(name=s_name)
self.assertIn('name', securitygroup)
securitygroup_name = securitygroup['name']
self.assertEqual(securitygroup_name, s_name,
@@ -108,15 +83,8 @@
# and not deleted if the server is active.
# Create a couple security groups that we will use
# for the server resource this test creates
- sg_name = data_utils.rand_name('sg')
- sg_desc = data_utils.rand_name('sg-desc')
- resp, sg = self.client.create_security_group(sg_name, sg_desc)
- sg_id = sg['id']
-
- sg2_name = data_utils.rand_name('sg')
- sg2_desc = data_utils.rand_name('sg-desc')
- resp, sg2 = self.client.create_security_group(sg2_name, sg2_desc)
- sg2_id = sg2['id']
+ resp, sg = self.create_security_group()
+ resp, sg2 = self.create_security_group()
# Create server and add the security group created
# above to the server we just created
@@ -125,50 +93,44 @@
server_id = server['id']
self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
resp, body = self.servers_client.add_security_group(server_id,
- sg_name)
+ sg['name'])
# Check that we are not able to delete the security
# group since it is in use by an active server
self.assertRaises(exceptions.BadRequest,
self.client.delete_security_group,
- sg_id)
+ sg['id'])
# Reboot and add the other security group
resp, body = self.servers_client.reboot(server_id, 'HARD')
self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
resp, body = self.servers_client.add_security_group(server_id,
- sg2_name)
+ sg2['name'])
# Check that we are not able to delete the other security
# group since it is in use by an active server
self.assertRaises(exceptions.BadRequest,
self.client.delete_security_group,
- sg2_id)
+ sg2['id'])
# Shutdown the server and then verify we can destroy the
# security groups, since no active server instance is using them
self.servers_client.delete_server(server_id)
self.servers_client.wait_for_server_termination(server_id)
- self.client.delete_security_group(sg_id)
+ self.client.delete_security_group(sg['id'])
self.assertEqual(202, resp.status)
-
- self.client.delete_security_group(sg2_id)
+ self.client.delete_security_group(sg2['id'])
self.assertEqual(202, resp.status)
@test.attr(type='gate')
def test_update_security_groups(self):
# Update security group name and description
# Create a security group
- s_name = data_utils.rand_name('sg-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
+ resp, securitygroup = self.create_security_group()
self.assertEqual(200, resp.status)
self.assertIn('id', securitygroup)
securitygroup_id = securitygroup['id']
- self.addCleanup(self._delete_security_group,
- securitygroup_id)
# Update the name and description
s_new_name = data_utils.rand_name('sg-hth-')
s_new_des = data_utils.rand_name('description-hth-')
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 ce1eada..edf38e9 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -33,10 +33,6 @@
cls.client = cls.security_groups_client
cls.neutron_available = CONF.service_available.neutron
- def _delete_security_group(self, securitygroup_id):
- resp, _ = self.client.delete_security_group(securitygroup_id)
- self.assertEqual(202, resp.status)
-
def _generate_a_non_existent_security_group_id(self):
security_group_id = []
resp, body = self.client.list_security_groups()
@@ -107,11 +103,8 @@
s_name = data_utils.rand_name('securitygroup-')
s_description = data_utils.rand_name('description-')
resp, security_group =\
- self.client.create_security_group(s_name, s_description)
+ self.create_security_group(s_name, s_description)
self.assertEqual(200, resp.status)
-
- self.addCleanup(self.client.delete_security_group,
- security_group['id'])
# Now try the Security Group with the same 'Name'
self.assertRaises(exceptions.BadRequest,
self.client.create_security_group, s_name,
@@ -163,15 +156,10 @@
@test.attr(type=['negative', 'gate'])
def test_update_security_group_with_invalid_sg_name(self):
# Update security_group with invalid sg_name should fail
- s_name = data_utils.rand_name('sg-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
+ resp, securitygroup = self.create_security_group()
self.assertEqual(200, resp.status)
self.assertIn('id', securitygroup)
securitygroup_id = securitygroup['id']
- self.addCleanup(self._delete_security_group,
- securitygroup_id)
# Update Security Group with group name longer than 255 chars
s_new_name = 'securitygroup-'.ljust(260, '0')
self.assertRaises(exceptions.BadRequest,
@@ -183,15 +171,10 @@
@test.attr(type=['negative', 'gate'])
def test_update_security_group_with_invalid_sg_des(self):
# Update security_group with invalid sg_des should fail
- s_name = data_utils.rand_name('sg-')
- s_description = data_utils.rand_name('description-')
- resp, securitygroup = \
- self.client.create_security_group(s_name, s_description)
+ resp, securitygroup = self.create_security_group()
self.assertEqual(200, resp.status)
self.assertIn('id', securitygroup)
securitygroup_id = securitygroup['id']
- self.addCleanup(self._delete_security_group,
- securitygroup_id)
# Update Security Group with group description longer than 255 chars
s_new_des = 'des-'.ljust(260, '0')
self.assertRaises(exceptions.BadRequest,
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index 8e432c4..d5528c4 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -14,8 +14,11 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest import test
+CONF = config.CONF
+
class ServerAddressesTestJSON(base.BaseV2ComputeTest):
_interface = 'json'
@@ -29,6 +32,8 @@
resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
+ @test.skip_because(bug="1210483",
+ condition=CONF.service_available.neutron)
@test.attr(type='smoke')
def test_list_server_addresses(self):
# All public and private addresses for
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 20c5d7f..0bf604c 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -24,6 +24,7 @@
@classmethod
def setUpClass(cls):
+ cls.set_network_resources(network=True, subnet=True, router=True)
super(ServerRescueTestJSON, cls).setUpClass()
cls.device = 'vdf'
@@ -41,20 +42,10 @@
cls.sg_id = cls.sg['id']
# Create a volume and wait for it to become ready for attach
- resp, cls.volume_to_attach = \
- cls.volumes_extensions_client.create_volume(1,
- display_name=
- 'test_attach')
+ resp, cls.volume = cls.volumes_extensions_client.create_volume(
+ 1, display_name=data_utils.rand_name(cls.__name__ + '_volume'))
cls.volumes_extensions_client.wait_for_volume_status(
- cls.volume_to_attach['id'], 'available')
-
- # Create a volume and wait for it to become ready for attach
- resp, cls.volume_to_detach = \
- cls.volumes_extensions_client.create_volume(1,
- display_name=
- 'test_detach')
- cls.volumes_extensions_client.wait_for_volume_status(
- cls.volume_to_detach['id'], 'available')
+ cls.volume['id'], 'available')
# Server for positive tests
resp, server = cls.create_test_server(wait_until='BUILD')
@@ -78,9 +69,7 @@
def tearDownClass(cls):
# Deleting the floating IP which is created in this method
cls.floating_ips_client.delete_floating_ip(cls.floating_ip_id)
- client = cls.volumes_extensions_client
- client.delete_volume(str(cls.volume_to_attach['id']).strip())
- client.delete_volume(str(cls.volume_to_detach['id']).strip())
+ cls.delete_volume(cls.volume['id'])
resp, cls.sg = cls.security_groups_client.delete_security_group(
cls.sg_id)
super(ServerRescueTestJSON, cls).tearDownClass()
@@ -93,9 +82,6 @@
self.volumes_extensions_client.wait_for_volume_status(volume_id,
'available')
- def _delete(self, volume_id):
- self.volumes_extensions_client.delete_volume(volume_id)
-
def _unrescue(self, server_id):
resp, body = self.servers_client.unrescue_server(server_id)
self.assertEqual(202, resp.status)
@@ -159,32 +145,31 @@
self.assertRaises(exceptions.Conflict,
self.servers_client.attach_volume,
self.server_id,
- self.volume_to_attach['id'],
+ self.volume['id'],
device='/dev/%s' % self.device)
@attr(type=['negative', 'gate'])
def test_rescued_vm_detach_volume(self):
# Attach the volume to the server
self.servers_client.attach_volume(self.server_id,
- self.volume_to_detach['id'],
+ self.volume['id'],
device='/dev/%s' % self.device)
self.volumes_extensions_client.wait_for_volume_status(
- self.volume_to_detach['id'], 'in-use')
+ self.volume['id'], 'in-use')
# Rescue the server
self.servers_client.rescue_server(self.server_id,
adminPass=self.password)
self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
# addCleanup is a LIFO queue
- self.addCleanup(self._detach, self.server_id,
- self.volume_to_detach['id'])
+ self.addCleanup(self._detach, self.server_id, self.volume['id'])
self.addCleanup(self._unrescue, self.server_id)
# Detach the volume from the server expecting failure
self.assertRaises(exceptions.Conflict,
self.servers_client.detach_volume,
self.server_id,
- self.volume_to_detach['id'])
+ self.volume['id'])
@attr(type='gate')
def test_rescued_vm_associate_dissociate_floating_ip(self):
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 4774fec..ed72061 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -57,7 +57,6 @@
cls.alt_keypairs_client = cls.alt_manager.keypairs_client
cls.alt_security_client = cls.alt_manager.security_groups_client
- cls.alt_security_client._set_auth()
resp, server = cls.create_test_server(wait_until='ACTIVE')
resp, cls.server = cls.client.get_server(server['id'])
@@ -174,16 +173,14 @@
def test_create_server_fails_when_tenant_incorrect(self):
# A create server request should fail if the tenant id does not match
# the current user
- saved_base_url = self.alt_client.base_url
- try:
- # Change the base URL to impersonate another user
- self.alt_client.base_url = self.client.base_url
- self.assertRaises(exceptions.BadRequest,
- self.alt_client.create_server, 'test',
- self.image['id'], self.flavor_ref)
- finally:
- # Reset the base_url...
- self.alt_client.base_url = saved_base_url
+ # Change the base URL to impersonate another user
+ self.alt_client.auth_provider.set_alt_auth_data(
+ request_part='url',
+ auth_data=self.client.auth_provider.auth_data
+ )
+ self.assertRaises(exceptions.BadRequest,
+ self.alt_client.create_server, 'test',
+ self.image['id'], self.flavor_ref)
@attr(type='gate')
def test_create_keypair_in_analt_user_tenant(self):
@@ -191,18 +188,18 @@
# the current user
# POST keypair with other user tenant
k_name = data_utils.rand_name('keypair-')
- self.alt_keypairs_client._set_auth()
- self.saved_base_url = self.alt_keypairs_client.base_url
try:
# Change the base URL to impersonate another user
- self.alt_keypairs_client.base_url = self.keypairs_client.base_url
+ self.alt_keypairs_client.auth_provider.set_alt_auth_data(
+ request_part='url',
+ auth_data=self.keypairs_client.auth_provider.auth_data
+ )
resp = {}
resp['status'] = None
self.assertRaises(exceptions.BadRequest,
self.alt_keypairs_client.create_keypair, k_name)
finally:
- # Reset the base_url...
- self.alt_keypairs_client.base_url = self.saved_base_url
+ # Next request the base_url is back to normal
if (resp['status'] is not None):
resp, _ = self.alt_keypairs_client.delete_keypair(k_name)
LOG.error("Create keypair request should not happen "
@@ -242,18 +239,19 @@
# POST security group with other user tenant
s_name = data_utils.rand_name('security-')
s_description = data_utils.rand_name('security')
- self.saved_base_url = self.alt_security_client.base_url
try:
# Change the base URL to impersonate another user
- self.alt_security_client.base_url = self.security_client.base_url
+ self.alt_security_client.auth_provider.set_alt_auth_data(
+ request_part='url',
+ auth_data=self.security_client.auth_provider.auth_data
+ )
resp = {}
resp['status'] = None
self.assertRaises(exceptions.BadRequest,
self.alt_security_client.create_security_group,
s_name, s_description)
finally:
- # Reset the base_url...
- self.alt_security_client.base_url = self.saved_base_url
+ # Next request the base_url is back to normal
if resp['status'] is not None:
self.alt_security_client.delete_security_group(resp['id'])
LOG.error("Create Security Group request should not happen if"
@@ -282,10 +280,12 @@
ip_protocol = 'icmp'
from_port = -1
to_port = -1
- self.saved_base_url = self.alt_security_client.base_url
try:
# Change the base URL to impersonate another user
- self.alt_security_client.base_url = self.security_client.base_url
+ self.alt_security_client.auth_provider.set_alt_auth_data(
+ request_part='url',
+ auth_data=self.security_client.auth_provider.auth_data
+ )
resp = {}
resp['status'] = None
self.assertRaises(exceptions.BadRequest,
@@ -294,8 +294,7 @@
parent_group_id, ip_protocol, from_port,
to_port)
finally:
- # Reset the base_url...
- self.alt_security_client.base_url = self.saved_base_url
+ # Next request the base_url is back to normal
if resp['status'] is not None:
self.alt_security_client.delete_security_group_rule(resp['id'])
LOG.error("Create security group rule request should not "
diff --git a/tempest/api/compute/v3/admin/test_flavors.py b/tempest/api/compute/v3/admin/test_flavors.py
new file mode 100644
index 0000000..597c99b
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_flavors.py
@@ -0,0 +1,311 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class FlavorsAdminV3Test(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Flavors API Create and Delete that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsAdminV3Test, cls).setUpClass()
+
+ cls.client = cls.flavors_admin_client
+ cls.user_client = cls.flavors_client
+ cls.flavor_name_prefix = 'test_flavor_'
+ cls.ram = 512
+ cls.vcpus = 1
+ cls.disk = 10
+ cls.ephemeral = 10
+ cls.swap = 1024
+ cls.rxtx = 2
+
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 204)
+ self.client.wait_for_resource_deletion(flavor_id)
+
+ def _create_flavor(self, flavor_id):
+ # Create a flavor and ensure it is listed
+ # This operation requires the user to have 'admin' role
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(201, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(flavor['ram'], self.ram)
+ self.assertEqual(flavor['swap'], self.swap)
+ if test.is_extension_enabled("os-flavor-rxtx", "compute_v3"):
+ self.assertEqual(flavor['os-flavor-rxtx:rxtx_factor'], self.rxtx)
+ self.assertEqual(flavor['ephemeral'],
+ self.ephemeral)
+ self.assertEqual(flavor['flavor-access:is_public'], True)
+
+ # Verify flavor is retrieved
+ resp, flavor = self.client.get_flavor_details(flavor['id'])
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
+
+ return flavor['id']
+
+ @test.attr(type='gate')
+ def test_create_flavor_with_int_id(self):
+ flavor_id = data_utils.rand_int_id(start=1000)
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, str(flavor_id))
+
+ @test.attr(type='gate')
+ def test_create_flavor_with_uuid_id(self):
+ flavor_id = str(uuid.uuid4())
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, flavor_id)
+
+ @test.attr(type='gate')
+ def test_create_flavor_with_none_id(self):
+ # If nova receives a request with None as flavor_id,
+ # nova generates flavor_id of uuid.
+ flavor_id = None
+ new_flavor_id = self._create_flavor(flavor_id)
+ self.assertEqual(new_flavor_id, str(uuid.UUID(new_flavor_id)))
+
+ @test.attr(type='gate')
+ def test_create_flavor_verify_entry_in_list_details(self):
+ # Create a flavor and ensure it's details are listed
+ # This operation requires the user to have 'admin' role
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ flag = False
+ # Verify flavor is retrieved
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
+
+ @test.attr(type='gate')
+ def test_create_list_flavor_without_extra_data(self):
+ # Create a flavor and ensure it is listed
+ # This operation requires the user to have 'admin' role
+
+ def verify_flavor_response_extension(flavor):
+ # check some extensions for the flavor create/show/detail response
+ self.assertEqual(flavor['swap'], 0)
+ if test.is_extension_enabled("os-flavor-rxtx", "compute_v3"):
+ self.assertEqual(int(flavor['os-flavor-rxtx:rxtx_factor']), 1)
+ self.assertEqual(int(flavor['ephemeral']), 0)
+ self.assertEqual(flavor['flavor-access:is_public'], True)
+
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(201, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['ram'], self.ram)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(int(flavor['id']), new_flavor_id)
+ verify_flavor_response_extension(flavor)
+
+ # Verify flavor is retrieved
+ resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
+ verify_flavor_response_extension(flavor)
+
+ # Check if flavor is present in list
+ resp, flavors = self.user_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ verify_flavor_response_extension(flavor)
+ flag = True
+ self.assertTrue(flag)
+
+ @test.skip_because(bug="1209101")
+ @test.attr(type='gate')
+ def test_list_non_public_flavor(self):
+ # Create a flavor with os-flavor-access:is_public false should
+ # be present in list_details.
+ # This operation requires the user to have 'admin' role
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public="False")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ # Verify flavor is retrieved
+ flag = False
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
+
+ # Verify flavor is not retrieved with other user
+ flag = False
+ resp, flavors = self.user_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertFalse(flag)
+
+ @test.attr(type='gate')
+ def test_create_server_with_non_public_flavor(self):
+ # Create a flavor with os-flavor-access:is_public false
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public="False")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(201, resp.status)
+
+ # Verify flavor is not used by other user
+ self.assertRaises(exceptions.BadRequest,
+ self.servers_client.create_server,
+ 'test', self.image_ref, flavor['id'])
+
+ @test.attr(type='gate')
+ def test_list_public_flavor_with_other_user(self):
+ # Create a Flavor with public access.
+ # Try to List/Get flavor with another user
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ # Create the flavor
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ is_public="True")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ flag = False
+ self.new_client = self.flavors_client
+ # Verify flavor is retrieved with new user
+ resp, flavors = self.new_client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = True
+ self.assertTrue(flag)
+
+ @test.attr(type='gate')
+ def test_is_public_string_variations(self):
+ flavor_id_not_public = data_utils.rand_int_id(start=1000)
+ flavor_name_not_public = data_utils.rand_name(self.flavor_name_prefix)
+ flavor_id_public = data_utils.rand_int_id(start=1000)
+ flavor_name_public = data_utils.rand_name(self.flavor_name_prefix)
+
+ # Create a non public flavor
+ resp, flavor = self.client.create_flavor(flavor_name_not_public,
+ self.ram, self.vcpus,
+ self.disk,
+ flavor_id_not_public,
+ is_public="False")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+
+ # Create a public flavor
+ resp, flavor = self.client.create_flavor(flavor_name_public,
+ self.ram, self.vcpus,
+ self.disk,
+ flavor_id_public,
+ is_public="True")
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+
+ def _flavor_lookup(flavors, flavor_name):
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ return flavor
+ return None
+
+ def _test_string_variations(variations, flavor_name):
+ for string in variations:
+ params = {'is_public': string}
+ r, flavors = self.client.list_flavors_with_detail(params)
+ self.assertEqual(r.status, 200)
+ flavor = _flavor_lookup(flavors, flavor_name)
+ self.assertIsNotNone(flavor)
+
+ _test_string_variations(['f', 'false', 'no', '0'],
+ flavor_name_not_public)
+
+ _test_string_variations(['t', 'true', 'yes', '1'],
+ flavor_name_public)
+
+ @test.attr(type='gate')
+ def test_create_flavor_using_string_ram(self):
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = data_utils.rand_int_id(start=1000)
+
+ ram = " 1024 "
+ resp, flavor = self.client.create_flavor(flavor_name,
+ ram, self.vcpus,
+ self.disk,
+ new_flavor_id)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+ self.assertEqual(201, resp.status)
+ self.assertEqual(flavor['name'], flavor_name)
+ self.assertEqual(flavor['vcpus'], self.vcpus)
+ self.assertEqual(flavor['disk'], self.disk)
+ self.assertEqual(flavor['ram'], int(ram))
+ self.assertEqual(int(flavor['id']), new_flavor_id)
diff --git a/tempest/api/compute/v3/admin/test_flavors_negative.py b/tempest/api/compute/v3/admin/test_flavors_negative.py
new file mode 100644
index 0000000..f54de79
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_flavors_negative.py
@@ -0,0 +1,335 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest import exceptions
+from tempest import test
+
+
+class FlavorsAdminNegativeV3Test(base.BaseV3ComputeAdminTest):
+
+ """
+ Tests Flavors API Create and Delete that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsAdminNegativeV3Test, cls).setUpClass()
+
+ cls.client = cls.flavors_admin_client
+ cls.user_client = cls.flavors_client
+ cls.flavor_name_prefix = 'test_flavor_'
+ cls.ram = 512
+ cls.vcpus = 1
+ cls.disk = 10
+ cls.ephemeral = 10
+ cls.swap = 1024
+ cls.rxtx = 2
+
+ def flavor_clean_up(self, flavor_id):
+ resp, body = self.client.delete_flavor(flavor_id)
+ self.assertEqual(resp.status, 204)
+ self.client.wait_for_resource_deletion(flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_flavor_details_for_deleted_flavor(self):
+ # Delete a flavor and ensure it is not listed
+ # Create a test flavor
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+
+ # no need to specify flavor_id, we can get the flavor_id from a
+ # response of create_flavor() call.
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram,
+ self.vcpus, self.disk,
+ '',
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ # Delete the flavor
+ new_flavor_id = flavor['id']
+ resp_delete, body = self.client.delete_flavor(new_flavor_id)
+ self.assertEqual(201, resp.status)
+ self.assertEqual(204, resp_delete.status)
+
+ # Deleted flavors can be seen via detailed GET
+ resp, flavor = self.client.get_flavor_details(new_flavor_id)
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(flavor['name'], flavor_name)
+
+ # Deleted flavors should not show up in a list however
+ resp, flavors = self.client.list_flavors_with_detail()
+ self.assertEqual(resp.status, 200)
+ flag = True
+ for flavor in flavors:
+ if flavor['name'] == flavor_name:
+ flag = False
+ self.assertTrue(flag)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_invalid_is_public_string(self):
+ # the 'is_public' parameter can be 'none/true/false' if it exists
+ self.assertRaises(exceptions.BadRequest,
+ self.client.list_flavors_with_detail,
+ {'is_public': 'invalid'})
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_as_user(self):
+ # only admin user can create a flavor
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.create_flavor,
+ flavor_name, self.ram, self.vcpus, self.disk,
+ new_flavor_id, ephemeral=self.ephemeral,
+ swap=self.swap, rxtx=self.rxtx)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_flavor_as_user(self):
+ # only admin user can delete a flavor
+ self.assertRaises(exceptions.Unauthorized,
+ self.user_client.delete_flavor,
+ self.flavor_ref_alt)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_ram(self):
+ # the 'ram' attribute must be positive integer
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, -1, self.vcpus,
+ self.disk, new_flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_using_invalid_vcpus(self):
+ # the 'vcpu' attribute must be positive integer
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ flavor_name, self.ram, -1,
+ self.disk, new_flavor_id)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_name_length_less_than_1(self):
+ # ensure name length >= 1
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ '',
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_name_length_exceeds_255(self):
+ # ensure name do not exceed 255 characters
+ new_flavor_name = 'a' * 256
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_name(self):
+ # the regex of flavor_name is '^[\w\.\- ]*$'
+ invalid_flavor_name = data_utils.rand_name('invalid-!@#$%-')
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ invalid_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_flavor_id(self):
+ # the regex of flavor_id is '^[\w\.\- ]*$', and it cannot contain
+ # leading and/or trailing whitespace
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ invalid_flavor_id = '!@#$%'
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ invalid_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_id_length_exceeds_255(self):
+ # the length of flavor_id should not exceed 255 characters
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ invalid_flavor_id = 'a' * 256
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ invalid_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_root_gb(self):
+ # root_gb attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ -1,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_ephemeral_gb(self):
+ # ephemeral_gb attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=-1,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_swap(self):
+ # swap attribute should be non-negative ( >= 0) integer
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=-1,
+ rxtx=self.rxtx,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_rxtx_factor(self):
+ # rxtx_factor attribute should be a positive float
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=-1.5,
+ is_public='False')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_with_invalid_is_public(self):
+ # is_public attribute should be boolean
+ new_flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.BadRequest,
+ self.client.create_flavor,
+ new_flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx,
+ is_public='Invalid')
+
+ @test.attr(type=['negative', 'gate'])
+ def test_create_flavor_already_exists(self):
+ flavor_name = data_utils.rand_name(self.flavor_name_prefix)
+ new_flavor_id = str(uuid.uuid4())
+
+ resp, flavor = self.client.create_flavor(flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+ self.assertEqual(201, resp.status)
+ self.addCleanup(self.flavor_clean_up, flavor['id'])
+
+ self.assertRaises(exceptions.Conflict,
+ self.client.create_flavor,
+ flavor_name,
+ self.ram, self.vcpus,
+ self.disk,
+ new_flavor_id,
+ ephemeral=self.ephemeral,
+ swap=self.swap,
+ rxtx=self.rxtx)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_delete_nonexistent_flavor(self):
+ nonexistent_flavor_id = str(uuid.uuid4())
+
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_flavor,
+ nonexistent_flavor_id)
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
deleted file mode 100644
index a86b7f5..0000000
--- a/tempest/api/compute/v3/admin/test_instance_usage_audit_log.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2013 IBM Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import datetime
-import urllib
-
-from tempest.api.compute import base
-from tempest import test
-
-
-class InstanceUsageAuditLogV3Test(base.BaseV3ComputeAdminTest):
-
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(InstanceUsageAuditLogV3Test, cls).setUpClass()
- cls.adm_client = cls.instance_usages_audit_log_admin_client
-
- @test.attr(type='gate')
- def test_list_instance_usage_audit_logs(self):
- # list instance usage audit logs
- resp, body = self.adm_client.list_instance_usage_audit_logs()
- self.assertEqual(200, resp.status)
- expected_items = ['total_errors', 'total_instances', 'log',
- 'num_hosts_running', 'num_hosts_done',
- 'num_hosts', 'hosts_not_run', 'overall_status',
- 'period_ending', 'period_beginning',
- 'num_hosts_not_run']
- for item in expected_items:
- self.assertIn(item, body)
-
- @test.attr(type='gate')
- def test_list_instance_usage_audit_logs_with_filter_before(self):
- # Get instance usage audit log before specified time
- ending_time = datetime.datetime(2012, 12, 24)
- resp, body = self.adm_client.list_instance_usage_audit_logs(
- urllib.quote(ending_time.strftime("%Y-%m-%d %H:%M:%S")))
-
- self.assertEqual(200, resp.status)
- expected_items = ['total_errors', 'total_instances', 'log',
- 'num_hosts_running', 'num_hosts_done', 'num_hosts',
- 'hosts_not_run', 'overall_status', 'period_ending',
- 'period_beginning', 'num_hosts_not_run']
- for item in expected_items:
- self.assertIn(item, body)
- self.assertEqual(body['period_ending'], "2012-12-23 23:00:00")
diff --git a/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
deleted file mode 100644
index 0438825..0000000
--- a/tempest/api/compute/v3/admin/test_instance_usage_audit_log_negative.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2013 IBM Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.compute import base
-from tempest import exceptions
-from tempest import test
-
-
-class InstanceUsageLogNegativeV3Test(base.BaseV3ComputeAdminTest):
-
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(InstanceUsageLogNegativeV3Test, cls).setUpClass()
- cls.adm_client = cls.instance_usages_audit_log_admin_client
-
- @test.attr(type=['negative', 'gate'])
- def test_instance_usage_audit_logs_with_nonadmin_user(self):
- # the instance_usage_audit_logs API just can be accessed by admin user
- self.assertRaises(exceptions.Unauthorized,
- self.instance_usages_audit_log_client.
- list_instance_usage_audit_logs)
-
- @test.attr(type=['negative', 'gate'])
- def test_get_instance_usage_audit_logs_with_invalid_time(self):
- self.assertRaises(exceptions.BadRequest,
- self.adm_client.list_instance_usage_audit_logs,
- "invalid_time")
diff --git a/tempest/api/compute/v3/admin/test_servers.py b/tempest/api/compute/v3/admin/test_servers.py
index ef9eedc..653eaf0 100644
--- a/tempest/api/compute/v3/admin/test_servers.py
+++ b/tempest/api/compute/v3/admin/test_servers.py
@@ -16,8 +16,6 @@
from tempest.common.utils import data_utils
from tempest import exceptions
from tempest import test
-from tempest.test import attr
-from tempest.test import skip_because
class ServersAdminV3Test(base.BaseV3ComputeAdminTest):
@@ -26,6 +24,7 @@
Tests Servers API using admin privileges
"""
+ _host_key = 'os-extended-server-attributes:host'
_interface = 'json'
@classmethod
@@ -55,7 +54,7 @@
flavor_id = data_utils.rand_int_id(start=1000)
return flavor_id
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_admin(self):
# Listing servers by admin user returns empty list by default
resp, body = self.client.list_servers_with_detail()
@@ -64,7 +63,7 @@
self.assertEqual([], servers)
@test.skip_because(bug='1265416')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_by_admin_with_all_tenants(self):
# Listing servers by admin user with all tenants parameter
# Here should be listed all servers
@@ -76,7 +75,34 @@
self.assertIn(self.s1_name, servers_name)
self.assertIn(self.s2_name, servers_name)
- @attr(type='gate')
+ @test.attr(type='gate')
+ def test_list_servers_filter_by_existent_host(self):
+ # Filter the list of servers by existent host
+ name = data_utils.rand_name('server')
+ flavor = self.flavor_ref
+ image_id = self.image_ref
+ resp, test_server = self.client.create_server(
+ name, image_id, flavor)
+ self.assertEqual('202', resp['status'])
+ self.addCleanup(self.client.delete_server, test_server['id'])
+ self.client.wait_for_server_status(test_server['id'], 'ACTIVE')
+ resp, server = self.client.get_server(test_server['id'])
+ self.assertEqual(server['status'], 'ACTIVE')
+ hostname = server[self._host_key]
+ params = {'host': hostname}
+ resp, body = self.client.list_servers(params)
+ self.assertEqual('200', resp['status'])
+ servers = body['servers']
+ nonexistent_params = {'host': 'nonexistent_host'}
+ resp, nonexistent_body = self.client.list_servers(
+ nonexistent_params)
+ self.assertEqual('200', resp['status'])
+ nonexistent_servers = nonexistent_body['servers']
+ self.assertIn(test_server['id'], map(lambda x: x['id'], servers))
+ self.assertNotIn(test_server['id'],
+ map(lambda x: x['id'], nonexistent_servers))
+
+ @test.attr(type='gate')
def test_admin_delete_servers_of_others(self):
# Administrator can delete servers of others
_, server = self.create_test_server()
@@ -84,7 +110,7 @@
self.assertEqual('204', resp['status'])
self.servers_client.wait_for_server_termination(server['id'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_delete_server_while_in_error_state(self):
# Delete a server while it's VM state is error
resp, server = self.create_test_server(wait_until='ACTIVE')
@@ -96,7 +122,7 @@
resp, _ = self.client.delete_server(server['id'])
self.assertEqual('204', resp['status'])
- @attr(type='gate')
+ @test.attr(type='gate')
def test_reset_state_server(self):
# Reset server's state to 'error'
resp, server = self.client.reset_state(self.s1_id)
@@ -114,8 +140,8 @@
resp, server = self.client.get_server(self.s1_id)
self.assertEqual(server['status'], 'ACTIVE')
- @attr(type='gate')
- @skip_because(bug="1240043")
+ @test.attr(type='gate')
+ @test.skip_because(bug="1240043")
def test_get_server_diagnostics_by_admin(self):
# Retrieve server diagnostics by admin user
resp, diagnostic = self.client.get_server_diagnostics(self.s1_id)
@@ -126,7 +152,7 @@
for key in basic_attrs:
self.assertIn(key, str(diagnostic.keys()))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_list_servers_filter_by_error_status(self):
# Filter the list of servers by server error status
params = {'status': 'error'}
@@ -142,12 +168,12 @@
self.assertIn(self.s1_id, map(lambda x: x['id'], servers))
self.assertNotIn(self.s2_id, map(lambda x: x['id'], servers))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_rebuild_server_in_error_state(self):
# The server in error state should be rebuilt using the provided
# image and changed to ACTIVE state
- # resetting vm state require admin priviledge
+ # resetting vm state require admin privilege
resp, server = self.client.reset_state(self.s1_id, state='error')
self.assertEqual(202, resp.status)
resp, rebuilt_server = self.non_admin_client.rebuild(
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
deleted file mode 100644
index e16332f..0000000
--- a/tempest/api/compute/v3/admin/test_simple_tenant_usage.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2013 NEC Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import datetime
-
-from tempest.api.compute import base
-from tempest import test
-from tempest.test import attr
-import time
-
-
-class TenantUsagesV3Test(base.BaseV3ComputeAdminTest):
-
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(TenantUsagesV3Test, cls).setUpClass()
- cls.adm_client = cls.tenant_usages_admin_client
- cls.client = cls.tenant_usages_client
- cls.identity_client = cls._get_identity_admin_client()
-
- resp, tenants = cls.identity_client.list_tenants()
- cls.tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- cls.client.tenant_name][0]
-
- # Create a server in the demo tenant
- resp, server = cls.create_test_server(wait_until='ACTIVE')
- time.sleep(2)
-
- now = datetime.datetime.now()
- cls.start = cls._parse_strtime(now - datetime.timedelta(days=1))
- cls.end = cls._parse_strtime(now + datetime.timedelta(days=1))
-
- @classmethod
- def _parse_strtime(cls, at):
- # Returns formatted datetime
- return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
-
- @test.skip_because(bug='1265416')
- @attr(type='gate')
- def test_list_usage_all_tenants(self):
- # Get usage for all tenants
- params = {'start': self.start,
- 'end': self.end,
- 'detailed': int(bool(True))}
- resp, tenant_usage = self.adm_client.list_tenant_usages(params)
- self.assertEqual(200, resp.status)
- self.assertEqual(len(tenant_usage), 8)
-
- @test.skip_because(bug='1265416')
- @attr(type='gate')
- def test_get_usage_tenant(self):
- # Get usage for a specific tenant
- params = {'start': self.start,
- 'end': self.end}
- resp, tenant_usage = self.adm_client.get_tenant_usage(
- self.tenant_id, params)
-
- self.assertEqual(200, resp.status)
- self.assertEqual(len(tenant_usage), 8)
-
- @test.skip_because(bug='1265416')
- @attr(type='gate')
- def test_get_usage_tenant_with_non_admin_user(self):
- # Get usage for a specific tenant with non admin user
- params = {'start': self.start,
- 'end': self.end}
- resp, tenant_usage = self.client.get_tenant_usage(
- self.tenant_id, params)
-
- self.assertEqual(200, resp.status)
- self.assertEqual(len(tenant_usage), 8)
diff --git a/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py
deleted file mode 100644
index 17849c5..0000000
--- a/tempest/api/compute/v3/admin/test_simple_tenant_usage_negative.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2013 NEC Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import datetime
-
-from tempest.api.compute import base
-from tempest import exceptions
-from tempest import test
-from tempest.test import attr
-
-
-class TenantUsagesNegativeV3Test(base.BaseV3ComputeAdminTest):
-
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(TenantUsagesNegativeV3Test, cls).setUpClass()
- cls.adm_client = cls.os_adm.tenant_usages_client
- cls.client = cls.os.tenant_usages_client
- cls.identity_client = cls._get_identity_admin_client()
- now = datetime.datetime.now()
- cls.start = cls._parse_strtime(now - datetime.timedelta(days=1))
- cls.end = cls._parse_strtime(now + datetime.timedelta(days=1))
-
- @classmethod
- def _parse_strtime(cls, at):
- # Returns formatted datetime
- return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
-
- @attr(type=['negative', 'gate'])
- def test_get_usage_tenant_with_empty_tenant_id(self):
- # Get usage for a specific tenant empty
- params = {'start': self.start,
- 'end': self.end}
- self.assertRaises(exceptions.NotFound,
- self.adm_client.get_tenant_usage,
- '', params)
-
- @test.skip_because(bug='1265416')
- @attr(type=['negative', 'gate'])
- def test_get_usage_tenant_with_invalid_date(self):
- # Get usage for tenant with invalid date
- params = {'start': self.end,
- 'end': self.start}
- resp, tenants = self.identity_client.list_tenants()
- tenant_id = [tnt['id'] for tnt in tenants if tnt['name'] ==
- self.client.tenant_name][0]
- self.assertRaises(exceptions.BadRequest,
- self.adm_client.get_tenant_usage,
- tenant_id, params)
-
- @test.skip_because(bug='1265416')
- @attr(type=['negative', 'gate'])
- def test_list_usage_all_tenants_with_non_admin_user(self):
- # Get usage for all tenants with non admin user
- params = {'start': self.start,
- 'end': self.end,
- 'detailed': int(bool(True))}
- self.assertRaises(exceptions.Unauthorized,
- self.client.list_tenant_usages, params)
diff --git a/tempest/api/compute/v3/flavors/__init__.py b/tempest/api/compute/v3/flavors/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/compute/v3/flavors/__init__.py
diff --git a/tempest/api/compute/v3/flavors/test_flavors.py b/tempest/api/compute/v3/flavors/test_flavors.py
new file mode 100644
index 0000000..812358f
--- /dev/null
+++ b/tempest/api/compute/v3/flavors/test_flavors.py
@@ -0,0 +1,128 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import test
+
+
+class FlavorsV3Test(base.BaseV3ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsV3Test, cls).setUpClass()
+ cls.client = cls.flavors_client
+
+ @test.attr(type='smoke')
+ def test_list_flavors(self):
+ # List of all flavors should contain the expected flavor
+ resp, flavors = self.client.list_flavors()
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
+ flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
+ 'name': flavor['name']}
+ self.assertIn(flavor_min_detail, flavors)
+
+ @test.attr(type='smoke')
+ def test_list_flavors_with_detail(self):
+ # Detailed list of all flavors should contain the expected flavor
+ resp, flavors = self.client.list_flavors_with_detail()
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
+ self.assertIn(flavor, flavors)
+
+ @test.attr(type='smoke')
+ def test_get_flavor(self):
+ # The expected flavor details should be returned
+ resp, flavor = self.client.get_flavor_details(self.flavor_ref)
+ self.assertEqual(self.flavor_ref, flavor['id'])
+
+ @test.attr(type='gate')
+ def test_list_flavors_limit_results(self):
+ # Only the expected number of flavors should be returned
+ params = {'limit': 1}
+ resp, flavors = self.client.list_flavors(params)
+ self.assertEqual(1, len(flavors))
+
+ @test.attr(type='gate')
+ def test_list_flavors_detailed_limit_results(self):
+ # Only the expected number of flavors (detailed) should be returned
+ params = {'limit': 1}
+ resp, flavors = self.client.list_flavors_with_detail(params)
+ self.assertEqual(1, len(flavors))
+
+ @test.attr(type='gate')
+ def test_list_flavors_using_marker(self):
+ # The list of flavors should start from the provided marker
+ resp, flavors = self.client.list_flavors()
+ flavor_id = flavors[0]['id']
+
+ params = {'marker': flavor_id}
+ resp, 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.attr(type='gate')
+ def test_list_flavors_detailed_using_marker(self):
+ # The list of flavors should start from the provided marker
+ resp, flavors = self.client.list_flavors_with_detail()
+ flavor_id = flavors[0]['id']
+
+ params = {'marker': flavor_id}
+ resp, flavors = self.client.list_flavors_with_detail(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.attr(type='gate')
+ def test_list_flavors_detailed_filter_by_min_disk(self):
+ # The detailed list of flavors should be filtered by disk space
+ resp, flavors = self.client.list_flavors_with_detail()
+ flavors = sorted(flavors, key=lambda k: k['disk'])
+ flavor_id = flavors[0]['id']
+
+ params = {'min_disk': flavors[0]['disk'] + 1}
+ resp, flavors = self.client.list_flavors_with_detail(params)
+ self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+
+ @test.attr(type='gate')
+ def test_list_flavors_detailed_filter_by_min_ram(self):
+ # The detailed list of flavors should be filtered by RAM
+ resp, flavors = self.client.list_flavors_with_detail()
+ flavors = sorted(flavors, key=lambda k: k['ram'])
+ flavor_id = flavors[0]['id']
+
+ params = {'min_ram': flavors[0]['ram'] + 1}
+ resp, flavors = self.client.list_flavors_with_detail(params)
+ self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+
+ @test.attr(type='gate')
+ def test_list_flavors_filter_by_min_disk(self):
+ # The list of flavors should be filtered by disk space
+ resp, flavors = self.client.list_flavors_with_detail()
+ flavors = sorted(flavors, key=lambda k: k['disk'])
+ flavor_id = flavors[0]['id']
+
+ params = {'min_disk': flavors[0]['disk'] + 1}
+ resp, flavors = self.client.list_flavors(params)
+ self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
+
+ @test.attr(type='gate')
+ def test_list_flavors_filter_by_min_ram(self):
+ # The list of flavors should be filtered by RAM
+ resp, flavors = self.client.list_flavors_with_detail()
+ flavors = sorted(flavors, key=lambda k: k['ram'])
+ flavor_id = flavors[0]['id']
+
+ params = {'min_ram': flavors[0]['ram'] + 1}
+ resp, 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/v3/flavors/test_flavors_negative.py b/tempest/api/compute/v3/flavors/test_flavors_negative.py
new file mode 100644
index 0000000..3d4100a
--- /dev/null
+++ b/tempest/api/compute/v3/flavors/test_flavors_negative.py
@@ -0,0 +1,54 @@
+# Copyright 2013 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import testscenarios
+
+from tempest.api.compute import base
+from tempest import test
+
+
+load_tests = testscenarios.load_tests_apply_scenarios
+
+
+class FlavorsListNegativeV3Test(base.BaseV3ComputeTest,
+ test.NegativeAutoTest):
+ _interface = 'json'
+ _service = 'computev3'
+ _schema_file = 'compute/flavors/flavors_list_v3.json'
+
+ scenarios = test.NegativeAutoTest.generate_scenario(_schema_file)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_list_flavors_with_detail(self):
+ self.execute(self._schema_file)
+
+
+class FlavorDetailsNegativeV3Test(base.BaseV3ComputeTest,
+ test.NegativeAutoTest):
+ _interface = 'json'
+ _service = 'computev3'
+ _schema_file = 'compute/flavors/flavor_details_v3.json'
+
+ scenarios = test.NegativeAutoTest.generate_scenario(_schema_file)
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorDetailsNegativeV3Test, cls).setUpClass()
+ cls.set_resource("flavor", cls.flavor_ref)
+
+ @test.attr(type=['negative', 'gate'])
+ def test_get_flavor_details(self):
+ # flavor details are not returned for non-existent flavors
+ self.execute(self._schema_file)
diff --git a/tempest/api/compute/v3/images/test_image_metadata.py b/tempest/api/compute/v3/images/test_image_metadata.py
deleted file mode 100644
index cd4e5e7..0000000
--- a/tempest/api/compute/v3/images/test_image_metadata.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import config
-from tempest.test import attr
-
-CONF = config.CONF
-
-
-class ImagesMetadataTest(base.BaseV2ComputeTest):
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(ImagesMetadataTest, cls).setUpClass()
- if not CONF.service_available.glance:
- skip_msg = ("%s skipped as glance is not available" % cls.__name__)
- raise cls.skipException(skip_msg)
-
- cls.servers_client = cls.servers_client
- cls.client = cls.images_client
-
- resp, server = cls.create_test_server(wait_until='ACTIVE')
- cls.server_id = server['id']
-
- # Snapshot the server once to save time
- name = data_utils.rand_name('image')
- resp, _ = cls.client.create_image(cls.server_id, name, {})
- cls.image_id = resp['location'].rsplit('/', 1)[1]
-
- cls.client.wait_for_image_status(cls.image_id, 'ACTIVE')
-
- @classmethod
- def tearDownClass(cls):
- cls.client.delete_image(cls.image_id)
- super(ImagesMetadataTest, cls).tearDownClass()
-
- def setUp(self):
- super(ImagesMetadataTest, self).setUp()
- meta = {'key1': 'value1', 'key2': 'value2'}
- resp, _ = self.client.set_image_metadata(self.image_id, meta)
- self.assertEqual(resp.status, 200)
-
- @attr(type='gate')
- def test_list_image_metadata(self):
- # All metadata key/value pairs for an image should be returned
- resp, resp_metadata = self.client.list_image_metadata(self.image_id)
- expected = {'key1': 'value1', 'key2': 'value2'}
- self.assertEqual(expected, resp_metadata)
-
- @attr(type='gate')
- def test_set_image_metadata(self):
- # The metadata for the image should match the new values
- req_metadata = {'meta2': 'value2', 'meta3': 'value3'}
- resp, body = self.client.set_image_metadata(self.image_id,
- req_metadata)
-
- resp, resp_metadata = self.client.list_image_metadata(self.image_id)
- self.assertEqual(req_metadata, resp_metadata)
-
- @attr(type='gate')
- def test_update_image_metadata(self):
- # The metadata for the image should match the updated values
- req_metadata = {'key1': 'alt1', 'key3': 'value3'}
- resp, metadata = self.client.update_image_metadata(self.image_id,
- req_metadata)
-
- resp, resp_metadata = self.client.list_image_metadata(self.image_id)
- expected = {'key1': 'alt1', 'key2': 'value2', 'key3': 'value3'}
- self.assertEqual(expected, resp_metadata)
-
- @attr(type='gate')
- def test_get_image_metadata_item(self):
- # The value for a specific metadata key should be returned
- resp, meta = self.client.get_image_metadata_item(self.image_id,
- 'key2')
- self.assertEqual('value2', meta['key2'])
-
- @attr(type='gate')
- def test_set_image_metadata_item(self):
- # The value provided for the given meta item should be set for
- # the image
- meta = {'key1': 'alt'}
- resp, body = self.client.set_image_metadata_item(self.image_id,
- 'key1', meta)
- resp, resp_metadata = self.client.list_image_metadata(self.image_id)
- expected = {'key1': 'alt', 'key2': 'value2'}
- self.assertEqual(expected, resp_metadata)
-
- @attr(type='gate')
- def test_delete_image_metadata_item(self):
- # The metadata value/key pair should be deleted from the image
- resp, body = self.client.delete_image_metadata_item(self.image_id,
- 'key1')
- resp, resp_metadata = self.client.list_image_metadata(self.image_id)
- expected = {'key2': 'value2'}
- self.assertEqual(expected, resp_metadata)
diff --git a/tempest/api/compute/v3/images/test_image_metadata_negative.py b/tempest/api/compute/v3/images/test_image_metadata_negative.py
deleted file mode 100644
index e76af2c..0000000
--- a/tempest/api/compute/v3/images/test_image_metadata_negative.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2013 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest import exceptions
-from tempest.test import attr
-
-
-class ImagesMetadataTest(base.BaseV2ComputeTest):
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(ImagesMetadataTest, cls).setUpClass()
- cls.client = cls.images_client
-
- @attr(type=['negative', 'gate'])
- def test_list_nonexistent_image_metadata(self):
- # Negative test: List on nonexistent image
- # metadata should not happen
- self.assertRaises(exceptions.NotFound, self.client.list_image_metadata,
- data_utils.rand_uuid())
-
- @attr(type=['negative', 'gate'])
- def test_update_nonexistent_image_metadata(self):
- # Negative test:An update should not happen for a non-existent image
- meta = {'key1': 'alt1', 'key2': 'alt2'}
- self.assertRaises(exceptions.NotFound,
- self.client.update_image_metadata,
- data_utils.rand_uuid(), meta)
-
- @attr(type=['negative', 'gate'])
- def test_get_nonexistent_image_metadata_item(self):
- # Negative test: Get on non-existent image should not happen
- self.assertRaises(exceptions.NotFound,
- self.client.get_image_metadata_item,
- data_utils.rand_uuid(), 'key2')
-
- @attr(type=['negative', 'gate'])
- def test_set_nonexistent_image_metadata(self):
- # Negative test: Metadata should not be set to a non-existent image
- meta = {'key1': 'alt1', 'key2': 'alt2'}
- self.assertRaises(exceptions.NotFound, self.client.set_image_metadata,
- data_utils.rand_uuid(), meta)
-
- @attr(type=['negative', 'gate'])
- def test_set_nonexistent_image_metadata_item(self):
- # Negative test: Metadata item should not be set to a
- # nonexistent image
- meta = {'key1': 'alt'}
- self.assertRaises(exceptions.NotFound,
- self.client.set_image_metadata_item,
- data_utils.rand_uuid(), 'key1',
- meta)
-
- @attr(type=['negative', 'gate'])
- def test_delete_nonexistent_image_metadata_item(self):
- # Negative test: Shouldn't be able to delete metadata
- # item from non-existent image
- self.assertRaises(exceptions.NotFound,
- self.client.delete_image_metadata_item,
- data_utils.rand_uuid(), 'key1')
diff --git a/tempest/api/compute/v3/images/test_images_oneserver.py b/tempest/api/compute/v3/images/test_images_oneserver.py
index 18772df..cd40948 100644
--- a/tempest/api/compute/v3/images/test_images_oneserver.py
+++ b/tempest/api/compute/v3/images/test_images_oneserver.py
@@ -20,13 +20,13 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
-class ImagesOneServerTest(base.BaseV2ComputeTest):
+class ImagesOneServerV3Test(base.BaseV3ComputeTest):
_interface = 'json'
def tearDown(self):
@@ -34,12 +34,12 @@
for image_id in self.image_ids:
self.client.delete_image(image_id)
self.image_ids.remove(image_id)
- super(ImagesOneServerTest, self).tearDown()
+ super(ImagesOneServerV3Test, self).tearDown()
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
# but if it has an issue, we build a new one
- super(ImagesOneServerTest, self).setUp()
+ super(ImagesOneServerV3Test, self).setUp()
# Check if the server is in a clean state after test
try:
self.servers_client.wait_for_server_status(self.server_id,
@@ -53,7 +53,7 @@
@classmethod
def setUpClass(cls):
- super(ImagesOneServerTest, cls).setUpClass()
+ super(ImagesOneServerV3Test, cls).setUpClass()
cls.client = cls.images_client
if not CONF.service_available.glance:
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
@@ -86,13 +86,14 @@
@testtools.skipUnless(CONF.compute_feature_enabled.create_image,
'Environment unable to create images.')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_delete_image(self):
# Create a new image
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
- resp, body = self.client.create_image(self.server_id, name, meta)
+ resp, body = self.servers_client.create_image(self.server_id,
+ name, meta)
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
self.client.wait_for_image_status(image_id, 'ACTIVE')
@@ -117,12 +118,13 @@
self.assertEqual('204', resp['status'])
self.client.wait_for_resource_deletion(image_id)
- @attr(type=['gate'])
+ @test.attr(type=['gate'])
def test_create_image_specify_multibyte_character_image_name(self):
# prefix character is:
# http://www.fileformat.info/info/unicode/char/1F4A9/index.htm
utf8_name = data_utils.rand_name(u'\xF0\x9F\x92\xA9')
- resp, body = self.client.create_image(self.server_id, utf8_name)
+ resp, body = self.servers_client.create_image(self.server_id,
+ utf8_name)
image_id = data_utils.parse_image_id(resp['location'])
self.addCleanup(self.client.delete_image, image_id)
self.assertEqual('202', resp['status'])
diff --git a/tempest/api/compute/v3/images/test_images_oneserver_negative.py b/tempest/api/compute/v3/images/test_images_oneserver_negative.py
index bc276d1..f2f2375 100644
--- a/tempest/api/compute/v3/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/v3/images/test_images_oneserver_negative.py
@@ -20,15 +20,14 @@
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
-from tempest.test import attr
-from tempest.test import skip_because
+from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
-class ImagesOneServerNegativeTest(base.BaseV2ComputeTest):
+class ImagesOneServerNegativeV3Test(base.BaseV3ComputeTest):
_interface = 'json'
def tearDown(self):
@@ -36,12 +35,12 @@
for image_id in self.image_ids:
self.client.delete_image(image_id)
self.image_ids.remove(image_id)
- super(ImagesOneServerNegativeTest, self).tearDown()
+ super(ImagesOneServerNegativeV3Test, self).tearDown()
def setUp(self):
# NOTE(afazekas): Normally we use the same server with all test cases,
# but if it has an issue, we build a new one
- super(ImagesOneServerNegativeTest, self).setUp()
+ super(ImagesOneServerNegativeV3Test, self).setUp()
# Check if the server is in a clean state after test
try:
self.servers_client.wait_for_server_status(self.server_id,
@@ -58,7 +57,7 @@
@classmethod
def setUpClass(cls):
- super(ImagesOneServerNegativeTest, cls).setUpClass()
+ super(ImagesOneServerNegativeV3Test, cls).setUpClass()
cls.client = cls.images_client
if not CONF.service_available.glance:
skip_msg = ("%s skipped as glance is not available" % cls.__name__)
@@ -85,41 +84,44 @@
cls.alt_manager = clients.AltManager()
cls.alt_client = cls.alt_manager.images_client
- @skip_because(bug="1006725")
- @attr(type=['negative', 'gate'])
+ @test.skip_because(bug="1006725")
+ @test.attr(type=['negative', 'gate'])
def test_create_image_specify_multibyte_character_image_name(self):
# invalid multibyte sequence from:
# http://stackoverflow.com/questions/1301402/
# example-invalid-utf8-string
invalid_name = data_utils.rand_name(u'\xc3\x28')
self.assertRaises(exceptions.BadRequest,
- self.client.create_image, self.server_id,
+ self.servers_client.create_image,
+ self.server_id,
invalid_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_image_specify_invalid_metadata(self):
# Return an error when creating image with invalid metadata
snapshot_name = data_utils.rand_name('test-snap-')
meta = {'': ''}
- self.assertRaises(exceptions.BadRequest, self.client.create_image,
+ self.assertRaises(exceptions.BadRequest,
+ self.servers_client.create_image,
self.server_id, snapshot_name, meta)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_image_specify_metadata_over_limits(self):
# Return an error when creating image with meta data over 256 chars
snapshot_name = data_utils.rand_name('test-snap-')
meta = {'a' * 260: 'b' * 260}
- self.assertRaises(exceptions.BadRequest, self.client.create_image,
+ self.assertRaises(exceptions.BadRequest,
+ self.servers_client.create_image,
self.server_id, snapshot_name, meta)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_second_image_when_first_image_is_being_saved(self):
# Disallow creating another image when first image is being saved
# Create first snapshot
snapshot_name = data_utils.rand_name('test-snap-')
- resp, body = self.client.create_image(self.server_id,
- snapshot_name)
+ resp, body = self.servers_client.create_image(self.server_id,
+ snapshot_name)
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
self.image_ids.append(image_id)
@@ -127,23 +129,26 @@
# Create second snapshot
alt_snapshot_name = data_utils.rand_name('test-snap-')
- self.assertRaises(exceptions.Conflict, self.client.create_image,
+ self.assertRaises(exceptions.Conflict,
+ self.servers_client.create_image,
self.server_id, alt_snapshot_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_image_specify_name_over_256_chars(self):
# Return an error if snapshot name over 256 characters is passed
snapshot_name = data_utils.rand_name('a' * 260)
- self.assertRaises(exceptions.BadRequest, self.client.create_image,
+ self.assertRaises(exceptions.BadRequest,
+ self.servers_client.create_image,
self.server_id, snapshot_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_image_that_is_not_yet_active(self):
# Return an error while trying to delete an image what is creating
snapshot_name = data_utils.rand_name('test-snap-')
- resp, body = self.client.create_image(self.server_id, snapshot_name)
+ resp, body = self.servers_client.create_image(self.server_id,
+ snapshot_name)
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
self.image_ids.append(image_id)
@@ -151,7 +156,7 @@
# Do not wait, attempt to delete the image, ensure it's successful
resp, body = self.client.delete_image(image_id)
- self.assertEqual('204', resp['status'])
+ self.assertEqual('200', resp['status'])
self.image_ids.remove(image_id)
self.assertRaises(exceptions.NotFound, self.client.get_image, image_id)
diff --git a/tempest/api/compute/v3/images/test_list_image_filters.py b/tempest/api/compute/v3/images/test_list_image_filters.py
deleted file mode 100644
index 457ca53..0000000
--- a/tempest/api/compute/v3/images/test_list_image_filters.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.compute import base
-from tempest import config
-from tempest import exceptions
-from tempest.openstack.common import log as logging
-from tempest.test import attr
-
-CONF = config.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class ListImageFiltersTest(base.BaseV2ComputeTest):
- _interface = 'json'
-
- @classmethod
- def setUpClass(cls):
- super(ListImageFiltersTest, cls).setUpClass()
- if not CONF.service_available.glance:
- skip_msg = ("%s skipped as glance is not available" % cls.__name__)
- raise cls.skipException(skip_msg)
- cls.client = cls.images_client
- cls.image_ids = []
-
- try:
- resp, cls.server1 = cls.create_test_server()
- resp, cls.server2 = cls.create_test_server(wait_until='ACTIVE')
- # NOTE(sdague) this is faster than doing the sync wait_util on both
- cls.servers_client.wait_for_server_status(cls.server1['id'],
- 'ACTIVE')
-
- # Create images to be used in the filter tests
- resp, cls.image1 = cls.create_image_from_server(
- cls.server1['id'], wait_until='ACTIVE')
- cls.image1_id = cls.image1['id']
-
- # Servers have a hidden property for when they are being imaged
- # Performing back-to-back create image calls on a single
- # server will sometimes cause failures
- resp, cls.image3 = cls.create_image_from_server(
- cls.server2['id'], wait_until='ACTIVE')
- cls.image3_id = cls.image3['id']
-
- # Wait for the server to be active after the image upload
- resp, cls.image2 = cls.create_image_from_server(
- cls.server1['id'], wait_until='ACTIVE')
- cls.image2_id = cls.image2['id']
- except Exception:
- LOG.exception('setUpClass failed')
- cls.tearDownClass()
- raise
-
- @attr(type=['negative', 'gate'])
- def test_get_image_not_existing(self):
- # Check raises a NotFound
- self.assertRaises(exceptions.NotFound, self.client.get_image,
- "nonexistingimageid")
-
- @attr(type='gate')
- def test_list_images_filter_by_status(self):
- # The list of images should contain only images with the
- # provided status
- params = {'status': 'ACTIVE'}
- resp, images = self.client.list_images(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_filter_by_name(self):
- # List of all images should contain the expected images filtered
- # by name
- params = {'name': self.image1['name']}
- resp, images = self.client.list_images(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_filter_by_server_id(self):
- # The images should contain images filtered by server id
- params = {'server': self.server1['id']}
- resp, images = self.client.list_images(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]),
- "Failed to find image %s in images. Got images %s" %
- (self.image1_id, images))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_filter_by_server_ref(self):
- # The list of servers should be filtered by server ref
- server_links = self.server2['links']
-
- # Try all server link types
- for link in server_links:
- params = {'server': link['href']}
- resp, images = self.client.list_images(params)
-
- self.assertFalse(any([i for i in images
- if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_filter_by_type(self):
- # The list of servers should be filtered by image type
- params = {'type': 'snapshot'}
- resp, images = self.client.list_images(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
-
- @attr(type='gate')
- def test_list_images_limit_results(self):
- # Verify only the expected number of results are returned
- params = {'limit': '1'}
- resp, images = self.client.list_images(params)
- self.assertEqual(1, len([x for x in images if 'id' in x]))
-
- @attr(type='gate')
- def test_list_images_filter_by_changes_since(self):
- # Verify only updated images are returned in the detailed list
-
- # Becoming ACTIVE will modify the updated time
- # Filter by the image's created time
- params = {'changes-since': self.image3['created']}
- resp, images = self.client.list_images(params)
- found = any([i for i in images if i['id'] == self.image3_id])
- self.assertTrue(found)
-
- @attr(type='gate')
- def test_list_images_with_detail_filter_by_status(self):
- # Detailed list of all images should only contain images
- # with the provided status
- params = {'status': 'ACTIVE'}
- resp, images = self.client.list_images_with_detail(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_with_detail_filter_by_name(self):
- # Detailed list of all images should contain the expected
- # images filtered by name
- params = {'name': self.image1['name']}
- resp, images = self.client.list_images_with_detail(params)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image2_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_with_detail_limit_results(self):
- # Verify only the expected number of results (with full details)
- # are returned
- params = {'limit': '1'}
- resp, images = self.client.list_images_with_detail(params)
- self.assertEqual(1, len(images))
-
- @attr(type='gate')
- def test_list_images_with_detail_filter_by_server_ref(self):
- # Detailed list of servers should be filtered by server ref
- server_links = self.server2['links']
-
- # Try all server link types
- for link in server_links:
- params = {'server': link['href']}
- resp, images = self.client.list_images_with_detail(params)
-
- self.assertFalse(any([i for i in images
- if i['id'] == self.image1_id]))
- self.assertFalse(any([i for i in images
- if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images
- if i['id'] == self.image3_id]))
-
- @attr(type='gate')
- def test_list_images_with_detail_filter_by_type(self):
- # The detailed list of servers should be filtered by image type
- params = {'type': 'snapshot'}
- resp, images = self.client.list_images_with_detail(params)
- resp, image4 = self.client.get_image(self.image_ref)
-
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image2_id]))
- self.assertTrue(any([i for i in images if i['id'] == self.image3_id]))
- self.assertFalse(any([i for i in images if i['id'] == self.image_ref]))
-
- @attr(type='gate')
- def test_list_images_with_detail_filter_by_changes_since(self):
- # Verify an update image is returned
-
- # Becoming ACTIVE will modify the updated time
- # Filter by the image's created time
- params = {'changes-since': self.image1['created']}
- resp, images = self.client.list_images_with_detail(params)
- self.assertTrue(any([i for i in images if i['id'] == self.image1_id]))
-
- @attr(type=['negative', 'gate'])
- def test_get_nonexistent_image(self):
- # Negative test: GET on non-existent image should fail
- self.assertRaises(exceptions.NotFound, self.client.get_image, 999)
diff --git a/tempest/api/compute/v3/servers/test_server_addresses.py b/tempest/api/compute/v3/servers/test_server_addresses.py
index bffa7c4..038e254 100644
--- a/tempest/api/compute/v3/servers/test_server_addresses.py
+++ b/tempest/api/compute/v3/servers/test_server_addresses.py
@@ -14,8 +14,11 @@
# under the License.
from tempest.api.compute import base
+from tempest import config
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
+
+CONF = config.CONF
class ServerAddressesV3Test(base.BaseV3ComputeTest):
@@ -30,20 +33,22 @@
resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_server_addresses_invalid_server_id(self):
# List addresses request should fail if server id not in system
self.assertRaises(exceptions.NotFound, self.client.list_addresses,
'999')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_list_server_addresses_by_network_neg(self):
# List addresses by network should fail if network name not valid
self.assertRaises(exceptions.NotFound,
self.client.list_addresses_by_network,
self.server['id'], 'invalid')
- @attr(type='smoke')
+ @test.skip_because(bug="1210483",
+ condition=CONF.service_available.neutron)
+ @test.attr(type='smoke')
def test_list_server_addresses(self):
# All public and private addresses for
# a server should be returned
@@ -60,7 +65,7 @@
self.assertTrue(address['addr'])
self.assertTrue(address['version'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_server_addresses_by_network(self):
# Providing a network type should filter
# the addresses return by that type
diff --git a/tempest/api/compute/v3/servers/test_server_metadata_negative.py b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
index 2c413db..ce6c340 100644
--- a/tempest/api/compute/v3/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_metadata_negative.py
@@ -34,6 +34,7 @@
cls.server_id = server['id']
+ @test.skip_because(bug="1273948")
@test.attr(type=['gate', 'negative'])
def test_server_create_metadata_key_too_long(self):
# Attempt to start a server with a meta-data key that is > 255
@@ -43,7 +44,7 @@
for sz in [256, 257, 511, 1023]:
key = "k" * sz
meta = {key: 'data1'}
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises(exceptions.BadRequest,
self.create_test_server,
meta=meta)
diff --git a/tempest/api/compute/v3/servers/test_server_password.py b/tempest/api/compute/v3/servers/test_server_password.py
new file mode 100644
index 0000000..579a8a5
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_server_password.py
@@ -0,0 +1,38 @@
+# Copyright 2013 IBM Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from tempest.api.compute import base
+from tempest import test
+
+
+class ServerPasswordV3Test(base.BaseV3ComputeTest):
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServerPasswordV3Test, cls).setUpClass()
+ cls.client = cls.servers_client
+ resp, cls.server = cls.create_test_server(wait_until="ACTIVE")
+
+ @test.attr(type='gate')
+ def test_get_server_password(self):
+ resp, body = self.client.get_password(self.server['id'])
+ self.assertEqual(200, resp.status)
+
+ @test.attr(type='gate')
+ def test_delete_server_password(self):
+ resp, body = self.client.delete_password(self.server['id'])
+ self.assertEqual(204, resp.status)
diff --git a/tempest/api/compute/v3/servers/test_server_rescue.py b/tempest/api/compute/v3/servers/test_server_rescue.py
index f8be1c1..fa7def0 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue.py
@@ -14,6 +14,7 @@
# under the License.
from tempest.api.compute import base
+from tempest.common.utils import data_utils
from tempest import exceptions
from tempest.test import attr
@@ -27,20 +28,10 @@
cls.device = 'vdf'
# Create a volume and wait for it to become ready for attach
- resp, cls.volume_to_attach = \
- cls.volumes_client.create_volume(1,
- display_name=
- 'test_attach')
+ resp, cls.volume = cls.volumes_client.create_volume(
+ 1, display_name=data_utils.rand_name(cls.__name__ + '_volume'))
cls.volumes_client.wait_for_volume_status(
- cls.volume_to_attach['id'], 'available')
-
- # Create a volume and wait for it to become ready for attach
- resp, cls.volume_to_detach = \
- cls.volumes_client.create_volume(1,
- display_name=
- 'test_detach')
- cls.volumes_client.wait_for_volume_status(
- cls.volume_to_detach['id'], 'available')
+ cls.volume['id'], 'available')
# Server for positive tests
resp, server = cls.create_test_server(wait_until='BUILD')
@@ -62,9 +53,7 @@
@classmethod
def tearDownClass(cls):
- client = cls.volumes_client
- client.delete_volume(str(cls.volume_to_attach['id']).strip())
- client.delete_volume(str(cls.volume_to_detach['id']).strip())
+ cls.delete_volume(cls.volume['id'])
super(ServerRescueV3Test, cls).tearDownClass()
def tearDown(self):
@@ -75,9 +64,6 @@
self.volumes_client.wait_for_volume_status(volume_id,
'available')
- def _delete(self, volume_id):
- self.volumes_client.delete_volume(volume_id)
-
def _unrescue(self, server_id):
resp, body = self.servers_client.unrescue_server(server_id)
self.assertEqual(202, resp.status)
@@ -141,29 +127,27 @@
self.assertRaises(exceptions.Conflict,
self.servers_client.attach_volume,
self.server_id,
- self.volume_to_attach['id'],
+ self.volume['id'],
device='/dev/%s' % self.device)
@attr(type=['negative', 'gate'])
def test_rescued_vm_detach_volume(self):
# Attach the volume to the server
self.servers_client.attach_volume(self.server_id,
- self.volume_to_detach['id'],
+ self.volume['id'],
device='/dev/%s' % self.device)
- self.volumes_client.wait_for_volume_status(
- self.volume_to_detach['id'], 'in-use')
+ self.volumes_client.wait_for_volume_status(self.volume['id'], 'in-use')
# Rescue the server
self.servers_client.rescue_server(self.server_id,
admin_password=self.password)
self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
# addCleanup is a LIFO queue
- self.addCleanup(self._detach, self.server_id,
- self.volume_to_detach['id'])
+ self.addCleanup(self._detach, self.server_id, self.volume['id'])
self.addCleanup(self._unrescue, self.server_id)
# Detach the volume from the server expecting failure
self.assertRaises(exceptions.Conflict,
self.servers_client.detach_volume,
self.server_id,
- self.volume_to_detach['id'])
+ self.volume['id'])
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index c153699..12e0ad8 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -191,12 +191,13 @@
self.create_test_server,
key_name=key_name)
+ @test.skip_because(bug="1273948")
@test.attr(type=['negative', 'gate'])
def test_create_server_metadata_exceeds_length_limit(self):
# Pass really long metadata while creating a server
metadata = {'a': 'b' * 260}
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises(exceptions.BadRequest,
self.create_test_server,
meta=metadata)
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index ab882cd..5b272ef 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -63,7 +63,7 @@
except Exception:
# ignore errors while auto removing created resource
pass
-
+ cls.clear_isolated_creds()
super(BaseDataProcessingTest, cls).tearDownClass()
@classmethod
diff --git a/tempest/api/data_processing/test_plugins.py b/tempest/api/data_processing/test_plugins.py
new file mode 100644
index 0000000..3b941d8
--- /dev/null
+++ b/tempest/api/data_processing/test_plugins.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2013 Mirantis Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.api.data_processing import base as dp_base
+from tempest.test import attr
+
+
+class PluginsTest(dp_base.BaseDataProcessingTest):
+ def _list_all_plugin_names(self):
+ """Returns all enabled plugin names.
+
+ It ensures response status and main plugins availability.
+ """
+ resp, plugins = self.client.list_plugins()
+
+ self.assertEqual(200, resp.status)
+
+ plugins_names = list([plugin['name'] for plugin in plugins])
+ self.assertIn('vanilla', plugins_names)
+ self.assertIn('hdp', plugins_names)
+
+ return plugins_names
+
+ @attr(type='smoke')
+ def test_plugin_list(self):
+ self._list_all_plugin_names()
+
+ @attr(type='smoke')
+ def test_plugin_get(self):
+ for plugin_name in self._list_all_plugin_names():
+ resp, plugin = self.client.get_plugin(plugin_name)
+
+ self.assertEqual(200, resp.status)
+ self.assertEqual(plugin_name, plugin['name'])
+
+ for plugin_version in plugin['versions']:
+ resp, detailed_plugin = self.client.get_plugin(plugin_name,
+ plugin_version)
+
+ self.assertEqual(200, resp.status)
+ self.assertEqual(plugin_name, detailed_plugin['name'])
+
+ # check that required image tags contains name and version
+ image_tags = detailed_plugin['required_image_tags']
+ self.assertIn(plugin_name, image_tags)
+ self.assertIn(plugin_version, image_tags)
diff --git a/tempest/api/identity/admin/test_roles.py b/tempest/api/identity/admin/test_roles.py
index f1124e4..aa64969 100644
--- a/tempest/api/identity/admin/test_roles.py
+++ b/tempest/api/identity/admin/test_roles.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class RolesTestJSON(base.BaseIdentityAdminTest):
+class RolesTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@classmethod
diff --git a/tempest/api/identity/admin/test_roles_negative.py b/tempest/api/identity/admin/test_roles_negative.py
index e316dc7..7a0bdea 100644
--- a/tempest/api/identity/admin/test_roles_negative.py
+++ b/tempest/api/identity/admin/test_roles_negative.py
@@ -21,7 +21,7 @@
from tempest.test import attr
-class RolesNegativeTestJSON(base.BaseIdentityAdminTest):
+class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
def _get_role_params(self):
@@ -41,10 +41,10 @@
@attr(type=['negative', 'gate'])
def test_list_roles_request_without_token(self):
# Request to list roles without a valid token should fail
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.list_roles)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_role_create_blank_name(self):
@@ -61,12 +61,12 @@
@attr(type=['negative', 'gate'])
def test_create_role_request_without_token(self):
# Request to create role without a valid token should fail
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
role_name = data_utils.rand_name(name='role-')
self.assertRaises(exceptions.Unauthorized,
self.client.create_role, role_name)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_role_create_duplicate(self):
@@ -99,12 +99,12 @@
self.assertEqual(200, resp.status)
self.data.roles.append(body)
role_id = body.get('id')
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized,
self.client.delete_role,
role_id)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_delete_role_non_existent(self):
@@ -126,12 +126,12 @@
def test_assign_user_role_request_without_token(self):
# Request to assign a role to a user without a valid token
(user, tenant, role) = self._get_role_params()
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized,
self.client.assign_user_role, tenant['id'],
user['id'], role['id'])
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_assign_user_role_for_non_existent_role(self):
@@ -176,12 +176,12 @@
resp, user_role = self.client.assign_user_role(tenant['id'],
user['id'],
role['id'])
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized,
self.client.remove_user_role, tenant['id'],
user['id'], role['id'])
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_remove_user_role_non_existent_role(self):
@@ -219,14 +219,14 @@
def test_list_user_roles_request_without_token(self):
# Request to list user's roles without a valid token should fail
(user, tenant, role) = self._get_role_params()
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
try:
self.assertRaises(exceptions.Unauthorized,
self.client.list_user_roles, tenant['id'],
user['id'])
finally:
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
class RolesTestXML(RolesNegativeTestJSON):
diff --git a/tempest/api/identity/admin/test_services.py b/tempest/api/identity/admin/test_services.py
index 8ba333f..cbf6b58 100644
--- a/tempest/api/identity/admin/test_services.py
+++ b/tempest/api/identity/admin/test_services.py
@@ -20,7 +20,7 @@
from tempest.test import attr
-class ServicesTestJSON(base.BaseIdentityAdminTest):
+class ServicesTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
def _del_service(self, service_id):
diff --git a/tempest/api/identity/admin/test_tenant_negative.py b/tempest/api/identity/admin/test_tenant_negative.py
index a4d9d97..44b54b8 100644
--- a/tempest/api/identity/admin/test_tenant_negative.py
+++ b/tempest/api/identity/admin/test_tenant_negative.py
@@ -21,7 +21,7 @@
from tempest.test import attr
-class TenantsNegativeTestJSON(base.BaseIdentityAdminTest):
+class TenantsNegativeTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@attr(type=['negative', 'gate'])
@@ -33,10 +33,10 @@
@attr(type=['negative', 'gate'])
def test_list_tenant_request_without_token(self):
# Request to list tenants without a valid token should fail
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.list_tenants)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_tenant_delete_by_unauthorized_user(self):
@@ -55,11 +55,11 @@
resp, tenant = self.client.create_tenant(tenant_name)
self.assertEqual(200, resp.status)
self.data.tenants.append(tenant)
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.delete_tenant,
tenant['id'])
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_delete_non_existent_tenant(self):
@@ -93,11 +93,11 @@
def test_create_tenant_request_without_token(self):
# Create tenant request without a token should not be authorized
tenant_name = data_utils.rand_name(name='tenant-')
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.create_tenant,
tenant_name)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_create_tenant_with_empty_name(self):
@@ -135,11 +135,11 @@
resp, tenant = self.client.create_tenant(tenant_name)
self.assertEqual(200, resp.status)
self.data.tenants.append(tenant)
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.update_tenant,
tenant['id'])
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
class TenantsNegativeTestXML(TenantsNegativeTestJSON):
diff --git a/tempest/api/identity/admin/test_tenants.py b/tempest/api/identity/admin/test_tenants.py
index 46fa7a9..c7cacb4 100644
--- a/tempest/api/identity/admin/test_tenants.py
+++ b/tempest/api/identity/admin/test_tenants.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class TenantsTestJSON(base.BaseIdentityAdminTest):
+class TenantsTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@attr(type='gate')
diff --git a/tempest/api/identity/admin/test_tokens.py b/tempest/api/identity/admin/test_tokens.py
index cfe17fb..239433b 100644
--- a/tempest/api/identity/admin/test_tokens.py
+++ b/tempest/api/identity/admin/test_tokens.py
@@ -13,14 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import json
-
from tempest.api.identity import base
from tempest.common.utils import data_utils
from tempest.test import attr
-class TokensTestJSON(base.BaseIdentityAdminTest):
+class TokensTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@attr(type='gate')
@@ -42,12 +40,11 @@
rsp, body = self.token_client.auth(user_name,
user_password,
tenant['name'])
- access_data = json.loads(body)['access']
self.assertEqual(rsp['status'], '200')
- self.assertEqual(access_data['token']['tenant']['name'],
+ self.assertEqual(body['token']['tenant']['name'],
tenant['name'])
# then delete the token
- token_id = access_data['token']['id']
+ token_id = body['token']['id']
resp, body = self.client.delete_token(token_id)
self.assertEqual(resp['status'], '204')
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index 14222fb..898cccc 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -20,7 +20,7 @@
from tempest.test import attr
-class UsersTestJSON(base.BaseIdentityAdminTest):
+class UsersTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@classmethod
@@ -115,7 +115,7 @@
self.token_client.auth(self.data.test_user, self.data.test_password,
self.data.test_tenant)
# Get the token of the current client
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
# Delete the token from database
self.client.delete_token(token)
# Re-auth
@@ -123,7 +123,7 @@
self.data.test_password,
self.data.test_tenant)
self.assertEqual('200', resp['status'])
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type='smoke')
def test_get_users(self):
diff --git a/tempest/api/identity/admin/test_users_negative.py b/tempest/api/identity/admin/test_users_negative.py
index ba7af09..1188325 100644
--- a/tempest/api/identity/admin/test_users_negative.py
+++ b/tempest/api/identity/admin/test_users_negative.py
@@ -20,7 +20,7 @@
import uuid
-class UsersNegativeTestJSON(base.BaseIdentityAdminTest):
+class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
_interface = 'json'
@classmethod
@@ -75,7 +75,7 @@
# Request to create a user without a valid token should fail
self.data.setup_test_tenant()
# Get the token of the current client
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
# Delete the token from database
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.create_user,
@@ -83,7 +83,7 @@
self.data.tenant['id'], self.alt_email)
# Unset the token to allow further tests to generate a new token
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_create_user_with_enabled_non_bool(self):
@@ -108,14 +108,14 @@
# Request to update a user without a valid token should fail
# Get the token of the current client
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
# Delete the token from database
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.update_user,
self.alt_user)
# Unset the token to allow further tests to generate a new token
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_update_user_by_unauthorized_user(self):
@@ -143,14 +143,14 @@
# Request to delete a user without a valid token should fail
# Get the token of the current client
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
# Delete the token from database
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.delete_user,
self.alt_user)
# Unset the token to allow further tests to generate a new token
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_authentication_for_disabled_user(self):
@@ -207,10 +207,10 @@
@attr(type=['negative', 'gate'])
def test_get_users_request_without_token(self):
# Request to get list of users without a valid token should fail
- token = self.client.get_auth()
+ token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(exceptions.Unauthorized, self.client.get_users)
- self.client.clear_auth()
+ self.client.auth_provider.clear_auth()
@attr(type=['negative', 'gate'])
def test_list_users_with_invalid_tenant(self):
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index 753eaa6..5f22d43 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class CredentialsTestJSON(base.BaseIdentityAdminTest):
+class CredentialsTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@classmethod
@@ -32,23 +32,23 @@
u_email = '%s@testmail.tm' % u_name
u_password = data_utils.rand_name('pass-')
for i in range(2):
- resp, cls.project = cls.v3_client.create_project(
+ resp, cls.project = cls.client.create_project(
data_utils.rand_name('project-'),
description=data_utils.rand_name('project-desc-'))
assert resp['status'] == '201', "Expected %s" % resp['status']
cls.projects.append(cls.project['id'])
- resp, cls.user_body = cls.v3_client.create_user(
+ resp, cls.user_body = cls.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email, project_id=cls.projects[0])
assert resp['status'] == '201', "Expected: %s" % resp['status']
@classmethod
def tearDownClass(cls):
- resp, _ = cls.v3_client.delete_user(cls.user_body['id'])
+ resp, _ = cls.client.delete_user(cls.user_body['id'])
assert resp['status'] == '204', "Expected: %s" % resp['status']
for p in cls.projects:
- resp, _ = cls.v3_client.delete_project(p)
+ resp, _ = cls.client.delete_project(p)
assert resp['status'] == '204', "Expected: %s" % resp['status']
super(CredentialsTestJSON, cls).tearDownClass()
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 4017b62..086d235 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -19,14 +19,14 @@
from tempest.test import attr
-class DomainsTestJSON(base.BaseIdentityAdminTest):
+class DomainsTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
def _delete_domain(self, domain_id):
# It is necessary to disable the domain before deleting,
# or else it would result in unauthorized error
- _, body = self.v3_client.update_domain(domain_id, enabled=False)
- resp, _ = self.v3_client.delete_domain(domain_id)
+ _, body = self.client.update_domain(domain_id, enabled=False)
+ resp, _ = self.client.delete_domain(domain_id)
self.assertEqual(204, resp.status)
@attr(type='smoke')
@@ -35,14 +35,14 @@
domain_ids = list()
fetched_ids = list()
for _ in range(3):
- _, domain = self.v3_client.create_domain(
+ _, domain = self.client.create_domain(
data_utils.rand_name('domain-'),
description=data_utils.rand_name('domain-desc-'))
# Delete the domain at the end of this method
self.addCleanup(self._delete_domain, domain['id'])
domain_ids.append(domain['id'])
# List and Verify Domains
- resp, body = self.v3_client.list_domains()
+ resp, body = self.client.list_domains()
self.assertEqual(resp['status'], '200')
for d in body:
fetched_ids.append(d['id'])
@@ -53,7 +53,7 @@
def test_create_update_delete_domain(self):
d_name = data_utils.rand_name('domain-')
d_desc = data_utils.rand_name('domain-desc-')
- resp_1, domain = self.v3_client.create_domain(
+ resp_1, domain = self.client.create_domain(
d_name, description=d_desc)
self.assertEqual(resp_1['status'], '201')
self.addCleanup(self._delete_domain, domain['id'])
@@ -72,7 +72,7 @@
new_desc = data_utils.rand_name('new-desc-')
new_name = data_utils.rand_name('new-name-')
- resp_2, updated_domain = self.v3_client.update_domain(
+ resp_2, updated_domain = self.client.update_domain(
domain['id'], name=new_name, description=new_desc)
self.assertEqual(resp_2['status'], '200')
self.assertIn('id', updated_domain)
@@ -85,7 +85,7 @@
self.assertEqual(new_desc, updated_domain['description'])
self.assertEqual('true', str(updated_domain['enabled']).lower())
- resp_3, fetched_domain = self.v3_client.get_domain(domain['id'])
+ resp_3, fetched_domain = self.client.get_domain(domain['id'])
self.assertEqual(resp_3['status'], '200')
self.assertEqual(new_name, fetched_domain['name'])
self.assertEqual(new_desc, fetched_domain['description'])
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 4ae7884..78ecf93 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class EndPointsTestJSON(base.BaseIdentityAdminTest):
+class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@classmethod
@@ -31,8 +31,8 @@
s_type = data_utils.rand_name('type--')
s_description = data_utils.rand_name('description-')
resp, cls.service_data =\
- cls.identity_client.create_service(s_name, s_type,
- description=s_description)
+ cls.service_client.create_service(s_name, s_type,
+ description=s_description)
cls.service_id = cls.service_data['id']
cls.service_ids.append(cls.service_id)
# Create endpoints so as to use for LIST and GET test cases
@@ -50,7 +50,7 @@
for e in cls.setup_endpoints:
cls.client.delete_endpoint(e['id'])
for s in cls.service_ids:
- cls.identity_client.delete_service(s)
+ cls.service_client.delete_service(s)
super(EndPointsTestJSON, cls).tearDownClass()
@attr(type='gate')
@@ -107,8 +107,8 @@
s_type = data_utils.rand_name('type--')
s_description = data_utils.rand_name('description-')
resp, self.service2 =\
- self.identity_client.create_service(s_name, s_type,
- description=s_description)
+ self.service_client.create_service(s_name, s_type,
+ description=s_description)
self.service_ids.append(self.service2['id'])
# Updating endpoint with new values
region2 = data_utils.rand_name('region')
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 70afec7..6e898b2 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -18,7 +18,7 @@
from tempest import test
-class GroupsV3TestJSON(base.BaseIdentityAdminTest):
+class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@classmethod
@@ -29,23 +29,23 @@
def test_group_create_update_get(self):
name = data_utils.rand_name('Group')
description = data_utils.rand_name('Description')
- resp, group = self.v3_client.create_group(name,
- description=description)
- self.addCleanup(self.v3_client.delete_group, group['id'])
+ resp, group = self.client.create_group(name,
+ description=description)
+ self.addCleanup(self.client.delete_group, group['id'])
self.assertEqual(resp['status'], '201')
self.assertEqual(group['name'], name)
self.assertEqual(group['description'], description)
new_name = data_utils.rand_name('UpdateGroup')
new_desc = data_utils.rand_name('UpdateDescription')
- resp, updated_group = self.v3_client.update_group(group['id'],
- name=new_name,
- description=new_desc)
+ resp, updated_group = self.client.update_group(group['id'],
+ name=new_name,
+ description=new_desc)
self.assertEqual(resp['status'], '200')
self.assertEqual(updated_group['name'], new_name)
self.assertEqual(updated_group['description'], new_desc)
- resp, new_group = self.v3_client.get_group(group['id'])
+ resp, new_group = self.client.get_group(group['id'])
self.assertEqual(resp['status'], '200')
self.assertEqual(group['id'], new_group['id'])
self.assertEqual(new_name, new_group['name'])
@@ -54,27 +54,27 @@
@test.attr(type='smoke')
def test_group_users_add_list_delete(self):
name = data_utils.rand_name('Group')
- resp, group = self.v3_client.create_group(name)
- self.addCleanup(self.v3_client.delete_group, group['id'])
+ resp, group = self.client.create_group(name)
+ self.addCleanup(self.client.delete_group, group['id'])
# add user into group
users = []
for i in range(3):
name = data_utils.rand_name('User')
- resp, user = self.v3_client.create_user(name)
+ resp, user = self.client.create_user(name)
users.append(user)
- self.addCleanup(self.v3_client.delete_user, user['id'])
- self.v3_client.add_group_user(group['id'], user['id'])
+ self.addCleanup(self.client.delete_user, user['id'])
+ self.client.add_group_user(group['id'], user['id'])
# list users in group
- resp, group_users = self.v3_client.list_group_users(group['id'])
+ resp, group_users = self.client.list_group_users(group['id'])
self.assertEqual(resp['status'], '200')
self.assertEqual(users.sort(), group_users.sort())
# delete user in group
for user in users:
- resp, body = self.v3_client.delete_group_user(group['id'],
- user['id'])
+ resp, body = self.client.delete_group_user(group['id'],
+ user['id'])
self.assertEqual(resp['status'], '204')
- resp, group_users = self.v3_client.list_group_users(group['id'])
+ resp, group_users = self.client.list_group_users(group['id'])
self.assertEqual(len(group_users), 0)
diff --git a/tempest/api/identity/admin/v3/test_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index 0e8e4c3..3e04b5f 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class PoliciesTestJSON(base.BaseIdentityAdminTest):
+class PoliciesTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
def _delete_policy(self, policy_id):
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 1fc5a6a..f309897 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -19,27 +19,27 @@
from tempest.test import attr
-class ProjectsTestJSON(base.BaseIdentityAdminTest):
+class ProjectsTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
def _delete_project(self, project_id):
- resp, _ = self.v3_client.delete_project(project_id)
+ resp, _ = self.client.delete_project(project_id)
self.assertEqual(resp['status'], '204')
self.assertRaises(
- exceptions.NotFound, self.v3_client.get_project, project_id)
+ exceptions.NotFound, self.client.get_project, project_id)
@attr(type='gate')
def test_project_list_delete(self):
# Create several projects and delete them
for _ in xrange(3):
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
data_utils.rand_name('project-new'))
self.addCleanup(self._delete_project, project['id'])
- resp, list_projects = self.v3_client.list_projects()
+ resp, list_projects = self.client.list_projects()
self.assertEqual(resp['status'], '200')
- resp, get_project = self.v3_client.get_project(project['id'])
+ resp, get_project = self.client.get_project(project['id'])
self.assertIn(get_project, list_projects)
@attr(type='gate')
@@ -47,16 +47,16 @@
# Create project with a description
project_name = data_utils.rand_name('project-')
project_desc = data_utils.rand_name('desc-')
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
project_name, description=project_desc)
- self.v3data.projects.append(project)
+ self.data.projects.append(project)
st1 = resp['status']
project_id = project['id']
desc1 = project['description']
self.assertEqual(st1, '201')
self.assertEqual(desc1, project_desc, 'Description should have '
'been sent in response for create')
- resp, body = self.v3_client.get_project(project_id)
+ resp, body = self.client.get_project(project_id)
desc2 = body['description']
self.assertEqual(desc2, project_desc, 'Description does not appear'
'to be set')
@@ -65,15 +65,15 @@
def test_project_create_enabled(self):
# Create a project that is enabled
project_name = data_utils.rand_name('project-')
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
project_name, enabled=True)
- self.v3data.projects.append(project)
+ self.data.projects.append(project)
project_id = project['id']
st1 = resp['status']
en1 = project['enabled']
self.assertEqual(st1, '201')
self.assertTrue(en1, 'Enable should be True in response')
- resp, body = self.v3_client.get_project(project_id)
+ resp, body = self.client.get_project(project_id)
en2 = body['enabled']
self.assertTrue(en2, 'Enable should be True in lookup')
@@ -81,15 +81,15 @@
def test_project_create_not_enabled(self):
# Create a project that is not enabled
project_name = data_utils.rand_name('project-')
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
project_name, enabled=False)
- self.v3data.projects.append(project)
+ self.data.projects.append(project)
st1 = resp['status']
en1 = project['enabled']
self.assertEqual(st1, '201')
self.assertEqual('false', str(en1).lower(),
'Enable should be False in response')
- resp, body = self.v3_client.get_project(project['id'])
+ resp, body = self.client.get_project(project['id'])
en2 = body['enabled']
self.assertEqual('false', str(en2).lower(),
'Enable should be False in lookup')
@@ -98,19 +98,19 @@
def test_project_update_name(self):
# Update name attribute of a project
p_name1 = data_utils.rand_name('project-')
- resp, project = self.v3_client.create_project(p_name1)
- self.v3data.projects.append(project)
+ resp, project = self.client.create_project(p_name1)
+ self.data.projects.append(project)
resp1_name = project['name']
p_name2 = data_utils.rand_name('project2-')
- resp, body = self.v3_client.update_project(project['id'], name=p_name2)
+ resp, body = self.client.update_project(project['id'], name=p_name2)
st2 = resp['status']
resp2_name = body['name']
self.assertEqual(st2, '200')
self.assertNotEqual(resp1_name, resp2_name)
- resp, body = self.v3_client.get_project(project['id'])
+ resp, body = self.client.get_project(project['id'])
resp3_name = body['name']
self.assertNotEqual(resp1_name, resp3_name)
@@ -122,20 +122,20 @@
# Update description attribute of a project
p_name = data_utils.rand_name('project-')
p_desc = data_utils.rand_name('desc-')
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
p_name, description=p_desc)
- self.v3data.projects.append(project)
+ self.data.projects.append(project)
resp1_desc = project['description']
p_desc2 = data_utils.rand_name('desc2-')
- resp, body = self.v3_client.update_project(
+ resp, body = self.client.update_project(
project['id'], description=p_desc2)
st2 = resp['status']
resp2_desc = body['description']
self.assertEqual(st2, '200')
self.assertNotEqual(resp1_desc, resp2_desc)
- resp, body = self.v3_client.get_project(project['id'])
+ resp, body = self.client.get_project(project['id'])
resp3_desc = body['description']
self.assertNotEqual(resp1_desc, resp3_desc)
@@ -147,20 +147,20 @@
# Update the enabled attribute of a project
p_name = data_utils.rand_name('project-')
p_en = False
- resp, project = self.v3_client.create_project(p_name, enabled=p_en)
- self.v3data.projects.append(project)
+ resp, project = self.client.create_project(p_name, enabled=p_en)
+ self.data.projects.append(project)
resp1_en = project['enabled']
p_en2 = True
- resp, body = self.v3_client.update_project(
+ resp, body = self.client.update_project(
project['id'], enabled=p_en2)
st2 = resp['status']
resp2_en = body['enabled']
self.assertEqual(st2, '200')
self.assertNotEqual(resp1_en, resp2_en)
- resp, body = self.v3_client.get_project(project['id'])
+ resp, body = self.client.get_project(project['id'])
resp3_en = body['enabled']
self.assertNotEqual(resp1_en, resp3_en)
@@ -172,23 +172,23 @@
#Associate a user to a project
#Create a Project
p_name = data_utils.rand_name('project-')
- resp, project = self.v3_client.create_project(p_name)
- self.v3data.projects.append(project)
+ resp, project = self.client.create_project(p_name)
+ self.data.projects.append(project)
#Create a User
u_name = data_utils.rand_name('user-')
u_desc = u_name + 'description'
u_email = u_name + '@testmail.tm'
u_password = data_utils.rand_name('pass-')
- resp, user = self.v3_client.create_user(
+ resp, user = self.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email, project_id=project['id'])
self.assertEqual(resp['status'], '201')
# Delete the User at the end of this method
- self.addCleanup(self.v3_client.delete_user, user['id'])
+ self.addCleanup(self.client.delete_user, user['id'])
# Get User To validate the user details
- resp, new_user_get = self.v3_client.get_user(user['id'])
+ resp, new_user_get = self.client.get_user(user['id'])
#Assert response body of GET
self.assertEqual(u_name, new_user_get['name'])
self.assertEqual(u_desc, new_user_get['description'])
@@ -200,53 +200,53 @@
def test_list_projects_by_unauthorized_user(self):
# Non-admin user should not be able to list projects
self.assertRaises(exceptions.Unauthorized,
- self.v3_non_admin_client.list_projects)
+ self.non_admin_client.list_projects)
@attr(type=['negative', 'gate'])
def test_project_create_duplicate(self):
# Project names should be unique
project_name = data_utils.rand_name('project-dup-')
- resp, project = self.v3_client.create_project(project_name)
- self.v3data.projects.append(project)
+ resp, project = self.client.create_project(project_name)
+ self.data.projects.append(project)
self.assertRaises(
- exceptions.Conflict, self.v3_client.create_project, project_name)
+ exceptions.Conflict, self.client.create_project, project_name)
@attr(type=['negative', 'gate'])
def test_create_project_by_unauthorized_user(self):
# Non-admin user should not be authorized to create a project
project_name = data_utils.rand_name('project-')
self.assertRaises(
- exceptions.Unauthorized, self.v3_non_admin_client.create_project,
+ exceptions.Unauthorized, self.non_admin_client.create_project,
project_name)
@attr(type=['negative', 'gate'])
def test_create_project_with_empty_name(self):
# Project name should not be empty
- self.assertRaises(exceptions.BadRequest, self.v3_client.create_project,
+ self.assertRaises(exceptions.BadRequest, self.client.create_project,
name='')
@attr(type=['negative', 'gate'])
def test_create_projects_name_length_over_64(self):
# Project name length should not be greater than 64 characters
project_name = 'a' * 65
- self.assertRaises(exceptions.BadRequest, self.v3_client.create_project,
+ self.assertRaises(exceptions.BadRequest, self.client.create_project,
project_name)
@attr(type=['negative', 'gate'])
def test_project_delete_by_unauthorized_user(self):
# Non-admin user should not be able to delete a project
project_name = data_utils.rand_name('project-')
- resp, project = self.v3_client.create_project(project_name)
- self.v3data.projects.append(project)
+ resp, project = self.client.create_project(project_name)
+ self.data.projects.append(project)
self.assertRaises(
- exceptions.Unauthorized, self.v3_non_admin_client.delete_project,
+ exceptions.Unauthorized, self.non_admin_client.delete_project,
project['id'])
@attr(type=['negative', 'gate'])
def test_delete_non_existent_project(self):
# Attempt to delete a non existent project should fail
- self.assertRaises(exceptions.NotFound, self.v3_client.delete_project,
+ self.assertRaises(exceptions.NotFound, self.client.delete_project,
'junk_Project_123456abc')
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index efaed39..467d28b 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class RolesV3TestJSON(base.BaseIdentityAdminTest):
+class RolesV3TestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@classmethod
@@ -30,20 +30,20 @@
u_email = '%s@testmail.tm' % u_name
u_password = data_utils.rand_name('pass-')
resp = [None] * 5
- resp[0], cls.project = cls.v3_client.create_project(
+ resp[0], cls.project = cls.client.create_project(
data_utils.rand_name('project-'),
description=data_utils.rand_name('project-desc-'))
- resp[1], cls.domain = cls.v3_client.create_domain(
+ resp[1], cls.domain = cls.client.create_domain(
data_utils.rand_name('domain-'),
description=data_utils.rand_name('domain-desc-'))
- resp[2], cls.group_body = cls.v3_client.create_group(
+ resp[2], cls.group_body = cls.client.create_group(
data_utils.rand_name('Group-'), project_id=cls.project['id'],
domain_id=cls.domain['id'])
- resp[3], cls.user_body = cls.v3_client.create_user(
+ resp[3], cls.user_body = cls.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email, project_id=cls.project['id'],
domain_id=cls.domain['id'])
- resp[4], cls.role = cls.v3_client.create_role(
+ resp[4], cls.role = cls.client.create_role(
data_utils.rand_name('Role-'))
for r in resp:
assert r['status'] == '201', "Expected: %s" % r['status']
@@ -51,14 +51,14 @@
@classmethod
def tearDownClass(cls):
resp = [None] * 5
- resp[0], _ = cls.v3_client.delete_role(cls.role['id'])
- resp[1], _ = cls.v3_client.delete_group(cls.group_body['id'])
- resp[2], _ = cls.v3_client.delete_user(cls.user_body['id'])
- resp[3], _ = cls.v3_client.delete_project(cls.project['id'])
+ resp[0], _ = cls.client.delete_role(cls.role['id'])
+ resp[1], _ = cls.client.delete_group(cls.group_body['id'])
+ resp[2], _ = cls.client.delete_user(cls.user_body['id'])
+ resp[3], _ = cls.client.delete_project(cls.project['id'])
# NOTE(harika-vakadi): It is necessary to disable the domain
# before deleting,or else it would result in unauthorized error
- cls.v3_client.update_domain(cls.domain['id'], enabled=False)
- resp[4], _ = cls.v3_client.delete_domain(cls.domain['id'])
+ cls.client.update_domain(cls.domain['id'], enabled=False)
+ resp[4], _ = cls.client.delete_domain(cls.domain['id'])
for r in resp:
assert r['status'] == '204', "Expected: %s" % r['status']
super(RolesV3TestJSON, cls).tearDownClass()
@@ -71,32 +71,32 @@
@attr(type='smoke')
def test_role_create_update_get(self):
r_name = data_utils.rand_name('Role-')
- resp, role = self.v3_client.create_role(r_name)
- self.addCleanup(self.v3_client.delete_role, role['id'])
+ resp, role = self.client.create_role(r_name)
+ self.addCleanup(self.client.delete_role, role['id'])
self.assertEqual(resp['status'], '201')
self.assertIn('name', role)
self.assertEqual(role['name'], r_name)
new_name = data_utils.rand_name('NewRole-')
- resp, updated_role = self.v3_client.update_role(new_name, role['id'])
+ resp, updated_role = self.client.update_role(new_name, role['id'])
self.assertEqual(resp['status'], '200')
self.assertIn('name', updated_role)
self.assertIn('id', updated_role)
self.assertIn('links', updated_role)
self.assertNotEqual(r_name, updated_role['name'])
- resp, new_role = self.v3_client.get_role(role['id'])
+ resp, new_role = self.client.get_role(role['id'])
self.assertEqual(resp['status'], '200')
self.assertEqual(new_name, new_role['name'])
self.assertEqual(updated_role['id'], new_role['id'])
@attr(type='smoke')
def test_grant_list_revoke_role_to_user_on_project(self):
- resp, _ = self.v3_client.assign_user_role_on_project(
+ resp, _ = self.client.assign_user_role_on_project(
self.project['id'], self.user_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
- resp, roles = self.v3_client.list_user_roles_on_project(
+ resp, roles = self.client.list_user_roles_on_project(
self.project['id'], self.user_body['id'])
for i in roles:
@@ -105,17 +105,17 @@
self._list_assertions(resp, roles, self.fetched_role_ids,
self.role['id'])
- resp, _ = self.v3_client.revoke_role_from_user_on_project(
+ resp, _ = self.client.revoke_role_from_user_on_project(
self.project['id'], self.user_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
@attr(type='smoke')
def test_grant_list_revoke_role_to_user_on_domain(self):
- resp, _ = self.v3_client.assign_user_role_on_domain(
+ resp, _ = self.client.assign_user_role_on_domain(
self.domain['id'], self.user_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
- resp, roles = self.v3_client.list_user_roles_on_domain(
+ resp, roles = self.client.list_user_roles_on_domain(
self.domain['id'], self.user_body['id'])
for i in roles:
@@ -124,17 +124,17 @@
self._list_assertions(resp, roles, self.fetched_role_ids,
self.role['id'])
- resp, _ = self.v3_client.revoke_role_from_user_on_domain(
+ resp, _ = self.client.revoke_role_from_user_on_domain(
self.domain['id'], self.user_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
@attr(type='smoke')
def test_grant_list_revoke_role_to_group_on_project(self):
- resp, _ = self.v3_client.assign_group_role_on_project(
+ resp, _ = self.client.assign_group_role_on_project(
self.project['id'], self.group_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
- resp, roles = self.v3_client.list_group_roles_on_project(
+ resp, roles = self.client.list_group_roles_on_project(
self.project['id'], self.group_body['id'])
for i in roles:
@@ -143,17 +143,17 @@
self._list_assertions(resp, roles, self.fetched_role_ids,
self.role['id'])
- resp, _ = self.v3_client.revoke_role_from_group_on_project(
+ resp, _ = self.client.revoke_role_from_group_on_project(
self.project['id'], self.group_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
@attr(type='smoke')
def test_grant_list_revoke_role_to_group_on_domain(self):
- resp, _ = self.v3_client.assign_group_role_on_domain(
+ resp, _ = self.client.assign_group_role_on_domain(
self.domain['id'], self.group_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
- resp, roles = self.v3_client.list_group_roles_on_domain(
+ resp, roles = self.client.list_group_roles_on_domain(
self.domain['id'], self.group_body['id'])
for i in roles:
@@ -162,7 +162,7 @@
self._list_assertions(resp, roles, self.fetched_role_ids,
self.role['id'])
- resp, _ = self.v3_client.revoke_role_from_group_on_domain(
+ resp, _ = self.client.revoke_role_from_group_on_domain(
self.domain['id'], self.group_body['id'], self.role['id'])
self.assertEqual(resp['status'], '204')
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index 4d6c22f..c5d4ddf 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -19,20 +19,20 @@
from tempest.test import attr
-class ServicesTestJSON(base.BaseIdentityAdminTest):
+class ServicesTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@attr(type='gate')
def test_update_service(self):
# Update description attribute of service
name = data_utils.rand_name('service-')
- type = data_utils.rand_name('type--')
- description = data_utils.rand_name('description-')
- resp, body = self.client.create_service(
- name, type, description=description)
- self.assertEqual('200', resp['status'])
+ serv_type = data_utils.rand_name('type--')
+ desc = data_utils.rand_name('description-')
+ resp, body = self.service_client.create_service(name, serv_type,
+ description=desc)
+ self.assertEqual('201', resp['status'])
# Deleting the service created in this method
- self.addCleanup(self.client.delete_service, body['id'])
+ self.addCleanup(self.service_client.delete_service, body['id'])
s_id = body['id']
resp1_desc = body['description']
@@ -45,7 +45,7 @@
self.assertNotEqual(resp1_desc, resp2_desc)
# Get service
- resp, body = self.client.get_service(s_id)
+ resp, body = self.service_client.get_service(s_id)
resp3_desc = body['description']
self.assertNotEqual(resp1_desc, resp3_desc)
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index d17dc4a..802113a 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -19,7 +19,7 @@
from tempest.test import attr
-class UsersTestJSON(base.BaseIdentityAdminTest):
+class UsersTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@attr(type='smoke')
@@ -30,24 +30,24 @@
u_desc = '%s-description' % u_name
u_email = '%s@testmail.tm' % u_name
u_password = data_utils.rand_name('pass-')
- resp, user = self.v3_client.create_user(
+ resp, user = self.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email)
self.assertTrue(resp['status'].startswith('2'))
- self.addCleanup(self.v3_client.delete_user, user['id'])
+ self.addCleanup(self.client.delete_user, user['id'])
# Perform Authentication
- resp, body = self.v3_token.auth(user['id'], u_password)
+ resp, body = self.token.auth(user['id'], u_password)
self.assertEqual(resp['status'], '201')
subject_token = resp['x-subject-token']
# Perform GET Token
- resp, token_details = self.v3_client.get_token(subject_token)
+ resp, token_details = self.client.get_token(subject_token)
self.assertEqual(resp['status'], '200')
self.assertEqual(resp['x-subject-token'], subject_token)
self.assertEqual(token_details['user']['id'], user['id'])
self.assertEqual(token_details['user']['name'], u_name)
# Perform Delete Token
- resp, _ = self.v3_client.delete_token(subject_token)
- self.assertRaises(exceptions.NotFound, self.v3_client.get_token,
+ resp, _ = self.client.delete_token(subject_token)
+ self.assertRaises(exceptions.NotFound, self.client.get_token,
subject_token)
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1bebad4..c2eef36 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -23,7 +23,7 @@
CONF = config.CONF
-class BaseTrustsV3Test(base.BaseIdentityAdminTest):
+class BaseTrustsV3Test(base.BaseIdentityV3AdminTest):
def setUp(self):
super(BaseTrustsV3Test, self).setUp()
@@ -43,7 +43,7 @@
def create_trustor_and_roles(self):
# Get trustor project ID, use the admin project
- self.trustor_project_name = self.v3_client.tenant_name
+ self.trustor_project_name = self.client.tenant_name
self.trustor_project_id = self.get_tenant_by_name(
self.trustor_project_name)['id']
self.assertIsNotNone(self.trustor_project_id)
@@ -53,7 +53,7 @@
u_desc = self.trustor_username + 'description'
u_email = self.trustor_username + '@testmail.xx'
self.trustor_password = rand_name('pass-')
- resp, user = self.v3_client.create_user(
+ resp, user = self.client.create_user(
self.trustor_username,
description=u_desc,
password=self.trustor_password,
@@ -66,24 +66,24 @@
self.delegated_role = rand_name('DelegatedRole-')
self.not_delegated_role = rand_name('NotDelegatedRole-')
- resp, role = self.v3_client.create_role(self.delegated_role)
+ resp, role = self.client.create_role(self.delegated_role)
self.assertEqual(resp['status'], '201')
self.delegated_role_id = role['id']
- resp, role = self.v3_client.create_role(self.not_delegated_role)
+ resp, role = self.client.create_role(self.not_delegated_role)
self.assertEqual(resp['status'], '201')
self.not_delegated_role_id = role['id']
# Assign roles to trustor
- self.v3_client.assign_user_role(self.trustor_project_id,
- self.trustor_user_id,
- self.delegated_role_id)
- self.v3_client.assign_user_role(self.trustor_project_id,
- self.trustor_user_id,
- self.not_delegated_role_id)
+ self.client.assign_user_role(self.trustor_project_id,
+ self.trustor_user_id,
+ self.delegated_role_id)
+ self.client.assign_user_role(self.trustor_project_id,
+ self.trustor_user_id,
+ self.not_delegated_role_id)
# Get trustee user ID, use the demo user
- trustee_username = self.v3_non_admin_client.user
+ trustee_username = self.non_admin_client.user
self.trustee_user_id = self.get_user_by_name(trustee_username)['id']
self.assertIsNotNone(self.trustee_user_id)
@@ -92,19 +92,19 @@
password=self.trustor_password,
tenant_name=self.trustor_project_name,
interface=self._interface)
- self.trustor_v3_client = os.identity_v3_client
+ self.trustor_client = os.identity_v3_client
def cleanup_user_and_roles(self):
if self.trustor_user_id:
- self.v3_client.delete_user(self.trustor_user_id)
+ self.client.delete_user(self.trustor_user_id)
if self.delegated_role_id:
- self.v3_client.delete_role(self.delegated_role_id)
+ self.client.delete_role(self.delegated_role_id)
if self.not_delegated_role_id:
- self.v3_client.delete_role(self.not_delegated_role_id)
+ self.client.delete_role(self.not_delegated_role_id)
def create_trust(self, impersonate=True, expires=None):
- resp, trust_create = self.trustor_v3_client.create_trust(
+ resp, trust_create = self.trustor_client.create_trust(
trustor_user_id=self.trustor_user_id,
trustee_user_id=self.trustee_user_id,
project_id=self.trustor_project_id,
@@ -137,7 +137,7 @@
self.assertEqual(1, len(trust['roles']))
def get_trust(self):
- resp, trust_get = self.trustor_v3_client.get_trust(self.trust_id)
+ resp, trust_get = self.trustor_client.get_trust(self.trust_id)
self.assertEqual('200', resp['status'])
return trust_get
@@ -153,37 +153,37 @@
def check_trust_roles(self):
# Check we find the delegated role
- resp, roles_get = self.trustor_v3_client.get_trust_roles(
+ resp, roles_get = self.trustor_client.get_trust_roles(
self.trust_id)
self.assertEqual('200', resp['status'])
self.assertEqual(1, len(roles_get))
self.validate_role(roles_get[0])
- resp, role_get = self.trustor_v3_client.get_trust_role(
+ resp, role_get = self.trustor_client.get_trust_role(
self.trust_id, self.delegated_role_id)
self.assertEqual('200', resp['status'])
self.validate_role(role_get)
- resp, role_get = self.trustor_v3_client.check_trust_role(
+ resp, role_get = self.trustor_client.check_trust_role(
self.trust_id, self.delegated_role_id)
self.assertEqual('204', resp['status'])
# And that we don't find not_delegated_role
self.assertRaises(exceptions.NotFound,
- self.trustor_v3_client.get_trust_role,
+ self.trustor_client.get_trust_role,
self.trust_id,
self.not_delegated_role_id)
self.assertRaises(exceptions.NotFound,
- self.trustor_v3_client.check_trust_role,
+ self.trustor_client.check_trust_role,
self.trust_id,
self.not_delegated_role_id)
def delete_trust(self):
- resp, trust_delete = self.trustor_v3_client.delete_trust(self.trust_id)
+ resp, trust_delete = self.trustor_client.delete_trust(self.trust_id)
self.assertEqual('204', resp['status'])
self.assertRaises(exceptions.NotFound,
- self.trustor_v3_client.get_trust,
+ self.trustor_client.get_trust,
self.trust_id)
self.trust_id = None
@@ -249,7 +249,7 @@
@attr(type='smoke')
def test_get_trusts_query(self):
self.create_trust()
- resp, trusts_get = self.trustor_v3_client.get_trusts(
+ resp, trusts_get = self.trustor_client.get_trusts(
trustor_user_id=self.trustor_user_id)
self.assertEqual('200', resp['status'])
self.assertEqual(1, len(trusts_get))
@@ -258,7 +258,7 @@
@attr(type='smoke')
def test_get_trusts_all(self):
self.create_trust()
- resp, trusts_get = self.v3_client.get_trusts()
+ resp, trusts_get = self.client.get_trusts()
self.assertEqual('200', resp['status'])
trusts = [t for t in trusts_get
if t['id'] == self.trust_id]
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 7cae856..a78d542 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -18,7 +18,7 @@
from tempest.test import attr
-class UsersV3TestJSON(base.BaseIdentityAdminTest):
+class UsersV3TestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
@attr(type='gate')
@@ -29,22 +29,22 @@
u_desc = u_name + 'description'
u_email = u_name + '@testmail.tm'
u_password = data_utils.rand_name('pass-')
- resp, user = self.v3_client.create_user(
+ resp, user = self.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email, enabled=False)
# Delete the User at the end of this method
- self.addCleanup(self.v3_client.delete_user, user['id'])
+ self.addCleanup(self.client.delete_user, user['id'])
# Creating second project for updation
- resp, project = self.v3_client.create_project(
+ resp, project = self.client.create_project(
data_utils.rand_name('project-'),
description=data_utils.rand_name('project-desc-'))
# Delete the Project at the end of this method
- self.addCleanup(self.v3_client.delete_project, project['id'])
+ self.addCleanup(self.client.delete_project, project['id'])
# Updating user details with new values
u_name2 = data_utils.rand_name('user2-')
u_email2 = u_name2 + '@testmail.tm'
u_description2 = u_name2 + ' description'
- resp, update_user = self.v3_client.update_user(
+ resp, update_user = self.client.update_user(
user['id'], name=u_name2, description=u_description2,
project_id=project['id'],
email=u_email2, enabled=False)
@@ -57,7 +57,7 @@
self.assertEqual(u_email2, update_user['email'])
self.assertEqual('false', str(update_user['enabled']).lower())
# GET by id after updation
- resp, new_user_get = self.v3_client.get_user(user['id'])
+ resp, new_user_get = self.client.get_user(user['id'])
# Assert response body of GET after updation
self.assertEqual(u_name2, new_user_get['name'])
self.assertEqual(u_description2, new_user_get['description'])
@@ -71,43 +71,43 @@
# List the projects that a user has access upon
assigned_project_ids = list()
fetched_project_ids = list()
- _, u_project = self.v3_client.create_project(
+ _, u_project = self.client.create_project(
data_utils.rand_name('project-'),
description=data_utils.rand_name('project-desc-'))
# Delete the Project at the end of this method
- self.addCleanup(self.v3_client.delete_project, u_project['id'])
+ self.addCleanup(self.client.delete_project, u_project['id'])
# Create a user.
u_name = data_utils.rand_name('user-')
u_desc = u_name + 'description'
u_email = u_name + '@testmail.tm'
u_password = data_utils.rand_name('pass-')
- _, user_body = self.v3_client.create_user(
+ _, user_body = self.client.create_user(
u_name, description=u_desc, password=u_password,
email=u_email, enabled=False, project_id=u_project['id'])
# Delete the User at the end of this method
- self.addCleanup(self.v3_client.delete_user, user_body['id'])
+ self.addCleanup(self.client.delete_user, user_body['id'])
# Creating Role
- _, role_body = self.v3_client.create_role(
+ _, role_body = self.client.create_role(
data_utils.rand_name('role-'))
# Delete the Role at the end of this method
- self.addCleanup(self.v3_client.delete_role, role_body['id'])
+ self.addCleanup(self.client.delete_role, role_body['id'])
- _, user = self.v3_client.get_user(user_body['id'])
- _, role = self.v3_client.get_role(role_body['id'])
+ _, user = self.client.get_user(user_body['id'])
+ _, role = self.client.get_role(role_body['id'])
for i in range(2):
# Creating project so as to assign role
- _, project_body = self.v3_client.create_project(
+ _, project_body = self.client.create_project(
data_utils.rand_name('project-'),
description=data_utils.rand_name('project-desc-'))
- _, project = self.v3_client.get_project(project_body['id'])
+ _, project = self.client.get_project(project_body['id'])
# Delete the Project at the end of this method
- self.addCleanup(self.v3_client.delete_project, project_body['id'])
+ self.addCleanup(self.client.delete_project, project_body['id'])
# Assigning roles to user on project
- self.v3_client.assign_user_role(project['id'],
- user['id'],
- role['id'])
+ self.client.assign_user_role(project['id'],
+ user['id'],
+ role['id'])
assigned_project_ids.append(project['id'])
- resp, body = self.v3_client.list_user_projects(user['id'])
+ resp, body = self.client.list_user_projects(user['id'])
self.assertEqual(200, resp.status)
for i in body:
fetched_project_ids.append(i['id'])
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index a3fc65a..10f5217 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -16,67 +16,98 @@
from tempest import clients
from tempest.common.utils import data_utils
+from tempest import config
import tempest.test
+CONF = config.CONF
+
class BaseIdentityAdminTest(tempest.test.BaseTestCase):
@classmethod
def setUpClass(cls):
super(BaseIdentityAdminTest, cls).setUpClass()
- os = clients.AdminManager(interface=cls._interface)
- cls.client = os.identity_client
- cls.token_client = os.token_client
- cls.endpoints_client = os.endpoints_client
- cls.v3_client = os.identity_v3_client
- cls.service_client = os.service_client
- cls.policy_client = os.policy_client
- cls.v3_token = os.token_v3_client
- cls.creds_client = os.credentials_client
-
- if not cls.client.has_admin_extensions():
- raise cls.skipException("Admin extensions disabled")
-
- cls.data = DataGenerator(cls.client)
- cls.v3data = DataGenerator(cls.v3_client)
-
- os = clients.Manager(interface=cls._interface)
- cls.non_admin_client = os.identity_client
- cls.v3_non_admin_client = os.identity_v3_client
+ cls.os_adm = clients.AdminManager(interface=cls._interface)
+ cls.os = clients.Manager(interface=cls._interface)
@classmethod
- def tearDownClass(cls):
- cls.data.teardown_all()
- cls.v3data.teardown_all()
- super(BaseIdentityAdminTest, cls).tearDownClass()
+ def disable_user(cls, user_name):
+ user = cls.get_user_by_name(user_name)
+ cls.client.enable_disable_user(user['id'], False)
- def disable_user(self, user_name):
- user = self.get_user_by_name(user_name)
- self.client.enable_disable_user(user['id'], False)
+ @classmethod
+ def disable_tenant(cls, tenant_name):
+ tenant = cls.get_tenant_by_name(tenant_name)
+ cls.client.update_tenant(tenant['id'], enabled=False)
- def disable_tenant(self, tenant_name):
- tenant = self.get_tenant_by_name(tenant_name)
- self.client.update_tenant(tenant['id'], enabled=False)
-
- def get_user_by_name(self, name):
- _, users = self.client.get_users()
+ @classmethod
+ def get_user_by_name(cls, name):
+ _, users = cls.client.get_users()
user = [u for u in users if u['name'] == name]
if len(user) > 0:
return user[0]
- def get_tenant_by_name(self, name):
- _, tenants = self.client.list_tenants()
+ @classmethod
+ def get_tenant_by_name(cls, name):
+ try:
+ _, tenants = cls.client.list_tenants()
+ except AttributeError:
+ _, tenants = cls.client.list_projects()
tenant = [t for t in tenants if t['name'] == name]
if len(tenant) > 0:
return tenant[0]
- def get_role_by_name(self, name):
- _, roles = self.client.list_roles()
+ @classmethod
+ def get_role_by_name(cls, name):
+ _, roles = cls.client.list_roles()
role = [r for r in roles if r['name'] == name]
if len(role) > 0:
return role[0]
+class BaseIdentityV2AdminTest(BaseIdentityAdminTest):
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.identity_feature_enabled.api_v2:
+ raise cls.skipException("Identity api v2 is not enabled")
+ super(BaseIdentityV2AdminTest, cls).setUpClass()
+ cls.client = cls.os_adm.identity_client
+ cls.token_client = cls.os_adm.token_client
+ if not cls.client.has_admin_extensions():
+ raise cls.skipException("Admin extensions disabled")
+ cls.data = DataGenerator(cls.client)
+ cls.non_admin_client = cls.os.identity_client
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.data.teardown_all()
+ super(BaseIdentityV2AdminTest, cls).tearDownClass()
+
+
+class BaseIdentityV3AdminTest(BaseIdentityAdminTest):
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.identity_feature_enabled.api_v3:
+ raise cls.skipException("Identity api v3 is not enabled")
+ super(BaseIdentityV3AdminTest, cls).setUpClass()
+ cls.client = cls.os_adm.identity_v3_client
+ cls.token = cls.os_adm.token_v3_client
+ cls.endpoints_client = cls.os_adm.endpoints_client
+ cls.data = DataGenerator(cls.client)
+ cls.non_admin_client = cls.os.identity_v3_client
+ cls.service_client = cls.os_adm.service_client
+ cls.policy_client = cls.os_adm.policy_client
+ cls.creds_client = cls.os_adm.credentials_client
+ cls.non_admin_client = cls.os.identity_v3_client
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.data.teardown_all()
+ super(BaseIdentityV3AdminTest, cls).tearDownClass()
+
+
class DataGenerator(object):
def __init__(self, client):
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 37b848c..e439238 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -106,7 +106,7 @@
cls.os_alt = clients.AltManager()
identity_client = cls._get_identity_admin_client()
cls.alt_tenant_id = identity_client.get_tenant_by_name(
- cls.os_alt.tenant_name)['id']
+ cls.os_alt.credentials['tenant_name'])['id']
cls.alt_img_cli = cls.os_alt.image_client
@@ -147,7 +147,7 @@
cls.alt_tenant_id = cls.isolated_creds.get_alt_tenant()['id']
else:
cls.os_alt = clients.AltManager()
- alt_tenant_name = cls.os_alt.tenant_name
+ alt_tenant_name = cls.os_alt.credentials['tenant_name']
identity_client = cls._get_identity_admin_client()
cls.alt_tenant_id = identity_client.get_tenant_by_name(
alt_tenant_name)['id']
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 8c62c05..517123d 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -16,8 +16,9 @@
import cStringIO as StringIO
from tempest.api.image import base
+from tempest.common.utils import data_utils
from tempest import config
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -25,7 +26,7 @@
class CreateRegisterImagesTest(base.BaseV1ImageTest):
"""Here we test the registration and creation of images."""
- @attr(type='gate')
+ @test.attr(type='gate')
def test_register_then_upload(self):
# Register, then upload an image
properties = {'prop1': 'val1'}
@@ -48,7 +49,7 @@
self.assertIn('size', body)
self.assertEqual(1024, body.get('size'))
- @attr(type='gate')
+ @test.attr(type='gate')
def test_register_remote_image(self):
# Register a new remote image
resp, body = self.create_image(name='New Remote Image',
@@ -66,12 +67,12 @@
self.assertEqual(properties['key1'], 'value1')
self.assertEqual(properties['key2'], 'value2')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_register_http_image(self):
resp, body = self.create_image(name='New Http Image',
container_format='bare',
disk_format='raw', is_public=True,
- copy_from=CONF.images.http_image)
+ copy_from=CONF.image.http_image)
self.assertIn('id', body)
image_id = body.get('id')
self.assertEqual('New Http Image', body.get('name'))
@@ -80,7 +81,7 @@
resp, body = self.client.get_image(image_id)
self.assertEqual(resp['status'], '200')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_register_image_with_min_ram(self):
# Register an image with min ram
properties = {'prop1': 'val1'}
@@ -110,7 +111,6 @@
@classmethod
def setUpClass(cls):
super(ListImagesTest, cls).setUpClass()
-
# We add a few images here to test the listing functionality of
# the images API
img1 = cls._create_remote_image('one', 'bare', 'raw')
@@ -131,7 +131,7 @@
# 1x with size 42
cls.size42_set = set((img5,))
# 3x with size 142
- cls.size142_set = set((img6, img7, img8))
+ cls.size142_set = set((img6, img7, img8,))
# dup named
cls.dup_set = set((img3, img4))
@@ -168,7 +168,7 @@
image_id = image['id']
return image_id
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_no_params(self):
# Simple test to see all fixture images returned
resp, images_list = self.client.image_list()
@@ -177,7 +177,7 @@
for image_id in self.created_images:
self.assertIn(image_id, image_list)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_disk_format(self):
resp, images_list = self.client.image_list(disk_format='ami')
self.assertEqual(resp['status'], '200')
@@ -187,7 +187,7 @@
self.assertTrue(self.ami_set <= result_set)
self.assertFalse(self.created_set - self.ami_set <= result_set)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_container_format(self):
resp, images_list = self.client.image_list(container_format='bare')
self.assertEqual(resp['status'], '200')
@@ -197,7 +197,7 @@
self.assertTrue(self.bare_set <= result_set)
self.assertFalse(self.created_set - self.bare_set <= result_set)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_max_size(self):
resp, images_list = self.client.image_list(size_max=42)
self.assertEqual(resp['status'], '200')
@@ -207,7 +207,7 @@
self.assertTrue(self.size42_set <= result_set)
self.assertFalse(self.created_set - self.size42_set <= result_set)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_min_size(self):
resp, images_list = self.client.image_list(size_min=142)
self.assertEqual(resp['status'], '200')
@@ -217,7 +217,7 @@
self.assertTrue(self.size142_set <= result_set)
self.assertFalse(self.size42_set <= result_set)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_status_active_detail(self):
resp, images_list = self.client.image_list_detail(status='active',
sort_key='size',
@@ -230,7 +230,7 @@
top_size = size
self.assertEqual(image['status'], 'active')
- @attr(type='gate')
+ @test.attr(type='gate')
def test_index_name(self):
resp, images_list = self.client.image_list_detail(
name='New Remote Image dup')
@@ -240,3 +240,139 @@
self.assertEqual(image['name'], 'New Remote Image dup')
self.assertTrue(self.dup_set <= result_set)
self.assertFalse(self.created_set - self.dup_set <= result_set)
+
+
+class ListSnapshotImagesTest(base.BaseV1ImageTest):
+ @classmethod
+ def setUpClass(cls):
+ super(ListSnapshotImagesTest, cls).setUpClass()
+ if not CONF.compute_feature_enabled.api_v3:
+ cls.servers_client = cls.os.servers_client
+ else:
+ cls.servers_client = cls.os.servers_v3_client
+ cls.servers = []
+ # We add a few images here to test the listing functionality of
+ # the images API
+ cls.snapshot = cls._create_snapshot(
+ 'snapshot', CONF.compute.image_ref,
+ CONF.compute.flavor_ref)
+ cls.snapshot_set = set((cls.snapshot,))
+
+ image_file = StringIO.StringIO('*' * 42)
+ resp, image = cls.create_image(name="Standard Image",
+ container_format='ami',
+ disk_format='ami',
+ is_public=True, data=image_file)
+ cls.image_id = image['id']
+ cls.client.wait_for_image_status(image['id'], 'active')
+
+ @classmethod
+ def tearDownClass(cls):
+ for server in cls.servers:
+ cls.servers_client.delete_server(server['id'])
+ super(ListSnapshotImagesTest, cls).tearDownClass()
+
+ @classmethod
+ def _create_snapshot(cls, name, image_id, flavor, **kwargs):
+ resp, server = cls.servers_client.create_server(
+ name, image_id, flavor, **kwargs)
+ cls.servers.append(server)
+ cls.servers_client.wait_for_server_status(
+ server['id'], 'ACTIVE')
+ resp, image = cls.servers_client.create_image(
+ server['id'], name)
+ image_id = data_utils.parse_image_id(resp['location'])
+ cls.created_images.append(image_id)
+ cls.client.wait_for_image_status(image_id,
+ 'active')
+ return image_id
+
+ @test.attr(type='gate')
+ def test_index_server_id(self):
+ # The images should contain images filtered by server id
+ resp, images = self.client.image_list_detail(
+ {'instance_uuid': self.servers[0]['id']})
+ self.assertEqual(200, resp.status)
+ result_set = set(map(lambda x: x['id'], images))
+ self.assertEqual(self.snapshot_set, result_set)
+
+ @test.attr(type='gate')
+ def test_index_type(self):
+ # The list of servers should be filtered by image type
+ params = {'image_type': 'snapshot'}
+ resp, images = self.client.image_list_detail(params)
+
+ self.assertEqual(200, resp.status)
+ result_set = set(map(lambda x: x['id'], images))
+ self.assertIn(self.snapshot, result_set)
+
+ @test.attr(type='gate')
+ def test_index_limit(self):
+ # Verify only the expected number of results are returned
+ resp, images = self.client.image_list_detail(limit=1)
+
+ self.assertEqual(200, resp.status)
+ self.assertEqual(1, len(images))
+
+ @test.attr(type='gate')
+ def test_index_by_change_since(self):
+ # Verify an update image is returned
+ # Becoming ACTIVE will modify the updated time
+ # Filter by the image's created time
+ resp, image = self.client.get_image_meta(self.snapshot)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(self.snapshot, image['id'])
+ resp, images = self.client.image_list_detail(
+ changes_since=image['updated_at'])
+
+ self.assertEqual(200, resp.status)
+ result_set = set(map(lambda x: x['id'], images))
+ self.assertIn(self.image_id, result_set)
+ self.assertNotIn(self.snapshot, result_set)
+
+
+class UpdateImageMetaTest(base.BaseV1ImageTest):
+ @classmethod
+ def setUpClass(cls):
+ super(UpdateImageMetaTest, cls).setUpClass()
+ cls.image_id = cls._create_standard_image('1', 'ami', 'ami', 42)
+
+ @classmethod
+ def _create_standard_image(cls, name, container_format,
+ disk_format, size):
+ """
+ Create a new standard image and return the ID of the newly-registered
+ image. Note that the size of the new image is a random number between
+ 1024 and 4096
+ """
+ image_file = StringIO.StringIO('*' * size)
+ name = 'New Standard Image %s' % name
+ resp, image = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
+ is_public=True, data=image_file,
+ properties={'key1': 'value1'})
+ image_id = image['id']
+ return image_id
+
+ @test.attr(type='gate')
+ def test_list_image_metadata(self):
+ # All metadata key/value pairs for an image should be returned
+ resp, resp_metadata = self.client.get_image_meta(self.image_id)
+ expected = {'key1': 'value1'}
+ self.assertEqual(expected, resp_metadata['properties'])
+
+ @test.attr(type='gate')
+ def test_update_image_metadata(self):
+ # The metadata for the image should match the updated values
+ req_metadata = {'key1': 'alt1', 'key2': 'value2'}
+ resp, metadata = self.client.get_image_meta(self.image_id)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(metadata['properties'], {'key1': 'value1'})
+ metadata['properties'].update(req_metadata)
+ resp, metadata = self.client.update_image(
+ self.image_id, properties=metadata['properties'])
+
+ resp, resp_metadata = self.client.get_image_meta(self.image_id)
+ expected = {'key1': 'alt1', 'key2': 'value2'}
+ self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/network/admin/test_agent_management.py b/tempest/api/network/admin/test_agent_management.py
index b05f275..342bc6a 100644
--- a/tempest/api/network/admin/test_agent_management.py
+++ b/tempest/api/network/admin/test_agent_management.py
@@ -14,7 +14,7 @@
from tempest.api.network import base
from tempest.common import tempest_fixtures as fixtures
-from tempest.test import attr
+from tempest import test
class AgentManagementTestJSON(base.BaseAdminNetworkTest):
@@ -23,11 +23,14 @@
@classmethod
def setUpClass(cls):
super(AgentManagementTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('agent', 'network'):
+ msg = "agent extension not enabled."
+ raise cls.skipException(msg)
resp, body = cls.admin_client.list_agents()
agents = body['agents']
cls.agent = agents[0]
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_agent(self):
resp, body = self.admin_client.list_agents()
self.assertEqual('200', resp['status'])
@@ -38,20 +41,20 @@
agent.pop('heartbeat_timestamp', None)
self.assertIn(self.agent, agents)
- @attr(type=['smoke'])
+ @test.attr(type=['smoke'])
def test_list_agents_non_admin(self):
resp, body = self.client.list_agents()
self.assertEqual('200', resp['status'])
self.assertEqual(len(body["agents"]), 0)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_show_agent(self):
resp, body = self.admin_client.show_agent(self.agent['id'])
agent = body['agent']
self.assertEqual('200', resp['status'])
self.assertEqual(agent['id'], self.agent['id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_agent_status(self):
origin_status = self.agent['admin_state_up']
# Try to update the 'admin_state_up' to the original
@@ -63,7 +66,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual(origin_status, updated_status)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_agent_description(self):
self.useFixture(fixtures.LockFixture('agent_description'))
description = 'description for update agent.'
diff --git a/tempest/api/network/admin/test_dhcp_agent_scheduler.py b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
index 13309cd..ecd992a 100644
--- a/tempest/api/network/admin/test_dhcp_agent_scheduler.py
+++ b/tempest/api/network/admin/test_dhcp_agent_scheduler.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.api.network import base
-from tempest.test import attr
+from tempest import test
class DHCPAgentSchedulersTestJSON(base.BaseAdminNetworkTest):
@@ -22,6 +22,9 @@
@classmethod
def setUpClass(cls):
super(DHCPAgentSchedulersTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('dhcp_agent_scheduler', 'network'):
+ msg = "dhcp_agent_scheduler extension not enabled."
+ raise cls.skipException(msg)
# Create a network and make sure it will be hosted by a
# dhcp agent.
cls.network = cls.create_network()
@@ -29,13 +32,13 @@
cls.cidr = cls.subnet['cidr']
cls.port = cls.create_port(cls.network)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_dhcp_agent_hosting_network(self):
resp, body = self.admin_client.list_dhcp_agent_hosting_network(
self.network['id'])
self.assertEqual(resp['status'], '200')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_networks_hosted_by_one_dhcp(self):
resp, body = self.admin_client.list_dhcp_agent_hosting_network(
self.network['id'])
@@ -55,7 +58,7 @@
network_ids.append(network['id'])
return network_id in network_ids
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_remove_network_from_dhcp_agent(self):
resp, body = self.admin_client.list_dhcp_agent_hosting_network(
self.network['id'])
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index bfb7b48..7c02787 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -17,7 +17,7 @@
from tempest import test
-class L3AgentSchedulerJSON(base.BaseAdminNetworkTest):
+class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
_interface = 'json'
"""
@@ -33,7 +33,7 @@
@classmethod
def setUpClass(cls):
- super(L3AgentSchedulerJSON, cls).setUpClass()
+ super(L3AgentSchedulerTestJSON, cls).setUpClass()
if not test.is_extension_enabled('l3_agent_scheduler', 'network'):
msg = "L3 Agent Scheduler Extension not enabled."
raise cls.skipException(msg)
@@ -61,5 +61,5 @@
self.assertEqual(204, resp.status)
-class L3AgentSchedulerXML(L3AgentSchedulerJSON):
+class L3AgentSchedulerTestXML(L3AgentSchedulerTestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/admin/test_lbaas_agent_scheduler.py b/tempest/api/network/admin/test_lbaas_agent_scheduler.py
new file mode 100644
index 0000000..a5ba90f
--- /dev/null
+++ b/tempest/api/network/admin/test_lbaas_agent_scheduler.py
@@ -0,0 +1,78 @@
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class LBaaSAgentSchedulerTestJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ """
+ Tests the following operations in the Neutron API using the REST client for
+ Neutron:
+
+ List pools the given LBaaS agent is hosting.
+ Show a LBaaS agent hosting the given pool.
+
+ v2.0 of the Neutron API is assumed. It is also assumed that the following
+ options are defined in the [networki-feature-enabled] section of
+ etc/tempest.conf:
+
+ api_extensions
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(LBaaSAgentSchedulerTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('lbaas_agent_scheduler', 'network'):
+ msg = "LBaaS Agent Scheduler Extension not enabled."
+ raise cls.skipException(msg)
+ cls.network = cls.create_network()
+ cls.subnet = cls.create_subnet(cls.network)
+ pool_name = data_utils.rand_name('pool-')
+ cls.pool = cls.create_pool(pool_name, "ROUND_ROBIN",
+ "HTTP", cls.subnet)
+
+ @test.attr(type='smoke')
+ def test_list_pools_on_lbaas_agent(self):
+ found = False
+ resp, body = self.admin_client.list_agents(
+ agent_type="Loadbalancer agent")
+ self.assertEqual('200', resp['status'])
+ agents = body['agents']
+ for a in agents:
+ msg = 'Load Balancer agent expected'
+ self.assertEqual(a['agent_type'], 'Loadbalancer agent', msg)
+ resp, body = (
+ self.admin_client.list_pools_hosted_by_one_lbaas_agent(
+ a['id']))
+ self.assertEqual('200', resp['status'])
+ pools = body['pools']
+ if self.pool['id'] in [p['id'] for p in pools]:
+ found = True
+ msg = 'Unable to find Load Balancer agent hosting pool'
+ self.assertTrue(found, msg)
+
+ @test.attr(type='smoke')
+ def test_show_lbaas_agent_hosting_pool(self):
+ resp, body = self.admin_client.show_lbaas_agent_hosting_pool(
+ self.pool['id'])
+ self.assertEqual('200', resp['status'])
+ self.assertEqual('Loadbalancer agent', body['agent']['agent_type'])
+
+
+class LBaaSAgentSchedulerTestXML(LBaaSAgentSchedulerTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 1c2c4b0..dd888a6 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -49,6 +49,13 @@
neutron as True
"""
+ force_tenant_isolation = False
+
+ # Default to ipv4.
+ _ip_version = 4
+ _tenant_network_cidr = CONF.network.tenant_network_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_mask_bits
+
@classmethod
def setUpClass(cls):
# Create no network resources for these test.
@@ -57,6 +64,10 @@
os = clients.Manager(interface=cls._interface)
if not CONF.service_available.neutron:
raise cls.skipException("Neutron support is required")
+
+ os = cls.get_client_manager()
+
+ cls.network_cfg = CONF.network
cls.client = os.network_client
cls.networks = []
cls.subnets = []
@@ -69,6 +80,8 @@
cls.vpnservices = []
cls.ikepolicies = []
cls.floating_ips = []
+ cls.metering_labels = []
+ cls.metering_label_rules = []
@classmethod
def tearDownClass(cls):
@@ -101,6 +114,13 @@
# Clean up pools
for pool in cls.pools:
cls.client.delete_pool(pool['id'])
+ # Clean up metering label rules
+ for metering_label_rule in cls.metering_label_rules:
+ cls.admin_client.delete_metering_label_rule(
+ metering_label_rule['id'])
+ # Clean up metering labels
+ for metering_label in cls.metering_labels:
+ cls.admin_client.delete_metering_label(metering_label['id'])
# Clean up ports
for port in cls.ports:
cls.client.delete_port(port['id'])
@@ -110,6 +130,7 @@
# Clean up networks
for network in cls.networks:
cls.client.delete_network(network['id'])
+ cls.clear_isolated_creds()
super(BaseNetworkTest, cls).tearDownClass()
@classmethod
@@ -123,10 +144,11 @@
return network
@classmethod
- def create_subnet(cls, network, ip_version=4):
+ def create_subnet(cls, network):
"""Wrapper utility that returns a test subnet."""
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ # The cidr and mask_bits depend on the ip version.
+ cidr = netaddr.IPNetwork(cls._tenant_network_cidr)
+ mask_bits = cls._tenant_network_mask_bits
# Find a cidr that is not in use yet and create a subnet with it
body = None
failure = None
@@ -135,7 +157,7 @@
resp, body = cls.client.create_subnet(
network_id=network['id'],
cidr=str(subnet_cidr),
- ip_version=ip_version)
+ ip_version=cls._ip_version)
break
except exceptions.BadRequest as e:
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
@@ -206,13 +228,22 @@
@classmethod
def create_vip(cls, name, protocol, protocol_port, subnet, pool):
"""Wrapper utility that returns a test vip."""
- resp, body = cls.client.create_vip(name, protocol, protocol_port,
- subnet['id'], pool['id'])
+ resp, body = cls.client.create_vip(name=name,
+ protocol=protocol,
+ protocol_port=protocol_port,
+ subnet_id=subnet['id'],
+ pool_id=pool['id'])
vip = body['vip']
cls.vips.append(vip)
return vip
@classmethod
+ def update_vip(cls, name):
+ resp, body = cls.client.update_vip(name=name)
+ vip = body['vip']
+ return vip
+
+ @classmethod
def create_member(cls, protocol_port, pool):
"""Wrapper utility that returns a test member."""
resp, body = cls.client.create_member("10.0.9.46",
@@ -225,14 +256,21 @@
@classmethod
def create_health_monitor(cls, delay, max_retries, Type, timeout):
"""Wrapper utility that returns a test health monitor."""
- resp, body = cls.client.create_health_monitor(delay,
- max_retries,
- Type, timeout)
+ resp, body = cls.client.create_health_monitor(delay=delay,
+ max_retries=max_retries,
+ type=Type,
+ timeout=timeout)
health_monitor = body['health_monitor']
cls.health_monitors.append(health_monitor)
return health_monitor
@classmethod
+ def update_health_monitor(cls, admin_state_up):
+ resp, body = cls.client.update_vip(admin_state_up=admin_state_up)
+ health_monitor = body['health_monitor']
+ return health_monitor
+
+ @classmethod
def create_router_interface(cls, router_id, subnet_id):
"""Wrapper utility that returns a router interface."""
resp, interface = cls.client.add_router_interface_with_subnet_id(
@@ -269,5 +307,35 @@
msg = ("Missing Administrative Network API credentials "
"in configuration.")
raise cls.skipException(msg)
- cls.admin_manager = clients.AdminManager(interface=cls._interface)
- cls.admin_client = cls.admin_manager.network_client
+ if (CONF.compute.allow_tenant_isolation or
+ cls.force_tenant_isolation is True):
+ creds = cls.isolated_creds.get_admin_creds()
+ admin_username, admin_tenant_name, admin_password = creds
+ cls.os_adm = clients.Manager(username=admin_username,
+ password=admin_password,
+ tenant_name=admin_tenant_name,
+ interface=cls._interface)
+ else:
+ cls.os_adm = clients.ComputeAdminManager(interface=cls._interface)
+ cls.admin_client = cls.os_adm.network_client
+
+ @classmethod
+ def create_metering_label(cls, name, description):
+ """Wrapper utility that returns a test metering label."""
+ resp, body = cls.admin_client.create_metering_label(
+ description=description,
+ name=data_utils.rand_name("metering-label"))
+ metering_label = body['metering_label']
+ cls.metering_labels.append(metering_label)
+ return metering_label
+
+ @classmethod
+ def create_metering_label_rule(cls, remote_ip_prefix, direction,
+ metering_label_id):
+ """Wrapper utility that returns a test metering label rule."""
+ resp, body = cls.admin_client.create_metering_label_rule(
+ remote_ip_prefix=remote_ip_prefix, direction=direction,
+ metering_label_id=metering_label_id)
+ metering_label_rule = body['metering_label_rule']
+ cls.metering_label_rules.append(metering_label_rule)
+ return metering_label_rule
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index 0ce1769..d68ff1a 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -126,3 +126,21 @@
def delete(self):
self.client.delete_security_group_rule(self.id)
+
+
+class DeletablePool(DeletableResource):
+
+ def delete(self):
+ self.client.delete_pool(self.id)
+
+
+class DeletableMember(DeletableResource):
+
+ def delete(self):
+ self.client.delete_member(self.id)
+
+
+class DeletableVip(DeletableResource):
+
+ def delete(self):
+ self.client.delete_vip(self.id)
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index a177d65..529f8e9 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -44,6 +44,8 @@
'agent', 'dhcp_agent_scheduler', 'provider',
'router', 'extraroute', 'external-net',
'allowed-address-pairs', 'extra_dhcp_opt']
+ expected_alias = [ext for ext in expected_alias if
+ test.is_extension_enabled(ext, 'network')]
actual_alias = list()
resp, extensions = self.client.list_extensions()
self.assertEqual('200', resp['status'])
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 69367ab..b31c090 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -16,7 +16,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
from tempest import config
-from tempest.test import attr
+from tempest import test
CONF = config.CONF
@@ -46,6 +46,9 @@
@classmethod
def setUpClass(cls):
super(FloatingIPTestJSON, cls).setUpClass()
+ if not test.is_extension_enabled('router', 'network'):
+ msg = "router extension not enabled."
+ raise cls.skipException(msg)
cls.ext_net_id = CONF.network.public_network_id
# Create network, subnet, router and add interface
@@ -59,7 +62,7 @@
for i in range(2):
cls.create_port(cls.network)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_list_show_update_delete_floating_ip(self):
# Creates a floating IP
created_floating_ip = self.create_floating_ip(
@@ -110,7 +113,7 @@
self.assertIsNone(updated_floating_ip['fixed_ip_address'])
self.assertIsNone(updated_floating_ip['router_id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_floating_ip_delete_port(self):
# Create a floating IP
created_floating_ip = self.create_floating_ip(self.ext_net_id)
@@ -133,7 +136,7 @@
self.assertIsNone(shown_floating_ip['fixed_ip_address'])
self.assertIsNone(shown_floating_ip['router_id'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_floating_ip_update_different_router(self):
# Associate a floating IP to a port on a router
created_floating_ip = self.create_floating_ip(
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index 65eebf2..03e095d 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -18,7 +18,7 @@
from tempest import test
-class LoadBalancerJSON(base.BaseNetworkTest):
+class LoadBalancerTestJSON(base.BaseNetworkTest):
_interface = 'json'
"""
@@ -39,7 +39,7 @@
@classmethod
def setUpClass(cls):
- super(LoadBalancerJSON, cls).setUpClass()
+ super(LoadBalancerTestJSON, cls).setUpClass()
if not test.is_extension_enabled('lbaas', 'network'):
msg = "lbaas extension not enabled."
raise cls.skipException(msg)
@@ -50,9 +50,16 @@
vip_name = data_utils.rand_name('vip-')
cls.pool = cls.create_pool(pool_name, "ROUND_ROBIN",
"HTTP", cls.subnet)
- cls.vip = cls.create_vip(vip_name, "HTTP", 80, cls.subnet, cls.pool)
+ cls.vip = cls.create_vip(name=vip_name,
+ protocol="HTTP",
+ protocol_port=80,
+ subnet=cls.subnet,
+ pool=cls.pool)
cls.member = cls.create_member(80, cls.pool)
- cls.health_monitor = cls.create_health_monitor(4, 3, "TCP", 1)
+ cls.health_monitor = cls.create_health_monitor(delay=4,
+ max_retries=3,
+ Type="TCP",
+ timeout=1)
@test.attr(type='smoke')
def test_list_vips(self):
@@ -76,14 +83,17 @@
protocol='HTTP',
subnet_id=self.subnet['id'])
pool = body['pool']
- resp, body = self.client.create_vip(name, "HTTP", 80,
- self.subnet['id'], pool['id'])
+ resp, body = self.client.create_vip(name=name,
+ protocol="HTTP",
+ protocol_port=80,
+ subnet_id=self.subnet['id'],
+ pool_id=pool['id'])
self.assertEqual('201', resp['status'])
vip = body['vip']
vip_id = vip['id']
# Verification of vip update
new_name = "New_vip"
- resp, body = self.client.update_vip(vip_id, new_name)
+ resp, body = self.client.update_vip(vip_id, name=new_name)
self.assertEqual('200', resp['status'])
updated_vip = body['vip']
self.assertEqual(updated_vip['name'], new_name)
@@ -143,11 +153,10 @@
self.assertEqual('201', resp['status'])
member = body['member']
# Verification of member update
- admin_state = [False, 'False']
- resp, body = self.client.update_member(admin_state[0], member['id'])
+ resp, body = self.client.update_member(False, member['id'])
self.assertEqual('200', resp['status'])
updated_member = body['member']
- self.assertIn(updated_member['admin_state_up'], admin_state)
+ self.assertFalse(updated_member['admin_state_up'])
# Verification of member delete
resp, body = self.client.delete_member(member['id'])
self.assertEqual('204', resp['status'])
@@ -174,16 +183,19 @@
@test.attr(type='smoke')
def test_create_update_delete_health_monitor(self):
# Creates a health_monitor
- resp, body = self.client.create_health_monitor(4, 3, "TCP", 1)
+ resp, body = self.client.create_health_monitor(delay=4,
+ max_retries=3,
+ type="TCP",
+ timeout=1)
self.assertEqual('201', resp['status'])
health_monitor = body['health_monitor']
# Verification of health_monitor update
- admin_state = [False, 'False']
- resp, body = self.client.update_health_monitor(admin_state[0],
- health_monitor['id'])
+ resp, body = (self.client.update_health_monitor
+ (health_monitor['id'],
+ admin_state_up=False))
self.assertEqual('200', resp['status'])
updated_health_monitor = body['health_monitor']
- self.assertIn(updated_health_monitor['admin_state_up'], admin_state)
+ self.assertFalse(updated_health_monitor['admin_state_up'])
# Verification of health_monitor delete
resp, body = self.client.delete_health_monitor(health_monitor['id'])
self.assertEqual('204', resp['status'])
@@ -209,6 +221,17 @@
(self.health_monitor['id'], self.pool['id']))
self.assertEqual('204', resp['status'])
+ @test.attr(type='smoke')
+ def test_get_lb_pool_stats(self):
+ # Verify the details of pool stats
+ resp, body = self.client.list_lb_pool_stats(self.pool['id'])
+ self.assertEqual('200', resp['status'])
+ stats = body['stats']
+ self.assertIn("bytes_in", stats)
+ self.assertIn("total_connections", stats)
+ self.assertIn("active_connections", stats)
+ self.assertIn("bytes_out", stats)
-class LoadBalancerXML(LoadBalancerJSON):
+
+class LoadBalancerTestXML(LoadBalancerTestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/test_metering_extensions.py b/tempest/api/network/test_metering_extensions.py
new file mode 100644
index 0000000..08ccbfe
--- /dev/null
+++ b/tempest/api/network/test_metering_extensions.py
@@ -0,0 +1,159 @@
+# Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
+#
+# Author: Emilien Macchi <emilien.macchi@enovance.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.network import base
+from tempest.common.utils import data_utils
+from tempest.openstack.common import log as logging
+from tempest import test
+
+
+LOG = logging.getLogger(__name__)
+
+
+class MeteringJSON(base.BaseAdminNetworkTest):
+ _interface = 'json'
+
+ """
+ Tests the following operations in the Neutron API using the REST client for
+ Neutron:
+
+ List, Show, Create, Delete Metering labels
+ List, Show, Create, Delete Metering labels rules
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(MeteringJSON, cls).setUpClass()
+ if not test.is_extension_enabled('metering', 'network'):
+ msg = "metering extension not enabled."
+ raise cls.skipException(msg)
+ description = "metering label created by tempest"
+ name = data_utils.rand_name("metering-label")
+ try:
+ cls.metering_label = cls.create_metering_label(name, description)
+ remote_ip_prefix = "10.0.0.0/24"
+ direction = "ingress"
+ cls.metering_label_rule = cls.create_metering_label_rule(
+ remote_ip_prefix, direction,
+ metering_label_id=cls.metering_label['id'])
+ except Exception:
+ LOG.exception('setUpClass failed')
+ cls.tearDownClass()
+ raise
+
+ def _delete_metering_label(self, metering_label_id):
+ # Deletes a label and verifies if it is deleted or not
+ resp, body = self.admin_client.delete_metering_label(metering_label_id)
+ self.assertEqual(204, resp.status)
+ # Asserting that the label is not found in list after deletion
+ resp, labels = (self.admin_client.list_metering_labels(
+ id=metering_label_id))
+ self.assertEqual(len(labels['metering_labels']), 0)
+
+ def _delete_metering_label_rule(self, metering_label_rule_id):
+ # Deletes a rule and verifies if it is deleted or not
+ resp, body = (self.admin_client.delete_metering_label_rule(
+ metering_label_rule_id))
+ self.assertEqual(204, resp.status)
+ # Asserting that the rule is not found in list after deletion
+ resp, rules = (self.admin_client.list_metering_label_rules(
+ id=metering_label_rule_id))
+ self.assertEqual(len(rules['metering_label_rules']), 0)
+
+ @test.attr(type='smoke')
+ def test_list_metering_labels(self):
+ # Verify label filtering
+ resp, body = self.admin_client.list_metering_labels(id=33)
+ self.assertEqual('200', resp['status'])
+ metering_labels = body['metering_labels']
+ self.assertEqual(0, len(metering_labels))
+
+ @test.attr(type='smoke')
+ def test_create_delete_metering_label_with_filters(self):
+ # Creates a label
+ name = data_utils.rand_name('metering-label-')
+ description = "label created by tempest"
+ resp, body = (self.admin_client.create_metering_label(name=name,
+ description=description))
+ self.assertEqual('201', resp['status'])
+ metering_label = body['metering_label']
+ self.addCleanup(self._delete_metering_label,
+ metering_label['id'])
+ # Assert whether created labels are found in labels list or fail
+ # if created labels are not found in labels list
+ resp, labels = (self.admin_client.list_metering_labels(
+ id=metering_label['id']))
+ self.assertEqual(len(labels['metering_labels']), 1)
+
+ @test.attr(type='smoke')
+ def test_show_metering_label(self):
+ # Verifies the details of a label
+ resp, body = (self.admin_client.show_metering_label(
+ self.metering_label['id']))
+ self.assertEqual('200', resp['status'])
+ metering_label = body['metering_label']
+ self.assertEqual(self.metering_label['id'], metering_label['id'])
+ self.assertEqual(self.metering_label['tenant_id'],
+ metering_label['tenant_id'])
+ self.assertEqual(self.metering_label['name'], metering_label['name'])
+ self.assertEqual(self.metering_label['description'],
+ metering_label['description'])
+
+ @test.attr(type='smoke')
+ def test_list_metering_label_rules(self):
+ # Verify rule filtering
+ resp, body = self.admin_client.list_metering_label_rules(id=33)
+ self.assertEqual('200', resp['status'])
+ metering_label_rules = body['metering_label_rules']
+ self.assertEqual(0, len(metering_label_rules))
+
+ @test.attr(type='smoke')
+ def test_create_delete_metering_label_rule_with_filters(self):
+ # Creates a rule
+ resp, body = (self.admin_client.create_metering_label_rule(
+ remote_ip_prefix="10.0.1.0/24",
+ direction="ingress",
+ metering_label_id=self.metering_label['id']))
+ self.assertEqual('201', resp['status'])
+ metering_label_rule = body['metering_label_rule']
+ self.addCleanup(self._delete_metering_label_rule,
+ metering_label_rule['id'])
+ # Assert whether created rules are found in rules list or fail
+ # if created rules are not found in rules list
+ resp, rules = (self.admin_client.list_metering_label_rules(
+ id=metering_label_rule['id']))
+ self.assertEqual(len(rules['metering_label_rules']), 1)
+
+ @test.attr(type='smoke')
+ def test_show_metering_label_rule(self):
+ # Verifies the details of a rule
+ resp, body = (self.admin_client.show_metering_label_rule(
+ self.metering_label_rule['id']))
+ self.assertEqual('200', resp['status'])
+ metering_label_rule = body['metering_label_rule']
+ self.assertEqual(self.metering_label_rule['id'],
+ metering_label_rule['id'])
+ self.assertEqual(self.metering_label_rule['remote_ip_prefix'],
+ metering_label_rule['remote_ip_prefix'])
+ self.assertEqual(self.metering_label_rule['direction'],
+ metering_label_rule['direction'])
+ self.assertEqual(self.metering_label_rule['metering_label_id'],
+ metering_label_rule['metering_label_id'])
+ self.assertFalse(metering_label_rule['excluded'])
+
+
+class MeteringXML(MeteringJSON):
+ interface = 'xml'
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 3aa765c..9029b1f 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -45,14 +45,20 @@
network update
subnet update
+ All subnet tests are run once with ipv4 and once with ipv6.
+
v2.0 of the Neutron API is assumed. It is also assumed that the following
options are defined in the [network] section of etc/tempest.conf:
tenant_network_cidr with a block of cidr's from which smaller blocks
- can be allocated for tenant networks
+ can be allocated for tenant ipv4 subnets
+
+ tenant_network_v6_cidr is the equivalent for ipv6 subnets
tenant_network_mask_bits with the mask bits to be used to partition the
- block defined by tenant-network_cidr
+ block defined by tenant_network_cidr
+
+ tenant_network_v6_mask_bits is the equivalent for ipv6 subnets
"""
@classmethod
@@ -79,14 +85,14 @@
updated_net = body['network']
self.assertEqual(updated_net['name'], new_name)
# Find a cidr that is not in use yet and create a subnet with it
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ cidr = netaddr.IPNetwork(self._tenant_network_cidr)
+ mask_bits = self._tenant_network_mask_bits
for subnet_cidr in cidr.subnet(mask_bits):
try:
resp, body = self.client.create_subnet(
network_id=net_id,
cidr=str(subnet_cidr),
- ip_version=4)
+ ip_version=self._ip_version)
break
except exceptions.BadRequest as e:
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
@@ -118,6 +124,18 @@
self.assertEqual(self.name, network['name'])
@attr(type='smoke')
+ def test_show_network_fields(self):
+ # Verifies showing some fields of a network works
+ field_list = [('fields', 'id'), ('fields', 'name'), ]
+ resp, body = self.client.show_network(self.network['id'],
+ field_list=field_list)
+ self.assertEqual('200', resp['status'])
+ network = body['network']
+ self.assertEqual(len(network), 2)
+ self.assertEqual(self.network['id'], network['id'])
+ self.assertEqual(self.name, network['name'])
+
+ @attr(type='smoke')
def test_list_networks(self):
# Verify the network exists in the list of all networks
resp, body = self.client.list_networks()
@@ -155,6 +173,18 @@
self.assertEqual(self.cidr, subnet['cidr'])
@attr(type='smoke')
+ def test_show_subnet_fields(self):
+ # Verifies showing some fields of a subnet works
+ field_list = [('fields', 'id'), ('fields', 'cidr'), ]
+ resp, body = self.client.show_subnet(self.subnet['id'],
+ field_list=field_list)
+ self.assertEqual('200', resp['status'])
+ subnet = body['subnet']
+ self.assertEqual(len(subnet), 2)
+ self.assertEqual(self.subnet['id'], subnet['id'])
+ self.assertEqual(self.cidr, subnet['cidr'])
+
+ @attr(type='smoke')
def test_list_subnets(self):
# Verify the subnet exists in the list of all subnets
resp, body = self.client.list_subnets()
@@ -189,12 +219,17 @@
network_id=self.network['id'])
self.assertEqual('201', resp['status'])
port = body['port']
+ self.assertTrue(port['admin_state_up'])
# Verification of port update
new_port = "New_Port"
- resp, body = self.client.update_port(port['id'], name=new_port)
+ resp, body = self.client.update_port(
+ port['id'],
+ name=new_port,
+ admin_state_up=False)
self.assertEqual('200', resp['status'])
updated_port = body['port']
self.assertEqual(updated_port['name'], new_port)
+ self.assertFalse(updated_port['admin_state_up'])
# Verification of port delete
resp, body = self.client.delete_port(port['id'])
self.assertEqual('204', resp['status'])
@@ -208,6 +243,17 @@
self.assertEqual(self.port['id'], port['id'])
@attr(type='smoke')
+ def test_show_port_fields(self):
+ # Verifies showing fields of a port works
+ field_list = [('fields', 'id'), ]
+ resp, body = self.client.show_port(self.port['id'],
+ field_list=field_list)
+ self.assertEqual('200', resp['status'])
+ port = body['port']
+ self.assertEqual(len(port), 1)
+ self.assertEqual(self.port['id'], port['id'])
+
+ @attr(type='smoke')
def test_list_ports(self):
# Verify the port exists in the list of all ports
resp, body = self.client.list_ports()
@@ -220,6 +266,28 @@
self.assertIsNotNone(found, "Port list doesn't contain created port")
@attr(type='smoke')
+ def test_port_list_filter_by_router_id(self):
+ # Create a router
+ network = self.create_network()
+ self.create_subnet(network)
+ router = self.create_router(data_utils.rand_name('router-'))
+ resp, port = self.client.create_port(
+ network_id=network['id'])
+ # Add router interface to port created above
+ resp, interface = self.client.add_router_interface_with_port_id(
+ router['id'], port['port']['id'])
+ self.addCleanup(self.client.remove_router_interface_with_port_id,
+ router['id'], port['port']['id'])
+ # list ports filtered by router_id
+ resp, port_list = self.client.list_ports(
+ device_id=router['id'])
+ self.assertEqual('200', resp['status'])
+ # Verify if only corresponding port is listed and assert router_id
+ self.assertEqual(len(port_list['ports']), 1)
+ self.assertEqual(port['port']['id'], port_list['ports'][0]['id'])
+ self.assertEqual(router['id'], port_list['ports'][0]['device_id'])
+
+ @attr(type='smoke')
def test_list_ports_fields(self):
# Verify listing some fields of the ports
resp, body = self.client.list_ports(fields='id')
@@ -239,7 +307,7 @@
_interface = 'xml'
-class BulkNetworkOpsJSON(base.BaseNetworkTest):
+class BulkNetworkOpsTestJSON(base.BaseNetworkTest):
_interface = 'json'
"""
@@ -263,7 +331,7 @@
@classmethod
def setUpClass(cls):
- super(BulkNetworkOpsJSON, cls).setUpClass()
+ super(BulkNetworkOpsTestJSON, cls).setUpClass()
cls.network1 = cls.create_network()
cls.network2 = cls.create_network()
@@ -390,5 +458,15 @@
self.assertIn(n['id'], ports_list)
-class BulkNetworkOpsXML(BulkNetworkOpsJSON):
+class BulkNetworkOpsTestXML(BulkNetworkOpsTestJSON):
+ _interface = 'xml'
+
+
+class NetworksIpV6TestJSON(NetworksTestJSON):
+ _ip_version = 6
+ _tenant_network_cidr = CONF.network.tenant_network_v6_cidr
+ _tenant_network_mask_bits = CONF.network.tenant_network_v6_mask_bits
+
+
+class NetworksIpV6TestXML(NetworksIpV6TestJSON):
_interface = 'xml'
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
index a5be395..38784d8 100644
--- a/tempest/api/network/test_quotas.py
+++ b/tempest/api/network/test_quotas.py
@@ -17,7 +17,7 @@
from tempest.api.network import base
from tempest import clients
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class QuotasTest(base.BaseNetworkTest):
@@ -46,11 +46,14 @@
@classmethod
def setUpClass(cls):
super(QuotasTest, cls).setUpClass()
+ if not test.is_extension_enabled('quotas', 'network'):
+ msg = "quotas extension not enabled."
+ raise cls.skipException(msg)
admin_manager = clients.AdminManager()
cls.admin_client = admin_manager.network_client
cls.identity_admin_client = admin_manager.identity_client
- @attr(type='gate')
+ @test.attr(type='gate')
def test_quotas(self):
# Add a tenant to conduct the test
test_tenant = data_utils.rand_name('test_tenant_')
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index f3fac93..d552c70 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -29,6 +29,9 @@
@classmethod
def setUpClass(cls):
super(RoutersTest, cls).setUpClass()
+ if not test.is_extension_enabled('router', 'network'):
+ msg = "router extension not enabled."
+ raise cls.skipException(msg)
@test.attr(type='smoke')
def test_create_show_list_update_delete_router(self):
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 0d65b64..e6ad4de 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -16,7 +16,7 @@
from tempest.api.network import base_routers as base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class RoutersNegativeTest(base.BaseRouterTest):
@@ -25,11 +25,14 @@
@classmethod
def setUpClass(cls):
super(RoutersNegativeTest, cls).setUpClass()
+ if not test.is_extension_enabled('router', 'network'):
+ msg = "router extension not enabled."
+ raise cls.skipException(msg)
cls.router = cls.create_router(data_utils.rand_name('router-'))
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_router_add_gateway_invalid_network_returns_404(self):
self.assertRaises(exceptions.NotFound,
self.client.update_router,
@@ -37,7 +40,7 @@
external_gateway_info={
'network_id': self.router['id']})
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_router_add_gateway_net_not_external_returns_400(self):
self.create_subnet(self.network)
self.assertRaises(exceptions.BadRequest,
@@ -46,7 +49,7 @@
external_gateway_info={
'network_id': self.network['id']})
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_router_remove_interface_in_use_returns_409(self):
self.client.add_router_interface_with_subnet_id(
self.router['id'], self.subnet['id'])
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index b95182d..6eebf5b 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -14,13 +14,20 @@
# under the License.
from tempest.api.network import base_security_groups as base
-from tempest.test import attr
+from tempest import test
class SecGroupTest(base.BaseSecGroupTest):
_interface = 'json'
- @attr(type='smoke')
+ @classmethod
+ def setUpClass(cls):
+ super(SecGroupTest, cls).setUpClass()
+ if not test.is_extension_enabled('security-group', 'network'):
+ msg = "security-group extension not enabled."
+ raise cls.skipException(msg)
+
+ @test.attr(type='smoke')
def test_list_security_groups(self):
# Verify the that security group belonging to tenant exist in list
resp, body = self.client.list_security_groups()
@@ -33,7 +40,7 @@
msg = "Security-group list doesn't contain default security-group"
self.assertIsNotNone(found, msg)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_show_delete_security_group(self):
group_create_body, name = self._create_security_group()
@@ -51,7 +58,7 @@
secgroup_list.append(secgroup['id'])
self.assertIn(group_create_body['security_group']['id'], secgroup_list)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_show_delete_security_group_rule(self):
group_create_body, _ = self._create_security_group()
@@ -80,7 +87,7 @@
for rule in rule_list_body['security_group_rules']]
self.assertIn(rule_create_body['security_group_rule']['id'], rule_list)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_security_group_rule_with_additional_args(self):
# Verify creating security group rule with the following
# arguments works: "protocol": "tcp", "port_range_max": 77,
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 98e109e..e1f4055 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -17,26 +17,33 @@
from tempest.api.network import base_security_groups as base
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class NegativeSecGroupTest(base.BaseSecGroupTest):
_interface = 'json'
- @attr(type=['negative', 'gate'])
+ @classmethod
+ def setUpClass(cls):
+ super(NegativeSecGroupTest, cls).setUpClass()
+ if not test.is_extension_enabled('security-group', 'network'):
+ msg = "security-group extension not enabled."
+ raise cls.skipException(msg)
+
+ @test.attr(type=['negative', 'gate'])
def test_show_non_existent_security_group(self):
non_exist_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound, self.client.show_security_group,
non_exist_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_show_non_existent_security_group_rule(self):
non_exist_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound,
self.client.show_security_group_rule,
non_exist_id)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_non_existent_security_group(self):
non_exist_id = str(uuid.uuid4())
self.assertRaises(exceptions.NotFound,
@@ -44,7 +51,7 @@
non_exist_id
)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_security_group_rule_with_bad_protocol(self):
group_create_body, _ = self._create_security_group()
@@ -55,7 +62,7 @@
group_create_body['security_group']['id'],
protocol=pname)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_create_security_group_rule_with_invalid_ports(self):
group_create_body, _ = self._create_security_group()
@@ -73,7 +80,7 @@
port_range_max=pmax)
self.assertIn(msg, str(ex))
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_additional_default_security_group_fails(self):
# Create security group named 'default', it should be failed.
name = 'default'
@@ -81,7 +88,7 @@
self.client.create_security_group,
name)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_create_security_group_rule_with_non_existent_security_group(self):
# Create security group rules with not existing security group.
non_existent_sg = str(uuid.uuid4())
diff --git a/tempest/api/network/test_vpnaas_extensions.py b/tempest/api/network/test_vpnaas_extensions.py
index 64b8a41..78bc80a 100644
--- a/tempest/api/network/test_vpnaas_extensions.py
+++ b/tempest/api/network/test_vpnaas_extensions.py
@@ -38,10 +38,10 @@
@classmethod
def setUpClass(cls):
- super(VPNaaSJSON, cls).setUpClass()
if not test.is_extension_enabled('vpnaas', 'network'):
msg = "vpnaas extension not enabled."
raise cls.skipException(msg)
+ super(VPNaaSJSON, cls).setUpClass()
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
cls.router = cls.create_router(
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 0eedea1..d08dc34 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -14,7 +14,7 @@
# under the License.
-from tempest.api.identity.base import DataGenerator
+from tempest.api.identity import base
from tempest import clients
from tempest.common import custom_matchers
from tempest.common import isolated_creds
@@ -74,7 +74,16 @@
cls.container_client_alt = cls.os_alt.container_client
cls.identity_client_alt = cls.os_alt.identity_client
- cls.data = DataGenerator(cls.identity_admin_client)
+ # Make sure we get fresh auth data after assigning swift role
+ cls.object_client.auth_provider.clear_auth()
+ cls.container_client.auth_provider.clear_auth()
+ cls.account_client.auth_provider.clear_auth()
+ cls.custom_object_client.auth_provider.clear_auth()
+ cls.custom_account_client.auth_provider.clear_auth()
+ cls.object_client_alt.auth_provider.clear_auth()
+ cls.container_client_alt.auth_provider.clear_auth()
+
+ cls.data = base.DataGenerator(cls.identity_admin_client)
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 5fde76a..a94c883 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -1,5 +1,3 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
# Copyright 2013 NTT Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index cacc66e..788292d 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -62,26 +62,34 @@
reseller_user_id,
reseller_role_id)
- # Retrieve a ResellerAdmin auth token and use it to set a quota
+ # Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
- cls.reselleradmin_token = cls.token_client.get_token(
- cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ cls.reselleradmin_auth_data = \
+ cls.os_reselleradmin.get_auth_provider().auth_data
def setUp(self):
super(AccountQuotasTest, self).setUp()
+ # Set the reselleradmin auth in headers for next custom_account_client
+ # request
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.reselleradmin_auth_data
+ )
# Set a quota of 20 bytes on the user's account before each test
- headers = {"X-Auth-Token": self.reselleradmin_token,
- "X-Account-Meta-Quota-Bytes": "20"}
+ headers = {"X-Account-Meta-Quota-Bytes": "20"}
self.os.custom_account_client.request("POST", "", headers, "")
def tearDown(self):
+ # Set the reselleradmin auth in headers for next custom_account_client
+ # request
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.reselleradmin_auth_data
+ )
# remove the quota from the container
- headers = {"X-Auth-Token": self.reselleradmin_token,
- "X-Remove-Account-Meta-Quota-Bytes": "x"}
+ headers = {"X-Remove-Account-Meta-Quota-Bytes": "x"}
self.os.custom_account_client.request("POST", "", headers, "")
super(AccountQuotasTest, self).tearDown()
@@ -118,8 +126,11 @@
"""
for quota in ("25", "", "20"):
- headers = {"X-Auth-Token": self.reselleradmin_token,
- "X-Account-Meta-Quota-Bytes": quota}
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.reselleradmin_auth_data
+ )
+ headers = {"X-Account-Meta-Quota-Bytes": quota}
resp, _ = self.os.custom_account_client.request("POST", "",
headers, "")
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index e35cd17..cab307d 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -62,26 +62,33 @@
reseller_user_id,
reseller_role_id)
- # Retrieve a ResellerAdmin auth token and use it to set a quota
+ # Retrieve a ResellerAdmin auth data and use it to set a quota
# on the client's account
- cls.reselleradmin_token = cls.token_client.get_token(
- cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
+ cls.reselleradmin_auth_data = \
+ cls.os_reselleradmin.get_auth_provider().auth_data
def setUp(self):
super(AccountQuotasNegativeTest, self).setUp()
-
+ # Set the reselleradmin auth in headers for next custom_account_client
+ # request
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.reselleradmin_auth_data
+ )
# Set a quota of 20 bytes on the user's account before each test
- headers = {"X-Auth-Token": self.reselleradmin_token,
- "X-Account-Meta-Quota-Bytes": "20"}
+ headers = {"X-Account-Meta-Quota-Bytes": "20"}
self.os.custom_account_client.request("POST", "", headers, "")
def tearDown(self):
+ # Set the reselleradmin auth in headers for next custom_account_client
+ # request
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.reselleradmin_auth_data
+ )
# remove the quota from the container
- headers = {"X-Auth-Token": self.reselleradmin_token,
- "X-Remove-Account-Meta-Quota-Bytes": "x"}
+ headers = {"X-Remove-Account-Meta-Quota-Bytes": "x"}
self.os.custom_account_client.request("POST", "", headers, "")
super(AccountQuotasNegativeTest, self).tearDown()
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 79fd99d..5456768 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -18,8 +18,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class AccountTest(base.BaseObjectTest):
@@ -38,7 +37,7 @@
cls.delete_containers(cls.containers)
super(AccountTest, cls).tearDownClass()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_containers(self):
# list of all containers should not be empty
params = {'format': 'json'}
@@ -51,14 +50,14 @@
for container_name in self.containers:
self.assertIn(container_name, container_names)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_extensions(self):
resp, extensions = self.account_client.list_extensions()
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertThat(resp, custom_matchers.AreAllWellFormatted())
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_containers_with_limit(self):
# list containers one of them, half of them then all of them
for limit in (1, self.containers_count / 2, self.containers_count):
@@ -69,7 +68,7 @@
self.assertEqual(len(container_list), limit)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_containers_with_marker(self):
# list containers using marker param
# first expect to get 0 container as we specified last
@@ -89,7 +88,7 @@
self.assertEqual(len(container_list), self.containers_count / 2 - 1)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_containers_with_end_marker(self):
# list containers using end_marker param
# first expect to get 0 container as we specified first container as
@@ -107,7 +106,7 @@
self.assertHeaders(resp, 'Account', 'GET')
self.assertEqual(len(container_list), self.containers_count / 2)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_containers_with_limit_and_marker(self):
# list containers combining marker and limit param
# result are always limitated by the limit whatever the marker
@@ -121,21 +120,21 @@
self.assertTrue(len(container_list) <= limit, str(container_list))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_account_metadata(self):
# list all account metadata
resp, metadata = self.account_client.list_account_metadata()
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Account', 'HEAD')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_and_delete_account_metadata(self):
header = 'test-account-meta'
data = 'Meta!'
# add metadata to account
resp, _ = self.account_client.create_account_metadata(
metadata={header: data})
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
@@ -147,7 +146,7 @@
# delete metadata from account
resp, _ = \
self.account_client.delete_account_metadata(metadata=[header])
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index c8f8096..ea93aa3 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -15,6 +15,7 @@
# under the License.
from tempest.api.object_storage import base
+from tempest import clients
from tempest import exceptions
from tempest.test import attr
@@ -27,18 +28,26 @@
# create user
self.data.setup_test_user()
- self.token_client.auth(self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
- new_token = \
- self.token_client.get_token(self.data.test_user,
- self.data.test_password,
- self.data.test_tenant)
- custom_headers = {'X-Auth-Token': new_token}
+ test_os = clients.Manager(self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+ test_auth_provider = test_os.get_auth_provider()
+ # Get auth for the test user
+ test_auth_provider.auth_data
+
+ # Get fresh auth for test user and set it to next auth request for
+ # custom_account_client
+ delattr(test_auth_provider, 'auth_data')
+ test_auth_new_data = test_auth_provider.auth_data
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=test_auth_new_data
+ )
+
params = {'format': 'json'}
# list containers with non-authorized user token
self.assertRaises(exceptions.Unauthorized,
self.custom_account_client.list_account_containers,
- params=params, metadata=custom_headers)
+ params=params)
# delete the user which was created
self.data.teardown_all()
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 0733524..085ef51 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -14,9 +14,9 @@
# under the License.
from tempest.api.object_storage import base
+from tempest import clients
from tempest.common.utils import data_utils
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class ObjectTestACLs(base.BaseObjectTest):
@@ -24,10 +24,10 @@
def setUpClass(cls):
super(ObjectTestACLs, cls).setUpClass()
cls.data.setup_test_user()
- cls.new_token = cls.token_client.get_token(cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
- cls.custom_headers = {'X-Auth-Token': cls.new_token}
+ test_os = clients.Manager(cls.data.test_user,
+ cls.data.test_password,
+ cls.data.test_tenant)
+ cls.test_auth_data = test_os.get_auth_provider().auth_data
@classmethod
def tearDownClass(cls):
@@ -43,7 +43,7 @@
self.delete_containers([self.container_name])
super(ObjectTestACLs, self).tearDown()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_read_object_with_rights(self):
# attempt to read object using authorized user
# update X-Container-Read metadata ACL
@@ -52,7 +52,7 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# create object
object_name = data_utils.rand_name(name='Object')
@@ -61,13 +61,16 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
# Trying to read the object with rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
resp, _ = self.custom_object_client.get_object(
- self.container_name, object_name,
- metadata=self.custom_headers)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.container_name, object_name)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'GET')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_write_object_with_rights(self):
# attempt to write object using authorized user
# update X-Container-Write metadata ACL
@@ -76,13 +79,16 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# Trying to write the object with rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
object_name = data_utils.rand_name(name='Object')
resp, _ = self.custom_object_client.create_object(
self.container_name,
- object_name, 'data',
- metadata=self.custom_headers)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ object_name, 'data')
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'PUT')
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index f8f29de..a5a0950 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -15,10 +15,10 @@
# under the License.
from tempest.api.object_storage import base
+from tempest import clients
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class ObjectACLsNegativeTest(base.BaseObjectTest):
@@ -26,10 +26,10 @@
def setUpClass(cls):
super(ObjectACLsNegativeTest, cls).setUpClass()
cls.data.setup_test_user()
- cls.new_token = cls.token_client.get_token(cls.data.test_user,
- cls.data.test_password,
- cls.data.test_tenant)
- cls.custom_headers = {'X-Auth-Token': cls.new_token}
+ test_os = clients.Manager(cls.data.test_user,
+ cls.data.test_password,
+ cls.data.test_tenant)
+ cls.test_auth_data = test_os.get_auth_provider().auth_data
@classmethod
def tearDownClass(cls):
@@ -45,16 +45,20 @@
self.delete_containers([self.container_name])
super(ObjectACLsNegativeTest, self).tearDown()
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_write_object_without_using_creds(self):
# trying to create object with empty headers
# X-Auth-Token is not provided
object_name = data_utils.rand_name(name='Object')
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.create_object,
self.container_name, object_name, 'data')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_object_without_using_creds(self):
# create object
object_name = data_utils.rand_name(name='Object')
@@ -62,22 +66,29 @@
object_name, 'data')
# trying to delete object with empty headers
# X-Auth-Token is not provided
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.delete_object,
self.container_name, object_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_write_object_with_non_authorized_user(self):
# attempt to upload another file using non-authorized user
# User provided token is forbidden. ACL are not set
object_name = data_utils.rand_name(name='Object')
# trying to create object with non-authorized user
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.create_object,
- self.container_name, object_name, 'data',
- metadata=self.custom_headers)
+ self.container_name, object_name, 'data')
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_read_object_with_non_authorized_user(self):
# attempt to read object using non-authorized user
# User provided token is forbidden. ACL are not set
@@ -87,12 +98,15 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
# trying to get object with non authorized user token
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.get_object,
- self.container_name, object_name,
- metadata=self.custom_headers)
+ self.container_name, object_name)
- @attr(type=['negative', 'gate'])
+ @test.attr(type=['negative', 'gate'])
def test_delete_object_with_non_authorized_user(self):
# attempt to delete object using non-authorized user
# User provided token is forbidden. ACL are not set
@@ -102,12 +116,15 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
# trying to delete object with non-authorized user token
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.delete_object,
- self.container_name, object_name,
- metadata=self.custom_headers)
+ self.container_name, object_name)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_read_object_without_rights(self):
# attempt to read object using non-authorized user
# update X-Container-Read metadata ACL
@@ -115,7 +132,7 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# create object
object_name = data_utils.rand_name(name='Object')
@@ -124,12 +141,15 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
# Trying to read the object without rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.get_object,
- self.container_name, object_name,
- metadata=self.custom_headers)
+ self.container_name, object_name)
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_write_object_without_rights(self):
# attempt to write object using non-authorized user
# update X-Container-Write metadata ACL
@@ -137,17 +157,20 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# Trying to write the object without rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
object_name = data_utils.rand_name(name='Object')
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.create_object,
self.container_name,
- object_name, 'data',
- metadata=self.custom_headers)
+ object_name, 'data')
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_write_object_without_write_rights(self):
# attempt to write object using non-authorized user
# update X-Container-Read and X-Container-Write metadata ACL
@@ -157,17 +180,20 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# Trying to write the object without write rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
object_name = data_utils.rand_name(name='Object')
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.create_object,
self.container_name,
- object_name, 'data',
- metadata=self.custom_headers)
+ object_name, 'data')
- @attr(type=['negative', 'smoke'])
+ @test.attr(type=['negative', 'smoke'])
def test_delete_object_without_write_rights(self):
# attempt to delete object using non-authorized user
# update X-Container-Read and X-Container-Write metadata ACL
@@ -177,7 +203,7 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# create object
object_name = data_utils.rand_name(name='Object')
@@ -186,8 +212,11 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
# Trying to delete the object without write rights
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=self.test_auth_data
+ )
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.delete_object,
self.container_name,
- object_name,
- metadata=self.custom_headers)
+ object_name)
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 84cc91e..8689d10 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -15,8 +15,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class ContainerTest(base.BaseObjectTest):
@@ -47,7 +46,7 @@
return object_name
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container(self):
container_name = data_utils.rand_name(name='TestContainer')
resp, body = self.container_client.create_container(container_name)
@@ -55,7 +54,7 @@
self.assertIn(resp['status'], ('202', '201'))
self.assertHeaders(resp, 'Container', 'PUT')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container_overwrite(self):
# overwrite container with the same name
container_name = data_utils.rand_name(name='TestContainer')
@@ -66,7 +65,7 @@
self.assertIn(resp['status'], ('202', '201'))
self.assertHeaders(resp, 'Container', 'PUT')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container_with_metadata_key(self):
# create container with the blank value of metadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -84,7 +83,7 @@
# in the server
self.assertNotIn('x-container-meta-test-container-meta', resp)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container_with_metadata_value(self):
# create container with metadata value
container_name = data_utils.rand_name(name='TestContainer')
@@ -103,7 +102,7 @@
self.assertEqual(resp['x-container-meta-test-container-meta'],
metadata['test-container-meta'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container_with_remove_metadata_key(self):
# create container with the blank value of remove metadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -124,7 +123,7 @@
container_name)
self.assertNotIn('x-container-meta-test-container-meta', resp)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_container_with_remove_metadata_value(self):
# create container with remove metadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -143,18 +142,18 @@
container_name)
self.assertNotIn('x-container-meta-test-container-meta', resp)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_delete_container(self):
# create a container
container_name = self._create_container()
# delete container
resp, _ = self.container_client.delete_container(container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'DELETE')
self.containers.remove(container_name)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents(self):
# get container contents list
container_name = self._create_container()
@@ -162,22 +161,22 @@
resp, object_list = self.container_client.list_container_contents(
container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_no_object(self):
# get empty container contents list
container_name = self._create_container()
resp, object_list = self.container_client.list_container_contents(
container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual('', object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_delimiter(self):
# get container contents list using delimiter param
container_name = self._create_container()
@@ -188,11 +187,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name.split('/')[0], object_list.strip('/\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_end_marker(self):
# get container contents list using end_marker param
container_name = self._create_container()
@@ -202,11 +201,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_format_json(self):
# get container contents list using format_json param
container_name = self._create_container()
@@ -216,7 +215,7 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertIsNotNone(object_list)
@@ -226,7 +225,7 @@
self.assertTrue([c['content_type'] for c in object_list])
self.assertTrue([c['last_modified'] for c in object_list])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_format_xml(self):
# get container contents list using format_xml param
container_name = self._create_container()
@@ -236,7 +235,7 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertIsNotNone(object_list)
@@ -251,7 +250,7 @@
self.assertEqual(object_list.find(".//last_modified").tag,
'last_modified')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_limit(self):
# get container contents list using limit param
container_name = self._create_container()
@@ -261,11 +260,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_marker(self):
# get container contents list using marker param
container_name = self._create_container()
@@ -275,11 +274,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_path(self):
# get container contents list using path param
container_name = self._create_container()
@@ -290,11 +289,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_contents_with_prefix(self):
# get container contents list using prefix param
container_name = self._create_container()
@@ -305,11 +304,11 @@
resp, object_list = self.container_client.list_container_contents(
container_name,
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual(object_name, object_list.strip('\n'))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_container_metadata(self):
# List container metadata
container_name = self._create_container()
@@ -321,23 +320,23 @@
resp, _ = self.container_client.list_container_metadata(
container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'HEAD')
self.assertIn('x-container-meta-name', resp)
self.assertEqual(resp['x-container-meta-name'], metadata['name'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_list_no_container_metadata(self):
# HEAD container without metadata
container_name = self._create_container()
resp, _ = self.container_client.list_container_metadata(
container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'HEAD')
self.assertNotIn('x-container-meta-', str(resp))
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_container_metadata_with_create_and_delete_matadata(self):
# Send one request of adding and deleting metadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -351,7 +350,7 @@
container_name,
metadata=metadata_2,
remove_metadata=metadata_1)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'POST')
resp, _ = self.container_client.list_container_metadata(
@@ -361,7 +360,7 @@
self.assertEqual(resp['x-container-meta-test-container-meta2'],
metadata_2['test-container-meta2'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_container_metadata_with_create_metadata(self):
# update container metadata using add metadata
container_name = self._create_container()
@@ -370,7 +369,7 @@
resp, _ = self.container_client.update_container_metadata(
container_name,
metadata=metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'POST')
resp, _ = self.container_client.list_container_metadata(
@@ -379,7 +378,7 @@
self.assertEqual(resp['x-container-meta-test-container-meta1'],
metadata['test-container-meta1'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_container_metadata_with_delete_metadata(self):
# update container metadata using delete metadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -391,14 +390,14 @@
resp, _ = self.container_client.delete_container_metadata(
container_name,
metadata=metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'POST')
resp, _ = self.container_client.list_container_metadata(
container_name)
self.assertNotIn('x-container-meta-test-container-meta1', resp)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_container_metadata_with_create_matadata_key(self):
# update container metadata with a blenk value of metadata
container_name = self._create_container()
@@ -407,14 +406,14 @@
resp, _ = self.container_client.update_container_metadata(
container_name,
metadata=metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'POST')
resp, _ = self.container_client.list_container_metadata(
container_name)
self.assertNotIn('x-container-meta-test-container-meta1', resp)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_update_container_metadata_with_delete_metadata_key(self):
# update container metadata with a blank value of matadata
container_name = data_utils.rand_name(name='TestContainer')
@@ -427,7 +426,7 @@
resp, _ = self.container_client.delete_container_metadata(
container_name,
metadata=metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'POST')
resp, _ = self.container_client.list_container_metadata(container_name)
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 9f9abd8..197e7fa 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -56,6 +56,12 @@
self.container_client.update_container_metadata(
self.container_name, metadata=headers)
+ # Maintain original headers, no auth added
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
+
# test GET on http://account_url/container_name
# we should retrieve the self.object_name file
resp, body = self.custom_account_client.request("GET",
@@ -112,6 +118,12 @@
self.container_client.update_container_metadata(
self.container_name, metadata=headers)
+ # Maintain original headers, no auth added
+ self.custom_account_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
+
# test GET on http://account_url/container_name
# we should retrieve a listing of objects
resp, body = self.custom_account_client.request("GET",
@@ -136,6 +148,12 @@
object_name_404,
object_data_404)
+ # Do not set auth in HTTP headers for next request
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
+
# Request non-existing object
resp, body = self.custom_object_client.get_object(self.container_name,
"notexisting")
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 207fced..9bd986f 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -19,8 +19,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
from tempest import config
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
CONF = config.CONF
@@ -66,7 +65,7 @@
cls.delete_containers(cls.containers, client[0], client[1])
super(ContainerSyncTest, cls).tearDownClass()
- @attr(type='slow')
+ @test.attr(type='slow')
def test_container_synchronization(self):
# container to container synchronization
# to allow/accept sync requests to/from other accounts
@@ -86,12 +85,12 @@
(client_base_url, str(cont[1]))}
resp, body = \
cont_client[0].put(str(cont[0]), body=None, headers=headers)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
# create object in container
object_name = data_utils.rand_name(name='TestSyncObject')
data = object_name[::-1] # data_utils.arbitrary_string()
resp, _ = obj_client[0].create_object(cont[0], object_name, data)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.objects.append(object_name)
# wait until container contents list is not empty
@@ -104,7 +103,7 @@
cont_client[client_index].\
list_container_contents(self.containers[client_index],
params=params)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
object_lists.append(dict(
(obj['name'], obj) for obj in object_list))
# check that containers are not empty and have equal keys()
@@ -124,5 +123,5 @@
for obj_client, cont in obj_clients:
for obj_name in object_lists[0]:
resp, object_content = obj_client.get_object(cont, obj_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertEqual(object_content, obj_name[::-1])
diff --git a/tempest/api/object_storage/test_crossdomain.py b/tempest/api/object_storage/test_crossdomain.py
index 71e123c..4f399b4 100644
--- a/tempest/api/object_storage/test_crossdomain.py
+++ b/tempest/api/object_storage/test_crossdomain.py
@@ -50,13 +50,12 @@
super(CrossdomainTest, self).setUp()
client = self.os_test_user.account_client
- client._set_auth()
# Turning http://.../v1/foobar into http://.../
- client.base_url = "/".join(client.base_url.split("/")[:-2])
+ client.skip_path()
def tearDown(self):
# clear the base_url for subsequent requests
- self.os_test_user.account_client.base_url = None
+ self.os_test_user.account_client.reset_path()
super(CrossdomainTest, self).tearDown()
diff --git a/tempest/api/object_storage/test_healthcheck.py b/tempest/api/object_storage/test_healthcheck.py
index 78bff03..e27c7ef 100644
--- a/tempest/api/object_storage/test_healthcheck.py
+++ b/tempest/api/object_storage/test_healthcheck.py
@@ -17,8 +17,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class HealthcheckTest(base.BaseObjectTest):
@@ -29,18 +28,16 @@
def setUp(self):
super(HealthcheckTest, self).setUp()
- self.account_client._set_auth()
# Turning http://.../v1/foobar into http://.../
- self.account_client.base_url = "/".join(
- self.account_client.base_url.split("/")[:-2])
+ self.account_client.skip_path()
- @attr('gate')
+ @test.attr('gate')
def test_get_healthcheck(self):
resp, _ = self.account_client.get("healthcheck", {})
# The status is expected to be 200
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
# The target of the request is not any Swift resource. Therefore, the
# existence of response header is checked without a custom matcher.
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 3aae0a1..53ca20d 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -18,7 +18,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
from tempest import exceptions
-from tempest.test import attr
+from tempest import test
class ObjectExpiryTest(base.BaseObjectTest):
@@ -67,12 +67,12 @@
self.assertRaises(exceptions.NotFound, self.object_client.get_object,
self.container_name, self.object_name)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_object_after_expiry_time(self):
metadata = {'X-Delete-After': '3'}
self._test_object_expiry(metadata)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_object_at_expiry_time(self):
metadata = {'X-Delete-At': str(int(time.time()) + 3)}
self._test_object_expiry(metadata)
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index 6f46ec9..98e36b5 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -21,8 +21,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class ObjectFormPostTest(base.BaseObjectTest):
@@ -93,7 +92,7 @@
content_type = 'multipart/form-data; boundary=%s' % boundary
return body, content_type
- @attr(type='gate')
+ @test.attr(type='gate')
def test_post_object_using_form(self):
body, content_type = self.get_multipart_form()
@@ -107,12 +106,12 @@
# Use a raw request, otherwise authentication headers are used
resp, body = self.object_client.http_obj.request(url, "POST",
body, headers=headers)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, "Object", "POST")
# Ensure object is available
resp, body = self.object_client.get("%s/%s%s" % (
self.container_name, self.object_name, "testfile"))
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, "Object", "GET")
self.assertEqual(body, "hello world")
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index bca9b93..33f3299 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -18,8 +18,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
-from tempest.test import attr
-from tempest.test import HTTP_SUCCESS
+from tempest import test
class ObjectTest(base.BaseObjectTest):
@@ -35,7 +34,7 @@
cls.delete_containers(cls.containers)
super(ObjectTest, cls).tearDownClass()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_create_object(self):
# create object
object_name = data_utils.rand_name(name='TestObject')
@@ -50,7 +49,7 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_delete_object(self):
# create object
object_name = data_utils.rand_name(name='TestObject')
@@ -60,10 +59,10 @@
# delete object
resp, _ = self.object_client.delete_object(self.container_name,
object_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'DELETE')
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_object_metadata(self):
# add metadata to storage object, test if metadata is retrievable
@@ -78,20 +77,20 @@
orig_metadata = {meta_key: meta_value}
resp, _ = self.object_client.update_object_metadata(
self.container_name, object_name, orig_metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'POST')
# get object metadata
resp, resp_metadata = self.object_client.list_object_metadata(
self.container_name, object_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'HEAD')
actual_meta_key = 'x-object-meta-' + meta_key
self.assertIn(actual_meta_key, resp)
self.assertEqual(resp[actual_meta_key], meta_value)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_get_object(self):
# retrieve object's data (in response body)
@@ -103,12 +102,12 @@
# get object
resp, body = self.object_client.get_object(self.container_name,
object_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'GET')
self.assertEqual(body, data)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_copy_object_in_same_container(self):
# create source object
src_object_name = data_utils.rand_name(name='SrcObject')
@@ -135,7 +134,7 @@
dst_object_name)
self.assertEqual(body, src_data)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_copy_object_to_itself(self):
# change the content type of an existing object
@@ -160,7 +159,7 @@
object_name)
self.assertEqual(resp['content-type'], metadata['content-type'])
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_copy_object_2d_way(self):
# create source object
src_object_name = data_utils.rand_name(name='SrcObject')
@@ -195,7 +194,7 @@
dst_object_name)
self.assertEqual(body, src_data)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_copy_object_across_containers(self):
# create a container to use as asource container
src_container_name = data_utils.rand_name(name='TestSourceContainer')
@@ -219,7 +218,7 @@
resp, _ = self.object_client.update_object_metadata(src_container_name,
object_name,
orig_metadata)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'POST')
# copy object from source container to destination container
@@ -237,7 +236,7 @@
self.assertIn(actual_meta_key, resp)
self.assertEqual(resp[actual_meta_key], meta_value)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_object_upload_in_segments(self):
# create object
object_name = data_utils.rand_name(name='LObject')
@@ -280,7 +279,7 @@
self.container_name, object_name)
self.assertEqual(''.join(data_segments), body)
- @attr(type='gate')
+ @test.attr(type='gate')
def test_get_object_if_different(self):
# http://en.wikipedia.org/wiki/HTTP_ETag
# Make a conditional request for an object using the If-None-Match
@@ -312,7 +311,7 @@
md5 = hashlib.md5(local_data).hexdigest()
headers = {'If-None-Match': md5}
resp, body = self.object_client.get(url, headers=headers)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Object', 'GET')
@@ -326,7 +325,7 @@
self.delete_containers([self.container_name])
super(PublicObjectTest, self).tearDown()
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_access_public_container_object_without_using_creds(self):
# make container public-readable and access an object in it object
# anonymously, without using credentials
@@ -335,7 +334,7 @@
cont_headers = {'X-Container-Read': '.r:*,.rlistings'}
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers, metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# create object
@@ -350,20 +349,24 @@
# list container metadata
resp_meta, _ = self.container_client.list_container_metadata(
self.container_name)
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'HEAD')
self.assertIn('x-container-read', resp_meta)
self.assertEqual(resp_meta['x-container-read'], '.r:*,.rlistings')
# trying to get object with empty headers as it is public readable
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=None
+ )
resp, body = self.custom_object_client.get_object(
- self.container_name, object_name, metadata={})
+ self.container_name, object_name)
self.assertHeaders(resp, 'Object', 'GET')
self.assertEqual(body, data)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_access_public_object_with_another_user_creds(self):
# make container public-readable and access an object in it using
# another user's credentials
@@ -371,7 +374,7 @@
resp_meta, body = self.container_client.update_container_metadata(
self.container_name, metadata=cont_headers,
metadata_prefix='')
- self.assertIn(int(resp_meta['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp_meta['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp_meta, 'Container', 'POST')
# create object
@@ -386,19 +389,21 @@
# list container metadata
resp, _ = self.container_client.list_container_metadata(
self.container_name)
- self.assertIn(int(resp['status']), HTTP_SUCCESS)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Container', 'HEAD')
self.assertIn('x-container-read', resp)
self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
# get auth token of alternative user
- token = self.identity_client_alt.get_auth()
- headers = {'X-Auth-Token': token}
+ alt_auth_data = self.identity_client_alt.auth_provider.auth_data
+ self.custom_object_client.auth_provider.set_alt_auth_data(
+ request_part='headers',
+ auth_data=alt_auth_data
+ )
# access object using alternate user creds
resp, body = self.custom_object_client.get_object(
- self.container_name, object_name,
- metadata=headers)
+ self.container_name, object_name)
self.assertHeaders(resp, 'Object', 'GET')
self.assertEqual(body, data)
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 75293d2..8d2ff9b 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -15,7 +15,7 @@
from tempest.api.object_storage import base
from tempest.common.utils import data_utils
-from tempest.test import attr
+from tempest import test
class ContainerTest(base.BaseObjectTest):
@@ -40,7 +40,7 @@
header_value = resp.get('x-versions-location', 'Missing Header')
self.assertEqual(header_value, versioned)
- @attr(type='smoke')
+ @test.attr(type='smoke')
def test_versioned_container(self):
# create container
vers_container_name = data_utils.rand_name(name='TestVersionContainer')
diff --git a/tempest/api/telemetry/base.py b/tempest/api/telemetry/base.py
index 1f661a6..c4614c6 100644
--- a/tempest/api/telemetry/base.py
+++ b/tempest/api/telemetry/base.py
@@ -10,7 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.common.utils import data_utils
from tempest import config
+from tempest import exceptions
import tempest.test
CONF = config.CONF
@@ -22,6 +24,28 @@
@classmethod
def setUpClass(cls):
- super(BaseTelemetryTest, cls).setUpClass()
if not CONF.service_available.ceilometer:
raise cls.skipException("Ceilometer support is required")
+ super(BaseTelemetryTest, cls).setUpClass()
+ os = cls.get_client_manager()
+ cls.telemetry_client = os.telemetry_client
+ cls.alarm_ids = []
+
+ @classmethod
+ def create_alarm(cls, **kwargs):
+ resp, body = cls.telemetry_client.create_alarm(
+ name=data_utils.rand_name('telemetry_alarm'),
+ type='threshold', **kwargs)
+ if resp['status'] == '201':
+ cls.alarm_ids.append(body['alarm_id'])
+ return resp, body
+
+ @classmethod
+ def tearDownClass(cls):
+ for alarm_id in cls.alarm_ids:
+ try:
+ cls.telemetry_client.delete_alarm(alarm_id)
+ except exceptions.NotFound:
+ pass
+ cls.clear_isolated_creds()
+ super(BaseTelemetryTest, cls).tearDownClass()
diff --git a/tempest/api/telemetry/test_telemetry_alarming_api.py b/tempest/api/telemetry/test_telemetry_alarming_api.py
new file mode 100644
index 0000000..907d3d0
--- /dev/null
+++ b/tempest/api/telemetry/test_telemetry_alarming_api.py
@@ -0,0 +1,43 @@
+# 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.telemetry import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class TelemetryAlarmingAPITestJSON(base.BaseTelemetryTest):
+ _interface = 'json'
+
+ @attr(type="gate")
+ def test_alarm_list(self):
+ resp, _ = self.telemetry_client.list_alarms()
+ self.assertEqual(int(resp['status']), 200)
+
+ @attr(type="gate")
+ def test_create_alarm(self):
+ rules = {'meter_name': 'cpu_util',
+ 'comparison_operator': 'gt',
+ 'threshold': 80.0,
+ 'period': 70}
+ resp, body = self.create_alarm(threshold_rule=rules)
+ self.alarm_id = body['alarm_id']
+ self.assertEqual(int(resp['status']), 201)
+ self.assertDictContainsSubset(rules, body['threshold_rule'])
+ resp, body = self.telemetry_client.get_alarm(self.alarm_id)
+ self.assertEqual(int(resp['status']), 200)
+ self.assertDictContainsSubset(rules, body['threshold_rule'])
+ resp, _ = self.telemetry_client.delete_alarm(self.alarm_id)
+ self.assertEqual(int(resp['status']), 204)
+ self.assertRaises(exceptions.NotFound,
+ self.telemetry_client.get_alarm,
+ self.alarm_id)
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index cb1a6cb..b0c878b 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -14,8 +14,6 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
-from tempest.services.volume.json.admin import volume_types_client
-from tempest.services.volume.json import volumes_client
from tempest.test import attr
CONF = config.CONF
@@ -36,20 +34,7 @@
cls.backend1_name = CONF.volume.backend1_name
cls.backend2_name = CONF.volume.backend2_name
- adm_user = CONF.identity.admin_username
- adm_pass = CONF.identity.admin_password
- adm_tenant = CONF.identity.admin_tenant_name
- auth_url = CONF.identity.uri
-
- cls.volume_client = volumes_client.VolumesClientJSON(adm_user,
- adm_pass,
- auth_url,
- adm_tenant)
- cls.type_client = volume_types_client.VolumeTypesClientJSON(adm_user,
- adm_pass,
- auth_url,
- adm_tenant)
-
+ cls.volume_client = cls.os_adm.volumes_client
cls.volume_type_id_list = []
cls.volume_id_list = []
try:
@@ -57,7 +42,7 @@
type1_name = data_utils.rand_name('Type-')
vol1_name = data_utils.rand_name('Volume-')
extra_specs1 = {"volume_backend_name": cls.backend1_name}
- resp, cls.type1 = cls.type_client.create_volume_type(
+ resp, cls.type1 = cls.client.create_volume_type(
type1_name, extra_specs=extra_specs1)
cls.volume_type_id_list.append(cls.type1['id'])
@@ -72,7 +57,7 @@
type2_name = data_utils.rand_name('Type-')
vol2_name = data_utils.rand_name('Volume-')
extra_specs2 = {"volume_backend_name": cls.backend2_name}
- resp, cls.type2 = cls.type_client.create_volume_type(
+ resp, cls.type2 = cls.client.create_volume_type(
type2_name, extra_specs=extra_specs2)
cls.volume_type_id_list.append(cls.type2['id'])
@@ -97,7 +82,7 @@
# volume types deletion
volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
for volume_type_id in volume_type_id_list:
- cls.type_client.delete_volume_type(volume_type_id)
+ cls.client.delete_volume_type(volume_type_id)
super(VolumeMultiBackendTest, cls).tearDownClass()
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index de2b240..6b6f638 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -69,18 +69,6 @@
# only in a single location in the source, and could be more general.
@classmethod
- def create_volume(cls, size=1, **kwargs):
- """Wrapper utility that returns a test volume."""
- vol_name = data_utils.rand_name('Volume')
- resp, volume = cls.volumes_client.create_volume(size,
- display_name=vol_name,
- **kwargs)
- assert 200 == resp.status
- cls.volumes.append(volume)
- cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
- return volume
-
- @classmethod
def clear_volumes(cls):
for volume in cls.volumes:
try:
@@ -120,6 +108,18 @@
cls.volumes_client = cls.os.volumes_client
cls.volumes_extension_client = cls.os.volumes_extension_client
+ @classmethod
+ def create_volume(cls, size=1, **kwargs):
+ """Wrapper utility that returns a test volume."""
+ vol_name = data_utils.rand_name('Volume')
+ resp, volume = cls.volumes_client.create_volume(size,
+ display_name=vol_name,
+ **kwargs)
+ assert 200 == resp.status
+ cls.volumes.append(volume)
+ cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ return volume
+
class BaseVolumeV1AdminTest(BaseVolumeV1Test):
"""Base test case class for all Volume Admin API tests."""
@@ -144,3 +144,25 @@
cls.os_adm = clients.AdminManager(interface=cls._interface)
cls.client = cls.os_adm.volume_types_client
cls.hosts_client = cls.os_adm.volume_hosts_client
+
+
+class BaseVolumeV2Test(BaseVolumeTest):
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.volume_feature_enabled.api_v2:
+ msg = "Volume API v2 not supported"
+ raise cls.skipException(msg)
+ super(BaseVolumeV2Test, cls).setUpClass()
+ cls.volumes_client = cls.os.volumes_v2_client
+
+ @classmethod
+ def create_volume(cls, size=1, **kwargs):
+ """Wrapper utility that returns a test volume."""
+ vol_name = data_utils.rand_name('Volume')
+ resp, volume = cls.volumes_client.create_volume(size,
+ name=vol_name,
+ **kwargs)
+ assert 202 == resp.status
+ cls.volumes.append(volume)
+ cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ return volume
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index ec732d1..7d2216d 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -43,7 +43,8 @@
# Create metadata for the volume
metadata = {"key1": "value1",
"key2": "value2",
- "key3": "value3"}
+ "key3": "value3",
+ "key4": "<value&special_chars>"}
rsp, body = self.volumes_client.create_volume_metadata(self.volume_id,
metadata)
@@ -59,6 +60,8 @@
self.assertEqual(200, rsp.status)
resp, body = self.volumes_client.get_volume_metadata(self.volume_id)
self.assertNotIn("key1", body)
+ del metadata["key1"]
+ self.assertThat(body.items(), ContainsAll(metadata.items()))
@test.attr(type='gate')
def test_update_volume_metadata(self):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index fc4f07d..f4b2d4c 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from testtools import matchers
+
from tempest.api.volume import base
from tempest import clients
from tempest import config
@@ -47,7 +49,7 @@
interface=cls._interface)
else:
cls.os_alt = clients.AltManager()
- alt_tenant_name = cls.os_alt.tenant_name
+ alt_tenant_name = cls.os_alt.credentials['tenant_name']
identity_client = cls._get_identity_admin_client()
_, tenants = identity_client.list_tenants()
cls.alt_tenant_id = [tnt['id'] for tnt in tenants
@@ -87,7 +89,7 @@
# or equal to 1
resp, body = self.client.list_volume_transfers()
self.assertEqual(200, resp.status)
- self.assertGreaterEqual(len(body), 1)
+ self.assertThat(len(body), matchers.GreaterThan(0))
# Accept a volume transfer by alt_tenant
resp, body = self.alt_client.accept_volume_transfer(transfer_id,
@@ -107,10 +109,14 @@
self.client.wait_for_volume_status(volume['id'],
'awaiting-transfer')
- # List all volume transfers, there's only one in this test
+ # List all volume transfers (looking for the one we created)
resp, body = self.client.list_volume_transfers()
self.assertEqual(200, resp.status)
- self.assertEqual(volume['id'], body[0]['volume_id'])
+ for transfer in body:
+ if volume['id'] == transfer['volume_id']:
+ break
+ else:
+ self.fail('Transfer not found for volume %s' % volume['id'])
# Delete a volume transfer
resp, body = self.client.delete_volume_transfer(transfer_id)
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 6d89e5b..ce29b82 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -91,6 +91,12 @@
self.assertEqual(boot_flag, False)
# Update Volume
+ # Test volume update when display_name is same with original value
+ resp, update_volume = \
+ self.client.update_volume(volume['id'],
+ display_name=v_name)
+ self.assertEqual(200, resp.status)
+ # Test volume update when display_name is new
new_v_name = data_utils.rand_name('new-Volume')
new_desc = 'This is the new description of volume'
resp, update_volume = \
@@ -111,6 +117,26 @@
ContainsAll(metadata.items()),
'The fetched Volume metadata misses data '
'from the created Volume')
+ # Test volume create when display_name is none and display_description
+ # contains specific characters,
+ # then test volume update if display_name is duplicated
+ new_volume = {}
+ new_v_desc = data_utils.rand_name('@#$%^* description')
+ resp, new_volume = \
+ self.client.create_volume(size=1,
+ display_description=new_v_desc,
+ availability_zone=volume[
+ 'availability_zone'])
+ self.assertEqual(200, resp.status)
+ self.assertIn('id', new_volume)
+ self.addCleanup(self._delete_volume, new_volume['id'])
+ self.client.wait_for_volume_status(new_volume['id'], 'available')
+ resp, update_volume = \
+ self.client.update_volume(new_volume['id'],
+ display_name=volume['display_name'],
+ display_description=volume[
+ 'display_description'])
+ self.assertEqual(200, resp.status)
# NOTE(jdg): Revert back to strict true/false checking
# after fix for bug #1227837 merges
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 487ada6..56915e6 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,10 +12,12 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest.openstack.common import log as logging
from tempest.test import attr
LOG = logging.getLogger(__name__)
+CONF = config.CONF
class VolumesSnapshotTest(base.BaseVolumeV1Test):
@@ -35,6 +37,11 @@
def tearDownClass(cls):
super(VolumesSnapshotTest, cls).tearDownClass()
+ def _detach(self, volume_id):
+ """Detach volume."""
+ self.volumes_client.detach_volume(volume_id)
+ self.volumes_client.wait_for_volume_status(volume_id, 'available')
+
def _list_by_param_values_and_assert(self, params, with_detail=False):
"""
Perform list or list_details action with given params
@@ -57,6 +64,32 @@
self.assertEqual(params[key], snap[key], msg)
@attr(type='gate')
+ def test_snapshot_create_with_volume_in_use(self):
+ # Create a snapshot when volume status is in-use
+ # Create a test instance
+ server_name = data_utils.rand_name('instance-')
+ resp, server = self.servers_client.create_server(server_name,
+ self.image_ref,
+ self.flavor_ref)
+ self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+ mountpoint = '/dev/%s' % CONF.compute.volume_device_name
+ resp, body = self.volumes_client.attach_volume(
+ self.volume_origin['id'], server['id'], mountpoint)
+ self.assertEqual(202, resp.status)
+ self.volumes_client.wait_for_volume_status(self.volume_origin['id'],
+ 'in-use')
+ self.addCleanup(self._detach, self.volume_origin['id'])
+ # Snapshot a volume even if it's attached to an instance
+ snapshot = self.create_snapshot(self.volume_origin['id'],
+ force=True)
+ # Delete the snapshot
+ self.snapshots_client.delete_snapshot(snapshot['id'])
+ self.assertEqual(202, resp.status)
+ self.snapshots_client.wait_for_resource_deletion(snapshot['id'])
+ self.snapshots.remove(snapshot)
+
+ @attr(type='gate')
def test_snapshot_create_get_list_update_delete(self):
# Create a snapshot
s_name = data_utils.rand_name('snap')
diff --git a/tempest/api/volume/v2/__init__.py b/tempest/api/volume/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api/volume/v2/__init__.py
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
new file mode 100644
index 0000000..d0e8b99
--- /dev/null
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -0,0 +1,214 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright 2013 IBM Corp.
+# 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 operator
+
+from tempest.api.volume import base
+from tempest.common.utils import data_utils
+from tempest.openstack.common import log as logging
+from tempest.test import attr
+from testtools.matchers import ContainsAll
+
+LOG = logging.getLogger(__name__)
+
+VOLUME_FIELDS = ('id', 'name')
+
+
+class VolumesV2ListTestJSON(base.BaseVolumeV2Test):
+
+ """
+ This test creates a number of 1G volumes. To run successfully,
+ ensure that the backing file for the volume group that Nova uses
+ has space for at least 3 1G volumes!
+ If you are running a Devstack environment, ensure that the
+ VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
+ """
+
+ _interface = 'json'
+
+ def assertVolumesIn(self, fetched_list, expected_list, fields=None):
+ if fields:
+ expected_list = map(operator.itemgetter(*fields), expected_list)
+ fetched_list = map(operator.itemgetter(*fields), fetched_list)
+
+ missing_vols = [v for v in expected_list if v not in fetched_list]
+ if len(missing_vols) == 0:
+ return
+
+ def str_vol(vol):
+ return "%s:%s" % (vol['id'], vol['name'])
+
+ raw_msg = "Could not find volumes %s in expected list %s; fetched %s"
+ self.fail(raw_msg % ([str_vol(v) for v in missing_vols],
+ [str_vol(v) for v in expected_list],
+ [str_vol(v) for v in fetched_list]))
+
+ @classmethod
+ def setUpClass(cls):
+ super(VolumesV2ListTestJSON, cls).setUpClass()
+ cls.client = cls.volumes_client
+
+ # Create 3 test volumes
+ cls.volume_list = []
+ cls.volume_id_list = []
+ cls.metadata = {'Type': 'work'}
+ for i in range(3):
+ try:
+ volume = cls.create_volume(metadata=cls.metadata)
+ resp, volume = cls.client.get_volume(volume['id'])
+ cls.volume_list.append(volume)
+ cls.volume_id_list.append(volume['id'])
+ except Exception:
+ LOG.exception('Failed to create volume. %d volumes were '
+ 'created' % len(cls.volume_id_list))
+ if cls.volume_list:
+ # We could not create all the volumes, though we were able
+ # to create *some* of the volumes. This is typically
+ # because the backing file size of the volume group is
+ # too small.
+ for volid in cls.volume_id_list:
+ cls.client.delete_volume(volid)
+ cls.client.wait_for_resource_deletion(volid)
+ raise
+
+ @classmethod
+ def tearDownClass(cls):
+ # Delete the created volumes
+ for volid in cls.volume_id_list:
+ resp, _ = cls.client.delete_volume(volid)
+ cls.client.wait_for_resource_deletion(volid)
+ super(VolumesV2ListTestJSON, cls).tearDownClass()
+
+ def _list_by_param_value_and_assert(self, params, expected_list=None,
+ with_detail=False):
+ """
+ Perform list or list_details action with given params
+ and validates result.
+ """
+ if with_detail:
+ resp, fetched_vol_list = \
+ self.client.list_volumes_with_detail(params=params)
+ else:
+ resp, fetched_vol_list = self.client.list_volumes(params=params)
+
+ self.assertEqual(200, resp.status)
+ if expected_list is None:
+ expected_list = self.volume_list
+ self.assertVolumesIn(fetched_vol_list, expected_list,
+ fields=VOLUME_FIELDS)
+ # Validating params of fetched volumes
+ if with_detail:
+ for volume in fetched_vol_list:
+ for key in params:
+ msg = "Failed to list volumes %s by %s" % \
+ ('details' if with_detail else '', key)
+ if key == 'metadata':
+ self.assertThat(volume[key].items(),
+ ContainsAll(params[key].items()),
+ msg)
+ else:
+ self.assertEqual(params[key], volume[key], msg)
+
+ @attr(type='smoke')
+ def test_volume_list(self):
+ # Get a list of Volumes
+ # Fetch all volumes
+ resp, fetched_list = self.client.list_volumes()
+ self.assertEqual(200, resp.status)
+ self.assertVolumesIn(fetched_list, self.volume_list,
+ fields=VOLUME_FIELDS)
+
+ @attr(type='gate')
+ def test_volume_list_with_details(self):
+ # Get a list of Volumes with details
+ # Fetch all Volumes
+ resp, fetched_list = self.client.list_volumes_with_detail()
+ self.assertEqual(200, resp.status)
+ self.assertVolumesIn(fetched_list, self.volume_list)
+
+ @attr(type='gate')
+ def test_volume_list_by_name(self):
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ params = {'name': volume['name']}
+ resp, fetched_vol = self.client.list_volumes(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(1, len(fetched_vol), str(fetched_vol))
+ self.assertEqual(fetched_vol[0]['name'], volume['name'])
+
+ @attr(type='gate')
+ def test_volume_list_details_by_name(self):
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ params = {'name': volume['name']}
+ resp, fetched_vol = self.client.list_volumes_with_detail(params)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(1, len(fetched_vol), str(fetched_vol))
+ self.assertEqual(fetched_vol[0]['name'], volume['name'])
+
+ @attr(type='gate')
+ def test_volumes_list_by_status(self):
+ params = {'status': 'available'}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_volumes_list_details_by_status(self):
+ params = {'status': 'available'}
+ self._list_by_param_value_and_assert(params, with_detail=True)
+
+ @attr(type='gate')
+ def test_volumes_list_by_availability_zone(self):
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ zone = volume['availability_zone']
+ params = {'availability_zone': zone}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_volumes_list_details_by_availability_zone(self):
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ zone = volume['availability_zone']
+ params = {'availability_zone': zone}
+ self._list_by_param_value_and_assert(params, with_detail=True)
+
+ @attr(type='gate')
+ def test_volume_list_with_param_metadata(self):
+ # Test to list volumes when metadata param is given
+ params = {'metadata': self.metadata}
+ self._list_by_param_value_and_assert(params)
+
+ @attr(type='gate')
+ def test_volume_list_with_detail_param_metadata(self):
+ # Test to list volumes details when metadata param is given
+ params = {'metadata': self.metadata}
+ self._list_by_param_value_and_assert(params, with_detail=True)
+
+ @attr(type='gate')
+ def test_volume_list_param_display_name_and_status(self):
+ # Test to list volume when display name and status param is given
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ params = {'name': volume['name'],
+ 'status': 'available'}
+ self._list_by_param_value_and_assert(params, expected_list=[volume])
+
+ @attr(type='gate')
+ def test_volume_list_with_detail_param_display_name_and_status(self):
+ # Test to list volume when name and status param is given
+ volume = self.volume_list[data_utils.rand_int_id(0, 2)]
+ params = {'name': volume['name'],
+ 'status': 'available'}
+ self._list_by_param_value_and_assert(params, expected_list=[volume],
+ with_detail=True)
+
+
+class VolumesV2ListTestXML(VolumesV2ListTestJSON):
+ _interface = 'xml'
diff --git a/tempest/auth.py b/tempest/auth.py
new file mode 100644
index 0000000..582cfdd
--- /dev/null
+++ b/tempest/auth.py
@@ -0,0 +1,396 @@
+# Copyright 2014 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.
+
+import copy
+import exceptions
+import re
+import urlparse
+
+from datetime import datetime
+from tempest import config
+from tempest.services.identity.json import identity_client as json_id
+from tempest.services.identity.v3.json import identity_client as json_v3id
+from tempest.services.identity.v3.xml import identity_client as xml_v3id
+from tempest.services.identity.xml import identity_client as xml_id
+
+from tempest.openstack.common import log as logging
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class AuthProvider(object):
+ """
+ Provide authentication
+ """
+
+ def __init__(self, credentials, client_type='tempest',
+ interface=None):
+ """
+ :param credentials: credentials for authentication
+ :param client_type: 'tempest' or 'official'
+ :param interface: 'json' or 'xml'. Applicable for tempest client only
+ """
+ if self.check_credentials(credentials):
+ self.credentials = credentials
+ else:
+ raise TypeError("Invalid credentials")
+ self.credentials = credentials
+ self.client_type = client_type
+ self.interface = interface
+ if self.client_type == 'tempest' and self.interface is None:
+ self.interface = 'json'
+ self.cache = None
+ self.alt_auth_data = None
+ self.alt_part = None
+
+ def __str__(self):
+ return "Creds :{creds}, client type: {client_type}, interface: " \
+ "{interface}, cached auth data: {cache}".format(
+ creds=self.credentials, client_type=self.client_type,
+ interface=self.interface, cache=self.cache
+ )
+
+ def _decorate_request(self, filters, method, url, headers=None, body=None,
+ auth_data=None):
+ """
+ Decorate request with authentication data
+ """
+ raise NotImplementedError
+
+ def _get_auth(self):
+ raise NotImplementedError
+
+ @classmethod
+ def check_credentials(cls, credentials):
+ """
+ Verify credentials are valid. Subclasses can do a better check.
+ """
+ return isinstance(credentials, dict)
+
+ @property
+ def auth_data(self):
+ if self.cache is None or self.is_expired(self.cache):
+ self.cache = self._get_auth()
+ return self.cache
+
+ @auth_data.deleter
+ def auth_data(self):
+ self.clear_auth()
+
+ def clear_auth(self):
+ """
+ Can be called to clear the access cache so that next request
+ will fetch a new token and base_url.
+ """
+ self.cache = None
+
+ def is_expired(self, auth_data):
+ raise NotImplementedError
+
+ def auth_request(self, method, url, headers=None, body=None, filters=None):
+ """
+ Obtains auth data and decorates a request with that.
+ :param method: HTTP method of the request
+ :param url: relative URL of the request (path)
+ :param headers: HTTP headers of the request
+ :param body: HTTP body in case of POST / PUT
+ :param filters: select a base URL out of the catalog
+ :returns a Tuple (url, headers, body)
+ """
+ orig_req = dict(url=url, headers=headers, body=body)
+
+ auth_url, auth_headers, auth_body = self._decorate_request(
+ filters, method, url, headers, body)
+ auth_req = dict(url=auth_url, headers=auth_headers, body=auth_body)
+
+ # Overwrite part if the request if it has been requested
+ if self.alt_part is not None:
+ if self.alt_auth_data is not None:
+ alt_url, alt_headers, alt_body = self._decorate_request(
+ filters, method, url, headers, body,
+ auth_data=self.alt_auth_data)
+ alt_auth_req = dict(url=alt_url, headers=alt_headers,
+ body=alt_body)
+ auth_req[self.alt_part] = alt_auth_req[self.alt_part]
+
+ else:
+ # If alt auth data is None, skip auth in the requested part
+ auth_req[self.alt_part] = orig_req[self.alt_part]
+
+ # Next auth request will be normal, unless otherwise requested
+ self.reset_alt_auth_data()
+
+ return auth_req['url'], auth_req['headers'], auth_req['body']
+
+ def reset_alt_auth_data(self):
+ """
+ Configure auth provider to provide valid authentication data
+ """
+ self.alt_part = None
+ self.alt_auth_data = None
+
+ def set_alt_auth_data(self, request_part, auth_data):
+ """
+ Configure auth provider to provide alt authentication data
+ on a part of the *next* auth_request. If credentials are None,
+ set invalid data.
+ :param request_part: request part to contain invalid auth: url,
+ headers, body
+ :param auth_data: alternative auth_data from which to get the
+ invalid data to be injected
+ """
+ self.alt_part = request_part
+ self.alt_auth_data = auth_data
+
+ def base_url(self, filters, auth_data=None):
+ """
+ Extracts the base_url based on provided filters
+ """
+ raise NotImplementedError
+
+
+class KeystoneAuthProvider(AuthProvider):
+
+ def __init__(self, credentials, client_type='tempest', interface=None):
+ super(KeystoneAuthProvider, self).__init__(credentials, client_type,
+ interface)
+ self.auth_client = self._auth_client()
+
+ def _decorate_request(self, filters, method, url, headers=None, body=None,
+ auth_data=None):
+ if auth_data is None:
+ auth_data = self.auth_data
+ token, _ = auth_data
+ base_url = self.base_url(filters=filters, auth_data=auth_data)
+ # build authenticated request
+ # returns new request, it does not touch the original values
+ _headers = copy.deepcopy(headers)
+ _headers['X-Auth-Token'] = token
+ if url is None or url == "":
+ _url = base_url
+ else:
+ # Join base URL and url, and remove multiple contiguous slashes
+ _url = "/".join([base_url, url])
+ parts = [x for x in urlparse.urlparse(_url)]
+ parts[2] = re.sub("/{2,}", "/", parts[2])
+ _url = urlparse.urlunparse(parts)
+ # no change to method or body
+ return _url, _headers, body
+
+ def _auth_client(self):
+ raise NotImplementedError
+
+ def _auth_params(self):
+ raise NotImplementedError
+
+ def _get_auth(self):
+ # Bypasses the cache
+ if self.client_type == 'tempest':
+ auth_func = getattr(self.auth_client, 'get_token')
+ auth_params = self._auth_params()
+
+ # returns token, auth_data
+ token, auth_data = auth_func(**auth_params)
+ return token, auth_data
+ else:
+ raise NotImplementedError
+
+ def get_token(self):
+ return self.auth_data[0]
+
+
+class KeystoneV2AuthProvider(KeystoneAuthProvider):
+
+ EXPIRY_DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
+
+ @classmethod
+ def check_credentials(cls, credentials, scoped=True):
+ # tenant_name is optional if not scoped
+ valid = super(KeystoneV2AuthProvider, cls).check_credentials(
+ credentials) and 'username' in credentials and \
+ 'password' in credentials
+ if scoped:
+ valid = valid and 'tenant_name' in credentials
+ return valid
+
+ def _auth_client(self):
+ if self.client_type == 'tempest':
+ if self.interface == 'json':
+ return json_id.TokenClientJSON()
+ else:
+ return xml_id.TokenClientXML()
+ else:
+ raise NotImplementedError
+
+ def _auth_params(self):
+ if self.client_type == 'tempest':
+ return dict(
+ user=self.credentials['username'],
+ password=self.credentials['password'],
+ tenant=self.credentials.get('tenant_name', None),
+ auth_data=True)
+ else:
+ raise NotImplementedError
+
+ def base_url(self, filters, auth_data=None):
+ """
+ Filters can be:
+ - service: compute, image, etc
+ - region: the service region
+ - endpoint_type: adminURL, publicURL, internalURL
+ - api_version: replace catalog version with this
+ - skip_path: take just the base URL
+ """
+ if auth_data is None:
+ auth_data = self.auth_data
+ token, _auth_data = auth_data
+ service = filters.get('service')
+ region = filters.get('region')
+ endpoint_type = filters.get('endpoint_type', 'publicURL')
+
+ if service is None:
+ raise exceptions.EndpointNotFound("No service provided")
+
+ _base_url = None
+ for ep in _auth_data['serviceCatalog']:
+ if ep["type"] == service:
+ for _ep in ep['endpoints']:
+ if region is not None and _ep['region'] == region:
+ _base_url = _ep.get(endpoint_type)
+ if not _base_url:
+ # No region matching, use the first
+ _base_url = ep['endpoints'][0].get(endpoint_type)
+ break
+ if _base_url is None:
+ raise exceptions.EndpointNotFound(service)
+
+ parts = urlparse.urlparse(_base_url)
+ if filters.get('api_version', None) is not None:
+ path = "/" + filters['api_version']
+ noversion_path = "/".join(parts.path.split("/")[2:])
+ if noversion_path != "":
+ path += "/" + noversion_path
+ _base_url = _base_url.replace(parts.path, path)
+ if filters.get('skip_path', None) is not None:
+ _base_url = _base_url.replace(parts.path, "/")
+
+ return _base_url
+
+ def is_expired(self, auth_data):
+ _, access = auth_data
+ expiry = datetime.strptime(access['token']['expires'],
+ self.EXPIRY_DATE_FORMAT)
+ return expiry <= datetime.now()
+
+
+class KeystoneV3AuthProvider(KeystoneAuthProvider):
+
+ EXPIRY_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
+
+ @classmethod
+ def check_credentials(cls, credentials, scoped=True):
+ # tenant_name is optional if not scoped
+ valid = super(KeystoneV3AuthProvider, cls).check_credentials(
+ credentials) and 'username' in credentials and \
+ 'password' in credentials and 'domain_name' in credentials
+ if scoped:
+ valid = valid and 'tenant_name' in credentials
+ return valid
+
+ def _auth_client(self):
+ if self.client_type == 'tempest':
+ if self.interface == 'json':
+ return json_v3id.V3TokenClientJSON()
+ else:
+ return xml_v3id.V3TokenClientXML()
+ else:
+ raise NotImplementedError
+
+ def _auth_params(self):
+ if self.client_type == 'tempest':
+ return dict(
+ user=self.credentials['username'],
+ password=self.credentials['password'],
+ tenant=self.credentials.get('tenant_name', None),
+ domain=self.credentials['domain_name'],
+ auth_data=True)
+ else:
+ raise NotImplementedError
+
+ def base_url(self, filters, auth_data=None):
+ """
+ Filters can be:
+ - service: compute, image, etc
+ - region: the service region
+ - endpoint_type: adminURL, publicURL, internalURL
+ - api_version: replace catalog version with this
+ - skip_path: take just the base URL
+ """
+ if auth_data is None:
+ auth_data = self.auth_data
+ token, _auth_data = auth_data
+ service = filters.get('service')
+ region = filters.get('region')
+ endpoint_type = filters.get('endpoint_type', 'public')
+
+ if service is None:
+ raise exceptions.EndpointNotFound("No service provided")
+
+ if 'URL' in endpoint_type:
+ endpoint_type = endpoint_type.replace('URL', '')
+ _base_url = None
+ catalog = _auth_data['catalog']
+ # Select entries with matching service type
+ service_catalog = [ep for ep in catalog if ep['type'] == service]
+ if len(service_catalog) > 0:
+ service_catalog = service_catalog[0]['endpoints']
+ else:
+ # No matching service
+ raise exceptions.EndpointNotFound(service)
+ # Filter by endpoint type (interface)
+ filtered_catalog = [ep for ep in service_catalog if
+ ep['interface'] == endpoint_type]
+ if len(filtered_catalog) == 0:
+ # No matching type, keep all and try matching by region at least
+ filtered_catalog = service_catalog
+ # Filter by region
+ filtered_catalog = [ep for ep in filtered_catalog if
+ ep['region'] == region]
+ if len(filtered_catalog) == 0:
+ # No matching region, take the first endpoint
+ filtered_catalog = [filtered_catalog[0]]
+ # There should be only one match. If not take the first.
+ _base_url = filtered_catalog[0].get('url', None)
+ if _base_url is None:
+ raise exceptions.EndpointNotFound(service)
+
+ parts = urlparse.urlparse(_base_url)
+ if filters.get('api_version', None) is not None:
+ path = "/" + filters['api_version']
+ noversion_path = "/".join(parts.path.split("/")[2:])
+ if noversion_path != "":
+ path += noversion_path
+ _base_url = _base_url.replace(parts.path, path)
+ if filters.get('skip_path', None) is not None:
+ _base_url = _base_url.replace(parts.path, "/")
+
+ return _base_url
+
+ def is_expired(self, auth_data):
+ _, access = auth_data
+ expiry = datetime.strptime(access['expires_at'],
+ self.EXPIRY_DATE_FORMAT)
+ return expiry <= datetime.now()
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index a5e0447..8c4ec45 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -83,6 +83,11 @@
return self.cmd_with_auth(
'neutron', action, flags, params, admin, fail_ok)
+ def savanna(self, action, flags='', params='', admin=True, fail_ok=False):
+ """Executes savanna command for the given action."""
+ return self.cmd_with_auth(
+ 'savanna', action, flags, params, admin, fail_ok)
+
def cmd_with_auth(self, cmd, action, flags='', params='',
admin=True, fail_ok=False):
"""Executes given command with auth attributes appended."""
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index ebf0dc4..c1d58b5 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -57,32 +57,34 @@
self.assertTableStruct(ext, ['alias', 'name'])
@test.attr(type='smoke')
+ @test.requires_ext(extension='dhcp_agent_scheduler', service='network')
def test_neutron_dhcp_agent_list_hosting_net(self):
self.neutron('dhcp-agent-list-hosting-net',
params=CONF.compute.fixed_network_name)
@test.attr(type='smoke')
+ @test.requires_ext(extension='agent', service='network')
def test_neutron_agent_list(self):
agents = self.parser.listing(self.neutron('agent-list'))
field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up']
self.assertTableStruct(agents, field_names)
@test.attr(type='smoke')
+ @test.requires_ext(extension='router', service='network')
def test_neutron_floatingip_list(self):
self.neutron('floatingip-list')
- @test.skip_because(bug="1240694")
@test.attr(type='smoke')
@test.requires_ext(extension='metering', service='network')
def test_neutron_meter_label_list(self):
self.neutron('meter-label-list')
- @test.skip_because(bug="1240694")
@test.attr(type='smoke')
@test.requires_ext(extension='metering', service='network')
def test_neutron_meter_label_rule_list(self):
self.neutron('meter-label-rule-list')
+ @test.requires_ext(extension='lbaas_agent_scheduler', service='network')
def _test_neutron_lbaas_command(self, command):
try:
self.neutron(command)
@@ -107,6 +109,7 @@
self._test_neutron_lbaas_command('lb-vip-list')
@test.attr(type='smoke')
+ @test.requires_ext(extension='external-net', service='network')
def test_neutron_net_external_list(self):
self.neutron('net-external-list')
@@ -115,19 +118,23 @@
self.neutron('port-list')
@test.attr(type='smoke')
+ @test.requires_ext(extension='quotas', service='network')
def test_neutron_quota_list(self):
self.neutron('quota-list')
@test.attr(type='smoke')
+ @test.requires_ext(extension='router', service='network')
def test_neutron_router_list(self):
self.neutron('router-list')
@test.attr(type='smoke')
+ @test.requires_ext(extension='security-group', service='network')
def test_neutron_security_group_list(self):
security_grp = self.parser.listing(self.neutron('security-group-list'))
self.assertTableStruct(security_grp, ['id', 'name', 'description'])
@test.attr(type='smoke')
+ @test.requires_ext(extension='security-group', service='network')
def test_neutron_security_group_rule_list(self):
self.neutron('security-group-rule-list')
diff --git a/tempest/cli/simple_read_only/test_savanna.py b/tempest/cli/simple_read_only/test_savanna.py
new file mode 100644
index 0000000..1e30978
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_savanna.py
@@ -0,0 +1,70 @@
+# Copyright (c) 2013 Mirantis Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import subprocess
+
+from tempest import cli
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlySavannaClientTest(cli.ClientTestBase):
+ """Basic, read-only tests for Savanna CLI client.
+
+ Checks return values and output of read-only commands.
+ These tests do not presume any content, nor do they create
+ their own. They only verify the structure of output if present.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.service_available.savanna:
+ msg = "Skipping all Savanna cli tests because it is not available"
+ raise cls.skipException(msg)
+ super(SimpleReadOnlySavannaClientTest, cls).setUpClass()
+
+ @test.attr(type='negative')
+ def test_savanna_fake_action(self):
+ self.assertRaises(subprocess.CalledProcessError,
+ self.savanna,
+ 'this-does-not-exist')
+
+ def test_savanna_plugins_list(self):
+ plugins = self.parser.listing(self.savanna('plugin-list'))
+ self.assertTableStruct(plugins, ['name', 'versions', 'title'])
+
+ def test_savanna_plugins_show(self):
+ plugin = self.parser.listing(self.savanna('plugin-show',
+ params='--name vanilla'))
+ self.assertTableStruct(plugin, ['Property', 'Value'])
+
+ def test_savanna_node_group_template_list(self):
+ plugins = self.parser.listing(self.savanna('node-group-template-list'))
+ self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
+ 'node_processes', 'description'])
+
+ def test_savanna_cluster_template_list(self):
+ plugins = self.parser.listing(self.savanna('cluster-template-list'))
+ self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
+ 'node_groups', 'description'])
+
+ def test_savanna_cluster_list(self):
+ plugins = self.parser.listing(self.savanna('cluster-list'))
+ self.assertTableStruct(plugins, ['name', 'id', 'status', 'node_count'])
diff --git a/tempest/clients.py b/tempest/clients.py
index f8361af..d0eb1cc 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest import auth
from tempest.common.rest_client import NegativeRestClient
from tempest import config
from tempest import exceptions
@@ -62,8 +63,6 @@
from tempest.services.compute.v3.json.hosts_client import HostsV3ClientJSON
from tempest.services.compute.v3.json.hypervisor_client import \
HypervisorV3ClientJSON
-from tempest.services.compute.v3.json.instance_usage_audit_log_client import \
- InstanceUsagesAuditLogV3ClientJSON
from tempest.services.compute.v3.json.interfaces_client import \
InterfacesV3ClientJSON
from tempest.services.compute.v3.json.keypairs_client import \
@@ -74,8 +73,6 @@
ServersV3ClientJSON
from tempest.services.compute.v3.json.services_client import \
ServicesV3ClientJSON
-from tempest.services.compute.v3.json.tenant_usages_client import \
- TenantUsagesV3ClientJSON
from tempest.services.compute.v3.json.version_client import \
VersionV3ClientJSON
from tempest.services.compute.xml.aggregates_client import AggregatesClientXML
@@ -155,6 +152,8 @@
ExtensionsClientJSON as VolumeExtensionClientJSON
from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
from tempest.services.volume.json.volumes_client import VolumesClientJSON
+from tempest.services.volume.v2.json.volumes_client import VolumesV2ClientJSON
+from tempest.services.volume.v2.xml.volumes_client import VolumesV2ClientXML
from tempest.services.volume.xml.admin.volume_hosts_client import \
VolumeHostsClientXML
from tempest.services.volume.xml.admin.volume_types_client import \
@@ -185,172 +184,206 @@
:param password: Override of the password
:param tenant_name: Override of the tenant name
"""
- # If no creds are provided, we fall back on the defaults
- # in the config file for the Compute API.
- self.username = username or CONF.identity.username
- self.password = password or CONF.identity.password
- self.tenant_name = tenant_name or CONF.identity.tenant_name
-
- if None in (self.username, self.password, self.tenant_name):
- msg = ("Missing required credentials. "
- "username: %(u)s, password: %(p)s, "
- "tenant_name: %(t)s" %
- {'u': username, 'p': password, 't': tenant_name})
- raise exceptions.InvalidConfiguration(msg)
-
- self.auth_url = CONF.identity.uri
- self.auth_url_v3 = CONF.identity.uri_v3
-
- client_args = (self.username, self.password,
- self.auth_url, self.tenant_name)
-
- if self.auth_url_v3:
- auth_version = 'v3'
- client_args_v3_auth = (self.username,
- self.password, self.auth_url_v3,
- self.tenant_name, auth_version)
+ self.interface = interface
+ self.auth_version = CONF.identity.auth_version
+ # FIXME(andreaf) Change Manager __init__ to accept a credentials dict
+ if username is None or password is None:
+ # Tenant None is a valid use case
+ self.credentials = self.get_default_credentials()
else:
- client_args_v3_auth = None
+ self.credentials = dict(username=username, password=password,
+ tenant_name=tenant_name)
+ if self.auth_version == 'v3':
+ self.credentials['domain_name'] = 'Default'
+ # Setup an auth provider
+ auth_provider = self.get_auth_provider(self.credentials)
- self.servers_client_v3_auth = None
-
- if interface == 'xml':
- self.certificates_client = CertificatesClientXML(*client_args)
- self.servers_client = ServersClientXML(*client_args)
- self.limits_client = LimitsClientXML(*client_args)
- self.images_client = ImagesClientXML(*client_args)
- self.keypairs_client = KeyPairsClientXML(*client_args)
- self.quotas_client = QuotasClientXML(*client_args)
- self.flavors_client = FlavorsClientXML(*client_args)
- self.extensions_client = ExtensionsClientXML(*client_args)
+ if self.interface == 'xml':
+ self.certificates_client = CertificatesClientXML(
+ auth_provider)
+ self.servers_client = ServersClientXML(auth_provider)
+ self.limits_client = LimitsClientXML(auth_provider)
+ self.images_client = ImagesClientXML(auth_provider)
+ self.keypairs_client = KeyPairsClientXML(auth_provider)
+ self.quotas_client = QuotasClientXML(auth_provider)
+ self.flavors_client = FlavorsClientXML(auth_provider)
+ self.extensions_client = ExtensionsClientXML(auth_provider)
self.volumes_extensions_client = VolumesExtensionsClientXML(
- *client_args)
- self.floating_ips_client = FloatingIPsClientXML(*client_args)
- self.snapshots_client = SnapshotsClientXML(*client_args)
- self.volumes_client = VolumesClientXML(*client_args)
- self.volume_types_client = VolumeTypesClientXML(*client_args)
- self.identity_client = IdentityClientXML(*client_args)
- self.identity_v3_client = IdentityV3ClientXML(*client_args)
- self.token_client = TokenClientXML()
+ auth_provider)
+ self.floating_ips_client = FloatingIPsClientXML(
+ auth_provider)
+ self.snapshots_client = SnapshotsClientXML(auth_provider)
+ self.volumes_client = VolumesClientXML(auth_provider)
+ self.volumes_v2_client = VolumesV2ClientXML(auth_provider)
+ self.volume_types_client = VolumeTypesClientXML(
+ auth_provider)
+ self.identity_client = IdentityClientXML(auth_provider)
+ self.identity_v3_client = IdentityV3ClientXML(
+ auth_provider)
self.security_groups_client = SecurityGroupsClientXML(
- *client_args)
- self.interfaces_client = InterfacesClientXML(*client_args)
- self.endpoints_client = EndPointClientXML(*client_args)
- self.fixed_ips_client = FixedIPsClientXML(*client_args)
+ auth_provider)
+ self.interfaces_client = InterfacesClientXML(auth_provider)
+ self.endpoints_client = EndPointClientXML(auth_provider)
+ self.fixed_ips_client = FixedIPsClientXML(auth_provider)
self.availability_zone_client = AvailabilityZoneClientXML(
- *client_args)
- self.service_client = ServiceClientXML(*client_args)
- self.aggregates_client = AggregatesClientXML(*client_args)
- self.services_client = ServicesClientXML(*client_args)
- self.tenant_usages_client = TenantUsagesClientXML(*client_args)
- self.policy_client = PolicyClientXML(*client_args)
- self.hosts_client = HostsClientXML(*client_args)
- self.hypervisor_client = HypervisorClientXML(*client_args)
- self.token_v3_client = V3TokenClientXML(*client_args)
- self.network_client = NetworkClientXML(*client_args)
- self.credentials_client = CredentialsClientXML(*client_args)
+ auth_provider)
+ self.service_client = ServiceClientXML(auth_provider)
+ self.aggregates_client = AggregatesClientXML(auth_provider)
+ self.services_client = ServicesClientXML(auth_provider)
+ self.tenant_usages_client = TenantUsagesClientXML(
+ auth_provider)
+ self.policy_client = PolicyClientXML(auth_provider)
+ self.hosts_client = HostsClientXML(auth_provider)
+ self.hypervisor_client = HypervisorClientXML(auth_provider)
+ self.network_client = NetworkClientXML(auth_provider)
+ self.credentials_client = CredentialsClientXML(
+ auth_provider)
self.instance_usages_audit_log_client = \
- InstanceUsagesAuditLogClientXML(*client_args)
- self.volume_hosts_client = VolumeHostsClientXML(*client_args)
+ InstanceUsagesAuditLogClientXML(auth_provider)
+ self.volume_hosts_client = VolumeHostsClientXML(
+ auth_provider)
self.volumes_extension_client = VolumeExtensionClientXML(
- *client_args)
-
- if client_args_v3_auth:
- self.servers_client_v3_auth = ServersClientXML(
- *client_args_v3_auth)
+ auth_provider)
if CONF.service_available.ceilometer:
- self.telemetry_client = TelemetryClientXML(*client_args)
+ self.telemetry_client = TelemetryClientXML(
+ auth_provider)
+ self.token_client = TokenClientXML()
+ self.token_v3_client = V3TokenClientXML()
- elif interface == 'json':
- self.certificates_client = CertificatesClientJSON(*client_args)
+ elif self.interface == 'json':
+ self.certificates_client = CertificatesClientJSON(
+ auth_provider)
self.certificates_v3_client = CertificatesV3ClientJSON(
- *client_args)
- self.baremetal_client = BaremetalClientJSON(*client_args)
- self.servers_client = ServersClientJSON(*client_args)
- self.servers_v3_client = ServersV3ClientJSON(*client_args)
- self.limits_client = LimitsClientJSON(*client_args)
- self.images_client = ImagesClientJSON(*client_args)
- self.keypairs_v3_client = KeyPairsV3ClientJSON(*client_args)
- self.keypairs_client = KeyPairsClientJSON(*client_args)
- self.keypairs_v3_client = KeyPairsV3ClientJSON(*client_args)
- self.quotas_client = QuotasClientJSON(*client_args)
- self.quotas_v3_client = QuotasV3ClientJSON(*client_args)
- self.flavors_client = FlavorsClientJSON(*client_args)
- self.flavors_v3_client = FlavorsV3ClientJSON(*client_args)
- self.extensions_v3_client = ExtensionsV3ClientJSON(*client_args)
- self.extensions_client = ExtensionsClientJSON(*client_args)
+ auth_provider)
+ self.baremetal_client = BaremetalClientJSON(auth_provider)
+ self.servers_client = ServersClientJSON(auth_provider)
+ self.servers_v3_client = ServersV3ClientJSON(auth_provider)
+ self.limits_client = LimitsClientJSON(auth_provider)
+ self.images_client = ImagesClientJSON(auth_provider)
+ self.keypairs_v3_client = KeyPairsV3ClientJSON(
+ auth_provider)
+ self.keypairs_client = KeyPairsClientJSON(auth_provider)
+ self.keypairs_v3_client = KeyPairsV3ClientJSON(
+ auth_provider)
+ self.quotas_client = QuotasClientJSON(auth_provider)
+ self.quotas_v3_client = QuotasV3ClientJSON(auth_provider)
+ self.flavors_client = FlavorsClientJSON(auth_provider)
+ self.flavors_v3_client = FlavorsV3ClientJSON(auth_provider)
+ self.extensions_v3_client = ExtensionsV3ClientJSON(
+ auth_provider)
+ self.extensions_client = ExtensionsClientJSON(
+ auth_provider)
self.volumes_extensions_client = VolumesExtensionsClientJSON(
- *client_args)
- self.floating_ips_client = FloatingIPsClientJSON(*client_args)
- self.snapshots_client = SnapshotsClientJSON(*client_args)
- self.volumes_client = VolumesClientJSON(*client_args)
- self.volume_types_client = VolumeTypesClientJSON(*client_args)
- self.identity_client = IdentityClientJSON(*client_args)
- self.identity_v3_client = IdentityV3ClientJSON(*client_args)
- self.token_client = TokenClientJSON()
+ auth_provider)
+ self.floating_ips_client = FloatingIPsClientJSON(
+ auth_provider)
+ self.snapshots_client = SnapshotsClientJSON(auth_provider)
+ self.volumes_client = VolumesClientJSON(auth_provider)
+ self.volumes_v2_client = VolumesV2ClientJSON(auth_provider)
+ self.volume_types_client = VolumeTypesClientJSON(
+ auth_provider)
+ self.identity_client = IdentityClientJSON(auth_provider)
+ self.identity_v3_client = IdentityV3ClientJSON(
+ auth_provider)
self.security_groups_client = SecurityGroupsClientJSON(
- *client_args)
- self.interfaces_v3_client = InterfacesV3ClientJSON(*client_args)
- self.interfaces_client = InterfacesClientJSON(*client_args)
- self.endpoints_client = EndPointClientJSON(*client_args)
- self.fixed_ips_client = FixedIPsClientJSON(*client_args)
+ auth_provider)
+ self.interfaces_v3_client = InterfacesV3ClientJSON(
+ auth_provider)
+ self.interfaces_client = InterfacesClientJSON(
+ auth_provider)
+ self.endpoints_client = EndPointClientJSON(auth_provider)
+ self.fixed_ips_client = FixedIPsClientJSON(auth_provider)
self.availability_zone_v3_client = AvailabilityZoneV3ClientJSON(
- *client_args)
+ auth_provider)
self.availability_zone_client = AvailabilityZoneClientJSON(
- *client_args)
- self.services_v3_client = ServicesV3ClientJSON(*client_args)
- self.service_client = ServiceClientJSON(*client_args)
- self.aggregates_v3_client = AggregatesV3ClientJSON(*client_args)
- self.aggregates_client = AggregatesClientJSON(*client_args)
- self.services_client = ServicesClientJSON(*client_args)
- self.tenant_usages_v3_client = TenantUsagesV3ClientJSON(
- *client_args)
- self.tenant_usages_client = TenantUsagesClientJSON(*client_args)
- self.version_v3_client = VersionV3ClientJSON(*client_args)
- self.policy_client = PolicyClientJSON(*client_args)
- self.hosts_client = HostsClientJSON(*client_args)
- self.hypervisor_v3_client = HypervisorV3ClientJSON(*client_args)
- self.hypervisor_client = HypervisorClientJSON(*client_args)
- self.token_v3_client = V3TokenClientJSON(*client_args)
- self.network_client = NetworkClientJSON(*client_args)
- self.credentials_client = CredentialsClientJSON(*client_args)
+ auth_provider)
+ self.services_v3_client = ServicesV3ClientJSON(
+ auth_provider)
+ self.service_client = ServiceClientJSON(auth_provider)
+ self.aggregates_v3_client = AggregatesV3ClientJSON(
+ auth_provider)
+ self.aggregates_client = AggregatesClientJSON(
+ auth_provider)
+ self.services_client = ServicesClientJSON(auth_provider)
+ self.tenant_usages_client = TenantUsagesClientJSON(
+ auth_provider)
+ self.version_v3_client = VersionV3ClientJSON(auth_provider)
+ self.policy_client = PolicyClientJSON(auth_provider)
+ self.hosts_client = HostsClientJSON(auth_provider)
+ self.hypervisor_v3_client = HypervisorV3ClientJSON(
+ auth_provider)
+ self.hypervisor_client = HypervisorClientJSON(
+ auth_provider)
+ self.network_client = NetworkClientJSON(auth_provider)
+ self.credentials_client = CredentialsClientJSON(
+ auth_provider)
self.instance_usages_audit_log_client = \
- InstanceUsagesAuditLogClientJSON(*client_args)
- self.instance_usages_audit_log_v3_client = \
- InstanceUsagesAuditLogV3ClientJSON(*client_args)
- self.volume_hosts_client = VolumeHostsClientJSON(*client_args)
+ InstanceUsagesAuditLogClientJSON(auth_provider)
+ self.volume_hosts_client = VolumeHostsClientJSON(
+ auth_provider)
self.volumes_extension_client = VolumeExtensionClientJSON(
- *client_args)
- self.hosts_v3_client = HostsV3ClientJSON(*client_args)
+ auth_provider)
+ self.hosts_v3_client = HostsV3ClientJSON(auth_provider)
if CONF.service_available.ceilometer:
- self.telemetry_client = TelemetryClientJSON(*client_args)
- self.negative_client = NegativeRestClient(*client_args)
+ self.telemetry_client = TelemetryClientJSON(
+ auth_provider)
+ self.token_client = TokenClientJSON()
+ self.token_v3_client = V3TokenClientJSON()
+ self.negative_client = NegativeRestClient(auth_provider)
self.negative_client.service = service
- if client_args_v3_auth:
- self.servers_client_v3_auth = ServersClientJSON(
- *client_args_v3_auth)
- self.negative_v3_client = NegativeRestClient(
- *client_args_v3_auth)
- self.negative_v3_client.service = service
else:
msg = "Unsupported interface type `%s'" % interface
raise exceptions.InvalidConfiguration(msg)
+ # TODO(andreaf) EC2 client still do their auth, v2 only
+ ec2_client_args = (self.credentials.get('username'),
+ self.credentials.get('password'),
+ CONF.identity.uri,
+ self.credentials.get('tenant_name'))
+
# common clients
- self.account_client = AccountClient(*client_args)
+ self.account_client = AccountClient(auth_provider)
if CONF.service_available.glance:
- self.image_client = ImageClientJSON(*client_args)
- self.image_client_v2 = ImageClientV2JSON(*client_args)
- self.container_client = ContainerClient(*client_args)
- self.object_client = ObjectClient(*client_args)
- self.orchestration_client = OrchestrationClient(*client_args)
- self.ec2api_client = botoclients.APIClientEC2(*client_args)
- self.s3_client = botoclients.ObjectClientS3(*client_args)
- self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
+ self.image_client = ImageClientJSON(auth_provider)
+ self.image_client_v2 = ImageClientV2JSON(auth_provider)
+ self.container_client = ContainerClient(auth_provider)
+ self.object_client = ObjectClient(auth_provider)
+ self.orchestration_client = OrchestrationClient(
+ auth_provider)
+ self.ec2api_client = botoclients.APIClientEC2(*ec2_client_args)
+ self.s3_client = botoclients.ObjectClientS3(*ec2_client_args)
+ self.custom_object_client = ObjectClientCustomizedHeader(
+ auth_provider)
self.custom_account_client = \
- AccountClientCustomizedHeader(*client_args)
- self.data_processing_client = DataProcessingClient(*client_args)
+ AccountClientCustomizedHeader(auth_provider)
+ self.data_processing_client = DataProcessingClient(
+ auth_provider)
+
+ @classmethod
+ def get_auth_provider_class(cls, auth_version):
+ if auth_version == 'v2':
+ return auth.KeystoneV2AuthProvider
+ else:
+ return auth.KeystoneV3AuthProvider
+
+ def get_default_credentials(self):
+ return dict(
+ username=CONF.identity.username,
+ password=CONF.identity.password,
+ tenant_name=CONF.identity.tenant_name
+ )
+
+ def get_auth_provider(self, credentials=None):
+ auth_params = dict(client_type='tempest',
+ interface=self.interface)
+ auth_provider_class = self.get_auth_provider_class(self.auth_version)
+ # If invalid / incomplete credentials are provided, use default ones
+ if credentials is None or \
+ not auth_provider_class.check_credentials(credentials):
+ credentials = self.credentials
+ auth_params['credentials'] = credentials
+ return auth_provider_class(**auth_params)
class AltManager(Manager):
@@ -406,6 +439,10 @@
"""
def __init__(self, interface='json', service=None):
base = super(OrchestrationManager, self)
+ # heat currently needs an admin user so that stacks can create users
+ # however the tests need the demo tenant so that the neutron
+ # private network is the default. DO NOT change this auth combination
+ # until heat can run with the demo user.
base.__init__(CONF.identity.admin_username,
CONF.identity.admin_password,
CONF.identity.tenant_name,
diff --git a/tempest/common/generate_json.py b/tempest/common/generate_json.py
index 0a0afe4..c8e86dc 100644
--- a/tempest/common/generate_json.py
+++ b/tempest/common/generate_json.py
@@ -203,36 +203,62 @@
return invalids
-type_map_valid = {"string": generate_valid_string,
- "integer": generate_valid_integer,
- "object": generate_valid_object}
+type_map_valid = {
+ "string": generate_valid_string,
+ "integer": generate_valid_integer,
+ "object": generate_valid_object
+}
-type_map_invalid = {"string": [gen_int,
- gen_none,
- gen_str_min_length,
- gen_str_max_length],
- "integer": [gen_string,
- gen_none,
- gen_int_min,
- gen_int_max],
- "object": [gen_obj_remove_attr,
- gen_obj_add_attr,
- gen_inv_prop_obj]}
+type_map_invalid = {
+ "string": [
+ gen_int,
+ gen_none,
+ gen_str_min_length,
+ gen_str_max_length],
+ "integer": [
+ gen_string,
+ gen_none,
+ gen_int_min,
+ gen_int_max],
+ "object": [
+ gen_obj_remove_attr,
+ gen_obj_add_attr,
+ gen_inv_prop_obj]
+}
-schema = {"type": "object",
- "properties":
- {"name": {"type": "string"},
- "http-method": {"enum": ["GET", "PUT", "HEAD",
- "POST", "PATCH", "DELETE", 'COPY']},
- "url": {"type": "string"},
- "json-schema": jsonschema._utils.load_schema("draft4"),
- "resources": {"type": "array", "items": {"type": "string"}},
- "results": {"type": "object",
- "properties": {}}
- },
- "required": ["name", "http-method", "url"],
- "additionalProperties": False,
- }
+schema = {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "http-method": {
+ "enum": ["GET", "PUT", "HEAD",
+ "POST", "PATCH", "DELETE", 'COPY']
+ },
+ "url": {"type": "string"},
+ "json-schema": jsonschema._utils.load_schema("draft4"),
+ "resources": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {"type": "string"},
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "expected_result": {"type": "integer"}
+ }
+ }
+ ]
+ }
+ },
+ "results": {
+ "type": "object",
+ "properties": {}
+ }
+ },
+ "required": ["name", "http-method", "url"],
+ "additionalProperties": False,
+}
def validate_negative_test_schema(nts):
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index d8afab3..4503f13 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -45,8 +45,10 @@
class HTTPClient(object):
- def __init__(self, endpoint, **kwargs):
- self.endpoint = endpoint
+ def __init__(self, auth_provider, filters, **kwargs):
+ self.auth_provider = auth_provider
+ self.filters = filters
+ self.endpoint = auth_provider.base_url(filters)
endpoint_parts = self.parse_endpoint(self.endpoint)
self.endpoint_scheme = endpoint_parts.scheme
self.endpoint_hostname = endpoint_parts.hostname
@@ -57,8 +59,6 @@
self.connection_kwargs = self.get_connection_kwargs(
self.endpoint_scheme, **kwargs)
- self.auth_token = kwargs.get('token')
-
@staticmethod
def parse_endpoint(endpoint):
return urlparse.urlparse(endpoint)
@@ -100,15 +100,15 @@
# Copy the kwargs so we can reuse the original in case of redirects
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
- if self.auth_token:
- kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
self._log_request(method, url, kwargs['headers'])
conn = self.get_connection()
try:
- conn_url = posixpath.normpath('%s/%s' % (self.endpoint_path, url))
+ url_parts = self.parse_endpoint(url)
+ conn_url = posixpath.normpath(url_parts.path)
+ LOG.debug('Actual Path: {path}'.format(path=conn_url))
if kwargs['headers'].get('Transfer-Encoding') == 'chunked':
conn.putrequest(method, conn_url)
for header, value in kwargs['headers'].items():
@@ -198,7 +198,13 @@
# We use 'Transfer-Encoding: chunked' because
# body size may not always be known in advance.
kwargs['headers']['Transfer-Encoding'] = 'chunked'
- return self._http_request(url, method, **kwargs)
+
+ # Decorate the request with auth
+ req_url, kwargs['headers'], kwargs['body'] = \
+ self.auth_provider.auth_request(
+ method=method, url=url, headers=kwargs['headers'],
+ body=kwargs.get('body', None), filters=self.filters)
+ return self._http_request(req_url, method, **kwargs)
class OpenSSLConnectionDelegator(object):
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 146fac9..ac8b14f 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -36,7 +36,6 @@
self.isolated_net_resources = {}
self.ports = []
self.name = name
- self.config = CONF
self.tempest_client = tempest_client
self.interface = interface
self.password = password
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index d80d4c0..a352db5 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -38,34 +38,31 @@
class RestClient(object):
+
TYPE = "json"
+
+ # This is used by _parse_resp method
+ # Redefine it for purposes of your xml service client
+ # List should contain top-xml_tag-names of data, which is like list/array
+ # For example, in keystone it is users, roles, tenants and services
+ # All of it has children with same tag-names
+ list_tags = []
+
+ # This is used by _parse_resp method too
+ # Used for selection of dict-like xmls,
+ # like metadata for Vms in nova, and volumes in cinder
+ dict_tags = ["metadata", ]
+
LOG = logging.getLogger(__name__)
- def __init__(self, user, password, auth_url, tenant_name=None,
- auth_version='v2'):
- self.user = user
- self.password = password
- self.auth_url = auth_url
- self.tenant_name = tenant_name
- self.auth_version = auth_version
+ def __init__(self, auth_provider):
+ self.auth_provider = auth_provider
- self.service = None
- self.token = None
- self.base_url = None
- self.region = {}
- for cfgname in dir(CONF):
- # Find all config.FOO.catalog_type and assume FOO is a service.
- cfg = getattr(CONF, cfgname)
- catalog_type = getattr(cfg, 'catalog_type', None)
- if not catalog_type:
- continue
- service_region = getattr(cfg, 'region', None)
- if not service_region:
- service_region = CONF.identity.region
- self.region[catalog_type] = service_region
self.endpoint_url = 'publicURL'
- self.headers = {'Content-Type': 'application/%s' % self.TYPE,
- 'Accept': 'application/%s' % self.TYPE}
+ self.service = None
+ # The version of the API this client implements
+ self.api_version = None
+ self._skip_path = False
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
self.general_header_lc = set(('cache-control', 'connection',
@@ -80,196 +77,87 @@
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
+ def _get_type(self):
+ return self.TYPE
+
+ def get_headers(self, accept_type=None, send_type=None):
+ if accept_type is None:
+ accept_type = self._get_type()
+ if send_type is None:
+ send_type = self._get_type()
+ return {'Content-Type': 'application/%s' % send_type,
+ 'Accept': 'application/%s' % accept_type}
+
def __str__(self):
STRING_LIMIT = 80
- str_format = ("user:%s, password:%s, "
- "auth_url:%s, tenant_name:%s, auth_version:%s, "
- "service:%s, base_url:%s, region:%s, "
- "endpoint_url:%s, build_interval:%s, build_timeout:%s"
+ str_format = ("config:%s, service:%s, base_url:%s, "
+ "filters: %s, build_interval:%s, build_timeout:%s"
"\ntoken:%s..., \nheaders:%s...")
- return str_format % (self.user, self.password,
- self.auth_url, self.tenant_name,
- self.auth_version, self.service,
- self.base_url, self.region, self.endpoint_url,
- self.build_interval, self.build_timeout,
+ return str_format % (CONF, self.service, self.base_url,
+ self.filters, self.build_interval,
+ self.build_timeout,
str(self.token)[0:STRING_LIMIT],
- str(self.headers)[0:STRING_LIMIT])
+ str(self.get_headers())[0:STRING_LIMIT])
- def _set_auth(self):
+ def _get_region(self, service):
"""
- Sets the token and base_url used in requests based on the strategy type
+ Returns the region for a specific service
"""
+ service_region = None
+ for cfgname in dir(CONF._config):
+ # Find all config.FOO.catalog_type and assume FOO is a service.
+ cfg = getattr(CONF, cfgname)
+ catalog_type = getattr(cfg, 'catalog_type', None)
+ if catalog_type == service:
+ service_region = getattr(cfg, 'region', None)
+ if not service_region:
+ service_region = CONF.identity.region
+ return service_region
- if self.auth_version == 'v3':
- auth_func = self.identity_auth_v3
- else:
- auth_func = self.keystone_auth
+ @property
+ def user(self):
+ return self.auth_provider.credentials.get('username', None)
- self.token, self.base_url = (
- auth_func(self.user, self.password, self.auth_url,
- self.service, self.tenant_name))
+ @property
+ def tenant_name(self):
+ return self.auth_provider.credentials.get('tenant_name', None)
- def clear_auth(self):
+ @property
+ def password(self):
+ return self.auth_provider.credentials.get('password', None)
+
+ @property
+ def base_url(self):
+ return self.auth_provider.base_url(filters=self.filters)
+
+ @property
+ def token(self):
+ return self.auth_provider.get_token()
+
+ @property
+ def filters(self):
+ _filters = dict(
+ service=self.service,
+ endpoint_type=self.endpoint_url,
+ region=self._get_region(self.service)
+ )
+ if self.api_version is not None:
+ _filters['api_version'] = self.api_version
+ if self._skip_path:
+ _filters['skip_path'] = self._skip_path
+ return _filters
+
+ def skip_path(self):
"""
- Can be called to clear the token and base_url so that the next request
- will fetch a new token and base_url.
+ When set, ignore the path part of the base URL from the catalog
"""
+ self._skip_path = True
- self.token = None
- self.base_url = None
-
- def get_auth(self):
- """Returns the token of the current request or sets the token if
- none.
+ def reset_path(self):
"""
-
- if not self.token:
- self._set_auth()
-
- return self.token
-
- def keystone_auth(self, user, password, auth_url, service, tenant_name):
+ When reset, use the base URL from the catalog as-is
"""
- Provides authentication via Keystone using v2 identity API.
- """
-
- # Normalize URI to ensure /tokens is in it.
- if 'tokens' not in auth_url:
- auth_url = auth_url.rstrip('/') + '/tokens'
-
- creds = {
- 'auth': {
- 'passwordCredentials': {
- 'username': user,
- 'password': password,
- },
- 'tenantName': tenant_name,
- }
- }
-
- headers = {'Content-Type': 'application/json'}
- body = json.dumps(creds)
- self._log_request('POST', auth_url, headers, body)
- resp, resp_body = self.http_obj.request(auth_url, 'POST',
- headers=headers, body=body)
- self._log_response(resp, resp_body)
-
- if resp.status == 200:
- try:
- auth_data = json.loads(resp_body)['access']
- token = auth_data['token']['id']
- except Exception as e:
- print("Failed to obtain token for user: %s" % e)
- raise
-
- mgmt_url = None
- for ep in auth_data['serviceCatalog']:
- if ep["type"] == service:
- for _ep in ep['endpoints']:
- if service in self.region and \
- _ep['region'] == self.region[service]:
- mgmt_url = _ep[self.endpoint_url]
- if not mgmt_url:
- mgmt_url = ep['endpoints'][0][self.endpoint_url]
- break
-
- if mgmt_url is None:
- raise exceptions.EndpointNotFound(service)
-
- return token, mgmt_url
-
- elif resp.status == 401:
- raise exceptions.AuthenticationFailure(user=user,
- password=password,
- tenant=tenant_name)
- raise exceptions.IdentityError('Unexpected status code {0}'.format(
- resp.status))
-
- def identity_auth_v3(self, user, password, auth_url, service,
- project_name, domain_id='default'):
- """Provides authentication using Identity API v3."""
-
- req_url = auth_url.rstrip('/') + '/auth/tokens'
-
- creds = {
- "auth": {
- "identity": {
- "methods": ["password"],
- "password": {
- "user": {
- "name": user, "password": password,
- "domain": {"id": domain_id}
- }
- }
- },
- "scope": {
- "project": {
- "domain": {"id": domain_id},
- "name": project_name
- }
- }
- }
- }
-
- headers = {'Content-Type': 'application/json'}
- body = json.dumps(creds)
- resp, body = self.http_obj.request(req_url, 'POST',
- headers=headers, body=body)
-
- if resp.status == 201:
- try:
- token = resp['x-subject-token']
- except Exception:
- self.LOG.exception("Failed to obtain token using V3"
- " authentication (auth URL is '%s')" %
- req_url)
- raise
-
- catalog = json.loads(body)['token']['catalog']
-
- mgmt_url = None
- for service_info in catalog:
- if service_info['type'] != service:
- continue # this isn't the entry for us.
-
- endpoints = service_info['endpoints']
-
- # Look for an endpoint in the region if configured.
- if service in self.region:
- region = self.region[service]
-
- for ep in endpoints:
- if ep['region'] != region:
- continue
-
- mgmt_url = ep['url']
- # FIXME(blk-u): this isn't handling endpoint type
- # (public, internal, admin).
- break
-
- if not mgmt_url:
- # Didn't find endpoint for region, use the first.
-
- ep = endpoints[0]
- mgmt_url = ep['url']
- # FIXME(blk-u): this isn't handling endpoint type
- # (public, internal, admin).
-
- break
-
- return token, mgmt_url
-
- elif resp.status == 401:
- raise exceptions.AuthenticationFailure(user=user,
- password=password,
- tenant=project_name)
- else:
- self.LOG.error("Failed to obtain token using V3 authentication"
- " (auth URL is '%s'), the response status is %s" %
- (req_url, resp.status))
- raise exceptions.AuthenticationFailure(user=user,
- password=password,
- tenant=project_name)
+ self._skip_path = False
def expected_success(self, expected_code, read_code):
assert_msg = ("This function only allowed to use for HTTP status"
@@ -285,7 +173,7 @@
details = pattern.format(read_code, expected_code)
raise exceptions.InvalidHttpSuccessCode(details)
- def post(self, url, body, headers):
+ def post(self, url, body, headers=None):
return self.request('POST', url, headers, body)
def get(self, url, headers=None):
@@ -294,10 +182,10 @@
def delete(self, url, headers=None, body=None):
return self.request('DELETE', url, headers, body)
- def patch(self, url, body, headers):
+ def patch(self, url, body, headers=None):
return self.request('PATCH', url, headers, body)
- def put(self, url, body, headers):
+ def put(self, url, body, headers=None):
return self.request('PUT', url, headers, body)
def head(self, url, headers=None):
@@ -309,7 +197,6 @@
def get_versions(self):
resp, body = self.get('')
body = self._parse_resp(body)
- body = body['versions']
versions = map(lambda x: x['id'], body)
return resp, versions
@@ -337,10 +224,10 @@
headers = resp.copy()
del headers['status']
if headers.get('x-compute-request-id'):
- self.LOG.info("Nova request id: %s" %
+ self.LOG.info("Nova/Cinder request id: %s" %
headers.pop('x-compute-request-id'))
elif headers.get('x-openstack-request-id'):
- self.LOG.info("Glance request id %s" %
+ self.LOG.info("OpenStack request id %s" %
headers.pop('x-openstack-request-id'))
if len(headers):
self.LOG.debug('Response Headers: ' + str(headers))
@@ -353,7 +240,48 @@
hashlib.md5(str_body).hexdigest())
def _parse_resp(self, body):
- return json.loads(body)
+ if self._get_type() is "json":
+ body = json.loads(body)
+
+ # We assume, that if the first value of the deserialized body's
+ # item set is a dict or a list, that we just return the first value
+ # of deserialized body.
+ # Essentially "cutting out" the first placeholder element in a body
+ # that looks like this:
+ #
+ # {
+ # "users": [
+ # ...
+ # ]
+ # }
+ try:
+ # Ensure there are not more than one top-level keys
+ if len(body.keys()) > 1:
+ return body
+ # Just return the "wrapped" element
+ first_key, first_item = body.items()[0]
+ if isinstance(first_item, (dict, list)):
+ return first_item
+ except (ValueError, IndexError):
+ pass
+ return body
+ elif self._get_type() is "xml":
+ element = etree.fromstring(body)
+ if any(s in element.tag for s in self.dict_tags):
+ # Parse dictionary-like xmls (metadata, etc)
+ dictionary = {}
+ for el in element.getchildren():
+ dictionary[u"%s" % el.get("key")] = u"%s" % el.text
+ return dictionary
+ if any(s in element.tag for s in self.list_tags):
+ # Parse list-like xmls (users, roles, etc)
+ array = []
+ for child in element.getchildren():
+ array.append(xml_to_json(child))
+ return array
+
+ # Parse one-item-like xmls (user, role, etc)
+ return xml_to_json(element)
def response_checker(self, method, url, headers, body, resp, resp_body):
if (resp.status in set((204, 205, 304)) or resp.status < 200 or
@@ -383,28 +311,29 @@
if not resp_body and resp.status >= 400:
self.LOG.warning("status >= 400 response with empty body")
- def _request(self, method, url,
- headers=None, body=None):
+ def _request(self, method, url, headers=None, body=None):
"""A simple HTTP request interface."""
-
- req_url = "%s/%s" % (self.base_url, url)
- self._log_request(method, req_url, headers, body)
- resp, resp_body = self.http_obj.request(req_url, method,
- headers=headers, body=body)
+ # Authenticate the request with the auth provider
+ req_url, req_headers, req_body = self.auth_provider.auth_request(
+ method, url, headers, body, self.filters)
+ self._log_request(method, req_url, req_headers, req_body)
+ # Do the actual request
+ resp, resp_body = self.http_obj.request(
+ req_url, method, headers=req_headers, body=req_body)
self._log_response(resp, resp_body)
- self.response_checker(method, url, headers, body, resp, resp_body)
+ # Verify HTTP response codes
+ self.response_checker(method, url, req_headers, req_body, resp,
+ resp_body)
return resp, resp_body
- def request(self, method, url,
- headers=None, body=None):
+ def request(self, method, url, headers=None, body=None):
retry = 0
- if (self.token is None) or (self.base_url is None):
- self._set_auth()
if headers is None:
- headers = {}
- headers['X-Auth-Token'] = self.token
+ # NOTE(vponomaryov): if some client do not need headers,
+ # it should explicitly pass empty dict
+ headers = self.get_headers()
resp, resp_body = self._request(method, url,
headers=headers, body=body)
@@ -524,10 +453,13 @@
if (not isinstance(resp_body, collections.Mapping) or
'retry-after' not in resp):
return True
- over_limit = resp_body.get('overLimit', None)
- if not over_limit:
- return True
- return 'exceed' in over_limit.get('message', 'blabla')
+ if self._get_type() is "json":
+ over_limit = resp_body.get('overLimit', None)
+ if not over_limit:
+ return True
+ return 'exceed' in over_limit.get('message', 'blabla')
+ elif self._get_type() is "xml":
+ return 'exceed' in resp_body.get('message', 'blabla')
def wait_for_resource_deletion(self, id):
"""Waits for a resource to be deleted."""
@@ -549,6 +481,11 @@
class RestClientXML(RestClient):
+
+ # NOTE(vponomaryov): This is deprecated class
+ # and should be removed after excluding it
+ # from all service clients
+
TYPE = "xml"
def _parse_resp(self, body):
@@ -574,11 +511,11 @@
if method == "GET":
resp, body = self.get(url)
elif method == "POST":
- resp, body = self.post(url, body, self.headers)
+ resp, body = self.post(url, body)
elif method == "PUT":
- resp, body = self.put(url, body, self.headers)
+ resp, body = self.put(url, body)
elif method == "PATCH":
- resp, body = self.patch(url, body, self.headers)
+ resp, body = self.patch(url, body)
elif method == "HEAD":
resp, body = self.head(url)
elif method == "DELETE":
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 0ed9b82..b6fa0a0 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -49,7 +49,7 @@
self.channel_timeout = float(channel_timeout)
self.buf_size = 1024
- def _get_ssh_connection(self, sleep=1.5, backoff=1.01):
+ def _get_ssh_connection(self, sleep=1.5, backoff=1):
"""Returns an ssh connection to the specified host."""
bsleep = sleep
ssh = paramiko.SSHClient()
@@ -72,23 +72,25 @@
look_for_keys=self.look_for_keys,
key_filename=self.key_filename,
timeout=self.channel_timeout, pkey=self.pkey)
- LOG.info("ssh connection to %s@%s sucessfuly created",
+ LOG.info("ssh connection to %s@%s successfuly created",
self.username, self.host)
return ssh
except (socket.error,
- paramiko.SSHException):
- attempts += 1
- time.sleep(bsleep)
- bsleep *= backoff
- if not self._is_timed_out(_start_time):
- continue
- else:
+ paramiko.SSHException) as e:
+ if self._is_timed_out(_start_time):
LOG.exception("Failed to establish authenticated ssh"
" connection to %s@%s after %d attempts",
self.username, self.host, attempts)
raise exceptions.SSHTimeout(host=self.host,
user=self.username,
password=self.password)
+ bsleep += backoff
+ attempts += 1
+ LOG.warning("Failed to establish authenticated ssh"
+ " connection to %s@%s (%s). Number attempts: %s."
+ " Retry after %d seconds.",
+ self.username, self.host, e, attempts, bsleep)
+ time.sleep(bsleep)
def _is_timed_out(self, start_time):
return (time.time() - self.timeout) > start_time
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index 38f3d38..04d898d 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -1,4 +1,3 @@
-LAST_REBOOT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
PING_IPV4_COMMAND = 'ping -c 3 '
PING_IPV6_COMMAND = 'ping6 -c 3 '
PING_PACKET_LOSS_REGEX = '(\d{1,3})\.?\d*\% packet loss'
diff --git a/tempest/common/utils/test_utils.py b/tempest/common/utils/test_utils.py
index eca716e..2e23782 100644
--- a/tempest/common/utils/test_utils.py
+++ b/tempest/common/utils/test_utils.py
@@ -112,6 +112,8 @@
"""
:return: a scenario with name and uuid of images
"""
+ if not CONF.service_available.glance:
+ return []
if not hasattr(self, '_scenario_images'):
images = self.client.images.list(detailed=False)
self._scenario_images = [
diff --git a/tempest/config.py b/tempest/config.py
index ccb3125..210c857 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -43,6 +43,10 @@
help="Full URI of the OpenStack Identity API (Keystone), v2"),
cfg.StrOpt('uri_v3',
help='Full URI of the OpenStack Identity API (Keystone), v3'),
+ cfg.StrOpt('auth_version',
+ default='v2',
+ help="Identity API version to be used for authentication "
+ "for API tests."),
cfg.StrOpt('region',
default='RegionOne',
help="The identity region name to use. Also used as the other "
@@ -95,7 +99,13 @@
cfg.BoolOpt('trust',
default=True,
help='Does the identity service have delegation and '
- 'impersonation enabled')
+ 'impersonation enabled'),
+ cfg.BoolOpt('api_v2',
+ default=True,
+ help='Is the v2 identity API enabled'),
+ cfg.BoolOpt('api_v3',
+ default=True,
+ help='Is the v3 identity API enabled'),
]
compute_group = cfg.OptGroup(name='compute',
@@ -309,10 +319,16 @@
"used."),
cfg.StrOpt('tenant_network_cidr',
default="10.100.0.0/16",
- help="The cidr block to allocate tenant networks from"),
+ help="The cidr block to allocate tenant ipv4 subnets from"),
cfg.IntOpt('tenant_network_mask_bits',
default=28,
- help="The mask bits for tenant networks"),
+ help="The mask bits for tenant ipv4 subnets"),
+ cfg.StrOpt('tenant_network_v6_cidr',
+ default="2003::/64",
+ help="The cidr block to allocate tenant ipv6 subnets from"),
+ cfg.IntOpt('tenant_network_v6_mask_bits',
+ default=96,
+ help="The mask bits for tenant ipv6 subnets"),
cfg.BoolOpt('tenant_networks_reachable',
default=False,
help="Whether tenant network connectivity should be "
@@ -388,6 +404,9 @@
cfg.BoolOpt('api_v1',
default=True,
help="Is the v1 volume API enabled"),
+ cfg.BoolOpt('api_v2',
+ default=True,
+ help="Is the v2 volume API enabled"),
]
@@ -582,7 +601,12 @@
help='time (in seconds) between log file error checks.'),
cfg.IntOpt('default_thread_number_per_action',
default=4,
- help='The number of threads created while stress test.')
+ help='The number of threads created while stress test.'),
+ cfg.BoolOpt('leave_dirty_stack',
+ default=False,
+ help='Prevent the cleaning (tearDownClass()) between'
+ ' each stress test run if an exception occurs'
+ ' during this run.')
]
@@ -776,7 +800,7 @@
self.compute_feature_enabled = cfg.CONF['compute-feature-enabled']
self.identity = cfg.CONF.identity
self.identity_feature_enabled = cfg.CONF['identity-feature-enabled']
- self.images = cfg.CONF.image
+ self.image = cfg.CONF.image
self.image_feature_enabled = cfg.CONF['image-feature-enabled']
self.network = cfg.CONF.network
self.network_feature_enabled = cfg.CONF['network-feature-enabled']
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 59a3aeb..0fc304a 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -22,7 +22,7 @@
import cinderclient.client
import glanceclient
import heatclient.client
-import keystoneclient.apiclient.exceptions
+import keystoneclient.exceptions
import keystoneclient.v2_0.client
import netaddr
from neutronclient.common import exceptions as exc
@@ -112,7 +112,7 @@
region = CONF.identity.region
endpoint = self.identity_client.service_catalog.url_for(
attr='region', filter_value=region,
- service_type='image', endpoint_type='publicURL')
+ service_type=CONF.image.catalog_type, endpoint_type='publicURL')
dscv = CONF.identity.disable_ssl_certificate_validation
return glanceclient.Client('1', endpoint=endpoint, token=token,
insecure=dscv)
@@ -146,7 +146,7 @@
keystone_admin.roles.add_user_role(self.identity_client.user_id,
member_role.id,
self.identity_client.tenant_id)
- except keystoneclient.apiclient.exceptions.Conflict:
+ except keystoneclient.exceptions.Conflict:
pass
return swiftclient.Connection(auth_url, username, password,
@@ -167,11 +167,12 @@
keystone = self._get_identity_client(username, password, tenant_name)
region = CONF.identity.region
token = keystone.auth_token
+ service_type = CONF.orchestration.catalog_type
try:
endpoint = keystone.service_catalog.url_for(
attr='region',
filter_value=region,
- service_type='orchestration',
+ service_type=service_type,
endpoint_type='publicURL')
except keystoneclient.exceptions.EndpointNotFound:
return None
@@ -670,13 +671,17 @@
"Unable to determine which port to target.")
return ports[0]['id']
- def _create_floating_ip(self, server, external_network_id):
- port_id = self._get_server_port_id(server)
+ def _create_floating_ip(self, thing, external_network_id,
+ port_filters=None):
+ if port_filters is None:
+ port_id = self._get_server_port_id(thing)
+ else:
+ port_id = port_filters
body = dict(
floatingip=dict(
floating_network_id=external_network_id,
port_id=port_id,
- tenant_id=server.tenant_id,
+ tenant_id=thing.tenant_id,
)
)
result = self.network_client.create_floatingip(body=body)
@@ -713,6 +718,58 @@
return tempest.test.call_until_true(
ping, CONF.compute.ping_timeout, 1)
+ def _create_pool(self, lb_method, protocol, subnet_id):
+ """Wrapper utility that returns a test pool."""
+ name = data_utils.rand_name('pool-')
+ body = {
+ "pool": {
+ "protocol": protocol,
+ "name": name,
+ "subnet_id": subnet_id,
+ "lb_method": lb_method
+ }
+ }
+ resp = self.network_client.create_pool(body=body)
+ pool = net_common.DeletablePool(client=self.network_client,
+ **resp['pool'])
+ self.assertEqual(pool['name'], name)
+ self.set_resource(name, pool)
+ return pool
+
+ def _create_member(self, address, protocol_port, pool_id):
+ """Wrapper utility that returns a test member."""
+ body = {
+ "member": {
+ "protocol_port": protocol_port,
+ "pool_id": pool_id,
+ "address": address
+ }
+ }
+ resp = self.network_client.create_member(body)
+ member = net_common.DeletableMember(client=self.network_client,
+ **resp['member'])
+ self.set_resource(data_utils.rand_name('member-'), member)
+ return member
+
+ def _create_vip(self, protocol, protocol_port, subnet_id, pool_id):
+ """Wrapper utility that returns a test vip."""
+ name = data_utils.rand_name('vip-')
+ body = {
+ "vip": {
+ "protocol": protocol,
+ "name": name,
+ "subnet_id": subnet_id,
+ "pool_id": pool_id,
+ "protocol_port": protocol_port
+ }
+ }
+ resp = self.network_client.create_vip(body)
+ vip = net_common.DeletableVip(client=self.network_client,
+ **resp['vip'])
+ self.assertEqual(vip['name'], name)
+ self.set_resource(name, vip)
+ return vip
+
def _check_vm_connectivity(self, ip_address,
username=None,
private_key=None,
diff --git a/tempest/scenario/test_load_balancer_basic.py b/tempest/scenario/test_load_balancer_basic.py
new file mode 100644
index 0000000..68f6e62
--- /dev/null
+++ b/tempest/scenario/test_load_balancer_basic.py
@@ -0,0 +1,240 @@
+# Copyright 2014 Mirantis.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.
+
+import time
+import urllib
+
+from tempest.api.network import common as net_common
+from tempest.common import ssh
+from tempest.common.utils import data_utils
+from tempest import config
+from tempest import exceptions
+from tempest.scenario import manager
+from tempest import test
+
+config = config.CONF
+
+
+class TestLoadBalancerBasic(manager.NetworkScenarioTest):
+
+ """
+ This test checks basic load balancing.
+
+ The following is the scenario outline:
+ 1. Create an instance
+ 2. SSH to the instance and start two servers
+ 3. Create a load balancer with two members and with ROUND_ROBIN algorithm
+ associate the VIP with a floating ip
+ 4. Send 10 requests to the floating ip and check that they are shared
+ between the two servers and that both of them get equal portions
+ of the requests
+ """
+
+ @classmethod
+ def check_preconditions(cls):
+ super(TestLoadBalancerBasic, cls).check_preconditions()
+ cfg = config.network
+ if not test.is_extension_enabled('lbaas', 'network'):
+ msg = 'LBaaS Extension is not enabled'
+ cls.enabled = False
+ raise cls.skipException(msg)
+ if not (cfg.tenant_networks_reachable or cfg.public_network_id):
+ msg = ('Either tenant_networks_reachable must be "true", or '
+ 'public_network_id must be defined.')
+ cls.enabled = False
+ raise cls.skipException(msg)
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestLoadBalancerBasic, cls).setUpClass()
+ cls.check_preconditions()
+ cls.security_groups = {}
+ cls.networks = []
+ cls.subnets = []
+ cls.servers_keypairs = {}
+ cls.pools = []
+ cls.members = []
+ cls.vips = []
+ cls.floating_ips = {}
+ cls.port1 = 80
+ cls.port2 = 88
+
+ def _create_security_groups(self):
+ self.security_groups[self.tenant_id] =\
+ self._create_security_group_neutron(tenant_id=self.tenant_id)
+
+ def _create_server(self):
+ tenant_id = self.tenant_id
+ name = data_utils.rand_name("smoke_server-")
+ keypair = self.create_keypair(name='keypair-%s' % name)
+ security_groups = [self.security_groups[tenant_id].name]
+ nets = self.network_client.list_networks()
+ for net in nets['networks']:
+ if net['tenant_id'] == self.tenant_id:
+ self.networks.append(net)
+ create_kwargs = {
+ 'nics': [
+ {'net-id': net['id']},
+ ],
+ 'key_name': keypair.name,
+ 'security_groups': security_groups,
+ }
+ server = self.create_server(name=name,
+ create_kwargs=create_kwargs)
+ self.servers_keypairs[server] = keypair
+ break
+ self.assertTrue(self.servers_keypairs)
+
+ def _start_servers(self):
+ """
+ 1. SSH to the instance
+ 2. Start two servers listening on ports 80 and 88 respectively
+ """
+ for server in self.servers_keypairs.keys():
+ ssh_login = config.compute.image_ssh_user
+ private_key = self.servers_keypairs[server].private_key
+ network_name = self.networks[0]['name']
+
+ ip_address = server.networks[network_name][0]
+ ssh_client = ssh.Client(ip_address, ssh_login,
+ pkey=private_key,
+ timeout=100)
+ start_server = "while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\n" \
+ "%(server)s' | sudo nc -l -p %(port)s ; done &"
+ cmd = start_server % {'server': 'server1',
+ 'port': self.port1}
+ ssh_client.exec_command(cmd)
+ cmd = start_server % {'server': 'server2',
+ 'port': self.port2}
+ ssh_client.exec_command(cmd)
+
+ def _check_connection(self, check_ip):
+ def try_connect(ip):
+ try:
+ urllib.urlopen("http://{0}/".format(ip))
+ return True
+ except IOError:
+ return False
+ timeout = config.compute.ping_timeout
+ timer = 0
+ while not try_connect(check_ip):
+ time.sleep(1)
+ timer += 1
+ if timer >= timeout:
+ message = "Timed out trying to connect to %s" % check_ip
+ raise exceptions.TimeoutException(message)
+
+ def _create_pool(self):
+ """Create a pool with ROUND_ROBIN algorithm."""
+ subnets = self.network_client.list_subnets()
+ for subnet in subnets['subnets']:
+ if subnet['tenant_id'] == self.tenant_id:
+ self.subnets.append(subnet)
+ pool = super(TestLoadBalancerBasic, self)._create_pool(
+ 'ROUND_ROBIN',
+ 'HTTP',
+ subnet['id'])
+ self.pools.append(pool)
+ break
+ self.assertTrue(self.pools)
+
+ def _create_members(self, network_name, server_ids):
+ """
+ Create two members.
+
+ In case there is only one server, create both members with the same ip
+ but with different ports to listen on.
+ """
+ servers = self.compute_client.servers.list()
+ for server in servers:
+ if server.id in server_ids:
+ ip = server.networks[network_name][0]
+ pool_id = self.pools[0]['id']
+ if len(set(server_ids)) == 1 or len(servers) == 1:
+ member1 = self._create_member(ip, self.port1, pool_id)
+ member2 = self._create_member(ip, self.port2, pool_id)
+ self.members.extend([member1, member2])
+ else:
+ member = self._create_member(ip, self.port1, pool_id)
+ self.members.append(member)
+ self.assertTrue(self.members)
+
+ def _assign_floating_ip_to_vip(self, vip):
+ public_network_id = config.network.public_network_id
+ port_id = vip['port_id']
+ floating_ip = self._create_floating_ip(vip,
+ public_network_id,
+ port_filters=port_id)
+ self.floating_ips.setdefault(vip['id'], [])
+ self.floating_ips[vip['id']].append(floating_ip)
+
+ def _create_load_balancer(self):
+ self._create_pool()
+ self._create_members(self.networks[0]['name'],
+ [self.servers_keypairs.keys()[0].id])
+ subnet_id = self.subnets[0]['id']
+ pool_id = self.pools[0]['id']
+ vip = super(TestLoadBalancerBasic, self)._create_vip('HTTP', 80,
+ subnet_id,
+ pool_id)
+ self.vips.append(vip)
+ self._status_timeout(NeutronRetriever(self.network_client,
+ self.network_client.vip_path,
+ net_common.DeletableVip),
+ self.vips[0]['id'],
+ expected_status='ACTIVE')
+ self._assign_floating_ip_to_vip(self.vips[0])
+
+ def _check_load_balancing(self):
+ """
+ 1. Send 10 requests on the floating ip associated with the VIP
+ 2. Check that the requests are shared between
+ the two servers and that both of them get equal portions
+ of the requests
+ """
+
+ vip = self.vips[0]
+ floating_ip_vip = self.floating_ips[
+ vip['id']][0]['floating_ip_address']
+ self._check_connection(floating_ip_vip)
+ resp = []
+ for count in range(10):
+ resp.append(
+ urllib.urlopen(
+ "http://{0}/".format(floating_ip_vip)).read())
+ self.assertEqual(set(["server1\n", "server2\n"]), set(resp))
+ self.assertEqual(5, resp.count("server1\n"))
+ self.assertEqual(5, resp.count("server2\n"))
+
+ @test.skip_because(bug="1277381")
+ @test.attr(type='smoke')
+ @test.services('compute', 'network')
+ def test_load_balancer_basic(self):
+ self._create_security_groups()
+ self._create_server()
+ self._start_servers()
+ self._create_load_balancer()
+ self._check_load_balancing()
+
+
+class NeutronRetriever(object):
+ def __init__(self, network_client, path, resource):
+ self.network_client = network_client
+ self.path = path
+ self.resource = resource
+
+ def get(self, thing_id):
+ obj = self.network_client.get(self.path % thing_id)
+ return self.resource(client=self.network_client, **obj.values()[0])
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 846e0cc..e868075 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -130,6 +130,7 @@
def ssh_to_server(self):
try:
self.linux_client = self.get_remote_client(self.floating_ip.ip)
+ self.linux_client.validate_authentication()
except Exception:
LOG.exception('ssh to server failed')
self._log_console_output()
@@ -160,10 +161,11 @@
self.nova_volume_attach()
self.addCleanup(self.nova_volume_detach)
self.cinder_show()
- self.nova_reboot()
self.nova_floating_ip_create()
self.nova_floating_ip_add()
self._create_loginable_secgroup_rule_nova()
self.ssh_to_server()
+ self.nova_reboot()
+ self.ssh_to_server()
self.check_partitions()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 020a256..0c0234f 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -19,9 +19,7 @@
from tempest import config
from tempest.openstack.common import log as logging
from tempest.scenario import manager
-
-from tempest.test import attr
-from tempest.test import services
+from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -113,6 +111,10 @@
@classmethod
def setUpClass(cls):
super(TestNetworkBasicOps, cls).setUpClass()
+ for ext in ['router', 'security-group']:
+ if not test.is_extension_enabled(ext, 'network'):
+ msg = "%s extension not enabled." % ext
+ raise cls.skipException(msg)
cls.check_preconditions()
# TODO(mnewby) Consider looking up entities as needed instead
# of storing them as collections on the class.
@@ -235,8 +237,8 @@
self._associate_floating_ip(floating_ip, server)
self.floating_ips[floating_ip] = server
- @attr(type='smoke')
- @services('compute', 'network')
+ @test.attr(type='smoke')
+ @test.services('compute', 'network')
def test_network_basic_ops(self):
self._create_security_groups()
self._create_networks()
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 8d043ae..841f9e1 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -114,7 +114,7 @@
detach_volume_client(server.id, volume.id)
self._wait_for_volume_status(volume, 'available')
- def _wait_for_volume_availible_on_the_system(self, server_or_ip):
+ def _wait_for_volume_available_on_the_system(self, server_or_ip):
ssh = self.get_remote_client(server_or_ip)
def _func():
@@ -161,7 +161,7 @@
ip_for_server = server
self._attach_volume(server, volume)
- self._wait_for_volume_availible_on_the_system(ip_for_server)
+ self._wait_for_volume_available_on_the_system(ip_for_server)
self._create_timestamp(ip_for_server)
self._detach_volume(server, volume)
@@ -189,7 +189,7 @@
# attach volume2 to instance2
self._attach_volume(server_from_snapshot, volume_from_snapshot)
- self._wait_for_volume_availible_on_the_system(ip_for_snapshot)
+ self._wait_for_volume_available_on_the_system(ip_for_snapshot)
# check the existence of the timestamp file in the volume2
self._check_timestamp(ip_for_snapshot)
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 0b5426e..5f6b513 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -47,9 +47,8 @@
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(BaremetalClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(BaremetalClient, self).__init__(auth_provider)
self.service = CONF.baremetal.catalog_type
self.uri_prefix = ''
@@ -115,7 +114,7 @@
"""
uri = self._get_uri(resource, permanent=permanent)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
return resp, self.deserialize(body)
@@ -128,7 +127,7 @@
"""
uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
- resp, body = self.get(uri, self.headers)
+ resp, body = self.get(uri)
return resp, self.deserialize(body)
@@ -146,7 +145,7 @@
body = self.serialize(object_type, object_dict)
uri = self._get_uri(resource)
- resp, body = self.post(uri, headers=self.headers, body=body)
+ resp, body = self.post(uri, body=body)
return resp, self.deserialize(body)
@@ -161,7 +160,7 @@
"""
uri = self._get_uri(resource, uuid)
- resp, body = self.delete(uri, self.headers)
+ resp, body = self.delete(uri)
return resp, body
def _patch_request(self, resource, uuid, patch_object):
@@ -177,7 +176,7 @@
uri = self._get_uri(resource, uuid)
patch_body = json.dumps(patch_object)
- resp, body = self.patch(uri, headers=self.headers, body=patch_body)
+ resp, body = self.patch(uri, body=patch_body)
return resp, self.deserialize(body)
@handle_errors
diff --git a/tempest/services/baremetal/v1/base_v1.py b/tempest/services/baremetal/v1/base_v1.py
index 13693e1..3f4c509 100644
--- a/tempest/services/baremetal/v1/base_v1.py
+++ b/tempest/services/baremetal/v1/base_v1.py
@@ -21,9 +21,8 @@
methods in order to send requests to Ironic.
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(BaremetalClientV1, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(BaremetalClientV1, self).__init__(auth_provider)
self.version = '1'
self.uri_prefix = 'v%s' % self.version
diff --git a/tempest/services/baremetal/v1/client_json.py b/tempest/services/baremetal/v1/client_json.py
index c98b20f..c9dc874 100644
--- a/tempest/services/baremetal/v1/client_json.py
+++ b/tempest/services/baremetal/v1/client_json.py
@@ -18,9 +18,8 @@
class BaremetalClientJSON(base_v1.BaremetalClientV1):
"""Tempest REST client for Ironic JSON API v1."""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(BaremetalClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(BaremetalClientJSON, self).__init__(auth_provider)
self.serialize = lambda obj_type, obj_body: json.dumps(obj_body)
self.deserialize = json.loads
diff --git a/tempest/services/compute/json/aggregates_client.py b/tempest/services/compute/json/aggregates_client.py
index dc8a316..e26f570 100644
--- a/tempest/services/compute/json/aggregates_client.py
+++ b/tempest/services/compute/json/aggregates_client.py
@@ -24,9 +24,8 @@
class AggregatesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AggregatesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(AggregatesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_aggregates(self):
@@ -44,7 +43,7 @@
def create_aggregate(self, **kwargs):
"""Creates a new aggregate."""
post_body = json.dumps({'aggregate': kwargs})
- resp, body = self.post('os-aggregates', post_body, self.headers)
+ resp, body = self.post('os-aggregates', post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -56,8 +55,7 @@
'availability_zone': availability_zone
}
put_body = json.dumps({'aggregate': put_body})
- resp, body = self.put('os-aggregates/%s' % str(aggregate_id),
- put_body, self.headers)
+ resp, body = self.put('os-aggregates/%s' % str(aggregate_id), put_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -80,7 +78,7 @@
}
post_body = json.dumps({'add_host': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -91,7 +89,7 @@
}
post_body = json.dumps({'remove_host': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -102,6 +100,6 @@
}
post_body = json.dumps({'set_metadata': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
diff --git a/tempest/services/compute/json/availability_zone_client.py b/tempest/services/compute/json/availability_zone_client.py
index 2b7e23c..ea4e95e 100644
--- a/tempest/services/compute/json/availability_zone_client.py
+++ b/tempest/services/compute/json/availability_zone_client.py
@@ -23,10 +23,9 @@
class AvailabilityZoneClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AvailabilityZoneClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(AvailabilityZoneClientJSON, self).__init__(
+ auth_provider)
self.service = CONF.compute.catalog_type
def get_availability_zone_list(self):
diff --git a/tempest/services/compute/json/certificates_client.py b/tempest/services/compute/json/certificates_client.py
index 06e2ae7..de0f1a8 100644
--- a/tempest/services/compute/json/certificates_client.py
+++ b/tempest/services/compute/json/certificates_client.py
@@ -23,10 +23,8 @@
class CertificatesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(CertificatesClientJSON, self).__init__(username,
- password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(CertificatesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_certificate(self, id):
@@ -38,6 +36,6 @@
def create_certificate(self):
"""create certificates."""
url = "os-certificates"
- resp, body = self.post(url, None, self.headers)
+ resp, body = self.post(url, None)
body = json.loads(body)
return resp, body['certificate']
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index 8505d93..f7e2737 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -23,9 +23,8 @@
class ExtensionsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ExtensionsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ExtensionsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_extensions(self):
diff --git a/tempest/services/compute/json/fixed_ips_client.py b/tempest/services/compute/json/fixed_ips_client.py
index 19bf4e2..af750a3 100644
--- a/tempest/services/compute/json/fixed_ips_client.py
+++ b/tempest/services/compute/json/fixed_ips_client.py
@@ -23,9 +23,8 @@
class FixedIPsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FixedIPsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FixedIPsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_fixed_ip_details(self, fixed_ip):
@@ -37,5 +36,5 @@
def reserve_fixed_ip(self, ip, body):
"""This reserves and unreserves fixed ips."""
url = "os-fixed-ips/%s/action" % (ip)
- resp, body = self.post(url, json.dumps(body), self.headers)
+ resp, body = self.post(url, json.dumps(body))
return resp, body
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 7fcf1b2..289b09e 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -24,9 +24,8 @@
class FlavorsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FlavorsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FlavorsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_flavors(self, params=None):
@@ -70,7 +69,7 @@
if kwargs.get('is_public'):
post_body['os-flavor-access:is_public'] = kwargs.get('is_public')
post_body = json.dumps({'flavor': post_body})
- resp, body = self.post('flavors', post_body, self.headers)
+ resp, body = self.post('flavors', post_body)
body = json.loads(body)
return resp, body['flavor']
@@ -93,7 +92,7 @@
"""Sets extra Specs to the mentioned flavor."""
post_body = json.dumps({'extra_specs': specs})
resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['extra_specs']
@@ -113,8 +112,7 @@
def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
"""Update specified extra Specs of the mentioned flavor and key."""
resp, body = self.put('flavors/%s/os-extra_specs/%s' %
- (flavor_id, key),
- json.dumps(kwargs), self.headers)
+ (flavor_id, key), json.dumps(kwargs))
body = json.loads(body)
return resp, body
@@ -125,8 +123,7 @@
def list_flavor_access(self, flavor_id):
"""Gets flavor access information given the flavor id."""
- resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id,
- self.headers)
+ resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id)
body = json.loads(body)
return resp, body['flavor_access']
@@ -138,8 +135,7 @@
}
}
post_body = json.dumps(post_body)
- resp, body = self.post('flavors/%s/action' % flavor_id,
- post_body, self.headers)
+ resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
body = json.loads(body)
return resp, body['flavor_access']
@@ -151,7 +147,6 @@
}
}
post_body = json.dumps(post_body)
- resp, body = self.post('flavors/%s/action' % flavor_id,
- post_body, self.headers)
+ resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
body = json.loads(body)
return resp, body['flavor_access']
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 7c2139d..0385160 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -24,9 +24,8 @@
class FloatingIPsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FloatingIPsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FloatingIPsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_floating_ips(self, params=None):
@@ -53,7 +52,7 @@
url = 'os-floating-ips'
post_body = {'pool': pool_name}
post_body = json.dumps(post_body)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
body = json.loads(body)
return resp, body['floating_ip']
@@ -73,7 +72,7 @@
}
post_body = json.dumps(post_body)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def disassociate_floating_ip_from_server(self, floating_ip, server_id):
@@ -86,7 +85,7 @@
}
post_body = json.dumps(post_body)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def is_resource_deleted(self, id):
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 33e5345..d826a78 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -23,9 +23,8 @@
class HostsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HostsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(HostsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_hosts(self, params=None):
@@ -56,8 +55,7 @@
request_body.update(**kwargs)
request_body = json.dumps(request_body)
- resp, body = self.put("os-hosts/%s" % str(hostname), request_body,
- self.headers)
+ resp, body = self.put("os-hosts/%s" % str(hostname), request_body)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/compute/json/hypervisor_client.py b/tempest/services/compute/json/hypervisor_client.py
index a4d8660..74844dc 100644
--- a/tempest/services/compute/json/hypervisor_client.py
+++ b/tempest/services/compute/json/hypervisor_client.py
@@ -23,10 +23,8 @@
class HypervisorClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HypervisorClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(HypervisorClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_hypervisor_list(self):
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index c9adc3f..b3d8c35 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -26,9 +26,8 @@
class ImagesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ImagesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ImagesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -47,7 +46,7 @@
post_body = json.dumps(post_body)
resp, body = self.post('servers/%s/action' % str(server_id),
- post_body, self.headers)
+ post_body)
return resp, body
def list_images(self, params=None):
@@ -94,16 +93,14 @@
def set_image_metadata(self, image_id, meta):
"""Sets the metadata for an image."""
post_body = json.dumps({'metadata': meta})
- resp, body = self.put('images/%s/metadata' % str(image_id),
- post_body, self.headers)
+ resp, body = self.put('images/%s/metadata' % str(image_id), post_body)
body = json.loads(body)
return resp, body['metadata']
def update_image_metadata(self, image_id, meta):
"""Updates the metadata for an image."""
post_body = json.dumps({'metadata': meta})
- resp, body = self.post('images/%s/metadata' % str(image_id),
- post_body, self.headers)
+ resp, body = self.post('images/%s/metadata' % str(image_id), post_body)
body = json.loads(body)
return resp, body['metadata']
@@ -117,7 +114,7 @@
"""Sets the value for a specific image metadata key."""
post_body = json.dumps({'meta': meta})
resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['meta']
diff --git a/tempest/services/compute/json/instance_usage_audit_log_client.py b/tempest/services/compute/json/instance_usage_audit_log_client.py
index 2673bd7..27930f2 100644
--- a/tempest/services/compute/json/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/json/instance_usage_audit_log_client.py
@@ -23,9 +23,9 @@
class InstanceUsagesAuditLogClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
+ def __init__(self, auth_provider):
super(InstanceUsagesAuditLogClientJSON, self).__init__(
- username, password, auth_url, tenant_name)
+ auth_provider)
self.service = CONF.compute.catalog_type
def list_instance_usage_audit_logs(self):
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
index c0a2db2..f4c4c64 100644
--- a/tempest/services/compute/json/interfaces_client.py
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -25,9 +25,8 @@
class InterfacesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(InterfacesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(InterfacesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_interfaces(self, server):
@@ -47,7 +46,6 @@
post_body['interfaceAttachment']['fixed_ips'] = [fip]
post_body = json.dumps(post_body)
resp, body = self.post('servers/%s/os-interface' % server,
- headers=self.headers,
body=post_body)
body = json.loads(body)
return resp, body['interfaceAttachment']
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index dd4e3e1..356c2e6 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -23,9 +23,8 @@
class KeyPairsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(KeyPairsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(KeyPairsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_keypairs(self):
@@ -48,8 +47,7 @@
if pub_key:
post_body['keypair']['public_key'] = pub_key
post_body = json.dumps(post_body)
- resp, body = self.post("os-keypairs",
- headers=self.headers, body=post_body)
+ resp, body = self.post("os-keypairs", body=post_body)
body = json.loads(body)
return resp, body['keypair']
diff --git a/tempest/services/compute/json/limits_client.py b/tempest/services/compute/json/limits_client.py
index c5cc988..765ba79 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -23,9 +23,8 @@
class LimitsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(LimitsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(LimitsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_absolute_limits(self):
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index df12467..7607cc0 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -23,9 +23,8 @@
class QuotasClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(QuotasClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(QuotasClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_quota_set(self, tenant_id):
@@ -97,8 +96,7 @@
post_body['security_groups'] = security_groups
post_body = json.dumps({'quota_set': post_body})
- resp, body = self.put('os-quota-sets/%s' % str(tenant_id), post_body,
- self.headers)
+ resp, body = self.put('os-quota-sets/%s' % str(tenant_id), post_body)
body = json.loads(body)
return resp, body['quota_set']
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index 299a6e6..2cd2d2e 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -25,10 +25,8 @@
class SecurityGroupsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(SecurityGroupsClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(SecurityGroupsClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_security_groups(self, params=None):
@@ -60,7 +58,7 @@
'description': description,
}
post_body = json.dumps({'security_group': post_body})
- resp, body = self.post('os-security-groups', post_body, self.headers)
+ resp, body = self.post('os-security-groups', post_body)
body = json.loads(body)
return resp, body['security_group']
@@ -79,7 +77,7 @@
post_body['description'] = description
post_body = json.dumps({'security_group': post_body})
resp, body = self.put('os-security-groups/%s' % str(security_group_id),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['security_group']
@@ -109,7 +107,7 @@
}
post_body = json.dumps({'security_group_rule': post_body})
url = 'os-security-group-rules'
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
body = json.loads(body)
return resp, body['security_group_rule']
@@ -125,3 +123,10 @@
if sg['id'] == security_group_id:
return resp, sg['rules']
raise exceptions.NotFound('No such Security Group')
+
+ def is_resource_deleted(self, id):
+ try:
+ self.get_security_group(id)
+ except exceptions.NotFound:
+ return True
+ return False
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 0987f05..025c4e5 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -28,11 +28,9 @@
class ServersClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None,
- auth_version='v2'):
- super(ServersClientJSON, self).__init__(username, password,
- auth_url, tenant_name,
- auth_version=auth_version)
+ def __init__(self, auth_provider):
+ super(ServersClientJSON, self).__init__(auth_provider)
+
self.service = CONF.compute.catalog_type
def create_server(self, name, image_ref, flavor_ref, **kwargs):
@@ -80,7 +78,7 @@
if value is not None:
post_body[post_param] = value
post_body = json.dumps({'server': post_body})
- resp, body = self.post('servers', post_body, self.headers)
+ resp, body = self.post('servers', post_body)
body = json.loads(body)
# NOTE(maurosr): this deals with the case of multiple server create
@@ -118,8 +116,7 @@
post_body['OS-DCF:diskConfig'] = disk_config
post_body = json.dumps({'server': post_body})
- resp, body = self.put("servers/%s" % str(server_id),
- post_body, self.headers)
+ resp, body = self.put("servers/%s" % str(server_id), post_body)
body = json.loads(body)
return resp, body['server']
@@ -196,7 +193,7 @@
def action(self, server_id, action_name, response_key, **kwargs):
post_body = json.dumps({action_name: kwargs})
resp, body = self.post('servers/%s/action' % str(server_id),
- post_body, self.headers)
+ post_body)
if response_key is not None:
body = json.loads(body)[response_key]
return resp, body
@@ -271,14 +268,14 @@
else:
post_body = json.dumps({'metadata': meta})
resp, body = self.put('servers/%s/metadata' % str(server_id),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['metadata']
def update_server_metadata(self, server_id, meta):
post_body = json.dumps({'metadata': meta})
resp, body = self.post('servers/%s/metadata' % str(server_id),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['metadata']
@@ -290,7 +287,7 @@
def set_server_metadata_item(self, server_id, key, meta):
post_body = json.dumps({'meta': meta})
resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['meta']
@@ -314,7 +311,7 @@
}
})
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
- post_body, self.headers)
+ post_body)
return resp, body
def detach_volume(self, server_id, volume_id):
@@ -342,8 +339,7 @@
req_body = json.dumps({'os-migrateLive': migrate_params})
- resp, body = self.post("servers/%s/action" % str(server_id),
- req_body, self.headers)
+ resp, body = self.post("servers/%s/action" % str(server_id), req_body)
return resp, body
def migrate_server(self, server_id, **kwargs):
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index c209cf3..8380dc2 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -25,9 +25,8 @@
class ServicesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ServicesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ServicesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_services(self, params=None):
@@ -46,7 +45,7 @@
binary: Service binary
"""
post_body = json.dumps({'binary': binary, 'host': host_name})
- resp, body = self.put('os-services/enable', post_body, self.headers)
+ resp, body = self.put('os-services/enable', post_body)
body = json.loads(body)
return resp, body['service']
@@ -57,6 +56,6 @@
binary: Service binary
"""
post_body = json.dumps({'binary': binary, 'host': host_name})
- resp, body = self.put('os-services/disable', post_body, self.headers)
+ resp, body = self.put('os-services/disable', post_body)
body = json.loads(body)
return resp, body['service']
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index c141fa7..b14fa9b 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -24,9 +24,8 @@
class TenantUsagesClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(TenantUsagesClientJSON, self).__init__(
- username, password, auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(TenantUsagesClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_tenant_usages(self, params=None):
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index d61b8a5..4b9dc0b 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -26,10 +26,9 @@
class VolumesExtensionsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumesExtensionsClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumesExtensionsClientJSON, self).__init__(
+ auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.volume.build_interval
self.build_timeout = CONF.volume.build_timeout
@@ -76,7 +75,7 @@
}
post_body = json.dumps({'volume': post_body})
- resp, body = self.post('os-volumes', post_body, self.headers)
+ resp, body = self.post('os-volumes', post_body)
body = json.loads(body)
return resp, body['volume']
diff --git a/tempest/services/compute/v3/json/aggregates_client.py b/tempest/services/compute/v3/json/aggregates_client.py
index f3824f8..20ce87b 100644
--- a/tempest/services/compute/v3/json/aggregates_client.py
+++ b/tempest/services/compute/v3/json/aggregates_client.py
@@ -24,10 +24,8 @@
class AggregatesV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AggregatesV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(AggregatesV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_aggregates(self):
@@ -45,7 +43,7 @@
def create_aggregate(self, **kwargs):
"""Creates a new aggregate."""
post_body = json.dumps({'aggregate': kwargs})
- resp, body = self.post('os-aggregates', post_body, self.headers)
+ resp, body = self.post('os-aggregates', post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -57,8 +55,7 @@
'availability_zone': availability_zone
}
put_body = json.dumps({'aggregate': put_body})
- resp, body = self.put('os-aggregates/%s' % str(aggregate_id),
- put_body, self.headers)
+ resp, body = self.put('os-aggregates/%s' % str(aggregate_id), put_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -81,7 +78,7 @@
}
post_body = json.dumps({'add_host': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -92,7 +89,7 @@
}
post_body = json.dumps({'remove_host': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
@@ -103,6 +100,6 @@
}
post_body = json.dumps({'set_metadata': post_body})
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['aggregate']
diff --git a/tempest/services/compute/v3/json/availability_zone_client.py b/tempest/services/compute/v3/json/availability_zone_client.py
index a3f4d94..4a6db55 100644
--- a/tempest/services/compute/v3/json/availability_zone_client.py
+++ b/tempest/services/compute/v3/json/availability_zone_client.py
@@ -23,10 +23,9 @@
class AvailabilityZoneV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AvailabilityZoneV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(AvailabilityZoneV3ClientJSON, self).__init__(
+ auth_provider)
self.service = CONF.compute.catalog_v3_type
def get_availability_zone_list(self):
diff --git a/tempest/services/compute/v3/json/certificates_client.py b/tempest/services/compute/v3/json/certificates_client.py
index 1833cb9..620eedf 100644
--- a/tempest/services/compute/v3/json/certificates_client.py
+++ b/tempest/services/compute/v3/json/certificates_client.py
@@ -23,10 +23,8 @@
class CertificatesV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(CertificatesV3ClientJSON, self).__init__(username,
- password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(CertificatesV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def get_certificate(self, id):
@@ -38,6 +36,6 @@
def create_certificate(self):
"""create certificates."""
url = "os-certificates"
- resp, body = self.post(url, None, self.headers)
+ resp, body = self.post(url, None)
body = json.loads(body)
return resp, body['certificate']
diff --git a/tempest/services/compute/v3/json/extensions_client.py b/tempest/services/compute/v3/json/extensions_client.py
index f760093..54f0aba 100644
--- a/tempest/services/compute/v3/json/extensions_client.py
+++ b/tempest/services/compute/v3/json/extensions_client.py
@@ -23,10 +23,8 @@
class ExtensionsV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ExtensionsV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(ExtensionsV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_extensions(self):
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index d8a54a6..f9df2b8 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -24,9 +24,8 @@
class FlavorsV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FlavorsV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FlavorsV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_flavors(self, params=None):
@@ -70,7 +69,7 @@
if kwargs.get('is_public'):
post_body['flavor-access:is_public'] = kwargs.get('is_public')
post_body = json.dumps({'flavor': post_body})
- resp, body = self.post('flavors', post_body, self.headers)
+ resp, body = self.post('flavors', post_body)
body = json.loads(body)
return resp, body['flavor']
@@ -93,7 +92,7 @@
"""Sets extra Specs to the mentioned flavor."""
post_body = json.dumps({'extra_specs': specs})
resp, body = self.post('flavors/%s/flavor-extra-specs' % flavor_id,
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['extra_specs']
@@ -113,8 +112,7 @@
def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
"""Update specified extra Specs of the mentioned flavor and key."""
resp, body = self.put('flavors/%s/flavor-extra-specs/%s' %
- (flavor_id, key),
- json.dumps(kwargs), self.headers)
+ (flavor_id, key), json.dumps(kwargs))
body = json.loads(body)
return resp, body
@@ -125,8 +123,7 @@
def list_flavor_access(self, flavor_id):
"""Gets flavor access information given the flavor id."""
- resp, body = self.get('flavors/%s/flavor-access' % flavor_id,
- self.headers)
+ resp, body = self.get('flavors/%s/flavor-access' % flavor_id)
body = json.loads(body)
return resp, body['flavor_access']
@@ -138,8 +135,7 @@
}
}
post_body = json.dumps(post_body)
- resp, body = self.post('flavors/%s/action' % flavor_id,
- post_body, self.headers)
+ resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
body = json.loads(body)
return resp, body['flavor_access']
@@ -151,7 +147,6 @@
}
}
post_body = json.dumps(post_body)
- resp, body = self.post('flavors/%s/action' % flavor_id,
- post_body, self.headers)
+ resp, body = self.post('flavors/%s/action' % flavor_id, post_body)
body = json.loads(body)
return resp, body['flavor_access']
diff --git a/tempest/services/compute/v3/json/hosts_client.py b/tempest/services/compute/v3/json/hosts_client.py
index d15b237..76af626 100644
--- a/tempest/services/compute/v3/json/hosts_client.py
+++ b/tempest/services/compute/v3/json/hosts_client.py
@@ -23,9 +23,8 @@
class HostsV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HostsV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(HostsV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_hosts(self, params=None):
@@ -56,8 +55,7 @@
request_body.update(**kwargs)
request_body = json.dumps({'host': request_body})
- resp, body = self.put("os-hosts/%s" % str(hostname), request_body,
- self.headers)
+ resp, body = self.put("os-hosts/%s" % str(hostname), request_body)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/compute/v3/json/hypervisor_client.py b/tempest/services/compute/v3/json/hypervisor_client.py
index a4ec606..e07a1fb 100644
--- a/tempest/services/compute/v3/json/hypervisor_client.py
+++ b/tempest/services/compute/v3/json/hypervisor_client.py
@@ -23,10 +23,8 @@
class HypervisorV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HypervisorV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(HypervisorV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def get_hypervisor_list(self):
diff --git a/tempest/services/compute/v3/json/instance_usage_audit_log_client.py b/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
deleted file mode 100644
index b51f490..0000000
--- a/tempest/services/compute/v3/json/instance_usage_audit_log_client.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2013 IBM Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-
-from tempest.common.rest_client import RestClient
-from tempest import config
-
-CONF = config.CONF
-
-
-class InstanceUsagesAuditLogV3ClientJSON(RestClient):
-
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(InstanceUsagesAuditLogV3ClientJSON, self).__init__(
- username, password, auth_url, tenant_name)
- self.service = CONF.compute.catalog_v3_type
-
- def list_instance_usage_audit_logs(self, time_before=None):
- if time_before:
- url = 'os-instance-usage-audit-log?before=%s' % time_before
- else:
- url = 'os-instance-usage-audit-log'
- resp, body = self.get(url)
- body = json.loads(body)
- return resp, body["instance_usage_audit_log"]
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
index dc06395..c167520 100644
--- a/tempest/services/compute/v3/json/interfaces_client.py
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -25,10 +25,8 @@
class InterfacesV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(InterfacesV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(InterfacesV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_interfaces(self, server):
@@ -47,7 +45,6 @@
post_body['fixed_ips'] = [dict(ip_address=fixed_ip)]
post_body = json.dumps({'interface_attachment': post_body})
resp, body = self.post('servers/%s/os-attach-interfaces' % server,
- headers=self.headers,
body=post_body)
body = json.loads(body)
return resp, body['interface_attachment']
diff --git a/tempest/services/compute/v3/json/keypairs_client.py b/tempest/services/compute/v3/json/keypairs_client.py
index c2efb84..821b86f 100644
--- a/tempest/services/compute/v3/json/keypairs_client.py
+++ b/tempest/services/compute/v3/json/keypairs_client.py
@@ -23,9 +23,8 @@
class KeyPairsV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(KeyPairsV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(KeyPairsV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_keypairs(self):
@@ -48,8 +47,7 @@
if pub_key:
post_body['keypair']['public_key'] = pub_key
post_body = json.dumps(post_body)
- resp, body = self.post("keypairs",
- headers=self.headers, body=post_body)
+ resp, body = self.post("keypairs", body=post_body)
body = json.loads(body)
return resp, body['keypair']
diff --git a/tempest/services/compute/v3/json/quotas_client.py b/tempest/services/compute/v3/json/quotas_client.py
index ea0d3a2..a01b9d2 100644
--- a/tempest/services/compute/v3/json/quotas_client.py
+++ b/tempest/services/compute/v3/json/quotas_client.py
@@ -23,9 +23,8 @@
class QuotasV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(QuotasV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(QuotasV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def get_quota_set(self, tenant_id):
@@ -85,8 +84,7 @@
post_body['security_groups'] = security_groups
post_body = json.dumps({'quota_set': post_body})
- resp, body = self.put('os-quota-sets/%s' % str(tenant_id), post_body,
- self.headers)
+ resp, body = self.put('os-quota-sets/%s' % str(tenant_id), post_body)
body = json.loads(body)
return resp, body['quota_set']
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index aa8c95a..840e914 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -29,11 +29,8 @@
class ServersV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url,
- tenant_name=None, auth_version='v2'):
- super(ServersV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name,
- auth_version=auth_version)
+ def __init__(self, auth_provider):
+ super(ServersV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def create_server(self, name, image_ref, flavor_ref, **kwargs):
@@ -87,7 +84,7 @@
if value is not None:
post_body[post_param] = value
post_body = json.dumps({'server': post_body})
- resp, body = self.post('servers', post_body, self.headers)
+ resp, body = self.post('servers', post_body)
body = json.loads(body)
# NOTE(maurosr): this deals with the case of multiple server create
@@ -124,8 +121,7 @@
post_body['os-disk-config:disk_config'] = disk_config
post_body = json.dumps({'server': post_body})
- resp, body = self.put("servers/%s" % str(server_id),
- post_body, self.headers)
+ resp, body = self.put("servers/%s" % str(server_id), post_body)
body = json.loads(body)
return resp, body['server']
@@ -202,7 +198,7 @@
def action(self, server_id, action_name, response_key, **kwargs):
post_body = json.dumps({action_name: kwargs})
resp, body = self.post('servers/%s/action' % str(server_id),
- post_body, self.headers)
+ post_body)
if response_key is not None:
body = json.loads(body)[response_key]
return resp, body
@@ -219,6 +215,21 @@
return self.action(server_id, 'change_password', None,
admin_password=admin_password)
+ def get_password(self, server_id):
+ resp, body = self.get("servers/%s/os-server-password" %
+ str(server_id))
+ body = json.loads(body)
+ return resp, body
+
+ def delete_password(self, server_id):
+ """
+ Removes the encrypted server password from the metadata server
+ Note that this does not actually change the instance server
+ password.
+ """
+ return self.delete("servers/%s/os-server-password" %
+ str(server_id))
+
def reboot(self, server_id, reboot_type):
"""Reboots a server."""
return self.action(server_id, 'reboot', None, type=reboot_type)
@@ -261,7 +272,7 @@
post_body = json.dumps(post_body)
resp, body = self.post('servers/%s/action' % str(server_id),
- post_body, self.headers)
+ post_body)
return resp, body
def list_server_metadata(self, server_id):
@@ -275,14 +286,14 @@
else:
post_body = json.dumps({'metadata': meta})
resp, body = self.put('servers/%s/metadata' % str(server_id),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['metadata']
def update_server_metadata(self, server_id, meta):
post_body = json.dumps({'metadata': meta})
resp, body = self.post('servers/%s/metadata' % str(server_id),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['metadata']
@@ -294,7 +305,7 @@
def set_server_metadata_item(self, server_id, key, meta):
post_body = json.dumps({'metadata': meta})
resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
- post_body, self.headers)
+ post_body)
body = json.loads(body)
return resp, body['metadata']
@@ -330,7 +341,7 @@
req_body = json.dumps({'migrate_live': migrate_params})
resp, body = self.post("servers/%s/action" % str(server_id),
- req_body, self.headers)
+ req_body)
return resp, body
def migrate_server(self, server_id, **kwargs):
diff --git a/tempest/services/compute/v3/json/services_client.py b/tempest/services/compute/v3/json/services_client.py
index 174a4f7..e20dfde 100644
--- a/tempest/services/compute/v3/json/services_client.py
+++ b/tempest/services/compute/v3/json/services_client.py
@@ -25,9 +25,8 @@
class ServicesV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ServicesV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ServicesV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def list_services(self, params=None):
@@ -51,7 +50,7 @@
'host': host_name
}
})
- resp, body = self.put('os-services/enable', post_body, self.headers)
+ resp, body = self.put('os-services/enable', post_body)
body = json.loads(body)
return resp, body['service']
@@ -67,6 +66,6 @@
'host': host_name
}
})
- resp, body = self.put('os-services/disable', post_body, self.headers)
+ resp, body = self.put('os-services/disable', post_body)
body = json.loads(body)
return resp, body['service']
diff --git a/tempest/services/compute/v3/json/tenant_usages_client.py b/tempest/services/compute/v3/json/tenant_usages_client.py
deleted file mode 100644
index fbc41de..0000000
--- a/tempest/services/compute/v3/json/tenant_usages_client.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2013 NEC Corporation
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-import urllib
-
-from tempest.common.rest_client import RestClient
-from tempest import config
-
-CONF = config.CONF
-
-
-class TenantUsagesV3ClientJSON(RestClient):
-
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(TenantUsagesV3ClientJSON, self).__init__(
- username, password, auth_url, tenant_name)
- self.service = CONF.compute.catalog_v3_type
-
- def list_tenant_usages(self, params=None):
- url = 'os-simple-tenant-usage'
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- return resp, body['tenant_usages'][0]
-
- def get_tenant_usage(self, tenant_id, params=None):
- url = 'os-simple-tenant-usage/%s' % tenant_id
- if params:
- url += '?%s' % urllib.urlencode(params)
-
- resp, body = self.get(url)
- body = json.loads(body)
- return resp, body['tenant_usage']
diff --git a/tempest/services/compute/v3/json/version_client.py b/tempest/services/compute/v3/json/version_client.py
index 419bbb8..b560c58 100644
--- a/tempest/services/compute/v3/json/version_client.py
+++ b/tempest/services/compute/v3/json/version_client.py
@@ -23,10 +23,8 @@
class VersionV3ClientJSON(rest_client.RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VersionV3ClientJSON, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(VersionV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.compute.catalog_v3_type
def get_version(self):
diff --git a/tempest/services/compute/xml/aggregates_client.py b/tempest/services/compute/xml/aggregates_client.py
index ddef18b..cf853ba 100644
--- a/tempest/services/compute/xml/aggregates_client.py
+++ b/tempest/services/compute/xml/aggregates_client.py
@@ -28,9 +28,8 @@
class AggregatesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AggregatesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(AggregatesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _format_aggregate(self, g):
@@ -52,14 +51,13 @@
def list_aggregates(self):
"""Get aggregate list."""
- resp, body = self.get("os-aggregates", self.headers)
+ resp, body = self.get("os-aggregates")
aggregates = self._parse_array(etree.fromstring(body))
return resp, aggregates
def get_aggregate(self, aggregate_id):
"""Get details of the given aggregate."""
- resp, body = self.get("os-aggregates/%s" % str(aggregate_id),
- self.headers)
+ resp, body = self.get("os-aggregates/%s" % str(aggregate_id))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
@@ -69,8 +67,7 @@
name=name,
availability_zone=availability_zone)
resp, body = self.post('os-aggregates',
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
@@ -80,15 +77,13 @@
name=name,
availability_zone=availability_zone)
resp, body = self.put('os-aggregates/%s' % str(aggregate_id),
- str(Document(put_body)),
- self.headers)
+ str(Document(put_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
def delete_aggregate(self, aggregate_id):
"""Deletes the given aggregate."""
- return self.delete("os-aggregates/%s" % str(aggregate_id),
- self.headers)
+ return self.delete("os-aggregates/%s" % str(aggregate_id))
def is_resource_deleted(self, id):
try:
@@ -101,8 +96,7 @@
"""Adds a host to the given aggregate."""
post_body = Element("add_host", host=host)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
@@ -110,8 +104,7 @@
"""Removes a host from the given aggregate."""
post_body = Element("remove_host", host=host)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
@@ -125,7 +118,6 @@
meta.append(Text(v))
metadata.append(meta)
resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
aggregate = self._format_aggregate(etree.fromstring(body))
return resp, aggregate
diff --git a/tempest/services/compute/xml/availability_zone_client.py b/tempest/services/compute/xml/availability_zone_client.py
index ac1bb4b..3d8ac8a 100644
--- a/tempest/services/compute/xml/availability_zone_client.py
+++ b/tempest/services/compute/xml/availability_zone_client.py
@@ -24,21 +24,20 @@
class AvailabilityZoneClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AvailabilityZoneClientXML, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(AvailabilityZoneClientXML, self).__init__(
+ auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
return [xml_to_json(x) for x in node]
def get_availability_zone_list(self):
- resp, body = self.get('os-availability-zone', self.headers)
+ resp, body = self.get('os-availability-zone')
availability_zone = self._parse_array(etree.fromstring(body))
return resp, availability_zone
def get_availability_zone_list_detail(self):
- resp, body = self.get('os-availability-zone/detail', self.headers)
+ resp, body = self.get('os-availability-zone/detail')
availability_zone = self._parse_array(etree.fromstring(body))
return resp, availability_zone
diff --git a/tempest/services/compute/xml/certificates_client.py b/tempest/services/compute/xml/certificates_client.py
index 3f2438d..4ee10c4 100644
--- a/tempest/services/compute/xml/certificates_client.py
+++ b/tempest/services/compute/xml/certificates_client.py
@@ -22,20 +22,19 @@
class CertificatesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(CertificatesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(CertificatesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_certificate(self, id):
url = "os-certificates/%s" % (id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_resp(body)
return resp, body
def create_certificate(self):
"""create certificates."""
url = "os-certificates"
- resp, body = self.post(url, None, self.headers)
+ resp, body = self.post(url, None)
body = self._parse_resp(body)
return resp, body
diff --git a/tempest/services/compute/xml/extensions_client.py b/tempest/services/compute/xml/extensions_client.py
index 98cd3e3..f97b64d 100644
--- a/tempest/services/compute/xml/extensions_client.py
+++ b/tempest/services/compute/xml/extensions_client.py
@@ -24,9 +24,8 @@
class ExtensionsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ExtensionsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ExtensionsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
@@ -37,7 +36,7 @@
def list_extensions(self):
url = 'extensions'
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
@@ -47,6 +46,6 @@
return any([e for e in exts if e['name'] == extension])
def get_extension(self, extension_alias):
- resp, body = self.get('extensions/%s' % extension_alias, self.headers)
+ resp, body = self.get('extensions/%s' % extension_alias)
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/fixed_ips_client.py b/tempest/services/compute/xml/fixed_ips_client.py
index f212e21..b89e096 100644
--- a/tempest/services/compute/xml/fixed_ips_client.py
+++ b/tempest/services/compute/xml/fixed_ips_client.py
@@ -25,14 +25,13 @@
class FixedIPsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FixedIPsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FixedIPsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_fixed_ip_details(self, fixed_ip):
url = "os-fixed-ips/%s" % (fixed_ip)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_resp(body)
return resp, body
@@ -45,5 +44,5 @@
key, value = body.popitem()
xml_body = Element(key)
xml_body.append(Text(value))
- resp, body = self.post(url, str(Document(xml_body)), self.headers)
+ resp, body = self.post(url, str(Document(xml_body)))
return resp, body
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index 74c0e47..554b253 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -35,9 +35,8 @@
class FlavorsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FlavorsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FlavorsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _format_flavor(self, f):
@@ -82,7 +81,7 @@
if params:
url += "?%s" % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
flavors = self._parse_array(etree.fromstring(body))
return resp, flavors
@@ -95,7 +94,7 @@
return self._list_flavors(url, params)
def get_flavor_details(self, flavor_id):
- resp, body = self.get("flavors/%s" % str(flavor_id), self.headers)
+ resp, body = self.get("flavors/%s" % str(flavor_id))
body = xml_to_json(etree.fromstring(body))
flavor = self._format_flavor(body)
return resp, flavor
@@ -121,14 +120,14 @@
kwargs.get('is_public'))
flavor.add_attr('xmlns:OS-FLV-EXT-DATA', XMLNS_OS_FLV_EXT_DATA)
flavor.add_attr('xmlns:os-flavor-access', XMLNS_OS_FLV_ACCESS)
- resp, body = self.post('flavors', str(Document(flavor)), self.headers)
+ resp, body = self.post('flavors', str(Document(flavor)))
body = xml_to_json(etree.fromstring(body))
flavor = self._format_flavor(body)
return resp, flavor
def delete_flavor(self, flavor_id):
"""Deletes the given flavor."""
- return self.delete("flavors/%s" % str(flavor_id), self.headers)
+ return self.delete("flavors/%s" % str(flavor_id))
def is_resource_deleted(self, id):
# Did not use get_flavor_details(id) for verification as it gives
@@ -146,21 +145,20 @@
for key in specs.keys():
extra_specs.add_attr(key, specs[key])
resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
- str(Document(extra_specs)), self.headers)
+ str(Document(extra_specs)))
body = xml_to_json(etree.fromstring(body))
return resp, body
def get_flavor_extra_spec(self, flavor_id):
"""Gets extra Specs of the mentioned flavor."""
- resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id,
- self.headers)
+ resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
body = xml_to_json(etree.fromstring(body))
return resp, body
def get_flavor_extra_spec_with_key(self, flavor_id, key):
"""Gets extra Specs key-value of the mentioned flavor and key."""
resp, xml_body = self.get('flavors/%s/os-extra_specs/%s' %
- (str(flavor_id), key), self.headers)
+ (str(flavor_id), key))
body = {}
element = etree.fromstring(xml_body)
key = element.get('key')
@@ -177,8 +175,7 @@
element.append(value)
resp, body = self.put('flavors/%s/os-extra_specs/%s' %
- (flavor_id, key),
- str(doc), self.headers)
+ (flavor_id, key), str(doc))
body = xml_to_json(etree.fromstring(body))
return resp, {key: body}
@@ -192,8 +189,7 @@
def list_flavor_access(self, flavor_id):
"""Gets flavor access information given the flavor id."""
- resp, body = self.get('flavors/%s/os-flavor-access' % str(flavor_id),
- self.headers)
+ resp, body = self.get('flavors/%s/os-flavor-access' % str(flavor_id))
body = self._parse_array(etree.fromstring(body))
return resp, body
@@ -203,8 +199,7 @@
server = Element("addTenantAccess")
doc.append(server)
server.add_attr("tenant", tenant_id)
- resp, body = self.post('flavors/%s/action' % str(flavor_id),
- str(doc), self.headers)
+ resp, body = self.post('flavors/%s/action' % str(flavor_id), str(doc))
body = self._parse_array_access(etree.fromstring(body))
return resp, body
@@ -214,7 +209,6 @@
server = Element("removeTenantAccess")
doc.append(server)
server.add_attr("tenant", tenant_id)
- resp, body = self.post('flavors/%s/action' % str(flavor_id),
- str(doc), self.headers)
+ resp, body = self.post('flavors/%s/action' % str(flavor_id), str(doc))
body = self._parse_array_access(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index bbfe86b..d6decf3 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -28,9 +28,8 @@
class FloatingIPsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(FloatingIPsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(FloatingIPsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
@@ -49,14 +48,14 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_floating_ip_details(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, self.headers)
+ resp, body = self.get(url)
body = self._parse_floating_ip(etree.fromstring(body))
if resp.status == 404:
raise exceptions.NotFound(body)
@@ -70,16 +69,16 @@
pool = Element("pool")
pool.append(Text(pool_name))
doc.append(pool)
- resp, body = self.post(url, str(doc), self.headers)
+ resp, body = self.post(url, str(doc))
else:
- resp, body = self.post(url, None, self.headers)
+ resp, body = self.post(url, None)
body = self._parse_floating_ip(etree.fromstring(body))
return resp, body
def delete_floating_ip(self, floating_ip_id):
"""Deletes the provided floating IP from the project."""
url = "os-floating-ips/%s" % str(floating_ip_id)
- resp, body = self.delete(url, self.headers)
+ resp, body = self.delete(url)
return resp, body
def associate_floating_ip_to_server(self, floating_ip, server_id):
@@ -89,7 +88,7 @@
server = Element("addFloatingIp")
doc.append(server)
server.add_attr("address", floating_ip)
- resp, body = self.post(url, str(doc), self.headers)
+ resp, body = self.post(url, str(doc))
return resp, body
def disassociate_floating_ip_from_server(self, floating_ip, server_id):
@@ -99,7 +98,7 @@
server = Element("removeFloatingIp")
doc.append(server)
server.add_attr("address", floating_ip)
- resp, body = self.post(url, str(doc), self.headers)
+ resp, body = self.post(url, str(doc))
return resp, body
def is_resource_deleted(self, id):
@@ -115,6 +114,6 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
index 441be0e..13abe18 100644
--- a/tempest/services/compute/xml/hosts_client.py
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -26,9 +26,8 @@
class HostsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HostsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(HostsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_hosts(self, params=None):
@@ -38,7 +37,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
@@ -46,7 +45,7 @@
def show_host_detail(self, hostname):
"""Show detail information for the host."""
- resp, body = self.get("os-hosts/%s" % str(hostname), self.headers)
+ resp, body = self.get("os-hosts/%s" % str(hostname))
node = etree.fromstring(body)
body = [xml_to_json(node)]
return resp, body
@@ -59,8 +58,7 @@
for k, v in kwargs.iteritems():
request_body.append(Element(k, v))
resp, body = self.put("os-hosts/%s" % str(hostname),
- str(Document(request_body)),
- self.headers)
+ str(Document(request_body)))
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
@@ -68,8 +66,7 @@
def startup_host(self, hostname):
"""Startup a host."""
- resp, body = self.get("os-hosts/%s/startup" % str(hostname),
- self.headers)
+ resp, body = self.get("os-hosts/%s/startup" % str(hostname))
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
@@ -77,8 +74,7 @@
def shutdown_host(self, hostname):
"""Shutdown a host."""
- resp, body = self.get("os-hosts/%s/shutdown" % str(hostname),
- self.headers)
+ resp, body = self.get("os-hosts/%s/shutdown" % str(hostname))
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
@@ -86,8 +82,7 @@
def reboot_host(self, hostname):
"""Reboot a host."""
- resp, body = self.get("os-hosts/%s/reboot" % str(hostname),
- self.headers)
+ resp, body = self.get("os-hosts/%s/reboot" % str(hostname))
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
diff --git a/tempest/services/compute/xml/hypervisor_client.py b/tempest/services/compute/xml/hypervisor_client.py
index bee2dfd..3c1ef08 100644
--- a/tempest/services/compute/xml/hypervisor_client.py
+++ b/tempest/services/compute/xml/hypervisor_client.py
@@ -24,10 +24,8 @@
class HypervisorClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(HypervisorClientXML, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(HypervisorClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
@@ -35,46 +33,42 @@
def get_hypervisor_list(self):
"""List hypervisors information."""
- resp, body = self.get('os-hypervisors', self.headers)
+ resp, body = self.get('os-hypervisors')
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_list_details(self):
"""Show detailed hypervisors information."""
- resp, body = self.get('os-hypervisors/detail', self.headers)
+ resp, body = self.get('os-hypervisors/detail')
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_show_details(self, hyper_id):
"""Display the details of the specified hypervisor."""
- resp, body = self.get('os-hypervisors/%s' % hyper_id,
- self.headers)
+ resp, body = self.get('os-hypervisors/%s' % hyper_id)
hypervisor = xml_to_json(etree.fromstring(body))
return resp, hypervisor
def get_hypervisor_servers(self, hyper_name):
"""List instances belonging to the specified hypervisor."""
- resp, body = self.get('os-hypervisors/%s/servers' % hyper_name,
- self.headers)
+ resp, body = self.get('os-hypervisors/%s/servers' % hyper_name)
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
def get_hypervisor_stats(self):
"""Get hypervisor statistics over all compute nodes."""
- resp, body = self.get('os-hypervisors/statistics', self.headers)
+ resp, body = self.get('os-hypervisors/statistics')
stats = xml_to_json(etree.fromstring(body))
return resp, stats
def get_hypervisor_uptime(self, hyper_id):
"""Display the uptime of the specified hypervisor."""
- resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id,
- self.headers)
+ resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id)
uptime = xml_to_json(etree.fromstring(body))
return resp, uptime
def search_hypervisor(self, hyper_name):
"""Search specified hypervisor."""
- resp, body = self.get('os-hypervisors/%s/search' % hyper_name,
- self.headers)
+ resp, body = self.get('os-hypervisors/%s/search' % hyper_name)
hypervisors = self._parse_array(etree.fromstring(body))
return resp, hypervisors
diff --git a/tempest/services/compute/xml/images_client.py b/tempest/services/compute/xml/images_client.py
index 98de7df..9f80c55 100644
--- a/tempest/services/compute/xml/images_client.py
+++ b/tempest/services/compute/xml/images_client.py
@@ -32,9 +32,8 @@
class ImagesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ImagesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ImagesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -103,7 +102,7 @@
data.append(Text(v))
metadata.append(data)
resp, body = self.post('servers/%s/action' % str(server_id),
- str(Document(post_body)), self.headers)
+ str(Document(post_body)))
return resp, body
def list_images(self, params=None):
@@ -112,7 +111,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_images(etree.fromstring(body))
return resp, body['images']
@@ -124,20 +123,20 @@
url = "images/detail?" + param_list
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_images(etree.fromstring(body))
return resp, body['images']
def get_image(self, image_id):
"""Returns the details of a single image."""
- resp, body = self.get("images/%s" % str(image_id), self.headers)
+ resp, body = self.get("images/%s" % str(image_id))
self.expected_success(200, resp)
body = self._parse_image(etree.fromstring(body))
return resp, body
def delete_image(self, image_id):
"""Deletes the provided image."""
- return self.delete("images/%s" % str(image_id), self.headers)
+ return self.delete("images/%s" % str(image_id))
def wait_for_image_status(self, image_id, status):
"""Waits for an image to reach a given status."""
@@ -153,8 +152,7 @@
def list_image_metadata(self, image_id):
"""Lists all metadata items for an image."""
- resp, body = self.get("images/%s/metadata" % str(image_id),
- self.headers)
+ resp, body = self.get("images/%s/metadata" % str(image_id))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -162,7 +160,7 @@
"""Sets the metadata for an image."""
post_body = self._metadata_body(meta)
resp, body = self.put('images/%s/metadata' % image_id,
- str(Document(post_body)), self.headers)
+ str(Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -170,14 +168,14 @@
"""Updates the metadata for an image."""
post_body = self._metadata_body(meta)
resp, body = self.post('images/%s/metadata' % str(image_id),
- str(Document(post_body)), self.headers)
+ str(Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def get_image_metadata_item(self, image_id, key):
"""Returns the value for a specific image metadata key."""
resp, body = self.get("images/%s/metadata/%s.xml" %
- (str(image_id), key), self.headers)
+ (str(image_id), key))
body = self._parse_metadata(etree.fromstring(body))
return resp, body
@@ -187,7 +185,7 @@
post_body = Element('meta', key=key)
post_body.append(Text(v))
resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
- str(Document(post_body)), self.headers)
+ str(Document(post_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -195,14 +193,13 @@
"""Sets the value for a specific image metadata key."""
post_body = Document('meta', Text(meta), key=key)
resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
- post_body, self.headers)
+ post_body)
body = xml_to_json(etree.fromstring(body))
return resp, body['meta']
def delete_image_metadata_item(self, image_id, key):
"""Deletes a single image metadata key/value pair."""
- return self.delete("images/%s/metadata/%s" % (str(image_id), key),
- self.headers)
+ return self.delete("images/%s/metadata/%s" % (str(image_id), key))
def is_resource_deleted(self, id):
try:
diff --git a/tempest/services/compute/xml/instance_usage_audit_log_client.py b/tempest/services/compute/xml/instance_usage_audit_log_client.py
index 2e1d6c8..baa6966 100644
--- a/tempest/services/compute/xml/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/xml/instance_usage_audit_log_client.py
@@ -24,19 +24,19 @@
class InstanceUsagesAuditLogClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
+ def __init__(self, auth_provider):
super(InstanceUsagesAuditLogClientXML, self).__init__(
- username, password, auth_url, tenant_name)
+ auth_provider)
self.service = CONF.compute.catalog_type
def list_instance_usage_audit_logs(self):
url = 'os-instance_usage_audit_log'
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
instance_usage_audit_logs = xml_to_json(etree.fromstring(body))
return resp, instance_usage_audit_logs
def get_instance_usage_audit_log(self, time_before):
url = 'os-instance_usage_audit_log/%s' % time_before
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
instance_usage_audit_log = xml_to_json(etree.fromstring(body))
return resp, instance_usage_audit_log
diff --git a/tempest/services/compute/xml/interfaces_client.py b/tempest/services/compute/xml/interfaces_client.py
index c90f507..6155cd6 100644
--- a/tempest/services/compute/xml/interfaces_client.py
+++ b/tempest/services/compute/xml/interfaces_client.py
@@ -30,9 +30,8 @@
class InterfacesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(InterfacesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(InterfacesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _process_xml_interface(self, node):
@@ -43,7 +42,7 @@
return iface
def list_interfaces(self, server):
- resp, body = self.get('servers/%s/os-interface' % server, self.headers)
+ resp, body = self.get('servers/%s/os-interface' % server)
node = etree.fromstring(body)
interfaces = [self._process_xml_interface(x)
for x in node.getchildren()]
@@ -71,14 +70,12 @@
iface.append(_fixed_ips)
doc.append(iface)
resp, body = self.post('servers/%s/os-interface' % server,
- headers=self.headers,
body=str(doc))
body = self._process_xml_interface(etree.fromstring(body))
return resp, body
def show_interface(self, server, port_id):
- resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id),
- self.headers)
+ resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
body = self._process_xml_interface(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/keypairs_client.py b/tempest/services/compute/xml/keypairs_client.py
index da3303e..5641251 100644
--- a/tempest/services/compute/xml/keypairs_client.py
+++ b/tempest/services/compute/xml/keypairs_client.py
@@ -28,19 +28,18 @@
class KeyPairsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(KeyPairsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(KeyPairsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_keypairs(self):
- resp, body = self.get("os-keypairs", self.headers)
+ resp, body = self.get("os-keypairs")
node = etree.fromstring(body)
body = [{'keypair': xml_to_json(x)} for x in node.getchildren()]
return resp, body
def get_keypair(self, key_name):
- resp, body = self.get("os-keypairs/%s" % str(key_name), self.headers)
+ resp, body = self.get("os-keypairs/%s" % str(key_name))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -62,8 +61,7 @@
doc.append(keypair_element)
- resp, body = self.post("os-keypairs",
- headers=self.headers, body=str(doc))
+ resp, body = self.post("os-keypairs", body=str(doc))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/limits_client.py b/tempest/services/compute/xml/limits_client.py
index 9233697..61c434c 100644
--- a/tempest/services/compute/xml/limits_client.py
+++ b/tempest/services/compute/xml/limits_client.py
@@ -25,13 +25,12 @@
class LimitsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(LimitsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(LimitsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def get_absolute_limits(self):
- resp, body = self.get("limits", self.headers)
+ resp, body = self.get("limits")
body = objectify.fromstring(body)
lim = NS + 'absolute'
ret = {}
@@ -42,7 +41,7 @@
return resp, ret
def get_specific_absolute_limit(self, absolute_limit):
- resp, body = self.get("limits", self.headers)
+ resp, body = self.get("limits")
body = objectify.fromstring(body)
lim = NS + 'absolute'
ret = {}
diff --git a/tempest/services/compute/xml/quotas_client.py b/tempest/services/compute/xml/quotas_client.py
index 74aad1b..00c3275 100644
--- a/tempest/services/compute/xml/quotas_client.py
+++ b/tempest/services/compute/xml/quotas_client.py
@@ -27,9 +27,8 @@
class QuotasClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(QuotasClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(QuotasClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _format_quota(self, q):
@@ -51,7 +50,7 @@
"""List the quota set for a tenant."""
url = 'os-quota-sets/%s' % str(tenant_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
return resp, body
@@ -60,7 +59,7 @@
"""List the default quota set for a tenant."""
url = 'os-quota-sets/%s/defaults' % str(tenant_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
return resp, body
@@ -120,8 +119,7 @@
post_body.add_attr('security_groups', security_groups)
resp, body = self.put('os-quota-sets/%s' % str(tenant_id),
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = xml_to_json(etree.fromstring(body))
body = self._format_quota(body)
return resp, body
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index c32a81e..947f6da 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -30,10 +30,8 @@
class SecurityGroupsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(SecurityGroupsClientXML, self).__init__(
- username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(SecurityGroupsClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
@@ -53,14 +51,14 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_security_group(self, security_group_id):
"""Get the details of a Security Group."""
url = "os-security-groups/%s" % str(security_group_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -75,8 +73,7 @@
des.append(Text(content=description))
security_group.append(des)
resp, body = self.post('os-security-groups',
- str(Document(security_group)),
- self.headers)
+ str(Document(security_group)))
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -99,15 +96,13 @@
security_group.append(des)
resp, body = self.put('os-security-groups/%s' %
str(security_group_id),
- str(Document(security_group)),
- self.headers)
+ str(Document(security_group)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_security_group(self, security_group_id):
"""Deletes the provided Security Group."""
- return self.delete('os-security-groups/%s' %
- str(security_group_id), self.headers)
+ return self.delete('os-security-groups/%s' % str(security_group_id))
def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
to_port, **kwargs):
@@ -138,19 +133,19 @@
group_rule.append(element)
url = 'os-security-group-rules'
- resp, body = self.post(url, str(Document(group_rule)), self.headers)
+ resp, body = self.post(url, str(Document(group_rule)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_security_group_rule(self, group_rule_id):
"""Deletes the provided Security Group rule."""
return self.delete('os-security-group-rules/%s' %
- str(group_rule_id), self.headers)
+ str(group_rule_id))
def list_security_group_rules(self, security_group_id):
"""List all rules for a security group."""
url = "os-security-groups"
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
secgroups = body.getchildren()
for secgroup in secgroups:
@@ -159,3 +154,10 @@
rules = [xml_to_json(x) for x in node.getchildren()]
return resp, rules
raise exceptions.NotFound('No such Security Group')
+
+ def is_resource_deleted(self, id):
+ try:
+ self.get_security_group(id)
+ except exceptions.NotFound:
+ return True
+ return False
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index c16d4a3..a182d35 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -141,11 +141,8 @@
class ServersClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None,
- auth_version='v2'):
- super(ServersClientXML, self).__init__(username, password,
- auth_url, tenant_name,
- auth_version=auth_version)
+ def __init__(self, auth_provider):
+ super(ServersClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_key_value(self, node):
@@ -189,7 +186,7 @@
def get_server(self, server_id):
"""Returns the details of an existing server."""
- resp, body = self.get("servers/%s" % str(server_id), self.headers)
+ resp, body = self.get("servers/%s" % str(server_id))
server = self._parse_server(etree.fromstring(body))
return resp, server
@@ -248,7 +245,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
servers = self._parse_array(etree.fromstring(body))
return resp, {"servers": servers}
@@ -257,7 +254,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
servers = self._parse_array(etree.fromstring(body))
return resp, {"servers": servers}
@@ -285,8 +282,7 @@
meta.append(Text(v))
metadata.append(meta)
- resp, body = self.put('servers/%s' % str(server_id),
- str(doc), self.headers)
+ resp, body = self.put('servers/%s' % str(server_id), str(doc))
return resp, xml_to_json(etree.fromstring(body))
def create_server(self, name, image_ref, flavor_ref, **kwargs):
@@ -359,7 +355,7 @@
temp.append(Text(k['contents']))
personality.append(temp)
- resp, body = self.post('servers', str(Document(server)), self.headers)
+ resp, body = self.post('servers', str(Document(server)))
server = self._parse_server(etree.fromstring(body))
return resp, server
@@ -397,7 +393,7 @@
def list_addresses(self, server_id):
"""Lists all addresses for a server."""
- resp, body = self.get("servers/%s/ips" % str(server_id), self.headers)
+ resp, body = self.get("servers/%s/ips" % str(server_id))
networks = {}
xml_list = etree.fromstring(body)
@@ -410,8 +406,7 @@
def list_addresses_by_network(self, server_id, network_id):
"""Lists all addresses of a specific network type for a server."""
resp, body = self.get("servers/%s/ips/%s" % (str(server_id),
- network_id),
- self.headers)
+ network_id))
network = self._parse_network(etree.fromstring(body))
return resp, network
@@ -420,8 +415,7 @@
if 'xmlns' not in kwargs:
kwargs['xmlns'] = XMLNS_11
doc = Document((Element(action_name, **kwargs)))
- resp, body = self.post("servers/%s/action" % server_id,
- str(doc), self.headers)
+ resp, body = self.post("servers/%s/action" % server_id, str(doc))
if response_key is not None:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -438,8 +432,7 @@
adminPass=password)
def get_password(self, server_id):
- resp, body = self.get("servers/%s/os-server-password" %
- str(server_id), self.headers)
+ resp, body = self.get("servers/%s/os-server-password" % str(server_id))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -449,8 +442,7 @@
Note that this does not actually change the instance server
password.
"""
- return self.delete("servers/%s/os-server-password" %
- str(server_id))
+ return self.delete("servers/%s/os-server-password" % str(server_id))
def reboot(self, server_id, reboot_type):
return self.action(server_id, "reboot", None, type=reboot_type)
@@ -481,7 +473,7 @@
metadata.append(meta)
resp, body = self.post('servers/%s/action' % server_id,
- str(Document(rebuild)), self.headers)
+ str(Document(rebuild)))
server = self._parse_server(etree.fromstring(body))
return resp, server
@@ -526,12 +518,11 @@
host=dest_host)
resp, body = self.post("servers/%s/action" % str(server_id),
- str(Document(req_body)), self.headers)
+ str(Document(req_body)))
return resp, body
def list_server_metadata(self, server_id):
- resp, body = self.get("servers/%s/metadata" % str(server_id),
- self.headers)
+ resp, body = self.get("servers/%s/metadata" % str(server_id))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -544,8 +535,7 @@
meta_element = Element("meta", key=k)
meta_element.append(Text(v))
metadata.append(meta_element)
- resp, body = self.put('servers/%s/metadata' % str(server_id),
- str(doc), self.headers)
+ resp, body = self.put('servers/%s/metadata' % str(server_id), str(doc))
return resp, xml_to_json(etree.fromstring(body))
def update_server_metadata(self, server_id, meta):
@@ -557,13 +547,12 @@
meta_element.append(Text(v))
metadata.append(meta_element)
resp, body = self.post("/servers/%s/metadata" % str(server_id),
- str(doc), headers=self.headers)
+ str(doc))
body = xml_to_json(etree.fromstring(body))
return resp, body
def get_server_metadata_item(self, server_id, key):
- resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key),
- headers=self.headers)
+ resp, body = self.get("servers/%s/metadata/%s" % (str(server_id), key))
return resp, dict([(etree.fromstring(body).attrib['key'],
xml_to_json(etree.fromstring(body)))])
@@ -574,7 +563,7 @@
meta_element.append(Text(v))
doc.append(meta_element)
resp, body = self.put('servers/%s/metadata/%s' % (str(server_id), key),
- str(doc), self.headers)
+ str(doc))
return resp, xml_to_json(etree.fromstring(body))
def delete_server_metadata_item(self, server_id, key):
@@ -591,7 +580,7 @@
List the virtual interfaces used in an instance.
"""
resp, body = self.get('/'.join(['servers', server_id,
- 'os-virtual-interfaces']), self.headers)
+ 'os-virtual-interfaces']))
virt_int = self._parse_xml_virtual_interfaces(etree.fromstring(body))
return resp, virt_int
@@ -607,7 +596,7 @@
post_body = Element("volumeAttachment", volumeId=volume_id,
device=device)
resp, body = self.post('servers/%s/os-volume_attachments' % server_id,
- str(Document(post_body)), self.headers)
+ str(Document(post_body)))
return resp, body
def detach_volume(self, server_id, volume_id):
@@ -619,22 +608,20 @@
def get_server_diagnostics(self, server_id):
"""Get the usage data for a server."""
- resp, body = self.get("servers/%s/diagnostics" % server_id,
- self.headers)
+ resp, body = self.get("servers/%s/diagnostics" % server_id)
body = xml_to_json(etree.fromstring(body))
return resp, body
def list_instance_actions(self, server_id):
"""List the provided server action."""
- resp, body = self.get("servers/%s/os-instance-actions" % server_id,
- self.headers)
+ resp, body = self.get("servers/%s/os-instance-actions" % server_id)
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_instance_action(self, server_id, request_id):
"""Returns the action details of the provided server."""
resp, body = self.get("servers/%s/os-instance-actions/%s" %
- (server_id, request_id), self.headers)
+ (server_id, request_id))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/services_client.py b/tempest/services/compute/xml/services_client.py
index bfc824d..5943ea9 100644
--- a/tempest/services/compute/xml/services_client.py
+++ b/tempest/services/compute/xml/services_client.py
@@ -29,9 +29,8 @@
class ServicesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ServicesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ServicesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def list_services(self, params=None):
@@ -39,7 +38,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
node = etree.fromstring(body)
body = [xml_to_json(x) for x in node.getchildren()]
return resp, body
@@ -54,8 +53,7 @@
post_body.add_attr('binary', binary)
post_body.add_attr('host', host_name)
- resp, body = self.put('os-services/enable', str(Document(post_body)),
- self.headers)
+ resp, body = self.put('os-services/enable', str(Document(post_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -69,7 +67,6 @@
post_body.add_attr('binary', binary)
post_body.add_attr('host', host_name)
- resp, body = self.put('os-services/disable', str(Document(post_body)),
- self.headers)
+ resp, body = self.put('os-services/disable', str(Document(post_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/compute/xml/tenant_usages_client.py b/tempest/services/compute/xml/tenant_usages_client.py
index ae813a6..96c3147 100644
--- a/tempest/services/compute/xml/tenant_usages_client.py
+++ b/tempest/services/compute/xml/tenant_usages_client.py
@@ -26,10 +26,8 @@
class TenantUsagesClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(TenantUsagesClientXML, self).__init__(username,
- password, auth_url,
- tenant_name)
+ def __init__(self, auth_provider):
+ super(TenantUsagesClientXML, self).__init__(auth_provider)
self.service = CONF.compute.catalog_type
def _parse_array(self, node):
@@ -41,7 +39,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
tenant_usage = self._parse_array(etree.fromstring(body))
return resp, tenant_usage['tenant_usage']
@@ -50,6 +48,6 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
tenant_usage = self._parse_array(etree.fromstring(body))
return resp, tenant_usage
diff --git a/tempest/services/compute/xml/volumes_extensions_client.py b/tempest/services/compute/xml/volumes_extensions_client.py
index d6723e1..a43fc21 100644
--- a/tempest/services/compute/xml/volumes_extensions_client.py
+++ b/tempest/services/compute/xml/volumes_extensions_client.py
@@ -32,9 +32,9 @@
class VolumesExtensionsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumesExtensionsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumesExtensionsClientXML, self).__init__(
+ auth_provider)
self.service = CONF.compute.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -60,7 +60,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
@@ -74,7 +74,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
@@ -84,7 +84,7 @@
def get_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "os-volumes/%s" % str(volume_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
return resp, self._parse_volume(body)
@@ -110,8 +110,7 @@
meta.append(Text(value))
_metadata.append(meta)
- resp, body = self.post('os-volumes', str(Document(volume)),
- self.headers)
+ resp, body = self.post('os-volumes', str(Document(volume)))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/data_processing/v1_1/client.py b/tempest/services/data_processing/v1_1/client.py
index a1d558a..e96b44b 100644
--- a/tempest/services/data_processing/v1_1/client.py
+++ b/tempest/services/data_processing/v1_1/client.py
@@ -22,9 +22,8 @@
class DataProcessingClient(rest_client.RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(DataProcessingClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(DataProcessingClient, self).__init__(auth_provider)
self.service = CONF.data_processing.catalog_type
@classmethod
@@ -78,3 +77,17 @@
uri = "node-group-templates/%s" % tmpl_id
return self.delete(uri)
+
+ def list_plugins(self):
+ """List all enabled plugins."""
+
+ uri = 'plugins'
+ return self._request_and_parse(self.get, uri, 'plugins')
+
+ def get_plugin(self, plugin_name, plugin_version=None):
+ """Returns the details of a single plugin."""
+
+ uri = "plugins/%s" % plugin_name
+ if plugin_version:
+ uri += '/%s' % plugin_version
+ return self._request_and_parse(self.get, uri, 'plugin')
diff --git a/tempest/services/identity/json/identity_client.py b/tempest/services/identity/json/identity_client.py
index 1ed7044..349a9e9 100644
--- a/tempest/services/identity/json/identity_client.py
+++ b/tempest/services/identity/json/identity_client.py
@@ -12,22 +12,23 @@
import json
-from tempest.common import http
-from tempest.common.rest_client import RestClient
+from tempest.common import rest_client
from tempest import config
from tempest import exceptions
CONF = config.CONF
-class IdentityClientJSON(RestClient):
+class IdentityClientJSON(rest_client.RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(IdentityClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(IdentityClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
+ # Needed for xml service client
+ self.list_tags = ["roles", "tenants", "users", "services"]
+
def has_admin_extensions(self):
"""
Returns True if the KSADM Admin Extensions are supported
@@ -45,9 +46,8 @@
'name': name,
}
post_body = json.dumps({'role': post_body})
- resp, body = self.post('OS-KSADM/roles', post_body, self.headers)
- body = json.loads(body)
- return resp, body['role']
+ resp, body = self.post('OS-KSADM/roles', post_body)
+ return resp, self._parse_resp(body)
def create_tenant(self, name, **kwargs):
"""
@@ -62,30 +62,24 @@
'enabled': kwargs.get('enabled', True),
}
post_body = json.dumps({'tenant': post_body})
- resp, body = self.post('tenants', post_body, self.headers)
- body = json.loads(body)
- return resp, body['tenant']
+ resp, body = self.post('tenants', post_body)
+ return resp, self._parse_resp(body)
def delete_role(self, role_id):
"""Delete a role."""
- resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
- return resp, body
+ return self.delete('OS-KSADM/roles/%s' % str(role_id))
def list_user_roles(self, tenant_id, user_id):
"""Returns a list of roles assigned to a user for a tenant."""
url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
resp, body = self.get(url)
- body = json.loads(body)
- return resp, body['roles']
+ return resp, self._parse_resp(body)
def assign_user_role(self, tenant_id, user_id, role_id):
"""Add roles to a user on a tenant."""
- post_body = json.dumps({})
resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
- (tenant_id, user_id, role_id), post_body,
- self.headers)
- body = json.loads(body)
- return resp, body['role']
+ (tenant_id, user_id, role_id), "")
+ return resp, self._parse_resp(body)
def remove_user_role(self, tenant_id, user_id, role_id):
"""Removes a role assignment for a user on a tenant."""
@@ -94,20 +88,17 @@
def delete_tenant(self, tenant_id):
"""Delete a tenant."""
- resp, body = self.delete('tenants/%s' % str(tenant_id))
- return resp, body
+ return self.delete('tenants/%s' % str(tenant_id))
def get_tenant(self, tenant_id):
"""Get tenant details."""
resp, body = self.get('tenants/%s' % str(tenant_id))
- body = json.loads(body)
- return resp, body['tenant']
+ return resp, self._parse_resp(body)
def list_roles(self):
"""Returns roles."""
resp, body = self.get('OS-KSADM/roles')
- body = json.loads(body)
- return resp, body['roles']
+ return resp, self._parse_resp(body)
def list_tenants(self):
"""Returns tenants."""
@@ -135,10 +126,8 @@
'enabled': en,
}
post_body = json.dumps({'tenant': post_body})
- resp, body = self.post('tenants/%s' % tenant_id, post_body,
- self.headers)
- body = json.loads(body)
- return resp, body['tenant']
+ resp, body = self.post('tenants/%s' % tenant_id, post_body)
+ return resp, self._parse_resp(body)
def create_user(self, name, password, tenant_id, email, **kwargs):
"""Create a user."""
@@ -151,34 +140,28 @@
if kwargs.get('enabled') is not None:
post_body['enabled'] = kwargs.get('enabled')
post_body = json.dumps({'user': post_body})
- resp, body = self.post('users', post_body, self.headers)
- body = json.loads(body)
- return resp, body['user']
+ resp, body = self.post('users', post_body)
+ return resp, self._parse_resp(body)
def update_user(self, user_id, **kwargs):
"""Updates a user."""
put_body = json.dumps({'user': kwargs})
- resp, body = self.put('users/%s' % user_id, put_body,
- self.headers)
- body = json.loads(body)
- return resp, body['user']
+ resp, body = self.put('users/%s' % user_id, put_body)
+ return resp, self._parse_resp(body)
def get_user(self, user_id):
"""GET a user."""
resp, body = self.get("users/%s" % user_id)
- body = json.loads(body)
- return resp, body['user']
+ return resp, self._parse_resp(body)
def delete_user(self, user_id):
"""Delete a user."""
- resp, body = self.delete("users/%s" % user_id)
- return resp, body
+ return self.delete("users/%s" % user_id)
def get_users(self):
"""Get the list of users."""
resp, body = self.get("users")
- body = json.loads(body)
- return resp, body['users']
+ return resp, self._parse_resp(body)
def enable_disable_user(self, user_id, enabled):
"""Enables or disables a user."""
@@ -186,21 +169,17 @@
'enabled': enabled
}
put_body = json.dumps({'user': put_body})
- resp, body = self.put('users/%s/enabled' % user_id,
- put_body, self.headers)
- body = json.loads(body)
- return resp, body
+ resp, body = self.put('users/%s/enabled' % user_id, put_body)
+ return resp, self._parse_resp(body)
def delete_token(self, token_id):
"""Delete a token."""
- resp, body = self.delete("tokens/%s" % token_id)
- return resp, body
+ return self.delete("tokens/%s" % token_id)
def list_users_for_tenant(self, tenant_id):
"""List users for a Tenant."""
resp, body = self.get('/tenants/%s/users' % tenant_id)
- body = json.loads(body)
- return resp, body['users']
+ return resp, self._parse_resp(body)
def get_user_by_username(self, tenant_id, username):
resp, users = self.list_users_for_tenant(tenant_id)
@@ -217,22 +196,19 @@
'description': kwargs.get('description')
}
post_body = json.dumps({'OS-KSADM:service': post_body})
- resp, body = self.post('/OS-KSADM/services', post_body, self.headers)
- body = json.loads(body)
- return resp, body['OS-KSADM:service']
+ resp, body = self.post('/OS-KSADM/services', post_body)
+ return resp, self._parse_resp(body)
def get_service(self, service_id):
"""Get Service."""
url = '/OS-KSADM/services/%s' % service_id
resp, body = self.get(url)
- body = json.loads(body)
- return resp, body['OS-KSADM:service']
+ return resp, self._parse_resp(body)
def list_services(self):
"""List Service - Returns Services."""
resp, body = self.get('/OS-KSADM/services/')
- body = json.loads(body)
- return resp, body['OS-KSADM:services']
+ return resp, self._parse_resp(body)
def delete_service(self, service_id):
"""Delete Service."""
@@ -240,12 +216,12 @@
return self.delete(url)
-class TokenClientJSON(RestClient):
+class TokenClientJSON(IdentityClientJSON):
def __init__(self):
+ super(TokenClientJSON, self).__init__(None)
auth_url = CONF.identity.uri
- # TODO(jaypipes) Why is this all repeated code in here?
# Normalize URI to ensure /tokens is in it.
if 'tokens' not in auth_url:
auth_url = auth_url.rstrip('/') + '/tokens'
@@ -262,34 +238,41 @@
'tenantName': tenant,
}
}
- headers = {'Content-Type': 'application/json'}
body = json.dumps(creds)
- resp, body = self.post(self.auth_url, headers=headers, body=body)
- return resp, body
+ resp, body = self.post(self.auth_url, body=body)
+
+ return resp, body['access']
def request(self, method, url, headers=None, body=None):
"""A simple HTTP request interface."""
- dscv = CONF.identity.disable_ssl_certificate_validation
- self.http_obj = http.ClosingHttp(
- disable_ssl_certificate_validation=dscv)
if headers is None:
- headers = {}
-
+ # Always accept 'json', for TokenClientXML too.
+ # Because XML response is not easily
+ # converted to the corresponding JSON one
+ headers = self.get_headers(accept_type="json")
self._log_request(method, url, headers, body)
resp, resp_body = self.http_obj.request(url, method,
headers=headers, body=body)
self._log_response(resp, resp_body)
- if resp.status in (401, 403):
+ if resp.status in [401, 403]:
resp_body = json.loads(resp_body)
raise exceptions.Unauthorized(resp_body['error']['message'])
+ elif resp.status not in [200, 201]:
+ raise exceptions.IdentityError(
+ 'Unexpected status code {0}'.format(resp.status))
+ if isinstance(resp_body, str):
+ resp_body = json.loads(resp_body)
return resp, resp_body
- def get_token(self, user, password, tenant):
+ def get_token(self, user, password, tenant, auth_data=False):
+ """
+ Returns (token id, token data) for supplied credentials
+ """
resp, body = self.auth(user, password, tenant)
- if resp['status'] != '202':
- body = json.loads(body)
- access = body['access']
- token = access['token']
- return token['id']
+
+ if auth_data:
+ return body['token']['id'], body
+ else:
+ return body['token']['id']
diff --git a/tempest/services/identity/v3/json/credentials_client.py b/tempest/services/identity/v3/json/credentials_client.py
index 1250d79..5d6632a 100644
--- a/tempest/services/identity/v3/json/credentials_client.py
+++ b/tempest/services/identity/v3/json/credentials_client.py
@@ -14,7 +14,6 @@
# under the License.
import json
-from urlparse import urlparse
from tempest.common.rest_client import RestClient
from tempest import config
@@ -24,20 +23,11 @@
class CredentialsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(CredentialsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(CredentialsClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(CredentialsClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def create_credential(self, access_key, secret_key, user_id, project_id):
"""Creates a credential."""
@@ -50,8 +40,7 @@
"user_id": user_id
}
post_body = json.dumps({'credential': post_body})
- resp, body = self.post('credentials', post_body,
- self.headers)
+ resp, body = self.post('credentials', post_body)
body = json.loads(body)
body['credential']['blob'] = json.loads(body['credential']['blob'])
return resp, body['credential']
@@ -73,8 +62,7 @@
"user_id": user_id
}
post_body = json.dumps({'credential': post_body})
- resp, body = self.patch('credentials/%s' % credential_id, post_body,
- self.headers)
+ resp, body = self.patch('credentials/%s' % credential_id, post_body)
body = json.loads(body)
body['credential']['blob'] = json.loads(body['credential']['blob'])
return resp, body['credential']
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index bb2230b..652c345 100644
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -14,7 +14,6 @@
# under the License.
import json
-import urlparse
from tempest.common.rest_client import RestClient
from tempest import config
@@ -24,20 +23,11 @@
class EndPointClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(EndPointClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(EndPointClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(
- urlparse.urlparse(self.base_url).path, "/v3")
- return super(EndPointClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def list_endpoints(self):
"""GET endpoints."""
@@ -57,7 +47,7 @@
'enabled': enabled
}
post_body = json.dumps({'endpoint': post_body})
- resp, body = self.post('endpoints', post_body, self.headers)
+ resp, body = self.post('endpoints', post_body)
body = json.loads(body)
return resp, body['endpoint']
@@ -76,8 +66,7 @@
if enabled is not None:
post_body['enabled'] = enabled
post_body = json.dumps({'endpoint': post_body})
- resp, body = self.patch('endpoints/%s' % endpoint_id, post_body,
- self.headers)
+ resp, body = self.patch('endpoints/%s' % endpoint_id, post_body)
body = json.loads(body)
return resp, body['endpoint']
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index 7dd5c6e..b044e4d 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -14,30 +14,21 @@
# under the License.
import json
-from urlparse import urlparse
from tempest.common.rest_client import RestClient
from tempest import config
+from tempest import exceptions
CONF = config.CONF
class IdentityV3ClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(IdentityV3ClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(IdentityV3ClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(IdentityV3ClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def create_user(self, user_name, **kwargs):
"""Creates a user."""
@@ -57,8 +48,7 @@
'password': password
}
post_body = json.dumps({'user': post_body})
- resp, body = self.post('users', post_body,
- self.headers)
+ resp, body = self.post('users', post_body)
body = json.loads(body)
return resp, body['user']
@@ -80,14 +70,13 @@
'description': description
}
post_body = json.dumps({'user': post_body})
- resp, body = self.patch('users/%s' % user_id, post_body,
- self.headers)
+ resp, body = self.patch('users/%s' % user_id, post_body)
body = json.loads(body)
return resp, body['user']
def list_user_projects(self, user_id):
"""Lists the projects on which a user has roles assigned."""
- resp, body = self.get('users/%s/projects' % user_id, self.headers)
+ resp, body = self.get('users/%s/projects' % user_id)
body = json.loads(body)
return resp, body['projects']
@@ -120,7 +109,7 @@
'name': name
}
post_body = json.dumps({'project': post_body})
- resp, body = self.post('projects', post_body, self.headers)
+ resp, body = self.post('projects', post_body)
body = json.loads(body)
return resp, body['project']
@@ -143,8 +132,7 @@
'domain_id': domain_id,
}
post_body = json.dumps({'project': post_body})
- resp, body = self.patch('projects/%s' % project_id, post_body,
- self.headers)
+ resp, body = self.patch('projects/%s' % project_id, post_body)
body = json.loads(body)
return resp, body['project']
@@ -165,7 +153,7 @@
'name': name
}
post_body = json.dumps({'role': post_body})
- resp, body = self.post('roles', post_body, self.headers)
+ resp, body = self.post('roles', post_body)
body = json.loads(body)
return resp, body['role']
@@ -181,8 +169,7 @@
'name': name
}
post_body = json.dumps({'role': post_body})
- resp, body = self.patch('roles/%s' % str(role_id), post_body,
- self.headers)
+ resp, body = self.patch('roles/%s' % str(role_id), post_body)
body = json.loads(body)
return resp, body['role']
@@ -194,8 +181,7 @@
def assign_user_role(self, project_id, user_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
- (project_id, user_id, role_id), None,
- self.headers)
+ (project_id, user_id, role_id), None)
return resp, body
def create_domain(self, name, **kwargs):
@@ -208,7 +194,7 @@
'name': name
}
post_body = json.dumps({'domain': post_body})
- resp, body = self.post('domains', post_body, self.headers)
+ resp, body = self.post('domains', post_body)
body = json.loads(body)
return resp, body['domain']
@@ -235,8 +221,7 @@
'name': name
}
post_body = json.dumps({'domain': post_body})
- resp, body = self.patch('domains/%s' % domain_id, post_body,
- self.headers)
+ resp, body = self.patch('domains/%s' % domain_id, post_body)
body = json.loads(body)
return resp, body['domain']
@@ -271,13 +256,13 @@
'name': name
}
post_body = json.dumps({'group': post_body})
- resp, body = self.post('groups', post_body, self.headers)
+ resp, body = self.post('groups', post_body)
body = json.loads(body)
return resp, body['group']
def get_group(self, group_id):
"""Get group details."""
- resp, body = self.get('groups/%s' % group_id, self.headers)
+ resp, body = self.get('groups/%s' % group_id)
body = json.loads(body)
return resp, body['group']
@@ -291,8 +276,7 @@
'description': description
}
post_body = json.dumps({'group': post_body})
- resp, body = self.patch('groups/%s' % group_id, post_body,
- self.headers)
+ resp, body = self.patch('groups/%s' % group_id, post_body)
body = json.loads(body)
return resp, body['group']
@@ -304,33 +288,30 @@
def add_group_user(self, group_id, user_id):
"""Add user into group."""
resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
- None, self.headers)
+ None)
return resp, body
def list_group_users(self, group_id):
"""List users in group."""
- resp, body = self.get('groups/%s/users' % group_id, self.headers)
+ resp, body = self.get('groups/%s/users' % group_id)
body = json.loads(body)
return resp, body['users']
def delete_group_user(self, group_id, user_id):
"""Delete user in group."""
- resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id),
- self.headers)
+ resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
return resp, body
def assign_user_role_on_project(self, project_id, user_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
- (project_id, user_id, role_id), None,
- self.headers)
+ (project_id, user_id, role_id), None)
return resp, body
def assign_user_role_on_domain(self, domain_id, user_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/users/%s/roles/%s' %
- (domain_id, user_id, role_id), None,
- self.headers)
+ (domain_id, user_id, role_id), None)
return resp, body
def list_user_roles_on_project(self, project_id, user_id):
@@ -362,15 +343,13 @@
def assign_group_role_on_project(self, project_id, group_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/groups/%s/roles/%s' %
- (project_id, group_id, role_id), None,
- self.headers)
+ (project_id, group_id, role_id), None)
return resp, body
def assign_group_role_on_domain(self, domain_id, group_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/groups/%s/roles/%s' %
- (domain_id, group_id, role_id), None,
- self.headers)
+ (domain_id, group_id, role_id), None)
return resp, body
def list_group_roles_on_project(self, project_id, group_id):
@@ -412,7 +391,7 @@
'expires_at': expires_at
}
post_body = json.dumps({'trust': post_body})
- resp, body = self.post('OS-TRUST/trusts', post_body, self.headers)
+ resp, body = self.post('OS-TRUST/trusts', post_body)
body = json.loads(body)
return resp, body['trust']
@@ -462,43 +441,93 @@
class V3TokenClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(V3TokenClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
- self.service = CONF.identity.catalog_type
- self.endpoint_url = 'adminURL'
-
- auth_url = CONF.identity.uri
-
- if 'tokens' not in auth_url:
- auth_url = auth_url.rstrip('/') + '/tokens'
+ def __init__(self):
+ super(V3TokenClientJSON, self).__init__(None)
+ auth_url = CONF.identity.uri_v3
+ if not auth_url and CONF.identity_feature_enabled.api_v3:
+ raise exceptions.InvalidConfiguration('you must specify a v3 uri '
+ 'if using the v3 identity '
+ 'api')
+ if 'auth/tokens' not in auth_url:
+ auth_url = auth_url.rstrip('/') + '/auth/tokens'
self.auth_url = auth_url
- def auth(self, user_id, password):
+ def auth(self, user, password, tenant=None, user_type='id', domain=None):
+ """
+ :param user: user id or name, as specified in user_type
+ :param domain: the user and tenant domain
+
+ Accepts different combinations of credentials. Restrictions:
+ - tenant and domain are only name (no id)
+ - user domain and tenant domain are assumed identical
+ - domain scope is not supported here
+ Sample sample valid combinations:
+ - user_id, password
+ - username, password, domain
+ - username, password, tenant, domain
+ Validation is left to the server side.
+ """
creds = {
'auth': {
'identity': {
'methods': ['password'],
'password': {
'user': {
- 'id': user_id,
- 'password': password
+ 'password': password,
}
}
}
}
}
- headers = {'Content-Type': 'application/json'}
+ if user_type == 'id':
+ creds['auth']['identity']['password']['user']['id'] = user
+ else:
+ creds['auth']['identity']['password']['user']['name'] = user
+ if domain is not None:
+ _domain = dict(name=domain)
+ creds['auth']['identity']['password']['user']['domain'] = _domain
+ if tenant is not None:
+ project = dict(name=tenant, domain=_domain)
+ scope = dict(project=project)
+ creds['auth']['scope'] = scope
+
body = json.dumps(creds)
- resp, body = self.post("auth/tokens", headers=headers, body=body)
+ resp, body = self.post(self.auth_url, body=body)
return resp, body
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(V3TokenClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ def request(self, method, url, headers=None, body=None):
+ """A simple HTTP request interface."""
+ if headers is None:
+ # Always accept 'json', for xml token client too.
+ # Because XML response is not easily
+ # converted to the corresponding JSON one
+ headers = self.get_headers(accept_type="json")
+ self._log_request(method, url, headers, body)
+ resp, resp_body = self.http_obj.request(url, method,
+ headers=headers, body=body)
+ self._log_response(resp, resp_body)
+
+ if resp.status in [401, 403]:
+ resp_body = json.loads(resp_body)
+ raise exceptions.Unauthorized(resp_body['error']['message'])
+ elif resp.status not in [200, 201, 204]:
+ raise exceptions.IdentityError(
+ 'Unexpected status code {0}'.format(resp.status))
+
+ return resp, json.loads(resp_body)
+
+ def get_token(self, user, password, tenant, domain='Default',
+ auth_data=False):
+ """
+ :param user: username
+ Returns (token id, token data) for supplied credentials
+ """
+ resp, body = self.auth(user, password, tenant, user_type='name',
+ domain=domain)
+
+ token = resp.get('x-subject-token')
+ if auth_data:
+ return token, body['token']
+ else:
+ return token
diff --git a/tempest/services/identity/v3/json/policy_client.py b/tempest/services/identity/v3/json/policy_client.py
index 3d98d99..5a3f891 100644
--- a/tempest/services/identity/v3/json/policy_client.py
+++ b/tempest/services/identity/v3/json/policy_client.py
@@ -14,7 +14,6 @@
# under the License.
import json
-from urlparse import urlparse
from tempest.common.rest_client import RestClient
from tempest import config
@@ -24,20 +23,11 @@
class PolicyClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(PolicyClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(PolicyClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(PolicyClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def create_policy(self, blob, type):
"""Creates a Policy."""
@@ -46,7 +36,7 @@
"type": type
}
post_body = json.dumps({'policy': post_body})
- resp, body = self.post('policies', post_body, self.headers)
+ resp, body = self.post('policies', post_body)
body = json.loads(body)
return resp, body['policy']
@@ -72,8 +62,7 @@
}
post_body = json.dumps({'policy': post_body})
url = 'policies/%s' % policy_id
- resp, body = self.patch(url, post_body,
- self.headers)
+ resp, body = self.patch(url, post_body)
body = json.loads(body)
return resp, body['policy']
diff --git a/tempest/services/identity/v3/json/service_client.py b/tempest/services/identity/v3/json/service_client.py
index 57b6e9e..c1faebb 100644
--- a/tempest/services/identity/v3/json/service_client.py
+++ b/tempest/services/identity/v3/json/service_client.py
@@ -14,7 +14,6 @@
# under the License.
import json
-from urlparse import urlparse
from tempest.common.rest_client import RestClient
from tempest import config
@@ -24,20 +23,11 @@
class ServiceClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ServiceClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ServiceClientJSON, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(ServiceClientJSON, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def update_service(self, service_id, **kwargs):
"""Updates a service."""
@@ -51,8 +41,7 @@
'name': name
}
patch_body = json.dumps({'service': patch_body})
- resp, body = self.patch('services/%s' % service_id,
- patch_body, self.headers)
+ resp, body = self.patch('services/%s' % service_id, patch_body)
body = json.loads(body)
return resp, body['service']
@@ -62,3 +51,21 @@
resp, body = self.get(url)
body = json.loads(body)
return resp, body['service']
+
+ def create_service(self, serv_type, name=None, description=None,
+ enabled=True):
+ body_dict = {
+ "name": name,
+ 'type': serv_type,
+ 'enabled': enabled,
+ "description": description,
+ }
+ body = json.dumps({'service': body_dict})
+ resp, body = self.post("services", body)
+ body = json.loads(body)
+ return resp, body["service"]
+
+ def delete_service(self, serv_id):
+ url = "services/" + serv_id
+ resp, body = self.delete(url)
+ return resp, body
diff --git a/tempest/services/identity/v3/xml/credentials_client.py b/tempest/services/identity/v3/xml/credentials_client.py
index c8cdce7..f6fa678 100644
--- a/tempest/services/identity/v3/xml/credentials_client.py
+++ b/tempest/services/identity/v3/xml/credentials_client.py
@@ -14,7 +14,6 @@
# under the License.
import json
-from urlparse import urlparse
from lxml import etree
@@ -32,20 +31,11 @@
class CredentialsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(CredentialsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(CredentialsClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(CredentialsClientXML, self).request(method, url,
- headers=headers,
- body=body)
+ self.api_version = "v3"
def _parse_body(self, body):
data = xml_to_json(body)
@@ -71,8 +61,7 @@
credential = Element('credential', project_id=project_id,
type=cred_type, user_id=user_id)
credential.append(blob)
- resp, body = self.post('credentials', str(Document(credential)),
- self.headers)
+ resp, body = self.post('credentials', str(Document(credential)))
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
@@ -95,27 +84,25 @@
type=cred_type, user_id=user_id)
credential.append(blob)
resp, body = self.patch('credentials/%s' % credential_id,
- str(Document(credential)),
- self.headers)
+ str(Document(credential)))
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
def get_credential(self, credential_id):
"""To GET Details of a credential."""
- resp, body = self.get('credentials/%s' % credential_id, self.headers)
+ resp, body = self.get('credentials/%s' % credential_id)
body = self._parse_body(etree.fromstring(body))
body['blob'] = json.loads(body['blob'])
return resp, body
def list_credentials(self):
"""Lists out all the available credentials."""
- resp, body = self.get('credentials', self.headers)
+ resp, body = self.get('credentials')
body = self._parse_creds(etree.fromstring(body))
return resp, body
def delete_credential(self, credential_id):
"""Deletes a credential."""
- resp, body = self.delete('credentials/%s' % credential_id,
- self.headers)
+ resp, body = self.delete('credentials/%s' % credential_id)
return resp, body
diff --git a/tempest/services/identity/v3/xml/endpoints_client.py b/tempest/services/identity/v3/xml/endpoints_client.py
index e1df3a9..2a88c15 100644
--- a/tempest/services/identity/v3/xml/endpoints_client.py
+++ b/tempest/services/identity/v3/xml/endpoints_client.py
@@ -12,7 +12,6 @@
# 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 urlparse
from lxml import etree
@@ -30,11 +29,11 @@
class EndPointClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(EndPointClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(EndPointClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
+ self.api_version = "v3"
def _parse_array(self, node):
array = []
@@ -53,16 +52,13 @@
dscv = CONF.identity.disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
- self._set_auth()
- self.base_url = self.base_url.replace(
- urlparse.urlparse(self.base_url).path, "/v3")
return super(EndPointClientXML, self).request(method, url,
headers=headers,
body=body)
def list_endpoints(self):
"""Get the list of endpoints."""
- resp, body = self.get("endpoints", self.headers)
+ resp, body = self.get("endpoints")
body = self._parse_array(etree.fromstring(body))
return resp, body
@@ -76,8 +72,7 @@
interface=interface,
url=url, region=region,
enabled=enabled)
- resp, body = self.post('endpoints', str(Document(create_endpoint)),
- self.headers)
+ resp, body = self.post('endpoints', str(Document(create_endpoint)))
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -98,8 +93,7 @@
endpoint.add_attr("region", region)
if enabled is not None:
endpoint.add_attr("enabled", enabled)
- resp, body = self.patch('endpoints/%s' % str(endpoint_id),
- str(doc), self.headers)
+ resp, body = self.patch('endpoints/%s' % str(endpoint_id), str(doc))
body = self._parse_body(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/identity/v3/xml/identity_client.py b/tempest/services/identity/v3/xml/identity_client.py
index de75fe5..5ae0461 100644
--- a/tempest/services/identity/v3/xml/identity_client.py
+++ b/tempest/services/identity/v3/xml/identity_client.py
@@ -13,12 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-from urlparse import urlparse
+import json
from lxml import etree
from tempest.common.rest_client import RestClientXML
from tempest import config
+from tempest import exceptions
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
from tempest.services.compute.xml.common import Text
@@ -31,11 +32,11 @@
class IdentityV3ClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(IdentityV3ClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(IdentityV3ClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
+ self.api_version = "v3"
def _parse_projects(self, node):
array = []
@@ -76,17 +77,8 @@
return array
def _parse_body(self, body):
- json = xml_to_json(body)
- return json
-
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class RestClient."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(IdentityV3ClientXML, self).request(method, url,
- headers=headers,
- body=body)
+ _json = xml_to_json(body)
+ return _json
def create_user(self, user_name, **kwargs):
"""Creates a user."""
@@ -105,8 +97,7 @@
enabled=str(en).lower(),
project_id=project_id,
domain_id=domain_id)
- resp, body = self.post('users', str(Document(post_body)),
- self.headers)
+ resp, body = self.post('users', str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -127,32 +118,31 @@
description=description,
enabled=str(en).lower())
resp, body = self.patch('users/%s' % user_id,
- str(Document(update_user)),
- self.headers)
+ str(Document(update_user)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_user_projects(self, user_id):
"""Lists the projects on which a user has roles assigned."""
- resp, body = self.get('users/%s/projects' % user_id, self.headers)
+ resp, body = self.get('users/%s/projects' % user_id)
body = self._parse_projects(etree.fromstring(body))
return resp, body
def get_users(self):
"""Get the list of users."""
- resp, body = self.get("users", self.headers)
+ resp, body = self.get("users")
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_user(self, user_id):
"""GET a user."""
- resp, body = self.get("users/%s" % user_id, self.headers)
+ resp, body = self.get("users/%s" % user_id)
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_user(self, user_id):
"""Deletes a User."""
- resp, body = self.delete("users/%s" % user_id, self.headers)
+ resp, body = self.delete("users/%s" % user_id)
return resp, body
def create_project(self, name, **kwargs):
@@ -167,14 +157,13 @@
enabled=str(en).lower(),
name=name)
resp, body = self.post('projects',
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_projects(self):
"""Get the list of projects."""
- resp, body = self.get("projects", self.headers)
+ resp, body = self.get("projects")
body = self._parse_projects(etree.fromstring(body))
return resp, body
@@ -192,14 +181,13 @@
enabled=str(en).lower(),
domain_id=domain_id)
resp, body = self.patch('projects/%s' % project_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_project(self, project_id):
"""GET a Project."""
- resp, body = self.get("projects/%s" % project_id, self.headers)
+ resp, body = self.get("projects/%s" % project_id)
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -213,15 +201,13 @@
post_body = Element("role",
xmlns=XMLNS,
name=name)
- resp, body = self.post('roles',
- str(Document(post_body)),
- self.headers)
+ resp, body = self.post('roles', str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_role(self, role_id):
"""GET a Role."""
- resp, body = self.get('roles/%s' % str(role_id), self.headers)
+ resp, body = self.get('roles/%s' % str(role_id))
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -231,21 +217,19 @@
xmlns=XMLNS,
name=name)
resp, body = self.patch('roles/%s' % str(role_id),
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_role(self, role_id):
"""Delete a role."""
- resp, body = self.delete('roles/%s' % str(role_id),
- self.headers)
+ resp, body = self.delete('roles/%s' % str(role_id))
return resp, body
def assign_user_role(self, project_id, user_id, role_id):
"""Add roles to a user on a tenant."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
- (project_id, user_id, role_id), '', self.headers)
+ (project_id, user_id, role_id), '')
return resp, body
def create_domain(self, name, **kwargs):
@@ -257,20 +241,19 @@
name=name,
description=description,
enabled=str(en).lower())
- resp, body = self.post('domains', str(Document(post_body)),
- self.headers)
+ resp, body = self.post('domains', str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_domains(self):
"""Get the list of domains."""
- resp, body = self.get("domains", self.headers)
+ resp, body = self.get("domains")
body = self._parse_domains(etree.fromstring(body))
return resp, body
def delete_domain(self, domain_id):
"""Delete a domain."""
- resp, body = self.delete('domains/%s' % domain_id, self.headers)
+ resp, body = self.delete('domains/%s' % domain_id)
return resp, body
def update_domain(self, domain_id, **kwargs):
@@ -285,14 +268,13 @@
description=description,
enabled=str(en).lower())
resp, body = self.patch('domains/%s' % domain_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_domain(self, domain_id):
"""Get Domain details."""
- resp, body = self.get('domains/%s' % domain_id, self.headers)
+ resp, body = self.get('domains/%s' % domain_id)
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -322,14 +304,13 @@
description=description,
domain_id=domain_id,
project_id=project_id)
- resp, body = self.post('groups', str(Document(post_body)),
- self.headers)
+ resp, body = self.post('groups', str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_group(self, group_id):
"""Get group details."""
- resp, body = self.get('groups/%s' % group_id, self.headers)
+ resp, body = self.get('groups/%s' % group_id)
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -343,136 +324,145 @@
name=name,
description=description)
resp, body = self.patch('groups/%s' % group_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def delete_group(self, group_id):
"""Delete a group."""
- resp, body = self.delete('groups/%s' % group_id, self.headers)
+ resp, body = self.delete('groups/%s' % group_id)
return resp, body
def add_group_user(self, group_id, user_id):
"""Add user into group."""
- resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
- '', self.headers)
+ resp, body = self.put('groups/%s/users/%s' % (group_id, user_id), '')
return resp, body
def list_group_users(self, group_id):
"""List users in group."""
- resp, body = self.get('groups/%s/users' % group_id, self.headers)
+ resp, body = self.get('groups/%s/users' % group_id)
body = self._parse_group_users(etree.fromstring(body))
return resp, body
def delete_group_user(self, group_id, user_id):
"""Delete user in group."""
- resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id),
- self.headers)
+ resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
return resp, body
def assign_user_role_on_project(self, project_id, user_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/users/%s/roles/%s' %
- (project_id, user_id, role_id), '',
- self.headers)
+ (project_id, user_id, role_id), '')
return resp, body
def assign_user_role_on_domain(self, domain_id, user_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/users/%s/roles/%s' %
- (domain_id, user_id, role_id), '',
- self.headers)
+ (domain_id, user_id, role_id), '')
return resp, body
def list_user_roles_on_project(self, project_id, user_id):
"""list roles of a user on a project."""
resp, body = self.get('projects/%s/users/%s/roles' %
- (project_id, user_id), self.headers)
+ (project_id, user_id))
body = self._parse_roles(etree.fromstring(body))
return resp, body
def list_user_roles_on_domain(self, domain_id, user_id):
"""list roles of a user on a domain."""
resp, body = self.get('domains/%s/users/%s/roles' %
- (domain_id, user_id), self.headers)
+ (domain_id, user_id))
body = self._parse_roles(etree.fromstring(body))
return resp, body
def revoke_role_from_user_on_project(self, project_id, user_id, role_id):
"""Delete role of a user on a project."""
resp, body = self.delete('projects/%s/users/%s/roles/%s' %
- (project_id, user_id, role_id), self.headers)
+ (project_id, user_id, role_id))
return resp, body
def revoke_role_from_user_on_domain(self, domain_id, user_id, role_id):
"""Delete role of a user on a domain."""
resp, body = self.delete('domains/%s/users/%s/roles/%s' %
- (domain_id, user_id, role_id), self.headers)
+ (domain_id, user_id, role_id))
return resp, body
def assign_group_role_on_project(self, project_id, group_id, role_id):
"""Add roles to a user on a project."""
resp, body = self.put('projects/%s/groups/%s/roles/%s' %
- (project_id, group_id, role_id), '',
- self.headers)
+ (project_id, group_id, role_id), '')
return resp, body
def assign_group_role_on_domain(self, domain_id, group_id, role_id):
"""Add roles to a user on a domain."""
resp, body = self.put('domains/%s/groups/%s/roles/%s' %
- (domain_id, group_id, role_id), '',
- self.headers)
+ (domain_id, group_id, role_id), '')
return resp, body
def list_group_roles_on_project(self, project_id, group_id):
"""list roles of a user on a project."""
resp, body = self.get('projects/%s/groups/%s/roles' %
- (project_id, group_id), self.headers)
+ (project_id, group_id))
body = self._parse_roles(etree.fromstring(body))
return resp, body
def list_group_roles_on_domain(self, domain_id, group_id):
"""list roles of a user on a domain."""
resp, body = self.get('domains/%s/groups/%s/roles' %
- (domain_id, group_id), self.headers)
+ (domain_id, group_id))
body = self._parse_roles(etree.fromstring(body))
return resp, body
def revoke_role_from_group_on_project(self, project_id, group_id, role_id):
"""Delete role of a user on a project."""
resp, body = self.delete('projects/%s/groups/%s/roles/%s' %
- (project_id, group_id, role_id), self.headers)
+ (project_id, group_id, role_id))
return resp, body
def revoke_role_from_group_on_domain(self, domain_id, group_id, role_id):
"""Delete role of a user on a domain."""
resp, body = self.delete('domains/%s/groups/%s/roles/%s' %
- (domain_id, group_id, role_id), self.headers)
+ (domain_id, group_id, role_id))
return resp, body
class V3TokenClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(V3TokenClientXML, self).__init__(username, password,
- auth_url, tenant_name)
- self.service = CONF.identity.catalog_type
- self.endpoint_url = 'adminURL'
-
- auth_url = CONF.identity.uri
-
- if 'tokens' not in auth_url:
- auth_url = auth_url.rstrip('/') + '/tokens'
+ def __init__(self):
+ super(V3TokenClientXML, self).__init__(None)
+ auth_url = CONF.identity.uri_v3
+ if not auth_url and CONF.identity_feature_enabled.api_v3:
+ raise exceptions.InvalidConfiguration('you must specify a v3 uri '
+ 'if using the v3 identity '
+ 'api')
+ if 'auth/tokens' not in auth_url:
+ auth_url = auth_url.rstrip('/') + '/auth/tokens'
self.auth_url = auth_url
- def auth(self, user_id, password):
- user = Element('user',
- id=user_id,
- password=password)
+ def auth(self, user, password, tenant=None, user_type='id', domain=None):
+ """
+ :param user: user id or name, as specified in user_type
+
+ Accepts different combinations of credentials. Restrictions:
+ - tenant and domain are only name (no id)
+ - user domain and tenant domain are assumed identical
+ Sample sample valid combinations:
+ - user_id, password
+ - username, password, domain
+ - username, password, tenant, domain
+ Validation is left to the server side.
+ """
+ if user_type == 'id':
+ _user = Element('user', id=user, password=password)
+ else:
+ _user = Element('user', name=user, password=password)
+ if domain is not None:
+ _domain = Element('domain', name=domain)
+ _user.append(_domain)
+
password = Element('password')
- password.append(user)
+ password.append(_user)
method = Element('method')
method.append(Text('password'))
@@ -481,18 +471,52 @@
identity = Element('identity')
identity.append(methods)
identity.append(password)
+
auth = Element('auth')
auth.append(identity)
- headers = {'Content-Type': 'application/xml'}
- resp, body = self.post("auth/tokens", headers=headers,
- body=str(Document(auth)))
+
+ if tenant is not None:
+ project = Element('project', name=tenant)
+ project.append(_domain)
+ scope = Element('scope')
+ scope.append(project)
+ auth.append(scope)
+
+ resp, body = self.post(self.auth_url, body=str(Document(auth)))
return resp, body
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(V3TokenClientXML, self).request(method, url,
- headers=headers,
- body=body)
+ def request(self, method, url, headers=None, body=None):
+ """A simple HTTP request interface."""
+ if headers is None:
+ # Always accept 'json', for xml token client too.
+ # Because XML response is not easily
+ # converted to the corresponding JSON one
+ headers = self.get_headers(accept_type="json")
+ self._log_request(method, url, headers, body)
+ resp, resp_body = self.http_obj.request(url, method,
+ headers=headers, body=body)
+ self._log_response(resp, resp_body)
+
+ if resp.status in [401, 403]:
+ resp_body = json.loads(resp_body)
+ raise exceptions.Unauthorized(resp_body['error']['message'])
+ elif resp.status not in [200, 201, 204]:
+ raise exceptions.IdentityError(
+ 'Unexpected status code {0}'.format(resp.status))
+
+ return resp, json.loads(resp_body)
+
+ def get_token(self, user, password, tenant, domain='Default',
+ auth_data=False):
+ """
+ :param user: username
+ Returns (token id, token data) for supplied credentials
+ """
+ resp, body = self.auth(user, password, tenant, user_type='name',
+ domain=domain)
+
+ token = resp.get('x-subject-token')
+ if auth_data:
+ return token, body['token']
+ else:
+ return token
diff --git a/tempest/services/identity/v3/xml/policy_client.py b/tempest/services/identity/v3/xml/policy_client.py
index 54b4ad8..a7e63a7 100644
--- a/tempest/services/identity/v3/xml/policy_client.py
+++ b/tempest/services/identity/v3/xml/policy_client.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from urlparse import urlparse
-
from lxml import etree
from tempest.common import http
@@ -31,11 +29,11 @@
class PolicyClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(PolicyClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(PolicyClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
+ self.api_version = "v3"
def _parse_array(self, node):
array = []
@@ -54,9 +52,6 @@
dscv = CONF.identity.disable_ssl_certificate_validation
self.http_obj = http.ClosingHttp(
disable_ssl_certificate_validation=dscv)
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
return super(PolicyClientXML, self).request(method, url,
headers=headers,
body=body)
@@ -64,21 +59,20 @@
def create_policy(self, blob, type):
"""Creates a Policy."""
create_policy = Element("policy", xmlns=XMLNS, blob=blob, type=type)
- resp, body = self.post('policies', str(Document(create_policy)),
- self.headers)
+ resp, body = self.post('policies', str(Document(create_policy)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def list_policies(self):
"""Lists the policies."""
- resp, body = self.get('policies', self.headers)
+ resp, body = self.get('policies')
body = self._parse_array(etree.fromstring(body))
return resp, body
def get_policy(self, policy_id):
"""Lists out the given policy."""
url = 'policies/%s' % policy_id
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_body(etree.fromstring(body))
return resp, body
@@ -88,8 +82,7 @@
type = kwargs.get('type')
update_policy = Element("policy", xmlns=XMLNS, type=type)
url = 'policies/%s' % policy_id
- resp, body = self.patch(url, str(Document(update_policy)),
- self.headers)
+ resp, body = self.patch(url, str(Document(update_policy)))
body = self._parse_body(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/identity/v3/xml/service_client.py b/tempest/services/identity/v3/xml/service_client.py
index 2997775..be6c443 100644
--- a/tempest/services/identity/v3/xml/service_client.py
+++ b/tempest/services/identity/v3/xml/service_client.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from urlparse import urlparse
-
from lxml import etree
from tempest.common.rest_client import RestClientXML
@@ -30,11 +28,11 @@
class ServiceClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ServiceClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ServiceClientXML, self).__init__(auth_provider)
self.service = CONF.identity.catalog_type
self.endpoint_url = 'adminURL'
+ self.api_version = "v3"
def _parse_array(self, node):
array = []
@@ -46,15 +44,6 @@
data = xml_to_json(body)
return data
- def request(self, method, url, headers=None, body=None, wait=None):
- """Overriding the existing HTTP request in super class rest_client."""
- self._set_auth()
- self.base_url = self.base_url.replace(urlparse(self.base_url).path,
- "/v3")
- return super(ServiceClientXML, self).request(method, url,
- headers=headers,
- body=body)
-
def update_service(self, service_id, **kwargs):
"""Updates a service_id."""
resp, body = self.get_service(service_id)
@@ -68,14 +57,28 @@
description=description,
type=type)
resp, body = self.patch('services/%s' % service_id,
- str(Document(update_service)),
- self.headers)
+ str(Document(update_service)))
body = self._parse_body(etree.fromstring(body))
return resp, body
def get_service(self, service_id):
"""Get Service."""
url = 'services/%s' % service_id
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_body(etree.fromstring(body))
return resp, body
+
+ def create_service(self, serv_type, name=None, description=None):
+ post_body = Element("service",
+ xmlns=XMLNS,
+ name=name,
+ description=description,
+ type=serv_type)
+ resp, body = self.post("services", str(Document(post_body)))
+ body = self._parse_body(etree.fromstring(body))
+ return resp, body
+
+ def delete_service(self, serv_id):
+ url = "services/" + serv_id
+ resp, body = self.delete(url)
+ return resp, body
diff --git a/tempest/services/identity/xml/identity_client.py b/tempest/services/identity/xml/identity_client.py
index e6a7188..50403fb 100644
--- a/tempest/services/identity/xml/identity_client.py
+++ b/tempest/services/identity/xml/identity_client.py
@@ -12,60 +12,24 @@
# 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 lxml import etree
-
-from tempest.common import http
-from tempest.common.rest_client import RestClientXML
from tempest import config
-from tempest import exceptions
-from tempest.services.compute.xml.common import Document
-from tempest.services.compute.xml.common import Element
-from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml import common as xml
+from tempest.services.identity.json import identity_client
CONF = config.CONF
XMLNS = "http://docs.openstack.org/identity/api/v2.0"
-class IdentityClientXML(RestClientXML):
-
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(IdentityClientXML, self).__init__(username, password,
- auth_url, tenant_name)
- self.service = CONF.identity.catalog_type
- self.endpoint_url = 'adminURL'
-
- def _parse_array(self, node):
- array = []
- for child in node.getchildren():
- array.append(xml_to_json(child))
- return array
-
- def _parse_body(self, body):
- data = xml_to_json(body)
- return data
-
- def has_admin_extensions(self):
- """
- Returns True if the KSADM Admin Extensions are supported
- False otherwise
- """
- if hasattr(self, '_has_admin_extensions'):
- return self._has_admin_extensions
- resp, body = self.list_roles()
- self._has_admin_extensions = ('status' in resp and resp.status != 503)
- return self._has_admin_extensions
+class IdentityClientXML(identity_client.IdentityClientJSON):
+ TYPE = "xml"
def create_role(self, name):
"""Create a role."""
- create_role = Element("role", xmlns=XMLNS, name=name)
- resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
+ create_role = xml.Element("role", xmlns=XMLNS, name=name)
+ resp, body = self.post('OS-KSADM/roles',
+ str(xml.Document(create_role)))
+ return resp, self._parse_resp(body)
def create_tenant(self, name, **kwargs):
"""
@@ -75,70 +39,18 @@
enabled <true|false>: Initial tenant status (default is true)
"""
en = kwargs.get('enabled', 'true')
- create_tenant = Element("tenant",
- xmlns=XMLNS,
- name=name,
- description=kwargs.get('description', ''),
- enabled=str(en).lower())
- resp, body = self.post('tenants', str(Document(create_tenant)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def delete_role(self, role_id):
- """Delete a role."""
- resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
- self.headers)
- return resp, body
-
- def list_user_roles(self, tenant_id, user_id):
- """Returns a list of roles assigned to a user for a tenant."""
- url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
- resp, body = self.get(url, self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
-
- def assign_user_role(self, tenant_id, user_id, role_id):
- """Add roles to a user on a tenant."""
- resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
- (tenant_id, user_id, role_id), '', self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def remove_user_role(self, tenant_id, user_id, role_id):
- """Removes a role assignment for a user on a tenant."""
- return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
- (tenant_id, user_id, role_id), self.headers)
-
- def delete_tenant(self, tenant_id):
- """Delete a tenant."""
- resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
- return resp, body
-
- def get_tenant(self, tenant_id):
- """Get tenant details."""
- resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def list_roles(self):
- """Returns roles."""
- resp, body = self.get('OS-KSADM/roles', self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
+ create_tenant = xml.Element("tenant",
+ xmlns=XMLNS,
+ name=name,
+ description=kwargs.get('description', ''),
+ enabled=str(en).lower())
+ resp, body = self.post('tenants', str(xml.Document(create_tenant)))
+ return resp, self._parse_resp(body)
def list_tenants(self):
"""Returns tenants."""
- resp, body = self.get('tenants', self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
-
- def get_tenant_by_name(self, tenant_name):
- resp, tenants = self.list_tenants()
- for tenant in tenants:
- if tenant['name'] == tenant_name:
- return tenant
- raise exceptions.NotFound('No such tenant')
+ resp, body = self.get('tenants')
+ return resp, self._parse_resp(body)
def update_tenant(self, tenant_id, **kwargs):
"""Updates a tenant."""
@@ -146,168 +58,69 @@
name = kwargs.get('name', body['name'])
desc = kwargs.get('description', body['description'])
en = kwargs.get('enabled', body['enabled'])
- update_tenant = Element("tenant",
- xmlns=XMLNS,
- id=tenant_id,
- name=name,
- description=desc,
- enabled=str(en).lower())
+ update_tenant = xml.Element("tenant",
+ xmlns=XMLNS,
+ id=tenant_id,
+ name=name,
+ description=desc,
+ enabled=str(en).lower())
resp, body = self.post('tenants/%s' % tenant_id,
- str(Document(update_tenant)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
+ str(xml.Document(update_tenant)))
+ return resp, self._parse_resp(body)
def create_user(self, name, password, tenant_id, email, **kwargs):
"""Create a user."""
- create_user = Element("user",
- xmlns=XMLNS,
- name=name,
- password=password,
- tenantId=tenant_id,
- email=email)
+ create_user = xml.Element("user",
+ xmlns=XMLNS,
+ name=name,
+ password=password,
+ tenantId=tenant_id,
+ email=email)
if 'enabled' in kwargs:
create_user.add_attr('enabled', str(kwargs['enabled']).lower())
- resp, body = self.post('users', str(Document(create_user)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
+ resp, body = self.post('users', str(xml.Document(create_user)))
+ return resp, self._parse_resp(body)
def update_user(self, user_id, **kwargs):
"""Updates a user."""
if 'enabled' in kwargs:
kwargs['enabled'] = str(kwargs['enabled']).lower()
- update_user = Element("user", xmlns=XMLNS, **kwargs)
+ update_user = xml.Element("user", xmlns=XMLNS, **kwargs)
resp, body = self.put('users/%s' % user_id,
- str(Document(update_user)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def get_user(self, user_id):
- """GET a user."""
- resp, body = self.get("users/%s" % user_id, self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def delete_user(self, user_id):
- """Delete a user."""
- resp, body = self.delete("users/%s" % user_id, self.headers)
- return resp, body
-
- def get_users(self):
- """Get the list of users."""
- resp, body = self.get("users", self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
+ str(xml.Document(update_user)))
+ return resp, self._parse_resp(body)
def enable_disable_user(self, user_id, enabled):
"""Enables or disables a user."""
- enable_user = Element("user", enabled=str(enabled).lower())
+ enable_user = xml.Element("user", enabled=str(enabled).lower())
resp, body = self.put('users/%s/enabled' % user_id,
- str(Document(enable_user)), self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
+ str(xml.Document(enable_user)))
+ return resp, self._parse_resp(body)
- def delete_token(self, token_id):
- """Delete a token."""
- resp, body = self.delete("tokens/%s" % token_id, self.headers)
- return resp, body
-
- def list_users_for_tenant(self, tenant_id):
- """List users for a Tenant."""
- resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
-
- def get_user_by_username(self, tenant_id, username):
- resp, users = self.list_users_for_tenant(tenant_id)
- for user in users:
- if user['name'] == username:
- return user
- raise exceptions.NotFound('No such user')
-
- def create_service(self, name, type, **kwargs):
+ def create_service(self, name, service_type, **kwargs):
"""Create a service."""
OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
- create_service = Element("service",
- xmlns=OS_KSADM,
- name=name,
- type=type,
- description=kwargs.get('description'))
+ create_service = xml.Element("service",
+ xmlns=OS_KSADM,
+ name=name,
+ type=service_type,
+ description=kwargs.get('description'))
resp, body = self.post('OS-KSADM/services',
- str(Document(create_service)),
- self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def list_services(self):
- """Returns services."""
- resp, body = self.get('OS-KSADM/services', self.headers)
- body = self._parse_array(etree.fromstring(body))
- return resp, body
-
- def get_service(self, service_id):
- """Get Service."""
- url = '/OS-KSADM/services/%s' % service_id
- resp, body = self.get(url, self.headers)
- body = self._parse_body(etree.fromstring(body))
- return resp, body
-
- def delete_service(self, service_id):
- """Delete Service."""
- url = '/OS-KSADM/services/%s' % service_id
- return self.delete(url, self.headers)
+ str(xml.Document(create_service)))
+ return resp, self._parse_resp(body)
-class TokenClientXML(RestClientXML):
-
- def __init__(self):
- auth_url = CONF.identity.uri
-
- # TODO(jaypipes) Why is this all repeated code in here?
- # Normalize URI to ensure /tokens is in it.
- if 'tokens' not in auth_url:
- auth_url = auth_url.rstrip('/') + '/tokens'
-
- self.auth_url = auth_url
+class TokenClientXML(identity_client.TokenClientJSON):
+ TYPE = "xml"
def auth(self, user, password, tenant):
- passwordCreds = Element("passwordCredentials",
- username=user,
- password=password)
- auth = Element("auth", tenantName=tenant)
+ passwordCreds = xml.Element("passwordCredentials",
+ username=user,
+ password=password)
+ auth = xml.Element("auth", tenantName=tenant)
auth.append(passwordCreds)
- headers = {'Content-Type': 'application/xml'}
- resp, body = self.post(self.auth_url, headers=headers,
- body=str(Document(auth)))
- return resp, body
-
- def request(self, method, url, headers=None, body=None):
- """A simple HTTP request interface."""
- dscv = CONF.identity.disable_ssl_certificate_validation
- self.http_obj = http.ClosingHttp(
- disable_ssl_certificate_validation=dscv)
- if headers is None:
- headers = {}
- self._log_request(method, url, headers, body)
- resp, resp_body = self.http_obj.request(url, method,
- headers=headers, body=body)
- self._log_response(resp, resp_body)
-
- if resp.status in (401, 403):
- resp_body = json.loads(resp_body)
- raise exceptions.Unauthorized(resp_body['error']['message'])
-
- return resp, resp_body
-
- def get_token(self, user, password, tenant):
- resp, body = self.auth(user, password, tenant)
- if resp['status'] != '202':
- body = json.loads(body)
- access = body['access']
- token = access['token']
- return token['id']
+ resp, body = self.post(self.auth_url, body=str(xml.Document(auth)))
+ return resp, body['access']
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index a5b93a0..932fa14 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -33,12 +33,10 @@
class ImageClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ImageClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
- self.service = CONF.images.catalog_type
- if CONF.service_available.glance:
- self.http = self._get_http()
+ def __init__(self, auth_provider):
+ super(ImageClientJSON, self).__init__(auth_provider)
+ self.service = CONF.image.catalog_type
+ self._http = None
def _image_meta_from_headers(self, headers):
meta = {'properties': {}}
@@ -106,13 +104,9 @@
return None
def _get_http(self):
- token, endpoint = self.keystone_auth(self.user,
- self.password,
- self.auth_url,
- self.service,
- self.tenant_name)
dscv = CONF.identity.disable_ssl_certificate_validation
- return glance_http.HTTPClient(endpoint=endpoint, token=token,
+ return glance_http.HTTPClient(auth_provider=self.auth_provider,
+ filters=self.filters,
insecure=dscv)
def _create_with_data(self, headers, data):
@@ -132,6 +126,13 @@
body = json.loads(''.join([c for c in body_iter]))
return resp, body['image']
+ @property
+ def http(self):
+ if self._http is None:
+ if CONF.service_available.glance:
+ self._http = self._get_http()
+ return self._http
+
def create_image(self, name, container_format, disk_format, **kwargs):
params = {
"name": name,
@@ -156,7 +157,7 @@
return resp, body['image']
def update_image(self, image_id, name=None, container_format=None,
- data=None):
+ data=None, properties=None):
params = {}
headers = {}
if name is not None:
@@ -165,6 +166,9 @@
if container_format is not None:
params['container_format'] = container_format
+ if properties is not None:
+ params['properties'] = properties
+
headers.update(self._image_meta_to_headers(params))
if data is not None:
@@ -189,7 +193,8 @@
body = json.loads(body)
return resp, body['images']
- def image_list_detail(self, properties=dict(), **kwargs):
+ def image_list_detail(self, properties=dict(), changes_since=None,
+ **kwargs):
url = 'v1/images/detail'
params = {}
@@ -198,6 +203,9 @@
kwargs.update(params)
+ if changes_since is not None:
+ kwargs['changes-since'] = changes_since
+
if len(kwargs) > 0:
url += '?%s' % urllib.urlencode(kwargs)
@@ -240,7 +248,7 @@
body = None
if can_share:
body = json.dumps({'member': {'can_share': True}})
- resp, __ = self.put(url, body, self.headers)
+ resp, __ = self.put(url, body)
return resp
def delete_member(self, member_id, image_id):
@@ -251,7 +259,7 @@
def replace_membership_list(self, image_id, member_list):
url = 'v1/images/%s/members' % image_id
body = json.dumps({'membership': member_list})
- resp, data = self.put(url, body, self.headers)
+ resp, data = self.put(url, body)
data = json.loads(data)
return resp, data
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 0c4fb5c..58819d0 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -28,19 +28,15 @@
class ImageClientV2JSON(rest_client.RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ImageClientV2JSON, self).__init__(username, password,
- auth_url, tenant_name)
- self.service = CONF.images.catalog_type
- if CONF.service_available.glance:
- self.http = self._get_http()
+ def __init__(self, auth_provider):
+ super(ImageClientV2JSON, self).__init__(auth_provider)
+ self.service = CONF.image.catalog_type
+ self._http = None
def _get_http(self):
- token, endpoint = self.keystone_auth(self.user, self.password,
- self.auth_url, self.service,
- self.tenant_name)
dscv = CONF.identity.disable_ssl_certificate_validation
- return glance_http.HTTPClient(endpoint=endpoint, token=token,
+ return glance_http.HTTPClient(auth_provider=self.auth_provider,
+ filters=self.filters,
insecure=dscv)
def get_images_schema(self):
@@ -65,6 +61,13 @@
jsonschema.validate(body, schema)
+ @property
+ def http(self):
+ if self._http is None:
+ if CONF.service_available.glance:
+ self._http = self._get_http()
+ return self._http
+
def create_image(self, name, container_format, disk_format, **kwargs):
params = {
"name": name,
@@ -83,7 +86,7 @@
data = json.dumps(params)
self._validate_schema(data)
- resp, body = self.post('v2/images', data, self.headers)
+ resp, body = self.post('v2/images', data)
body = json.loads(body)
return resp, body
@@ -129,7 +132,7 @@
def add_image_tag(self, image_id, tag):
url = 'v2/images/%s/tags/%s' % (image_id, tag)
- resp, body = self.put(url, body=None, headers=self.headers)
+ resp, body = self.put(url, body=None)
return resp, body
def delete_image_tag(self, image_id, tag):
@@ -147,7 +150,7 @@
def add_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, self.headers)
+ resp, body = self.post(url, data)
body = json.loads(body)
self.expected_success(200, resp)
return resp, body
@@ -156,7 +159,7 @@
"""Valid status are: ``pending``, ``accepted``, ``rejected``."""
url = 'v2/images/%s/members/%s' % (image_id, member_id)
data = json.dumps({'status': status})
- resp, body = self.put(url, data, self.headers)
+ resp, body = self.put(url, data)
body = json.loads(body)
self.expected_success(200, resp)
return resp, body
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 9908816..81dbfbc 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -31,9 +31,8 @@
quotas
"""
- def get_rest_client(self, username,
- password, auth_url, tenant_name=None):
- return RestClient(username, password, auth_url, tenant_name)
+ def get_rest_client(self, auth_provider):
+ return RestClient(auth_provider)
def deserialize_single(self, body):
return json.loads(body)
@@ -194,34 +193,6 @@
body = json.loads(body)
return resp, body
- def create_vip(self, name, protocol, protocol_port, subnet_id, pool_id):
- post_body = {
- "vip": {
- "protocol": protocol,
- "name": name,
- "subnet_id": subnet_id,
- "pool_id": pool_id,
- "protocol_port": protocol_port
- }
- }
- body = json.dumps(post_body)
- uri = '%s/lb/vips' % (self.uri_prefix)
- resp, body = self.post(uri, body)
- body = json.loads(body)
- return resp, body
-
- def update_vip(self, vip_id, new_name):
- put_body = {
- "vip": {
- "name": new_name,
- }
- }
- body = json.dumps(put_body)
- uri = '%s/lb/vips/%s' % (self.uri_prefix, vip_id)
- resp, body = self.put(uri, body)
- body = json.loads(body)
- return resp, body
-
def create_member(self, address, protocol_port, pool_id):
post_body = {
"member": {
@@ -248,33 +219,6 @@
body = json.loads(body)
return resp, body
- def create_health_monitor(self, delay, max_retries, Type, timeout):
- post_body = {
- "health_monitor": {
- "delay": delay,
- "max_retries": max_retries,
- "type": Type,
- "timeout": timeout
- }
- }
- body = json.dumps(post_body)
- uri = '%s/lb/health_monitors' % (self.uri_prefix)
- resp, body = self.post(uri, body)
- body = json.loads(body)
- return resp, body
-
- def update_health_monitor(self, admin_state_up, uuid):
- put_body = {
- "health_monitor": {
- "admin_state_up": admin_state_up
- }
- }
- body = json.dumps(put_body)
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, uuid)
- resp, body = self.put(uri, body)
- body = json.loads(body)
- return resp, body
-
def associate_health_monitor_with_pool(self, health_monitor_id,
pool_id):
post_body = {
@@ -341,6 +285,19 @@
body = json.loads(body)
return resp, body
+ def list_pools_hosted_by_one_lbaas_agent(self, agent_id):
+ uri = '%s/agents/%s/loadbalancer-pools' % (self.uri_prefix, agent_id)
+ resp, body = self.get(uri)
+ body = json.loads(body)
+ return resp, body
+
+ def show_lbaas_agent_hosting_pool(self, pool_id):
+ uri = ('%s/lb/pools/%s/loadbalancer-agent' %
+ (self.uri_prefix, pool_id))
+ resp, body = self.get(uri)
+ body = json.loads(body)
+ return resp, body
+
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
@@ -418,3 +375,9 @@
resp, body = self.put(uri, body)
body = json.loads(body)
return resp, body
+
+ def list_lb_pool_stats(self, pool_id):
+ uri = '%s/lb/pools/%s/stats' % (self.uri_prefix, pool_id)
+ resp, body = self.get(uri)
+ body = json.loads(body)
+ return resp, body
diff --git a/tempest/services/network/network_client_base.py b/tempest/services/network/network_client_base.py
index 467256e..f1bf548 100644
--- a/tempest/services/network/network_client_base.py
+++ b/tempest/services/network/network_client_base.py
@@ -27,7 +27,9 @@
'health_monitors': 'lb',
'members': 'lb',
'vpnservices': 'vpn',
- 'ikepolicies': 'vpn'
+ 'ikepolicies': 'vpn',
+ 'metering_labels': 'metering',
+ 'metering_label_rules': 'metering'
}
# The following list represents resource names that do not require
@@ -46,32 +48,26 @@
class NetworkClientBase(object):
- def __init__(self, username, password,
- auth_url, tenant_name=None):
+ def __init__(self, auth_provider):
self.rest_client = self.get_rest_client(
- username, password, auth_url, tenant_name)
+ auth_provider)
self.rest_client.service = CONF.network.catalog_type
self.version = '2.0'
self.uri_prefix = "v%s" % (self.version)
- def get_rest_client(self, username, password,
- auth_url, tenant_name):
+ def get_rest_client(self, auth_provider):
raise NotImplementedError
def post(self, uri, body, headers=None):
- headers = headers or self.rest_client.headers
return self.rest_client.post(uri, body, headers)
def put(self, uri, body, headers=None):
- headers = headers or self.rest_client.headers
return self.rest_client.put(uri, body, headers)
def get(self, uri, headers=None):
- headers = headers or self.rest_client.headers
return self.rest_client.get(uri, headers)
def delete(self, uri, headers=None):
- headers = headers or self.rest_client.headers
return self.rest_client.delete(uri, headers)
def deserialize_list(self, body):
@@ -117,9 +113,14 @@
return _delete
def _shower(self, resource_name):
- def _show(resource_id):
+ def _show(resource_id, field_list=[]):
+ # field_list is a sequence of two-element tuples, with the
+ # first element being 'fields'. An example:
+ # [('fields', 'id'), ('fields', 'name')]
plural = self.pluralize(resource_name)
uri = '%s/%s' % (self.get_uri(plural), resource_id)
+ if field_list:
+ uri += '?' + urllib.urlencode(field_list)
resp, body = self.get(uri)
body = self.deserialize_single(body)
return resp, body
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 4eb38be..e6f5c08 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -28,10 +28,8 @@
PLURALS = ['dns_nameservers', 'host_routes', 'allocation_pools',
'fixed_ips', 'extensions']
- def get_rest_client(self, username, password,
- auth_url, tenant_name=None):
- return RestClientXML(username, password,
- auth_url, tenant_name)
+ def get_rest_client(self, auth_provider):
+ return RestClientXML(auth_provider)
def _parse_array(self, node):
array = []
@@ -103,32 +101,6 @@
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def create_vip(self, name, protocol, protocol_port, subnet_id, pool_id):
- uri = '%s/lb/vips' % (self.uri_prefix)
- post_body = Element("vip")
- p1 = Element("name", name)
- p2 = Element("protocol", protocol)
- p3 = Element("protocol_port", protocol_port)
- p4 = Element("subnet_id", subnet_id)
- p5 = Element("pool_id", pool_id)
- post_body.append(p1)
- post_body.append(p2)
- post_body.append(p3)
- post_body.append(p4)
- post_body.append(p5)
- resp, body = self.post(uri, str(Document(post_body)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def update_vip(self, vip_id, new_name):
- uri = '%s/lb/vips/%s' % (self.uri_prefix, str(vip_id))
- put_body = Element("vip")
- p2 = Element("name", new_name)
- put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def create_member(self, address, protocol_port, pool_id):
uri = '%s/lb/members' % (self.uri_prefix)
post_body = Element("member")
@@ -151,30 +123,6 @@
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
- def create_health_monitor(self, delay, max_retries, Type, timeout):
- uri = '%s/lb/health_monitors' % (self.uri_prefix)
- post_body = Element("health_monitor")
- p1 = Element("delay", delay)
- p2 = Element("max_retries", max_retries)
- p3 = Element("type", Type)
- p4 = Element("timeout", timeout)
- post_body.append(p1)
- post_body.append(p2)
- post_body.append(p3)
- post_body.append(p4)
- resp, body = self.post(uri, str(Document(post_body)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
- def update_health_monitor(self, admin_state_up, uuid):
- uri = '%s/lb/health_monitors/%s' % (self.uri_prefix, str(uuid))
- put_body = Element("health_monitor")
- p2 = Element("admin_state_up", admin_state_up)
- put_body.append(p2)
- resp, body = self.put(uri, str(Document(put_body)))
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
- return resp, body
-
def associate_health_monitor_with_pool(self, health_monitor_id,
pool_id):
uri = '%s/lb/pools/%s/health_monitors' % (self.uri_prefix,
@@ -291,6 +239,20 @@
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
+ def list_pools_hosted_by_one_lbaas_agent(self, agent_id):
+ uri = '%s/agents/%s/loadbalancer-pools' % (self.uri_prefix, agent_id)
+ resp, body = self.get(uri)
+ pools = parse_array(etree.fromstring(body))
+ body = {'pools': pools}
+ return resp, body
+
+ def show_lbaas_agent_hosting_pool(self, pool_id):
+ uri = ('%s/lb/pools/%s/loadbalancer-agent' %
+ (self.uri_prefix, pool_id))
+ resp, body = self.get(uri)
+ body = _root_tag_fetcher_and_xml_to_json_parse(body)
+ return resp, body
+
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
@@ -323,6 +285,12 @@
resp, body = self.delete(uri)
return resp, body
+ def list_lb_pool_stats(self, pool_id):
+ uri = '%s/lb/pools/%s/stats' % (self.uri_prefix, pool_id)
+ resp, body = self.get(uri)
+ body = _root_tag_fetcher_and_xml_to_json_parse(body)
+ return resp, body
+
def _root_tag_fetcher_and_xml_to_json_parse(xml_returned_body):
body = ET.fromstring(xml_returned_body)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 28d10aa..efac5f5 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -25,9 +25,8 @@
class AccountClient(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AccountClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(AccountClient, self).__init__(auth_provider)
self.service = CONF.object_storage.catalog_type
self.format = 'json'
@@ -59,7 +58,7 @@
url += 'bulk-delete&'
url = '?%s%s' % (url, urllib.urlencode(params))
- resp, body = self.delete(url, headers=None, body=data)
+ resp, body = self.delete(url, headers={}, body=data)
return resp, body
def list_account_metadata(self):
@@ -128,20 +127,20 @@
return resp, body
def list_extensions(self):
- _base_url = self.base_url
- self.base_url = "/".join(self.base_url.split("/")[:-2])
+ self.skip_path()
resp, body = self.get('info')
- self.base_url = _base_url
+ self.reset_path()
body = json.loads(body)
return resp, body
class AccountClientCustomizedHeader(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(AccountClientCustomizedHeader, self).__init__(username,
- password, auth_url,
- tenant_name)
+ # TODO(andreaf) This class is now redundant, to be removed in next patch
+
+ def __init__(self, auth_provider):
+ super(AccountClientCustomizedHeader, self).__init__(
+ auth_provider)
# Overwrites json-specific header encoding in RestClient
self.service = CONF.object_storage.catalog_type
self.format = 'json'
@@ -151,14 +150,17 @@
self.http_obj = http.ClosingHttp()
if headers is None:
headers = {}
- if self.base_url is None:
- self._set_auth()
- req_url = "%s/%s" % (self.base_url, url)
-
+ # Authorize the request
+ req_url, req_headers, req_body = self.auth_provider.auth_request(
+ method=method, url=url, headers=headers, body=body,
+ filters=self.filters
+ )
self._log_request(method, req_url, headers, body)
+ # use original body
resp, resp_body = self.http_obj.request(req_url, method,
- headers=headers, body=body)
+ headers=req_headers,
+ body=req_body)
self._log_response(resp, resp_body)
if resp.status == 401 or resp.status == 403:
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index bc85c73..f224407 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -24,9 +24,8 @@
class ContainerClient(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ContainerClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ContainerClient, self).__init__(auth_provider)
# Overwrites json-specific header encoding in RestClient
self.headers = {}
@@ -182,7 +181,7 @@
url += '?'
url += '&%s' % urllib.urlencode(params)
- resp, body = self.get(url)
+ resp, body = self.get(url, headers={})
if params and params.get('format') == 'json':
body = json.loads(body)
elif params and params.get('format') == 'xml':
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 2a68a4f..09e484e 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -24,16 +24,15 @@
class ObjectClient(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ObjectClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ObjectClient, self).__init__(auth_provider)
self.service = CONF.object_storage.catalog_type
def create_object(self, container, object_name, data, params=None):
"""Create storage object."""
- headers = dict(self.headers)
+ headers = self.get_headers()
if not data:
headers['content-length'] = '0'
url = "%s/%s" % (str(container), str(object_name))
@@ -52,7 +51,7 @@
url = "%s/%s" % (str(container), str(object_name))
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.delete(url)
+ resp, body = self.delete(url, headers={})
return resp, body
def update_object_metadata(self, container, object_name, metadata,
@@ -132,16 +131,17 @@
def create_object_segments(self, container, object_name, segment, data):
"""Creates object segments."""
url = "{0}/{1}/{2}".format(container, object_name, segment)
- resp, body = self.put(url, data, self.headers)
+ resp, body = self.put(url, data)
return resp, body
class ObjectClientCustomizedHeader(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ObjectClientCustomizedHeader, self).__init__(username,
- password, auth_url,
- tenant_name)
+ # TODO(andreaf) This class is now redundant, to be removed in next patch
+
+ def __init__(self, auth_provider):
+ super(ObjectClientCustomizedHeader, self).__init__(
+ auth_provider)
# Overwrites json-specific header encoding in RestClient
self.service = CONF.object_storage.catalog_type
self.format = 'json'
@@ -153,13 +153,17 @@
disable_ssl_certificate_validation=dscv)
if headers is None:
headers = {}
- if self.base_url is None:
- self._set_auth()
- req_url = "%s/%s" % (self.base_url, url)
+ # Authorize the request
+ req_url, req_headers, req_body = self.auth_provider.auth_request(
+ method=method, url=url, headers=headers, body=body,
+ filters=self.filters
+ )
+ # Use original method
self._log_request(method, req_url, headers, body)
resp, resp_body = self.http_obj.request(req_url, method,
- headers=headers, body=body)
+ headers=req_headers,
+ body=req_body)
self._log_response(resp, resp_body)
if resp.status == 401 or resp.status == 403:
raise exceptions.Unauthorized()
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 273c2ae..113003c 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -27,9 +27,8 @@
class OrchestrationClient(rest_client.RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(OrchestrationClient, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(OrchestrationClient, self).__init__(auth_provider)
self.service = CONF.orchestration.catalog_type
self.build_interval = CONF.orchestration.build_interval
self.build_timeout = CONF.orchestration.build_timeout
@@ -91,7 +90,7 @@
# Password must be provided on stack create so that heat
# can perform future operations on behalf of the user
- headers = dict(self.headers)
+ headers = self.get_headers()
headers['X-Auth-Key'] = self.password
headers['X-Auth-User'] = self.user
return headers, body
@@ -107,14 +106,14 @@
"""Suspend a stack."""
url = 'stacks/%s/actions' % stack_identifier
body = {'suspend': None}
- resp, body = self.post(url, json.dumps(body), self.headers)
+ resp, body = self.post(url, json.dumps(body))
return resp, body
def resume_stack(self, stack_identifier):
"""Resume a stack."""
url = 'stacks/%s/actions' % stack_identifier
body = {'resume': None}
- resp, body = self.post(url, json.dumps(body), self.headers)
+ resp, body = self.post(url, json.dumps(body))
return resp, body
def list_resources(self, stack_identifier):
@@ -178,7 +177,7 @@
stack_name = body['stack_name']
stack_status = body['stack_status']
if stack_status == status:
- return
+ return body
if fail_regexp.search(stack_status):
raise exceptions.StackBuildErrorException(
stack_identifier=stack_identifier,
@@ -233,7 +232,7 @@
def _validate_template(self, post_body):
"""Returns the validation request result."""
post_body = json.dumps(post_body)
- resp, body = self.post('validate', post_body, self.headers)
+ resp, body = self.post('validate', post_body)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/telemetry/json/telemetry_client.py b/tempest/services/telemetry/json/telemetry_client.py
index a1112da..e666475 100644
--- a/tempest/services/telemetry/json/telemetry_client.py
+++ b/tempest/services/telemetry/json/telemetry_client.py
@@ -20,9 +20,8 @@
class TelemetryClientJSON(client.TelemetryClientBase):
- def get_rest_client(self, username,
- password, auth_url, tenant_name=None):
- return RestClient(username, password, auth_url, tenant_name)
+ def get_rest_client(self, auth_provider):
+ return RestClient(auth_provider)
def deserialize(self, body):
return json.loads(body.replace("\n", ""))
@@ -30,10 +29,6 @@
def serialize(self, body):
return json.dumps(body)
- def create_alarm(self, **kwargs):
- uri = "%s/alarms" % self.uri_prefix
- return self.post(uri, kwargs)
-
def add_sample(self, sample_list, meter_name, meter_unit, volume,
sample_type, resource_id, **kwargs):
sample = {"counter_name": meter_name, "counter_unit": meter_unit,
diff --git a/tempest/services/telemetry/telemetry_client_base.py b/tempest/services/telemetry/telemetry_client_base.py
index 24039c6..610f07b 100644
--- a/tempest/services/telemetry/telemetry_client_base.py
+++ b/tempest/services/telemetry/telemetry_client_base.py
@@ -35,17 +35,14 @@
statistics
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- self.rest_client = self.get_rest_client(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ self.rest_client = self.get_rest_client(auth_provider)
self.rest_client.service = CONF.telemetry.catalog_type
- self.headers = self.rest_client.headers
self.version = '2'
self.uri_prefix = "v%s" % self.version
@abc.abstractmethod
- def get_rest_client(self, username, password,
- auth_url, tenant_name):
+ def get_rest_client(self, auth_provider):
"""
:param config:
:param username:
@@ -71,12 +68,12 @@
def post(self, uri, body):
body = self.serialize(body)
- resp, body = self.rest_client.post(uri, body, self.headers)
+ resp, body = self.rest_client.post(uri, body)
body = self.deserialize(body)
return resp, body
def put(self, uri, body):
- return self.rest_client.put(uri, body, self.headers)
+ return self.rest_client.put(uri, body)
def get(self, uri):
resp, body = self.rest_client.get(uri)
@@ -126,9 +123,13 @@
return self.get(uri)
def get_alarm(self, alarm_id):
- uri = '%s/meter/%s' % (self.uri_prefix, alarm_id)
+ uri = '%s/alarms/%s' % (self.uri_prefix, alarm_id)
return self.get(uri)
def delete_alarm(self, alarm_id):
uri = "%s/alarms/%s" % (self.uri_prefix, alarm_id)
return self.delete(uri)
+
+ def create_alarm(self, **kwargs):
+ uri = "%s/alarms" % self.uri_prefix
+ return self.post(uri, kwargs)
diff --git a/tempest/services/telemetry/xml/telemetry_client.py b/tempest/services/telemetry/xml/telemetry_client.py
index 862d08f..f29fe22 100644
--- a/tempest/services/telemetry/xml/telemetry_client.py
+++ b/tempest/services/telemetry/xml/telemetry_client.py
@@ -23,9 +23,8 @@
class TelemetryClientXML(client.TelemetryClientBase):
- def get_rest_client(self, username,
- password, auth_url, tenant_name=None):
- return RestClientXML(username, password, auth_url, tenant_name)
+ def get_rest_client(self, auth_provider):
+ return RestClientXML(auth_provider)
def _parse_array(self, body):
array = []
diff --git a/tempest/services/volume/json/admin/volume_hosts_client.py b/tempest/services/volume/json/admin/volume_hosts_client.py
index e4178b9..6efb258 100644
--- a/tempest/services/volume/json/admin/volume_hosts_client.py
+++ b/tempest/services/volume/json/admin/volume_hosts_client.py
@@ -27,9 +27,8 @@
Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumeHostsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumeHostsClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
diff --git a/tempest/services/volume/json/admin/volume_types_client.py b/tempest/services/volume/json/admin/volume_types_client.py
index 5b6328b..0d50524 100644
--- a/tempest/services/volume/json/admin/volume_types_client.py
+++ b/tempest/services/volume/json/admin/volume_types_client.py
@@ -27,9 +27,8 @@
Client class to send CRUD Volume Types API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumeTypesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumeTypesClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
@@ -65,7 +64,7 @@
}
post_body = json.dumps({'volume_type': post_body})
- resp, body = self.post('types', post_body, self.headers)
+ resp, body = self.post('types', post_body)
body = json.loads(body)
return resp, body['volume_type']
@@ -99,7 +98,7 @@
"""
url = "types/%s/extra_specs" % str(vol_type_id)
post_body = json.dumps({'extra_specs': extra_spec})
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
body = json.loads(body)
return resp, body['extra_specs']
@@ -120,6 +119,6 @@
url = "types/%s/extra_specs/%s" % (str(vol_type_id),
str(extra_spec_name))
put_body = json.dumps(extra_spec)
- resp, body = self.put(url, put_body, self.headers)
+ resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/volume/json/extensions_client.py b/tempest/services/volume/json/extensions_client.py
index c3bbb20..257b7c8 100644
--- a/tempest/services/volume/json/extensions_client.py
+++ b/tempest/services/volume/json/extensions_client.py
@@ -23,9 +23,8 @@
class ExtensionsClientJSON(RestClient):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ExtensionsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ExtensionsClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
def list_extensions(self):
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index e8926bd..ba33c49 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -27,9 +27,8 @@
class SnapshotsClientJSON(RestClient):
"""Client class to send CRUD Volume API requests."""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(SnapshotsClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(SnapshotsClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
@@ -73,15 +72,14 @@
post_body = {'volume_id': volume_id}
post_body.update(kwargs)
post_body = json.dumps({'snapshot': post_body})
- resp, body = self.post('snapshots', post_body, self.headers)
+ resp, body = self.post('snapshots', post_body)
body = json.loads(body)
return resp, body['snapshot']
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot."""
put_body = json.dumps({'snapshot': kwargs})
- resp, body = self.put('snapshots/%s' % snapshot_id, put_body,
- self.headers)
+ resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
body = json.loads(body)
return resp, body['snapshot']
@@ -136,8 +134,7 @@
def reset_snapshot_status(self, snapshot_id, status):
"""Reset the specified snapshot's status."""
post_body = json.dumps({'os-reset_status': {"status": status}})
- resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body,
- self.headers)
+ resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
return resp, body
def update_snapshot_status(self, snapshot_id, status, progress):
@@ -148,21 +145,21 @@
}
post_body = json.dumps({'os-update_snapshot_status': post_body})
url = 'snapshots/%s/action' % str(snapshot_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def create_snapshot_metadata(self, snapshot_id, metadata):
"""Create metadata for the snapshot."""
put_body = json.dumps({'metadata': metadata})
url = "snapshots/%s/metadata" % str(snapshot_id)
- resp, body = self.post(url, put_body, self.headers)
+ resp, body = self.post(url, put_body)
body = json.loads(body)
return resp, body['metadata']
def get_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot."""
url = "snapshots/%s/metadata" % str(snapshot_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = json.loads(body)
return resp, body['metadata']
@@ -170,7 +167,7 @@
"""Update metadata for the snapshot."""
put_body = json.dumps({'metadata': metadata})
url = "snapshots/%s/metadata" % str(snapshot_id)
- resp, body = self.put(url, put_body, self.headers)
+ resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body['metadata']
@@ -178,19 +175,18 @@
"""Update metadata item for the snapshot."""
put_body = json.dumps({'meta': meta_item})
url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
- resp, body = self.put(url, put_body, self.headers)
+ resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body['meta']
def delete_snapshot_metadata_item(self, snapshot_id, id):
"""Delete metadata item for the snapshot."""
url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
- resp, body = self.delete(url, self.headers)
+ resp, body = self.delete(url)
return resp, body
def force_delete_snapshot(self, snapshot_id):
"""Force Delete Snapshot."""
post_body = json.dumps({'os-force_delete': {}})
- resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body,
- self.headers)
+ resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
return resp, body
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 6c09e02..2183c56 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -29,9 +29,8 @@
Client class to send CRUD Volume API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumesClientJSON, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumesClientJSON, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
@@ -82,15 +81,14 @@
post_body = {'size': size}
post_body.update(kwargs)
post_body = json.dumps({'volume': post_body})
- resp, body = self.post('volumes', post_body, self.headers)
+ resp, body = self.post('volumes', post_body)
body = json.loads(body)
return resp, body['volume']
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume."""
put_body = json.dumps({'volume': kwargs})
- resp, body = self.put('volumes/%s' % volume_id, put_body,
- self.headers)
+ resp, body = self.put('volumes/%s' % volume_id, put_body)
body = json.loads(body)
return resp, body['volume']
@@ -106,7 +104,7 @@
}
post_body = json.dumps({'os-volume_upload_image': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
body = json.loads(body)
return resp, body['os-volume_upload_image']
@@ -118,7 +116,7 @@
}
post_body = json.dumps({'os-attach': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def detach_volume(self, volume_id):
@@ -126,7 +124,7 @@
post_body = {}
post_body = json.dumps({'os-detach': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def reserve_volume(self, volume_id):
@@ -134,7 +132,7 @@
post_body = {}
post_body = json.dumps({'os-reserve': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def unreserve_volume(self, volume_id):
@@ -142,7 +140,7 @@
post_body = {}
post_body = json.dumps({'os-unreserve': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def wait_for_volume_status(self, volume_id, status):
@@ -179,28 +177,25 @@
}
post_body = json.dumps({'os-extend': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def reset_volume_status(self, volume_id, status):
"""Reset the Specified Volume's Status."""
post_body = json.dumps({'os-reset_status': {"status": status}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body,
- self.headers)
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
return resp, body
def volume_begin_detaching(self, volume_id):
"""Volume Begin Detaching."""
post_body = json.dumps({'os-begin_detaching': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body,
- self.headers)
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
return resp, body
def volume_roll_detaching(self, volume_id):
"""Volume Roll Detaching."""
post_body = json.dumps({'os-roll_detaching': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body,
- self.headers)
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
return resp, body
def create_volume_transfer(self, vol_id, display_name=None):
@@ -211,16 +206,14 @@
if display_name:
post_body['name'] = display_name
post_body = json.dumps({'transfer': post_body})
- resp, body = self.post('os-volume-transfer',
- post_body,
- self.headers)
+ resp, body = self.post('os-volume-transfer', post_body)
body = json.loads(body)
return resp, body['transfer']
def get_volume_transfer(self, transfer_id):
"""Returns the details of a volume transfer."""
url = "os-volume-transfer/%s" % str(transfer_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = json.loads(body)
return resp, body['transfer']
@@ -244,7 +237,7 @@
}
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': post_body})
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
body = json.loads(body)
return resp, body['transfer']
@@ -255,28 +248,27 @@
}
post_body = json.dumps({'os-update_readonly_flag': post_body})
url = 'volumes/%s/action' % (volume_id)
- resp, body = self.post(url, post_body, self.headers)
+ resp, body = self.post(url, post_body)
return resp, body
def force_delete_volume(self, volume_id):
"""Force Delete Volume."""
post_body = json.dumps({'os-force_delete': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body,
- self.headers)
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
return resp, body
def create_volume_metadata(self, volume_id, metadata):
"""Create metadata for the volume."""
put_body = json.dumps({'metadata': metadata})
url = "volumes/%s/metadata" % str(volume_id)
- resp, body = self.post(url, put_body, self.headers)
+ resp, body = self.post(url, put_body)
body = json.loads(body)
return resp, body['metadata']
def get_volume_metadata(self, volume_id):
"""Get metadata of the volume."""
url = "volumes/%s/metadata" % str(volume_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = json.loads(body)
return resp, body['metadata']
@@ -284,7 +276,7 @@
"""Update metadata for the volume."""
put_body = json.dumps({'metadata': metadata})
url = "volumes/%s/metadata" % str(volume_id)
- resp, body = self.put(url, put_body, self.headers)
+ resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body['metadata']
@@ -292,12 +284,12 @@
"""Update metadata item for the volume."""
put_body = json.dumps({'meta': meta_item})
url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
- resp, body = self.put(url, put_body, self.headers)
+ resp, body = self.put(url, put_body)
body = json.loads(body)
return resp, body['meta']
def delete_volume_metadata_item(self, volume_id, id):
"""Delete metadata item for the volume."""
url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
- resp, body = self.delete(url, self.headers)
+ resp, body = self.delete(url)
return resp, body
diff --git a/tempest/services/volume/v2/__init__.py b/tempest/services/volume/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/v2/__init__.py
diff --git a/tempest/services/volume/v2/json/__init__.py b/tempest/services/volume/v2/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/v2/json/__init__.py
diff --git a/tempest/services/volume/v2/json/volumes_client.py b/tempest/services/volume/v2/json/volumes_client.py
new file mode 100644
index 0000000..bd98402
--- /dev/null
+++ b/tempest/services/volume/v2/json/volumes_client.py
@@ -0,0 +1,296 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import time
+import urllib
+
+from tempest.common.rest_client import RestClient
+from tempest import config
+from tempest import exceptions
+
+CONF = config.CONF
+
+
+class VolumesV2ClientJSON(RestClient):
+ """
+ Client class to send CRUD Volume V2 API requests to a Cinder endpoint
+ """
+
+ def __init__(self, auth_provider):
+ super(VolumesV2ClientJSON, self).__init__(auth_provider)
+
+ self.api_version = "v2"
+ self.service = CONF.volume.catalog_type
+ self.build_interval = CONF.volume.build_interval
+ self.build_timeout = CONF.volume.build_timeout
+
+ def get_attachment_from_volume(self, volume):
+ """Return the element 'attachment' from input volumes."""
+ return volume['attachments'][0]
+
+ def list_volumes(self, params=None):
+ """List all the volumes created."""
+ url = 'volumes'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['volumes']
+
+ def list_volumes_with_detail(self, params=None):
+ """List the details of all volumes."""
+ url = 'volumes/detail'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['volumes']
+
+ def get_volume(self, volume_id):
+ """Returns the details of a single volume."""
+ url = "volumes/%s" % str(volume_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['volume']
+
+ def create_volume(self, size, **kwargs):
+ """
+ Creates a new Volume.
+ size(Required): Size of volume in GB.
+ Following optional keyword arguments are accepted:
+ name: Optional Volume Name.
+ metadata: A dictionary of values to be used as metadata.
+ volume_type: Optional Name of volume_type for the volume
+ snapshot_id: When specified the volume is created from this snapshot
+ imageRef: When specified the volume is created from this image
+ """
+ post_body = {'size': size}
+ post_body.update(kwargs)
+ post_body = json.dumps({'volume': post_body})
+ resp, body = self.post('volumes', post_body)
+ body = json.loads(body)
+ return resp, body['volume']
+
+ def update_volume(self, volume_id, **kwargs):
+ """Updates the Specified Volume."""
+ put_body = json.dumps({'volume': kwargs})
+ resp, body = self.put('volumes/%s' % volume_id, put_body)
+ body = json.loads(body)
+ return resp, body['volume']
+
+ def delete_volume(self, volume_id):
+ """Deletes the Specified Volume."""
+ return self.delete("volumes/%s" % str(volume_id))
+
+ def upload_volume(self, volume_id, image_name, disk_format):
+ """Uploads a volume in Glance."""
+ post_body = {
+ 'image_name': image_name,
+ 'disk_format': disk_format
+ }
+ post_body = json.dumps({'os-volume_upload_image': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ return resp, body['os-volume_upload_image']
+
+ def attach_volume(self, volume_id, instance_uuid, mountpoint):
+ """Attaches a volume to a given instance on a given mountpoint."""
+ post_body = {
+ 'instance_uuid': instance_uuid,
+ 'mountpoint': mountpoint,
+ }
+ post_body = json.dumps({'os-attach': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def detach_volume(self, volume_id):
+ """Detaches a volume from an instance."""
+ post_body = {}
+ post_body = json.dumps({'os-detach': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def reserve_volume(self, volume_id):
+ """Reserves a volume."""
+ post_body = {}
+ post_body = json.dumps({'os-reserve': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def unreserve_volume(self, volume_id):
+ """Restore a reserved volume ."""
+ post_body = {}
+ post_body = json.dumps({'os-unreserve': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def wait_for_volume_status(self, volume_id, status):
+ """Waits for a Volume to reach a given status."""
+ resp, body = self.get_volume(volume_id)
+ volume_name = body['name']
+ volume_status = body['status']
+ start = int(time.time())
+
+ while volume_status != status:
+ time.sleep(self.build_interval)
+ resp, body = self.get_volume(volume_id)
+ volume_status = body['status']
+ if volume_status == 'error':
+ raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
+
+ if int(time.time()) - start >= self.build_timeout:
+ message = ('Volume %s failed to reach %s status within '
+ 'the required time (%s s).' %
+ (volume_name, status, self.build_timeout))
+ raise exceptions.TimeoutException(message)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.get_volume(id)
+ except exceptions.NotFound:
+ return True
+ return False
+
+ def extend_volume(self, volume_id, extend_size):
+ """Extend a volume."""
+ post_body = {
+ 'new_size': extend_size
+ }
+ post_body = json.dumps({'os-extend': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def reset_volume_status(self, volume_id, status):
+ """Reset the Specified Volume's Status."""
+ post_body = json.dumps({'os-reset_status': {"status": status}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ return resp, body
+
+ def volume_begin_detaching(self, volume_id):
+ """Volume Begin Detaching."""
+ post_body = json.dumps({'os-begin_detaching': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ return resp, body
+
+ def volume_roll_detaching(self, volume_id):
+ """Volume Roll Detaching."""
+ post_body = json.dumps({'os-roll_detaching': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ return resp, body
+
+ def create_volume_transfer(self, vol_id, name=None):
+ """Create a volume transfer."""
+ post_body = {
+ 'volume_id': vol_id
+ }
+ if name:
+ post_body['name'] = name
+ post_body = json.dumps({'transfer': post_body})
+ resp, body = self.post('os-volume-transfer', post_body)
+ body = json.loads(body)
+ return resp, body['transfer']
+
+ def get_volume_transfer(self, transfer_id):
+ """Returns the details of a volume transfer."""
+ url = "os-volume-transfer/%s" % str(transfer_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['transfer']
+
+ def list_volume_transfers(self, params=None):
+ """List all the volume transfers created."""
+ url = 'os-volume-transfer'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['transfers']
+
+ def delete_volume_transfer(self, transfer_id):
+ """Delete a volume transfer."""
+ return self.delete("os-volume-transfer/%s" % str(transfer_id))
+
+ def accept_volume_transfer(self, transfer_id, transfer_auth_key):
+ """Accept a volume transfer."""
+ post_body = {
+ 'auth_key': transfer_auth_key,
+ }
+ url = 'os-volume-transfer/%s/accept' % transfer_id
+ post_body = json.dumps({'accept': post_body})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ return resp, body['transfer']
+
+ def update_volume_readonly(self, volume_id, readonly):
+ """Update the Specified Volume readonly."""
+ post_body = {
+ 'readonly': readonly
+ }
+ post_body = json.dumps({'os-update_readonly_flag': post_body})
+ url = 'volumes/%s/action' % (volume_id)
+ resp, body = self.post(url, post_body)
+ return resp, body
+
+ def force_delete_volume(self, volume_id):
+ """Force Delete Volume."""
+ post_body = json.dumps({'os-force_delete': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ return resp, body
+
+ def create_volume_metadata(self, volume_id, metadata):
+ """Create metadata for the volume."""
+ put_body = json.dumps({'metadata': metadata})
+ url = "volumes/%s/metadata" % str(volume_id)
+ resp, body = self.post(url, put_body)
+ body = json.loads(body)
+ return resp, body['metadata']
+
+ def get_volume_metadata(self, volume_id):
+ """Get metadata of the volume."""
+ url = "volumes/%s/metadata" % str(volume_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['metadata']
+
+ def update_volume_metadata(self, volume_id, metadata):
+ """Update metadata for the volume."""
+ put_body = json.dumps({'metadata': metadata})
+ url = "volumes/%s/metadata" % str(volume_id)
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ return resp, body['metadata']
+
+ def update_volume_metadata_item(self, volume_id, id, meta_item):
+ """Update metadata item for the volume."""
+ put_body = json.dumps({'meta': meta_item})
+ url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ return resp, body['meta']
+
+ def delete_volume_metadata_item(self, volume_id, id):
+ """Delete metadata item for the volume."""
+ url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ resp, body = self.delete(url)
+ return resp, body
diff --git a/tempest/services/volume/v2/xml/__init__.py b/tempest/services/volume/v2/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/v2/xml/__init__.py
diff --git a/tempest/services/volume/v2/xml/volumes_client.py b/tempest/services/volume/v2/xml/volumes_client.py
new file mode 100644
index 0000000..69e3f5b
--- /dev/null
+++ b/tempest/services/volume/v2/xml/volumes_client.py
@@ -0,0 +1,408 @@
+# Copyright 2012 IBM Corp.
+# 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 time
+import urllib
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest import config
+from tempest import exceptions
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
+from tempest.services.compute.xml.common import xml_to_json
+from tempest.services.compute.xml.common import XMLNS_11
+
+CONF = config.CONF
+
+
+class VolumesV2ClientXML(RestClientXML):
+ """
+ Client class to send CRUD Volume API requests to a Cinder endpoint
+ """
+
+ def __init__(self, auth_provider):
+ super(VolumesV2ClientXML, self).__init__(auth_provider)
+
+ self.api_version = "v2"
+ self.service = CONF.volume.catalog_type
+ self.build_interval = CONF.compute.build_interval
+ self.build_timeout = CONF.compute.build_timeout
+
+ def _parse_volume(self, body):
+ vol = dict((attr, body.get(attr)) for attr in body.keys())
+
+ for child in body.getchildren():
+ tag = child.tag
+ if tag.startswith("{"):
+ ns, tag = tag.split("}", 1)
+ if tag == 'metadata':
+ vol['metadata'] = dict((meta.get('key'),
+ meta.text) for meta in
+ child.getchildren())
+ else:
+ vol[tag] = xml_to_json(child)
+ return vol
+
+ def get_attachment_from_volume(self, volume):
+ """Return the element 'attachment' from input volumes."""
+ return volume['attachments']['attachment']
+
+ def _check_if_bootable(self, volume):
+ """
+ Check if the volume is bootable, also change the value
+ of 'bootable' from string to boolean.
+ """
+
+ # NOTE(jdg): Version 1 of Cinder API uses lc strings
+ # We should consider being explicit in this check to
+ # avoid introducing bugs like: LP #1227837
+
+ if volume['bootable'].lower() == 'true':
+ volume['bootable'] = True
+ elif volume['bootable'].lower() == 'false':
+ volume['bootable'] = False
+ else:
+ raise ValueError(
+ 'bootable flag is supposed to be either True or False,'
+ 'it is %s' % volume['bootable'])
+ return volume
+
+ def list_volumes(self, params=None):
+ """List all the volumes created."""
+ url = 'volumes'
+
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = etree.fromstring(body)
+ volumes = []
+ if body is not None:
+ volumes += [self._parse_volume(vol) for vol in list(body)]
+ return resp, volumes
+
+ def list_volumes_with_detail(self, params=None):
+ """List all the details of volumes."""
+ url = 'volumes/detail'
+
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = etree.fromstring(body)
+ volumes = []
+ if body is not None:
+ volumes += [self._parse_volume(vol) for vol in list(body)]
+ for v in volumes:
+ v = self._check_if_bootable(v)
+ return resp, volumes
+
+ def get_volume(self, volume_id):
+ """Returns the details of a single volume."""
+ url = "volumes/%s" % str(volume_id)
+ resp, body = self.get(url)
+ body = self._parse_volume(etree.fromstring(body))
+ body = self._check_if_bootable(body)
+ return resp, body
+
+ def create_volume(self, size, **kwargs):
+ """Creates a new Volume.
+
+ :param size: Size of volume in GB. (Required)
+ :param name: Optional Volume Name.
+ :param metadata: An optional dictionary of values for metadata.
+ :param volume_type: Optional Name of volume_type for the volume
+ :param snapshot_id: When specified the volume is created from
+ this snapshot
+ :param imageRef: When specified the volume is created from this
+ image
+ """
+ # NOTE(afazekas): it should use a volume namespace
+ volume = Element("volume", xmlns=XMLNS_11, size=size)
+
+ if 'metadata' in kwargs:
+ _metadata = Element('metadata')
+ volume.append(_metadata)
+ for key, value in kwargs['metadata'].items():
+ meta = Element('meta')
+ meta.add_attr('key', key)
+ meta.append(Text(value))
+ _metadata.append(meta)
+ attr_to_add = kwargs.copy()
+ del attr_to_add['metadata']
+ else:
+ attr_to_add = kwargs
+
+ for key, value in attr_to_add.items():
+ volume.add_attr(key, value)
+
+ resp, body = self.post('volumes', str(Document(volume)))
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def update_volume(self, volume_id, **kwargs):
+ """Updates the Specified Volume."""
+ put_body = Element("volume", xmlns=XMLNS_11, **kwargs)
+
+ resp, body = self.put('volumes/%s' % volume_id,
+ str(Document(put_body)))
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def delete_volume(self, volume_id):
+ """Deletes the Specified Volume."""
+ return self.delete("volumes/%s" % str(volume_id))
+
+ def wait_for_volume_status(self, volume_id, status):
+ """Waits for a Volume to reach a given status."""
+ resp, body = self.get_volume(volume_id)
+ volume_status = body['status']
+ start = int(time.time())
+
+ while volume_status != status:
+ time.sleep(self.build_interval)
+ resp, body = self.get_volume(volume_id)
+ volume_status = body['status']
+ if volume_status == 'error':
+ raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
+
+ if int(time.time()) - start >= self.build_timeout:
+ message = 'Volume %s failed to reach %s status within '\
+ 'the required time (%s s).' % (volume_id,
+ status,
+ self.build_timeout)
+ raise exceptions.TimeoutException(message)
+
+ def is_resource_deleted(self, id):
+ try:
+ self.get_volume(id)
+ except exceptions.NotFound:
+ return True
+ return False
+
+ def attach_volume(self, volume_id, instance_uuid, mountpoint):
+ """Attaches a volume to a given instance on a given mountpoint."""
+ post_body = Element("os-attach",
+ instance_uuid=instance_uuid,
+ mountpoint=mountpoint
+ )
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def detach_volume(self, volume_id):
+ """Detaches a volume from an instance."""
+ post_body = Element("os-detach")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def upload_volume(self, volume_id, image_name, disk_format):
+ """Uploads a volume in Glance."""
+ post_body = Element("os-volume_upload_image",
+ image_name=image_name,
+ disk_format=disk_format)
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ volume = xml_to_json(etree.fromstring(body))
+ return resp, volume
+
+ def extend_volume(self, volume_id, extend_size):
+ """Extend a volume."""
+ post_body = Element("os-extend",
+ new_size=extend_size)
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def reset_volume_status(self, volume_id, status):
+ """Reset the Specified Volume's Status."""
+ post_body = Element("os-reset_status",
+ status=status
+ )
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def volume_begin_detaching(self, volume_id):
+ """Volume Begin Detaching."""
+ post_body = Element("os-begin_detaching")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def volume_roll_detaching(self, volume_id):
+ """Volume Roll Detaching."""
+ post_body = Element("os-roll_detaching")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def reserve_volume(self, volume_id):
+ """Reserves a volume."""
+ post_body = Element("os-reserve")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def unreserve_volume(self, volume_id):
+ """Restore a reserved volume ."""
+ post_body = Element("os-unreserve")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def create_volume_transfer(self, vol_id, name=None):
+ """Create a volume transfer."""
+ post_body = Element("transfer",
+ volume_id=vol_id)
+ if name:
+ post_body.add_attr('name', name)
+ resp, body = self.post('os-volume-transfer',
+ str(Document(post_body)))
+ volume = xml_to_json(etree.fromstring(body))
+ return resp, volume
+
+ def get_volume_transfer(self, transfer_id):
+ """Returns the details of a volume transfer."""
+ url = "os-volume-transfer/%s" % str(transfer_id)
+ resp, body = self.get(url)
+ volume = xml_to_json(etree.fromstring(body))
+ return resp, volume
+
+ def list_volume_transfers(self, params=None):
+ """List all the volume transfers created."""
+ url = 'os-volume-transfer'
+ if params:
+ url += '?%s' % urllib.urlencode(params)
+
+ resp, body = self.get(url)
+ body = etree.fromstring(body)
+ volumes = []
+ if body is not None:
+ volumes += [self._parse_volume_transfer(vol) for vol in list(body)]
+ return resp, volumes
+
+ def _parse_volume_transfer(self, body):
+ vol = dict((attr, body.get(attr)) for attr in body.keys())
+ for child in body.getchildren():
+ tag = child.tag
+ if tag.startswith("{"):
+ tag = tag.split("}", 1)
+ vol[tag] = xml_to_json(child)
+ return vol
+
+ def delete_volume_transfer(self, transfer_id):
+ """Delete a volume transfer."""
+ return self.delete("os-volume-transfer/%s" % str(transfer_id))
+
+ def accept_volume_transfer(self, transfer_id, transfer_auth_key):
+ """Accept a volume transfer."""
+ post_body = Element("accept", auth_key=transfer_auth_key)
+ url = 'os-volume-transfer/%s/accept' % transfer_id
+ resp, body = self.post(url, str(Document(post_body)))
+ volume = xml_to_json(etree.fromstring(body))
+ return resp, volume
+
+ def update_volume_readonly(self, volume_id, readonly):
+ """Update the Specified Volume readonly."""
+ post_body = Element("os-update_readonly_flag",
+ readonly=readonly)
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def force_delete_volume(self, volume_id):
+ """Force Delete Volume."""
+ post_body = Element("os-force_delete")
+ url = 'volumes/%s/action' % str(volume_id)
+ resp, body = self.post(url, str(Document(post_body)))
+ if body:
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def _metadata_body(self, meta):
+ post_body = Element('metadata')
+ for k, v in meta.items():
+ data = Element('meta', key=k)
+ data.append(Text(v))
+ post_body.append(data)
+ return post_body
+
+ def _parse_key_value(self, node):
+ """Parse <foo key='key'>value</foo> data into {'key': 'value'}."""
+ data = {}
+ for node in node.getchildren():
+ data[node.get('key')] = node.text
+ return data
+
+ def create_volume_metadata(self, volume_id, metadata):
+ """Create metadata for the volume."""
+ post_body = self._metadata_body(metadata)
+ resp, body = self.post('volumes/%s/metadata' % volume_id,
+ str(Document(post_body)))
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
+
+ def get_volume_metadata(self, volume_id):
+ """Get metadata of the volume."""
+ url = "volumes/%s/metadata" % str(volume_id)
+ resp, body = self.get(url)
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
+
+ def update_volume_metadata(self, volume_id, metadata):
+ """Update metadata for the volume."""
+ put_body = self._metadata_body(metadata)
+ url = "volumes/%s/metadata" % str(volume_id)
+ resp, body = self.put(url, str(Document(put_body)))
+ body = self._parse_key_value(etree.fromstring(body))
+ return resp, body
+
+ def update_volume_metadata_item(self, volume_id, id, meta_item):
+ """Update metadata item for the volume."""
+ for k, v in meta_item.items():
+ put_body = Element('meta', key=k)
+ put_body.append(Text(v))
+ url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ resp, body = self.put(url, str(Document(put_body)))
+ body = xml_to_json(etree.fromstring(body))
+ return resp, body
+
+ def delete_volume_metadata_item(self, volume_id, id):
+ """Delete metadata item for the volume."""
+ url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ return self.delete(url)
diff --git a/tempest/services/volume/xml/admin/volume_hosts_client.py b/tempest/services/volume/xml/admin/volume_hosts_client.py
index 39b82e5..080e3d1 100644
--- a/tempest/services/volume/xml/admin/volume_hosts_client.py
+++ b/tempest/services/volume/xml/admin/volume_hosts_client.py
@@ -1,4 +1,4 @@
-# Copyright 2013 Openstack Foundation.
+# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -29,9 +29,8 @@
Client class to send CRUD Volume Hosts API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumeHostsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumeHostsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -68,6 +67,6 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/volume/xml/admin/volume_types_client.py b/tempest/services/volume/xml/admin/volume_types_client.py
index 942c4e1..802d27a 100644
--- a/tempest/services/volume/xml/admin/volume_types_client.py
+++ b/tempest/services/volume/xml/admin/volume_types_client.py
@@ -34,9 +34,8 @@
Client class to send CRUD Volume Types API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumeTypesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumeTypesClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -73,7 +72,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volume_types = []
if body is not None:
@@ -84,7 +83,7 @@
def get_volume_type(self, type_id):
"""Returns the details of a single volume_type."""
url = "types/%s" % str(type_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
return resp, self._parse_volume_type(body)
@@ -109,8 +108,7 @@
spec.append(Text(value))
_extra_specs.append(spec)
- resp, body = self.post('types', str(Document(vol_type)),
- self.headers)
+ resp, body = self.post('types', str(Document(vol_type)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -125,7 +123,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
extra_specs = []
if body is not None:
@@ -137,7 +135,7 @@
"""Returns the details of a single volume_type extra spec."""
url = "types/%s/extra_specs/%s" % (str(vol_type_id),
str(extra_spec_name))
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
return resp, self._parse_volume_type_extra_specs(body)
@@ -161,8 +159,7 @@
else:
extra_specs = None
- resp, body = self.post(url, str(Document(extra_specs)),
- self.headers)
+ resp, body = self.post(url, str(Document(extra_specs)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -191,8 +188,7 @@
spec.append(Text(value))
extra_specs.append(spec)
- resp, body = self.put(url, str(Document(extra_specs)),
- self.headers)
+ resp, body = self.put(url, str(Document(extra_specs)))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/volume/xml/extensions_client.py b/tempest/services/volume/xml/extensions_client.py
index a04616d..03743a6 100644
--- a/tempest/services/volume/xml/extensions_client.py
+++ b/tempest/services/volume/xml/extensions_client.py
@@ -24,9 +24,8 @@
class ExtensionsClientXML(RestClientXML):
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(ExtensionsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(ExtensionsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
def _parse_array(self, node):
@@ -37,6 +36,6 @@
def list_extensions(self):
url = 'extensions'
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_array(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 5e62b6d..9abe042 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -33,9 +33,8 @@
class SnapshotsClientXML(RestClientXML):
"""Client class to send CRUD Volume API requests."""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(SnapshotsClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(SnapshotsClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.volume.build_interval
@@ -48,7 +47,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
snapshots = []
for snap in body:
@@ -62,7 +61,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
snapshots = []
for snap in body:
@@ -72,7 +71,7 @@
def get_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot."""
url = "snapshots/%s" % str(snapshot_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
return resp, xml_to_json(body)
@@ -87,8 +86,7 @@
snapshot = Element("snapshot", xmlns=XMLNS_11, volume_id=volume_id)
for key, value in kwargs.items():
snapshot.add_attr(key, value)
- resp, body = self.post('snapshots', str(Document(snapshot)),
- self.headers)
+ resp, body = self.post('snapshots', str(Document(snapshot)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -97,8 +95,7 @@
put_body = Element("snapshot", xmlns=XMLNS_11, **kwargs)
resp, body = self.put('snapshots/%s' % snapshot_id,
- str(Document(put_body)),
- self.headers)
+ str(Document(put_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -156,7 +153,7 @@
status=status
)
url = 'snapshots/%s/action' % str(snapshot_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -168,7 +165,7 @@
progress=progress
)
url = 'snapshots/%s/action' % str(snapshot_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -192,15 +189,14 @@
"""Create metadata for the snapshot."""
post_body = self._metadata_body(metadata)
resp, body = self.post('snapshots/%s/metadata' % snapshot_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def get_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot."""
url = "snapshots/%s/metadata" % str(snapshot_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -208,7 +204,7 @@
"""Update metadata for the snapshot."""
put_body = self._metadata_body(metadata)
url = "snapshots/%s/metadata" % str(snapshot_id)
- resp, body = self.put(url, str(Document(put_body)), self.headers)
+ resp, body = self.put(url, str(Document(put_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -218,7 +214,7 @@
put_body = Element('meta', key=k)
put_body.append(Text(v))
url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
- resp, body = self.put(url, str(Document(put_body)), self.headers)
+ resp, body = self.put(url, str(Document(put_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -231,7 +227,7 @@
"""Force Delete Snapshot."""
post_body = Element("os-force_delete")
url = 'snapshots/%s/action' % str(snapshot_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 7adaf4b..94c1ff6 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -17,6 +17,7 @@
import urllib
from lxml import etree
+from xml.sax.saxutils import escape
from tempest.common.rest_client import RestClientXML
from tempest import config
@@ -35,9 +36,8 @@
Client class to send CRUD Volume API requests to a Cinder endpoint
"""
- def __init__(self, username, password, auth_url, tenant_name=None):
- super(VolumesClientXML, self).__init__(username, password,
- auth_url, tenant_name)
+ def __init__(self, auth_provider):
+ super(VolumesClientXML, self).__init__(auth_provider)
self.service = CONF.volume.catalog_type
self.build_interval = CONF.compute.build_interval
self.build_timeout = CONF.compute.build_timeout
@@ -88,7 +88,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
@@ -104,7 +104,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
@@ -116,7 +116,7 @@
def get_volume(self, volume_id):
"""Returns the details of a single volume."""
url = "volumes/%s" % str(volume_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_volume(etree.fromstring(body))
body = self._check_if_bootable(body)
return resp, body
@@ -152,8 +152,7 @@
for key, value in attr_to_add.items():
volume.add_attr(key, value)
- resp, body = self.post('volumes', str(Document(volume)),
- self.headers)
+ resp, body = self.post('volumes', str(Document(volume)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -162,8 +161,7 @@
put_body = Element("volume", xmlns=XMLNS_11, **kwargs)
resp, body = self.put('volumes/%s' % volume_id,
- str(Document(put_body)),
- self.headers)
+ str(Document(put_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -205,7 +203,7 @@
mountpoint=mountpoint
)
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -214,7 +212,7 @@
"""Detaches a volume from an instance."""
post_body = Element("os-detach")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -225,7 +223,7 @@
image_name=image_name,
disk_format=disk_format)
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
volume = xml_to_json(etree.fromstring(body))
return resp, volume
@@ -234,7 +232,7 @@
post_body = Element("os-extend",
new_size=extend_size)
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -245,7 +243,7 @@
status=status
)
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -254,7 +252,7 @@
"""Volume Begin Detaching."""
post_body = Element("os-begin_detaching")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -263,7 +261,7 @@
"""Volume Roll Detaching."""
post_body = Element("os-roll_detaching")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -272,7 +270,7 @@
"""Reserves a volume."""
post_body = Element("os-reserve")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -281,7 +279,7 @@
"""Restore a reserved volume ."""
post_body = Element("os-unreserve")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -293,15 +291,14 @@
if display_name:
post_body.add_attr('name', display_name)
resp, body = self.post('os-volume-transfer',
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
volume = xml_to_json(etree.fromstring(body))
return resp, volume
def get_volume_transfer(self, transfer_id):
"""Returns the details of a volume transfer."""
url = "os-volume-transfer/%s" % str(transfer_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
volume = xml_to_json(etree.fromstring(body))
return resp, volume
@@ -311,7 +308,7 @@
if params:
url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = etree.fromstring(body)
volumes = []
if body is not None:
@@ -335,7 +332,7 @@
"""Accept a volume transfer."""
post_body = Element("accept", auth_key=transfer_auth_key)
url = 'os-volume-transfer/%s/accept' % transfer_id
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
volume = xml_to_json(etree.fromstring(body))
return resp, volume
@@ -344,7 +341,7 @@
post_body = Element("os-update_readonly_flag",
readonly=readonly)
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -353,7 +350,7 @@
"""Force Delete Volume."""
post_body = Element("os-force_delete")
url = 'volumes/%s/action' % str(volume_id)
- resp, body = self.post(url, str(Document(post_body)), self.headers)
+ resp, body = self.post(url, str(Document(post_body)))
if body:
body = xml_to_json(etree.fromstring(body))
return resp, body
@@ -362,7 +359,8 @@
post_body = Element('metadata')
for k, v in meta.items():
data = Element('meta', key=k)
- data.append(Text(v))
+ # Escape value to allow for special XML chars
+ data.append(Text(escape(v)))
post_body.append(data)
return post_body
@@ -377,15 +375,14 @@
"""Create metadata for the volume."""
post_body = self._metadata_body(metadata)
resp, body = self.post('volumes/%s/metadata' % volume_id,
- str(Document(post_body)),
- self.headers)
+ str(Document(post_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
def get_volume_metadata(self, volume_id):
"""Get metadata of the volume."""
url = "volumes/%s/metadata" % str(volume_id)
- resp, body = self.get(url, self.headers)
+ resp, body = self.get(url)
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -393,7 +390,7 @@
"""Update metadata for the volume."""
put_body = self._metadata_body(metadata)
url = "volumes/%s/metadata" % str(volume_id)
- resp, body = self.put(url, str(Document(put_body)), self.headers)
+ resp, body = self.put(url, str(Document(put_body)))
body = self._parse_key_value(etree.fromstring(body))
return resp, body
@@ -403,7 +400,7 @@
put_body = Element('meta', key=k)
put_body.append(Text(v))
url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
- resp, body = self.put(url, str(Document(put_body)), self.headers)
+ resp, body = self.put(url, str(Document(put_body)))
body = xml_to_json(etree.fromstring(body))
return resp, body
diff --git a/tempest/stress/actions/unit_test.py b/tempest/stress/actions/unit_test.py
index 8bd2f22..2f1d28f 100644
--- a/tempest/stress/actions/unit_test.py
+++ b/tempest/stress/actions/unit_test.py
@@ -10,10 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest import config
from tempest.openstack.common import importutils
from tempest.openstack.common import log as logging
import tempest.stress.stressaction as stressaction
+CONF = config.CONF
+
class SetUpClassRunTime(object):
@@ -73,10 +76,14 @@
self.klass.setUpClass()
self.setupclass_called = True
- self.run_core()
-
- if (self.class_setup_per == SetUpClassRunTime.action):
- self.klass.tearDownClass()
+ try:
+ self.run_core()
+ except Exception as e:
+ raise e
+ finally:
+ if (CONF.stress.leave_dirty_stack is False
+ and self.class_setup_per == SetUpClassRunTime.action):
+ self.klass.tearDownClass()
else:
self.run_core()
diff --git a/tempest/test.py b/tempest/test.py
index 5464c03..22aa3f2 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -17,6 +17,7 @@
import functools
import json
import os
+import sys
import time
import urllib
import uuid
@@ -70,16 +71,35 @@
This decorator applies a testtools attr for each service that gets
exercised by a test case.
"""
- valid_service_list = ['compute', 'image', 'volume', 'orchestration',
- 'network', 'identity', 'object_storage', 'dashboard']
+ service_list = {
+ 'compute': CONF.service_available.nova,
+ 'image': CONF.service_available.glance,
+ 'volume': CONF.service_available.cinder,
+ 'orchestration': CONF.service_available.heat,
+ # NOTE(mtreinish) nova-network will provide networking functionality
+ # if neutron isn't available, so always set to True.
+ 'network': True,
+ 'identity': True,
+ 'object_storage': CONF.service_available.swift,
+ 'dashboard': CONF.service_available.horizon,
+ }
def decorator(f):
for service in args:
- if service not in valid_service_list:
+ if service not in service_list:
raise exceptions.InvalidServiceTag('%s is not a valid service'
% service)
attr(type=list(args))(f)
- return f
+
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ for service in args:
+ if not service_list[service]:
+ msg = 'Skipped because the %s service is not available' % (
+ service)
+ raise testtools.TestCase.skipException(msg)
+ return f(self, *func_args, **func_kwargs)
+ return wrapper
return decorator
@@ -129,6 +149,8 @@
else:
skip = True
if "bug" in kwargs and skip is True:
+ if not kwargs['bug'].isdigit():
+ raise ValueError('bug must be a valid bug number')
msg = "Skipped until Bug: %s is resolved." % kwargs["bug"]
raise testtools.TestCase.skipException(msg)
return f(self, *func_args, **func_kwargs)
@@ -222,10 +244,23 @@
atexit.register(validate_tearDownClass)
-
-class BaseTestCase(testtools.TestCase,
+if sys.version_info >= (2, 7):
+ class BaseDeps(testtools.TestCase,
testtools.testcase.WithAttributes,
testresources.ResourcedTestCase):
+ pass
+else:
+ # Define asserts for py26
+ import unittest2
+
+ class BaseDeps(testtools.TestCase,
+ testtools.testcase.WithAttributes,
+ testresources.ResourcedTestCase,
+ unittest2.TestCase):
+ pass
+
+
+class BaseTestCase(BaseDeps):
setUpClassCalled = False
_service = None
@@ -277,7 +312,7 @@
@classmethod
def get_client_manager(cls, interface=None):
"""
- Returns an Openstack client manager
+ Returns an OpenStack client manager
"""
cls.isolated_creds = isolated_creds.IsolatedCreds(
cls.__name__, network_resources=cls.network_resources)
@@ -405,11 +440,16 @@
schema = description.get("json-schema", None)
resources = description.get("resources", [])
scenario_list = []
+ expected_result = None
for resource in resources:
+ if isinstance(resource, dict):
+ expected_result = resource['expected_result']
+ resource = resource['name']
LOG.debug("Add resource to test %s" % resource)
scn_name = "inv_res_%s" % (resource)
scenario_list.append((scn_name, {"resource": (resource,
- str(uuid.uuid4()))
+ str(uuid.uuid4())),
+ "expected_result": expected_result
}))
if schema is not None:
for invalid in generate_json.generate_invalid(schema):
@@ -460,16 +500,12 @@
if schema:
valid = generate_json.generate_valid(schema)
new_url, body = self._http_arguments(valid, url, method)
- resp, resp_body = self.client.send_request(method, new_url,
- resources, body=body)
- self._check_negative_response(resp.status, resp_body)
- return
-
- if hasattr(self, "schema"):
+ elif hasattr(self, "schema"):
new_url, body = self._http_arguments(self.schema, url, method)
- resp, resp_body = self.client.send_request(method, new_url,
- resources, body=body)
- self._check_negative_response(resp.status, resp_body)
+
+ resp, resp_body = self.client.send_request(method, new_url,
+ resources, body=body)
+ self._check_negative_response(resp.status, resp_body)
def _http_arguments(self, json_dict, url, method):
LOG.debug("dict: %s url: %s method: %s" % (json_dict, url, method))
@@ -510,6 +546,8 @@
:param name: The name of the kind of resource such as "flavor", "role",
etc.
"""
+ if isinstance(name, dict):
+ name = name['name']
if hasattr(self, "resource") and self.resource[0] == name:
LOG.debug("Return invalid resource (%s) value: %s" %
(self.resource[0], self.resource[1]))
diff --git a/tempest/tests/fake_auth_provider.py b/tempest/tests/fake_auth_provider.py
new file mode 100644
index 0000000..bc68d26
--- /dev/null
+++ b/tempest/tests/fake_auth_provider.py
@@ -0,0 +1,20 @@
+# Copyright 2014 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.
+
+
+class FakeAuthProvider(object):
+
+ def auth_request(self, method, url, headers=None, body=None, filters=None):
+ return url, headers, body
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index a50aaeb..42237ca 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -22,5 +22,30 @@
class fake_identity(object):
disable_ssl_certificate_validation = True
+ class fake_default_feature_enabled(object):
+ api_extensions = ['all']
+
+ class fake_compute_feature_enabled(fake_default_feature_enabled):
+ api_v3_extensions = ['all']
+
+ class fake_object_storage_discoverable_apis(object):
+ discoverable_apis = ['all']
+
+ class fake_service_available(object):
+ nova = True
+ glance = True
+ cinder = True
+ heat = True
+ neutron = True
+ swift = True
+ horizon = True
+
+ compute_feature_enabled = fake_compute_feature_enabled()
+ volume_feature_enabled = fake_default_feature_enabled()
+ network_feature_enabled = fake_default_feature_enabled()
+ object_storage_feature_enabled = fake_object_storage_discoverable_apis()
+
+ service_available = fake_service_available()
+
compute = fake_compute()
identity = fake_identity()
diff --git a/tempest/tests/negative/__init__.py b/tempest/tests/negative/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/negative/__init__.py
diff --git a/tempest/tests/negative/test_generate_json.py b/tempest/tests/negative/test_generate_json.py
new file mode 100644
index 0000000..a0aa088
--- /dev/null
+++ b/tempest/tests/negative/test_generate_json.py
@@ -0,0 +1,53 @@
+# Copyright 2014 Deutsche Telekom AG
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.common import generate_json as gen
+import tempest.test
+
+
+class TestGenerateJson(tempest.test.BaseTestCase):
+
+ fake_input_str = {"type": "string",
+ "minLength": 2,
+ "maxLength": 8,
+ 'results': {'gen_number': 404}}
+
+ fake_input_int = {"type": "integer",
+ "maximum": 255,
+ "minimum": 1}
+
+ fake_input_obj = {"type": "object",
+ "properties": {"minRam": {"type": "integer"},
+ "diskName": {"type": "string"},
+ "maxRam": {"type": "integer", }
+ }
+ }
+
+ def _validate_result(self, data):
+ self.assertTrue(isinstance(data, list))
+ for t in data:
+ self.assertTrue(isinstance(t, tuple))
+
+ def test_generate_invalid_string(self):
+ result = gen.generate_invalid(self.fake_input_str)
+ self._validate_result(result)
+
+ def test_generate_invalid_integer(self):
+ result = gen.generate_invalid(self.fake_input_int)
+ self._validate_result(result)
+
+ def test_generate_invalid_obj(self):
+ result = gen.generate_invalid(self.fake_input_obj)
+ self._validate_result(result)
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
new file mode 100644
index 0000000..4c59383
--- /dev/null
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -0,0 +1,64 @@
+# Copyright 2014 Deutsche Telekom AG
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+import tempest.test as test
+
+
+class TestNegativeAutoTest(test.BaseTestCase):
+ # Fake entries
+ _interface = 'json'
+ _service = 'compute'
+
+ fake_input_desc = {"name": "list-flavors-with-detail",
+ "http-method": "GET",
+ "url": "flavors/detail",
+ "json-schema": {"type": "object",
+ "properties":
+ {"minRam": {"type": "integer"},
+ "minDisk": {"type": "integer"}}
+ },
+ "resources": ["flavor", "volume", "image"]
+ }
+
+ def _check_prop_entries(self, result, entry):
+ entries = [a for a in result if entry in a[0]]
+ self.assertIsNotNone(entries)
+ self.assertIs(len(entries), 2)
+ for entry in entries:
+ self.assertIsNotNone(entry[1]['schema'])
+
+ def _check_resource_entries(self, result, entry):
+ entries = [a for a in result if entry in a[0]]
+ self.assertIsNotNone(entries)
+ self.assertIs(len(entries), 3)
+ for entry in entries:
+ self.assertIsNotNone(entry[1]['resource'])
+
+ @mock.patch('tempest.test.NegativeAutoTest.load_schema')
+ def test_generate_scenario(self, open_mock):
+ open_mock.return_value = self.fake_input_desc
+ scenarios = test.NegativeAutoTest.\
+ generate_scenario(None)
+
+ self.assertIsInstance(scenarios, list)
+ for scenario in scenarios:
+ self.assertIsInstance(scenario, tuple)
+ self.assertIsInstance(scenario[0], str)
+ self.assertIsInstance(scenario[1], dict)
+ self._check_prop_entries(scenarios, "prop_minRam")
+ self._check_prop_entries(scenarios, "prop_minDisk")
+ self._check_resource_entries(scenarios, "inv_res")
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
new file mode 100644
index 0000000..88920dc
--- /dev/null
+++ b/tempest/tests/test_decorators.py
@@ -0,0 +1,238 @@
+# Copyright 2013 IBM Corp.
+#
+# 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 testtools
+
+from tempest import exceptions
+from tempest.openstack.common.fixture import mockpatch
+from tempest import test
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class BaseDecoratorsTest(base.TestCase):
+ def setUp(self):
+ super(BaseDecoratorsTest, self).setUp()
+ self.stubs.Set(test, 'CONF', fake_config.FakeConfig)
+
+
+class TestAttrDecorator(BaseDecoratorsTest):
+ def _test_attr_helper(self, expected_attrs, **decorator_args):
+ @test.attr(**decorator_args)
+ def foo():
+ pass
+
+ # By our test.attr decorator the attribute __testtools_attrs will be
+ # set only for 'type' argument, so we test it first.
+ if 'type' in decorator_args:
+ # this is what testtools sets
+ self.assertEqual(getattr(foo, '__testtools_attrs'),
+ set(expected_attrs))
+
+ # nose sets it anyway
+ for arg, value in decorator_args.items():
+ self.assertEqual(getattr(foo, arg), value)
+
+ def test_attr_without_type(self):
+ self._test_attr_helper(expected_attrs='baz', bar='baz')
+
+ def test_attr_decorator_with_smoke_type(self):
+ # smoke passed as type, so smoke and gate must have been set.
+ self._test_attr_helper(expected_attrs=['smoke', 'gate'], type='smoke')
+
+ def test_attr_decorator_with_list_type(self):
+ # if type is 'smoke' we'll get the original list of types plus 'gate'
+ self._test_attr_helper(expected_attrs=['smoke', 'foo', 'gate'],
+ type=['smoke', 'foo'])
+
+ def test_attr_decorator_with_unknown_type(self):
+ self._test_attr_helper(expected_attrs=['foo'], type='foo')
+
+ def test_attr_decorator_with_duplicated_type(self):
+ self._test_attr_helper(expected_attrs=['foo'], type=['foo', 'foo'])
+
+
+class TestServicesDecorator(BaseDecoratorsTest):
+ def _test_services_helper(self, *decorator_args):
+ class TestFoo(test.BaseTestCase):
+ @test.services(*decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ self.assertEqual(set(decorator_args), getattr(t.test_bar,
+ '__testtools_attrs'))
+ self.assertEqual(list(decorator_args), t.test_bar.type)
+ self.assertEqual(t.test_bar(), 0)
+
+ def test_services_decorator_with_single_service(self):
+ self._test_services_helper('compute')
+
+ def test_services_decorator_with_multiple_services(self):
+ self._test_services_helper('compute', 'network')
+
+ def test_services_decorator_with_duplicated_service(self):
+ self._test_services_helper('compute', 'compute')
+
+ def test_services_decorator_with_invalid_service(self):
+ self.assertRaises(exceptions.InvalidServiceTag,
+ self._test_services_helper, 'compute',
+ 'bad_service')
+
+ def test_services_decorator_with_service_valid_and_unavailable(self):
+ self.useFixture(mockpatch.PatchObject(test.CONF.service_available,
+ 'cinder', False))
+ self.assertRaises(testtools.TestCase.skipException,
+ self._test_services_helper, 'compute',
+ 'volume')
+
+
+class TestStressDecorator(BaseDecoratorsTest):
+ def _test_stresstest_helper(self, expected_frequency='process',
+ expected_inheritance=False,
+ **decorator_args):
+ @test.stresstest(**decorator_args)
+ def foo():
+ pass
+ self.assertEqual(getattr(foo, 'st_class_setup_per'),
+ expected_frequency)
+ self.assertEqual(getattr(foo, 'st_allow_inheritance'),
+ expected_inheritance)
+ self.assertEqual(foo.type, 'stress')
+ self.assertEqual(set(['stress']), getattr(foo, '__testtools_attrs'))
+
+ def test_stresstest_decorator_default(self):
+ self._test_stresstest_helper()
+
+ def test_stresstest_decorator_class_setup_frequency(self):
+ self._test_stresstest_helper('process', class_setup_per='process')
+
+ def test_stresstest_decorator_class_setup_frequency_non_default(self):
+ self._test_stresstest_helper(expected_frequency='application',
+ class_setup_per='application')
+
+ def test_stresstest_decorator_set_frequency_and_inheritance(self):
+ self._test_stresstest_helper(expected_frequency='application',
+ expected_inheritance=True,
+ class_setup_per='application',
+ allow_inheritance=True)
+
+
+class TestSkipBecauseDecorator(BaseDecoratorsTest):
+ def _test_skip_because_helper(self, expected_to_skip=True,
+ **decorator_args):
+ class TestFoo(test.BaseTestCase):
+ _interface = 'json'
+
+ @test.skip_because(**decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ else:
+ # assert that test_bar returned 0
+ self.assertEqual(TestFoo('test_bar').test_bar(), 0)
+
+ def test_skip_because_bug(self):
+ self._test_skip_because_helper(bug='12345')
+
+ def test_skip_because_bug_and_interface_match(self):
+ self._test_skip_because_helper(bug='12346', interface='json')
+
+ def test_skip_because_bug_interface_not_match(self):
+ self._test_skip_because_helper(expected_to_skip=False,
+ bug='12347', interface='xml')
+
+ def test_skip_because_bug_and_condition_true(self):
+ self._test_skip_because_helper(bug='12348', condition=True)
+
+ def test_skip_because_bug_and_condition_false(self):
+ self._test_skip_because_helper(expected_to_skip=False,
+ bug='12349', condition=False)
+
+ def test_skip_because_bug_condition_false_and_interface_match(self):
+ """
+ Assure that only condition will be evaluated if both parameters are
+ passed.
+ """
+ self._test_skip_because_helper(expected_to_skip=False,
+ bug='12350', condition=False,
+ interface='json')
+
+ def test_skip_because_bug_condition_true_and_interface_not_match(self):
+ """
+ Assure that only condition will be evaluated if both parameters are
+ passed.
+ """
+ self._test_skip_because_helper(bug='12351', condition=True,
+ interface='xml')
+
+ def test_skip_because_bug_without_bug_never_skips(self):
+ """Never skip without a bug parameter."""
+ self._test_skip_because_helper(expected_to_skip=False,
+ condition=True)
+ self._test_skip_because_helper(expected_to_skip=False,
+ interface='json')
+
+ def test_skip_because_invalid_bug_number(self):
+ """Raise ValueError if with an invalid bug number"""
+ self.assertRaises(ValueError, self._test_skip_because_helper,
+ bug='critical_bug')
+
+
+class TestRequiresExtDecorator(BaseDecoratorsTest):
+ def setUp(self):
+ super(TestRequiresExtDecorator, self).setUp()
+ self.fixture = self.useFixture(mockpatch.PatchObject(
+ test.CONF.compute_feature_enabled,
+ 'api_extensions',
+ new=['enabled_ext', 'another_ext']))
+
+ def _test_requires_ext_helper(self, expected_to_skip=True,
+ **decorator_args):
+ class TestFoo(test.BaseTestCase):
+ @test.requires_ext(**decorator_args)
+ def test_bar(self):
+ return 0
+
+ t = TestFoo('test_bar')
+ if expected_to_skip:
+ self.assertRaises(testtools.TestCase.skipException, t.test_bar)
+ else:
+ self.assertEqual(t.test_bar(), 0)
+
+ def test_requires_ext_decorator(self):
+ self._test_requires_ext_helper(expected_to_skip=False,
+ extension='enabled_ext',
+ service='compute')
+
+ def test_requires_ext_decorator_disabled_ext(self):
+ self._test_requires_ext_helper(extension='disabled_ext',
+ service='compute')
+
+ def test_requires_ext_decorator_with_all_ext_enabled(self):
+ # disable fixture so the default (all) is used.
+ self.fixture.cleanUp()
+ self._test_requires_ext_helper(expected_to_skip=False,
+ extension='random_ext',
+ service='compute')
+
+ def test_requires_ext_decorator_bad_service(self):
+ self.assertRaises(KeyError,
+ self._test_requires_ext_helper,
+ extension='enabled_ext',
+ service='bad_service')
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index f3c7440..ba43daf 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -13,29 +13,34 @@
# under the License.
import httplib2
+import json
from tempest.common import rest_client
from tempest import config
from tempest import exceptions
from tempest.openstack.common.fixture import mockpatch
+from tempest.services.compute.xml import common as xml
from tempest.tests import base
+from tempest.tests import fake_auth_provider
from tempest.tests import fake_config
from tempest.tests import fake_http
class BaseRestClientTestClass(base.TestCase):
- def _set_token(self):
- self.rest_client.token = 'fake token'
+ url = 'fake_endpoint'
+
+ def _get_region(self):
+ return 'fake region'
def setUp(self):
super(BaseRestClientTestClass, self).setUp()
self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
- self.rest_client = rest_client.RestClient('fake_user', 'fake_pass',
- 'http://fake_url/v2.0')
+ self.rest_client = rest_client.RestClient(
+ fake_auth_provider.FakeAuthProvider())
self.stubs.Set(httplib2.Http, 'request', self.fake_http.request)
- self.useFixture(mockpatch.PatchObject(self.rest_client, '_set_auth',
- side_effect=self._set_token()))
+ self.useFixture(mockpatch.PatchObject(self.rest_client, '_get_region',
+ side_effect=self._get_region()))
self.useFixture(mockpatch.PatchObject(self.rest_client,
'_log_response'))
@@ -48,36 +53,33 @@
'_error_checker'))
def test_post(self):
- __, return_dict = self.rest_client.post('fake_endpoint', {},
- {})
+ __, return_dict = self.rest_client.post(self.url, {}, {})
self.assertEqual('POST', return_dict['method'])
def test_get(self):
- __, return_dict = self.rest_client.get('fake_endpoint')
+ __, return_dict = self.rest_client.get(self.url)
self.assertEqual('GET', return_dict['method'])
def test_delete(self):
- __, return_dict = self.rest_client.delete('fake_endpoint')
+ __, return_dict = self.rest_client.delete(self.url)
self.assertEqual('DELETE', return_dict['method'])
def test_patch(self):
- __, return_dict = self.rest_client.patch('fake_endpoint', {},
- {})
+ __, return_dict = self.rest_client.patch(self.url, {}, {})
self.assertEqual('PATCH', return_dict['method'])
def test_put(self):
- __, return_dict = self.rest_client.put('fake_endpoint', {},
- {})
+ __, return_dict = self.rest_client.put(self.url, {}, {})
self.assertEqual('PUT', return_dict['method'])
def test_head(self):
self.useFixture(mockpatch.PatchObject(self.rest_client,
'response_checker'))
- __, return_dict = self.rest_client.head('fake_endpoint')
+ __, return_dict = self.rest_client.head(self.url)
self.assertEqual('HEAD', return_dict['method'])
def test_copy(self):
- __, return_dict = self.rest_client.copy('fake_endpoint')
+ __, return_dict = self.rest_client.copy(self.url)
self.assertEqual('COPY', return_dict['method'])
@@ -88,4 +90,143 @@
def test_post(self):
self.assertRaises(exceptions.NotFound, self.rest_client.post,
- 'fake_endpoint', {}, {})
+ self.url, {}, {})
+
+
+class TestRestClientHeadersJSON(TestRestClientHTTPMethods):
+ TYPE = "json"
+
+ def _verify_headers(self, resp):
+ self.assertEqual(self.rest_client._get_type(), self.TYPE)
+ resp = dict((k.lower(), v) for k, v in resp.iteritems())
+ self.assertEqual(self.header_value, resp['accept'])
+ self.assertEqual(self.header_value, resp['content-type'])
+
+ def setUp(self):
+ super(TestRestClientHeadersJSON, self).setUp()
+ self.rest_client.TYPE = self.TYPE
+ self.header_value = 'application/%s' % self.rest_client._get_type()
+
+ def test_post(self):
+ resp, __ = self.rest_client.post(self.url, {})
+ self._verify_headers(resp)
+
+ def test_get(self):
+ resp, __ = self.rest_client.get(self.url)
+ self._verify_headers(resp)
+
+ def test_delete(self):
+ resp, __ = self.rest_client.delete(self.url)
+ self._verify_headers(resp)
+
+ def test_patch(self):
+ resp, __ = self.rest_client.patch(self.url, {})
+ self._verify_headers(resp)
+
+ def test_put(self):
+ resp, __ = self.rest_client.put(self.url, {})
+ self._verify_headers(resp)
+
+ def test_head(self):
+ self.useFixture(mockpatch.PatchObject(self.rest_client,
+ 'response_checker'))
+ resp, __ = self.rest_client.head(self.url)
+ self._verify_headers(resp)
+
+ def test_copy(self):
+ resp, __ = self.rest_client.copy(self.url)
+ self._verify_headers(resp)
+
+
+class TestRestClientHeadersXML(TestRestClientHeadersJSON):
+ TYPE = "xml"
+
+ # These two tests are needed in one exemplar
+ def test_send_json_accept_xml(self):
+ resp, __ = self.rest_client.get(self.url,
+ self.rest_client.get_headers("xml",
+ "json"))
+ resp = dict((k.lower(), v) for k, v in resp.iteritems())
+ self.assertEqual("application/json", resp["content-type"])
+ self.assertEqual("application/xml", resp["accept"])
+
+ def test_send_xml_accept_json(self):
+ resp, __ = self.rest_client.get(self.url,
+ self.rest_client.get_headers("json",
+ "xml"))
+ resp = dict((k.lower(), v) for k, v in resp.iteritems())
+ self.assertEqual("application/json", resp["accept"])
+ self.assertEqual("application/xml", resp["content-type"])
+
+
+class TestRestClientParseRespXML(BaseRestClientTestClass):
+ TYPE = "xml"
+
+ keys = ["fake_key1", "fake_key2"]
+ values = ["fake_value1", "fake_value2"]
+ item_expected = {key: value for key, value in zip(keys, values)}
+ list_expected = {"body_list": [
+ {keys[0]: values[0]},
+ {keys[1]: values[1]},
+ ]}
+ dict_expected = {"body_dict": {
+ keys[0]: values[0],
+ keys[1]: values[1],
+ }}
+
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2()
+ super(TestRestClientParseRespXML, self).setUp()
+ self.rest_client.TYPE = self.TYPE
+
+ def test_parse_resp_body_item(self):
+ body_item = xml.Element("item", **self.item_expected)
+ body = self.rest_client._parse_resp(str(xml.Document(body_item)))
+ self.assertEqual(self.item_expected, body)
+
+ def test_parse_resp_body_list(self):
+ self.rest_client.list_tags = ["fake_list", ]
+ body_list = xml.Element(self.rest_client.list_tags[0])
+ for i in range(2):
+ body_list.append(xml.Element("fake_item",
+ **self.list_expected["body_list"][i]))
+ body = self.rest_client._parse_resp(str(xml.Document(body_list)))
+ self.assertEqual(self.list_expected["body_list"], body)
+
+ def test_parse_resp_body_dict(self):
+ self.rest_client.dict_tags = ["fake_dict", ]
+ body_dict = xml.Element(self.rest_client.dict_tags[0])
+
+ for i in range(2):
+ body_dict.append(xml.Element("fake_item", xml.Text(self.values[i]),
+ key=self.keys[i]))
+
+ body = self.rest_client._parse_resp(str(xml.Document(body_dict)))
+ self.assertEqual(self.dict_expected["body_dict"], body)
+
+
+class TestRestClientParseRespJSON(TestRestClientParseRespXML):
+ TYPE = "json"
+
+ def test_parse_resp_body_item(self):
+ body = self.rest_client._parse_resp(json.dumps(self.item_expected))
+ self.assertEqual(self.item_expected, body)
+
+ def test_parse_resp_body_list(self):
+ body = self.rest_client._parse_resp(json.dumps(self.list_expected))
+ self.assertEqual(self.list_expected["body_list"], body)
+
+ def test_parse_resp_body_dict(self):
+ body = self.rest_client._parse_resp(json.dumps(self.dict_expected))
+ self.assertEqual(self.dict_expected["body_dict"], body)
+
+ def test_parse_resp_two_top_keys(self):
+ dict_two_keys = self.dict_expected.copy()
+ dict_two_keys.update({"second_key": ""})
+ body = self.rest_client._parse_resp(json.dumps(dict_two_keys))
+ self.assertEqual(dict_two_keys, body)
+
+ def test_parse_resp_one_top_key_without_list_or_dict(self):
+ data = {"one_top_key": "not_list_or_dict_value"}
+ body = self.rest_client._parse_resp(json.dumps(data))
+ self.assertEqual(data, body)
diff --git a/tempest/tests/test_ssh.py b/tempest/tests/test_ssh.py
index 429ed56..a6eedc4 100644
--- a/tempest/tests/test_ssh.py
+++ b/tempest/tests/test_ssh.py
@@ -88,15 +88,17 @@
client_mock.connect.side_effect = [socket.error, socket.error, True]
t_mock.side_effect = [
1000, # Start time
+ 1000, # LOG.warning() calls time.time() loop 1
1001, # Sleep loop 1
+ 1001, # LOG.warning() calls time.time() loop 2
1002 # Sleep loop 2
]
client._get_ssh_connection(sleep=1)
expected_sleeps = [
- mock.call(1),
- mock.call(1.01)
+ mock.call(2),
+ mock.call(3)
]
self.assertEqual(expected_sleeps, s_mock.mock_calls)
@@ -111,7 +113,9 @@
]
t_mock.side_effect = [
1000, # Start time
+ 1000, # LOG.warning() calls time.time() loop 1
1001, # Sleep loop 1
+ 1001, # LOG.warning() calls time.time() loop 2
1002, # Sleep loop 2
1003, # Sleep loop 3
1004 # LOG.error() calls time.time()
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index a6932bc..399a3c8 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -278,7 +278,7 @@
# TODO(afazekas): ping test. dependecy/permission ?
self.assertVolumeStatusWait(volume, "available")
- # NOTE(afazekas): it may be reports availble before it is available
+ # NOTE(afazekas): it may be reports available before it is available
ssh = RemoteClient(address.public_ip,
CONF.compute.ssh_user,
diff --git a/test-requirements.txt b/test-requirements.txt
index 3fe2f27..8d64167 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,7 +3,7 @@
docutils==0.9.1
sphinx>=1.1.2,<1.2
python-subunit>=0.0.18
-oslo.sphinx
+oslosphinx
mox>=0.5.3
mock>=1.0
coverage>=3.6
diff --git a/tools/check_logs.py b/tools/check_logs.py
index f3204e3..15988a6 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -28,7 +28,7 @@
is_neutron = os.environ.get('DEVSTACK_GATE_NEUTRON', "0") == "1"
is_grenade = (os.environ.get('DEVSTACK_GATE_GRENADE', "0") == "1" or
os.environ.get('DEVSTACK_GATE_GRENADE_FORWARD', "0") == "1")
-dump_all_errors = is_neutron
+dump_all_errors = True
def process_files(file_specs, url_specs, whitelists):
@@ -69,6 +69,7 @@
print_log_name = False
if not whitelisted:
had_errors = True
+ print("*** Not Whitelisted ***"),
print(line)
return had_errors
diff --git a/tools/install_venv.py b/tools/install_venv.py
index e41ca43..96b8279 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -25,12 +25,12 @@
def print_help(venv, root):
help = """
- Openstack development environment setup is complete.
+ OpenStack development environment setup is complete.
- Openstack development uses virtualenv to track and manage Python
+ OpenStack development uses virtualenv to track and manage Python
dependencies while in development and testing.
- To activate the Openstack virtualenv for the extent of your current shell
+ To activate the OpenStack virtualenv for the extent of your current shell
session you can run:
$ source %s/bin/activate
diff --git a/tools/tempest_auto_config.py b/tools/tempest_auto_config.py
index 9aeb077..5b8d05b 100644
--- a/tools/tempest_auto_config.py
+++ b/tools/tempest_auto_config.py
@@ -13,14 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-# This script aims to configure an initial Openstack environment with all the
-# necessary configurations for tempest's run using nothing but Openstack's
+# This script aims to configure an initial OpenStack environment with all the
+# necessary configurations for tempest's run using nothing but OpenStack's
# native API.
# That includes, creating users, tenants, registering images (cirros),
# configuring neutron and so on.
#
# ASSUMPTION: this script is run by an admin user as it is meant to configure
-# the Openstack environment prior to actual use.
+# the OpenStack environment prior to actual use.
# Config
import ConfigParser
@@ -32,7 +32,7 @@
import glanceclient as glance_client
import keystoneclient.v2_0.client as keystone_client
-# Import Openstack exceptions
+# Import OpenStack exceptions
import glanceclient.exc as glance_exception
import keystoneclient.exceptions as keystone_exception
@@ -88,7 +88,7 @@
def get_image_client(self, version="1", *args, **kwargs):
"""
- This method returns Openstack glance python client
+ This method returns OpenStack glance python client
:param version: a string representing the version of the glance client
to use.
:param string endpoint: A user-supplied endpoint URL for the glance
@@ -333,7 +333,7 @@
"""
Creates images for tempest's use and registers the environment variables
IMAGE_ID and IMAGE_ID_ALT with registered images
- :param image_client: Openstack python image client
+ :param image_client: OpenStack python image client
:param config: a ConfigParser object representing the tempest config file
:param config_section: the section name where the IMAGE ids are set
:param download_url: the URL from which we should download the UEC tar
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 913c90b..29eed9d 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -40,12 +40,12 @@
def verify_nova_api_versions(os):
- # Check nova api versions
- os.servers_client._set_auth()
- v2_endpoint = os.servers_client.base_url
- endpoint = 'http://' + v2_endpoint.split('/')[2]
- __, body = RAW_HTTP.request(endpoint, 'GET')
+ # Check nova api versions - only get base URL without PATH
+ os.servers_client.skip_path = True
+ __, body = RAW_HTTP.request(os.servers_client.base_url, 'GET')
body = json.loads(body)
+ # Restore full base_url
+ os.servers_client.skip_path = False
versions = map(lambda x: x['id'], body['versions'])
if CONF.compute_feature_enabled.api_v3 != ('v3.0' in versions):
print('Config option compute api_v3 should be change to: %s' % (
diff --git a/tox.ini b/tox.ini
index 88f2537..1580b14 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,9 +6,6 @@
[testenv]
sitepackages = True
setenv = VIRTUAL_ENV={envdir}
- LANG=en_US.UTF-8
- LANGUAGE=en_US:en
- LC_ALL=C
OS_TEST_PATH=./tempest/test_discover
usedevelop = True
install_command = pip install {opts} {packages}
@@ -40,6 +37,12 @@
commands =
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
+[testenv:full-serial]
+# The regex below is used to select which tests to run and exclude the slow tag:
+# See the testrepostiory bug: https://bugs.launchpad.net/testrepository/+bug/1208610
+commands =
+ bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'
+
[testenv:testr-full]
commands =
bash tools/pretty_tox.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty|cli)) {posargs}'