Merge "Return 'DOWN' ports when Ironic enabled"
diff --git a/HACKING.rst b/HACKING.rst
index ec7ff6a..480650c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -21,6 +21,7 @@
- [T111] Check that service client names of DELETE should be consistent
- [T112] Check that tempest.lib should not import local tempest code
- [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+- [T114] Check that tempest.lib does not use tempest config
- [N322] Method's default argument shouldn't be mutable
Test Data/Configuration
diff --git a/README.rst b/README.rst
index 650a1ed..725a890 100644
--- a/README.rst
+++ b/README.rst
@@ -25,8 +25,7 @@
discover features of a cloud incorrectly, and give people an
incorrect assessment of their cloud. Explicit is always better.
- Tempest uses OpenStack public interfaces. Tests in Tempest should
- only touch public interfaces, API calls (native or 3rd party),
- or libraries.
+ only touch public OpenStack APIs.
- Tempest should not touch private or implementation specific
interfaces. This means not directly going to the database, not
directly hitting the hypervisors, not testing extensions not
@@ -163,7 +162,7 @@
Tempest also has a set of unit tests which test the Tempest code itself. These
tests can be run by specifying the test discovery path::
- $> OS_TEST_PATH=./tempest/tests testr run --parallel
+ $ OS_TEST_PATH=./tempest/tests testr run --parallel
By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
should only be run on the unit test directory. The default value of OS_TEST_PATH
@@ -214,8 +213,8 @@
To start you need to create a configuration file. The easiest way to create a
configuration file is to generate a sample in the ``etc/`` directory ::
- $> cd $TEMPEST_ROOT_DIR
- $> oslo-config-generator --config-file \
+ $ cd $TEMPEST_ROOT_DIR
+ $ oslo-config-generator --config-file \
etc/config-generator.tempest.conf \
--output-file etc/tempest.conf
@@ -237,21 +236,21 @@
After setting up your configuration file, you can execute the set of Tempest
tests by using ``testr`` ::
- $> testr run --parallel
+ $ testr run --parallel
To run one single test serially ::
- $> testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+ $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
Alternatively, you can use the run_tempest.sh script which will create a venv
and run the tests or use tox to do the same. Tox also contains several existing
job configurations. For example::
- $> tox -efull
+ $ tox -efull
which will run the same set of tests as the OpenStack gate. (it's exactly how
the gate invokes Tempest) Or::
- $> tox -esmoke
+ $ tox -esmoke
to run the tests tagged as smoke.
diff --git a/releasenotes/notes/new-test-utils-module-adf34468c4d52719.yaml b/releasenotes/notes/new-test-utils-module-adf34468c4d52719.yaml
new file mode 100644
index 0000000..55df2b3
--- /dev/null
+++ b/releasenotes/notes/new-test-utils-module-adf34468c4d52719.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - A new `test_utils` module has been added to tempest.lib.common.utils. It
+ should hold any common utility functions that help writing Tempest tests.
+ - A new utility function called `call_and_ignore_notfound_exc` has been
+ added to the `test_utils` module. That function call another function
+ passed as parameter and ignore the NotFound exception if it raised.
+deprecations:
+ - tempest.lib.common.utils.misc.find_test_caller has been moved into the
+ tempest.lib.common.utils.test_utils module. Calling the find_test_caller
+ function with its old location is deprecated.
diff --git a/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
new file mode 100644
index 0000000..294f6d9
--- /dev/null
+++ b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+ - The integrated dashboard scenario test has been
+ removed and is now in a separate tempest plugin
+ tempest-horizon. The removed test coverage can be
+ used by installing tempest-horizon on the server
+ where you run tempest.
diff --git a/requirements.txt b/requirements.txt
index d567082..45fe345 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,17 +9,17 @@
netaddr!=0.7.16,>=0.7.12 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
pyOpenSSL>=0.14 # Apache-2.0
-oslo.concurrency>=3.5.0 # Apache-2.0
-oslo.config>=3.9.0 # Apache-2.0
+oslo.concurrency>=3.8.0 # Apache-2.0
+oslo.config>=3.10.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.5.0 # Apache-2.0
+oslo.utils>=3.11.0 # Apache-2.0
six>=1.9.0 # MIT
-fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
+fixtures>=3.0.0 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD
PyYAML>=3.1.0 # MIT
stevedore>=1.10.0 # Apache-2.0
PrettyTable<0.8,>=0.7 # BSD
-os-testr>=0.4.1 # Apache-2.0
+os-testr>=0.7.0 # Apache-2.0
urllib3>=1.15.1 # MIT
diff --git a/setup.cfg b/setup.cfg
index 0ddb898..24e0214 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = http://docs.openstack.org/developer/tempest/
classifier =
Intended Audience :: Information Technology
Intended Audience :: System Administrators
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 671b139..4f48ad0 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -16,7 +16,7 @@
from tempest.api.compute import base
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
LOG = log.getLogger(__name__)
@@ -41,9 +41,8 @@
def tearDown(self):
try:
- self.client.delete_agent(self.agent_id)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ self.client.delete_agent, self.agent_id)
except Exception:
LOG.exception('Exception raised deleting agent %s', self.agent_id)
super(AgentsAdminTestJSON, self).tearDown()
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index a2b1e2f..25efd2e 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -16,7 +16,7 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -41,21 +41,14 @@
filter(lambda y: y['service'] == 'compute', hosts_all))
cls.host = hosts[0]
- def _try_delete_aggregate(self, aggregate_id):
- # delete aggregate, if it exists
- try:
- self.client.delete_aggregate(aggregate_id)
- # if aggregate not found, it depict it was deleted in the test
- except lib_exc.NotFound:
- pass
-
@test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
def test_aggregate_create_delete(self):
# Create and delete an aggregate.
aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
aggregate = (self.client.create_aggregate(name=aggregate_name)
['aggregate'])
- self.addCleanup(self._try_delete_aggregate, aggregate['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_aggregate, aggregate['id'])
self.assertEqual(aggregate_name, aggregate['name'])
self.assertIsNone(aggregate['availability_zone'])
@@ -69,7 +62,8 @@
az_name = data_utils.rand_name(self.az_name_prefix)
aggregate = self.client.create_aggregate(
name=aggregate_name, availability_zone=az_name)['aggregate']
- self.addCleanup(self._try_delete_aggregate, aggregate['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_aggregate, aggregate['id'])
self.assertEqual(aggregate_name, aggregate['name'])
self.assertEqual(az_name, aggregate['availability_zone'])
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 8986db8..a4ed8dc 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -59,7 +59,9 @@
return True
except e.InvalidHTTPResponseBody:
return False
- test.call_until_true(is_valid, duration, 1)
+ self.assertEqual(test.call_until_true(is_valid, duration, 1), True,
+ "%s not return valid response in %s secs" % (
+ func.__name__, duration))
return self.resp
@test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 77ed7cd..37aa5ac 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -24,6 +24,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import api_version_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -134,11 +135,8 @@
server['id'] for server in cls.servers))
for server in cls.servers:
try:
- cls.servers_client.delete_server(server['id'])
- except lib_exc.NotFound:
- # Something else already cleaned up the server, nothing to be
- # worried about
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.servers_client.delete_server, server['id'])
except Exception:
LOG.exception('Deleting server %s failed' % server['id'])
@@ -177,10 +175,8 @@
LOG.debug('Clearing images: %s', ','.join(cls.images))
for image_id in cls.images:
try:
- cls.compute_images_client.delete_image(image_id)
- except lib_exc.NotFound:
- # The image may have already been deleted which is OK.
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.compute_images_client.delete_image, image_id)
except Exception:
LOG.exception('Exception raised deleting image %s' % image_id)
@@ -190,10 +186,8 @@
str(sg['id']) for sg in cls.security_groups))
for sg in cls.security_groups:
try:
- cls.security_groups_client.delete_security_group(sg['id'])
- except lib_exc.NotFound:
- # The security group may have already been deleted which is OK.
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.security_groups_client.delete_security_group, sg['id'])
except Exception as exc:
LOG.info('Exception raised deleting security group %s',
sg['id'])
@@ -204,10 +198,10 @@
LOG.debug('Clearing server groups: %s', ','.join(cls.server_groups))
for server_group_id in cls.server_groups:
try:
- cls.server_groups_client.delete_server_group(server_group_id)
- except lib_exc.NotFound:
- # The server-group may have already been deleted which is OK.
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.server_groups_client.delete_server_group,
+ server_group_id
+ )
except Exception:
LOG.exception('Exception raised deleting server-group %s',
server_group_id)
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 3d7f4f8..3508ba9 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -17,6 +17,7 @@
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -53,14 +54,6 @@
cls.client.delete_floating_ip(cls.floating_ip_id)
super(FloatingIPsTestJSON, cls).resource_cleanup()
- def _try_delete_floating_ip(self, floating_ip_id):
- # delete floating ip, if it exists
- try:
- self.client.delete_floating_ip(floating_ip_id)
- # if not found, it depicts it was deleted in the test
- except lib_exc.NotFound:
- pass
-
@test.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
@test.services('network')
def test_allocate_floating_ip(self):
@@ -85,7 +78,8 @@
# Creating the floating IP that is to be deleted in this method
floating_ip_body = self.client.create_floating_ip(
pool=CONF.network.floating_network_name)['floating_ip']
- self.addCleanup(self._try_delete_floating_ip, floating_ip_body['id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_floating_ip, floating_ip_body['id'])
# Deleting the floating IP from the project
self.client.delete_floating_ip(floating_ip_body['id'])
# Check it was really deleted.
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c05045e..07423ff 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -204,10 +204,6 @@
@test.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
@testtools.skipUnless(CONF.service_available.neutron,
'Neutron service must be available.')
- # The below skipUnless should be removed once Kilo-eol happens.
- @testtools.skipUnless(CONF.compute_feature_enabled.
- allow_duplicate_networks,
- 'Duplicate networks must be allowed')
def test_verify_duplicate_network_nics(self):
# Verify that server creation does not fail when more than one nic
# is created on the same network.
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index 164caaf..decccf3 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -19,7 +19,7 @@
from tempest import config
from tempest import exceptions
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
import tempest.test
@@ -238,11 +238,7 @@
@staticmethod
def cleanup_resources(resource_id_list, method):
for resource_id in resource_id_list:
- try:
- method(resource_id)
- except lib_exc.NotFound:
- # ignore errors while auto removing created resource
- pass
+ test_utils.call_and_ignore_notfound_exc(method, resource_id)
@classmethod
def create_node_group_template(cls, name, plugin_name, hadoop_version,
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 3bcae17..2d4ff17 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -17,7 +17,7 @@
from tempest.common.utils import data_utils
from tempest import config
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
import tempest.test
CONF = config.CONF
@@ -225,9 +225,7 @@
@staticmethod
def _try_wrapper(func, item, **kwargs):
try:
- func(item['id'], **kwargs)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(func, item['id'], **kwargs)
except Exception:
LOG.exception("Unexpected exception occurred in %s deletion. "
"But ignored here." % item['id'])
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 0683936..bd04c0d 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -16,7 +16,7 @@
from tempest.common.utils import data_utils
from tempest import config
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
import tempest.test
CONF = config.CONF
@@ -47,10 +47,8 @@
@classmethod
def resource_cleanup(cls):
for image_id in cls.created_images:
- try:
- cls.client.delete_image(image_id)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.client.delete_image, image_id)
for image_id in cls.created_images:
cls.client.wait_for_resource_deletion(image_id)
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index de8299e..da0f4c1 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -15,6 +15,7 @@
from tempest.api.image import base
from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -36,7 +37,8 @@
display_name=namespace_name,
resource_type_associations=name,
protected=True)
- self.addCleanup(self._cleanup_namespace, namespace_name)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self._cleanup_namespace, namespace_name)
# get namespace details
body = self.client.show_namespace(namespace_name)
self.assertEqual(namespace_name, body['namespace'])
@@ -56,15 +58,11 @@
self.client.delete_namespace(namespace_name)
def _cleanup_namespace(self, namespace_name):
- # this is used to cleanup the resources
- try:
- body = self.client.show_namespace(namespace_name)
- self.assertEqual(namespace_name, body['namespace'])
- body = self.client.update_namespace(namespace=namespace_name,
- description='Tempest',
- visibility='private',
- display_name=namespace_name,
- protected=False)
- self.client.delete_namespace(namespace_name)
- except lib_exc.NotFound:
- pass
+ body = self.client.show_namespace(namespace_name)
+ self.assertEqual(namespace_name, body['namespace'])
+ body = self.client.update_namespace(namespace=namespace_name,
+ description='Tempest',
+ visibility='private',
+ display_name=namespace_name,
+ protected=False)
+ self.client.delete_namespace(namespace_name)
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index a32bfbc..2d53265 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -12,6 +12,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -97,7 +98,7 @@
body = self.admin_networks_client.create_network(
**{'router:external': True})
external_network = body['network']
- self.addCleanup(self._try_delete_resource,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.admin_networks_client.delete_network,
external_network['id'])
subnet = self.create_subnet(
@@ -106,7 +107,7 @@
body = self.admin_floating_ips_client.create_floatingip(
floating_network_id=external_network['id'])
created_floating_ip = body['floatingip']
- self.addCleanup(self._try_delete_resource,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.admin_floating_ips_client.delete_floatingip,
created_floating_ip['id'])
floatingip_list = self.admin_floating_ips_client.list_floatingips(
diff --git a/tempest/api/network/admin/test_external_networks_negative.py b/tempest/api/network/admin/test_external_networks_negative.py
index 57b144a..94d65c3 100644
--- a/tempest/api/network/admin/test_external_networks_negative.py
+++ b/tempest/api/network/admin/test_external_networks_negative.py
@@ -15,6 +15,7 @@
from tempest.api.network import base
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -34,7 +35,7 @@
body = self.admin_floating_ips_client.create_floatingip(
floating_network_id=CONF.network.public_network_id)
created_floating_ip = body['floatingip']
- self.addCleanup(self._try_delete_resource,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.admin_floating_ips_client.delete_floatingip,
created_floating_ip['id'])
floating_ip_address = created_floating_ip['floating_ip_address']
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 7a4547a..b2cb003 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -92,7 +92,7 @@
external_gateway_info = {
'network_id': CONF.network.public_network_id,
'enable_snat': True}
- cls.admin_routers_client.update_router_with_snat_gw_info(
+ cls.admin_routers_client.update_router(
cls.router['id'],
external_gateway_info=external_gateway_info)
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index ea3d59a..2ff31e0 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -17,7 +17,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
@@ -60,7 +60,8 @@
# Change quotas for project
quota_set = self.admin_quotas_client.update_quotas(
project_id, **new_quotas)['quota']
- self.addCleanup(self._cleanup_quotas, project_id)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.admin_quotas_client.reset_quotas, project_id)
for key, value in six.iteritems(new_quotas):
self.assertEqual(value, quota_set[key])
@@ -88,12 +89,3 @@
def test_quotas(self):
new_quotas = {'network': 0, 'security_group': 0}
self._check_quotas(new_quotas)
-
- def _cleanup_quotas(self, project_id):
- # try to clean up the resources.If it fails, then
- # assume that everything was already deleted, so
- # it is OK to continue.
- try:
- self.admin_quotas_client.reset_quotas(project_id)
- except lib_exc.NotFound:
- pass
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 9823345..89fa576 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -18,6 +18,7 @@
from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -97,7 +98,7 @@
if CONF.service_available.neutron:
# Clean up floating IPs
for floating_ip in cls.floating_ips:
- cls._try_delete_resource(
+ test_utils.call_and_ignore_notfound_exc(
cls.floating_ips_client.delete_floatingip,
floating_ip['id'])
@@ -106,53 +107,33 @@
if len(cls.metering_label_rules) > 0:
label_rules_client = cls.admin_metering_label_rules_client
for metering_label_rule in cls.metering_label_rules:
- cls._try_delete_resource(
+ test_utils.call_and_ignore_notfound_exc(
label_rules_client.delete_metering_label_rule,
metering_label_rule['id'])
# Clean up metering labels
for metering_label in cls.metering_labels:
- cls._try_delete_resource(
+ test_utils.call_and_ignore_notfound_exc(
cls.admin_metering_labels_client.delete_metering_label,
metering_label['id'])
# Clean up ports
for port in cls.ports:
- cls._try_delete_resource(cls.ports_client.delete_port,
- port['id'])
+ test_utils.call_and_ignore_notfound_exc(
+ cls.ports_client.delete_port, port['id'])
# Clean up routers
for router in cls.routers:
- cls._try_delete_resource(cls.delete_router,
- router)
+ test_utils.call_and_ignore_notfound_exc(
+ cls.delete_router, router)
# Clean up subnets
for subnet in cls.subnets:
- cls._try_delete_resource(cls.subnets_client.delete_subnet,
- subnet['id'])
+ test_utils.call_and_ignore_notfound_exc(
+ cls.subnets_client.delete_subnet, subnet['id'])
# Clean up networks
for network in cls.networks:
- cls._try_delete_resource(cls.networks_client.delete_network,
- network['id'])
+ test_utils.call_and_ignore_notfound_exc(
+ cls.networks_client.delete_network, network['id'])
super(BaseNetworkTest, cls).resource_cleanup()
@classmethod
- def _try_delete_resource(self, delete_callable, *args, **kwargs):
- """Cleanup resources in case of test-failure
-
- Some resources are explicitly deleted by the test.
- If the test failed to delete a resource, this method will execute
- the appropriate delete methods. Otherwise, the method ignores NotFound
- exceptions thrown for resources that were correctly deleted by the
- test.
-
- :param delete_callable: delete method
- :param args: arguments for delete method
- :param kwargs: keyword arguments for delete method
- """
- try:
- delete_callable(*args, **kwargs)
- # if resource is not found, this means it was deleted in the test
- except lib_exc.NotFound:
- pass
-
- @classmethod
def create_network(cls, network_name=None):
"""Wrapper utility that returns a test network."""
network_name = network_name or data_utils.rand_name('test-network-')
@@ -259,12 +240,9 @@
body = cls.ports_client.list_ports(device_id=router['id'])
interfaces = body['ports']
for i in interfaces:
- try:
- cls.routers_client.remove_router_interface(
- router['id'],
- subnet_id=i['fixed_ips'][0]['subnet_id'])
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.routers_client.remove_router_interface, router['id'],
+ subnet_id=i['fixed_ips'][0]['subnet_id'])
cls.routers_client.delete_router(router['id'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index a5fb25c..1d96439 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -20,6 +20,7 @@
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -273,14 +274,6 @@
for subnet in subnets:
self.assertEqual(sorted(subnet.keys()), sorted(fields))
- def _try_delete_network(self, net_id):
- # delete network, if it exists
- try:
- self.networks_client.delete_network(net_id)
- # if network is not found, this means it was deleted in the test
- except lib_exc.NotFound:
- pass
-
@test.idempotent_id('f04f61a9-b7f3-4194-90b2-9bcf660d1bfe')
def test_delete_network_with_subnet(self):
# Creates a network
@@ -288,7 +281,8 @@
body = self.networks_client.create_network(name=name)
network = body['network']
net_id = network['id']
- self.addCleanup(self._try_delete_network, net_id)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.networks_client.delete_network, net_id)
# Find a cidr that is not in use yet and create a subnet with it
subnet = self.create_subnet(network)
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 46b068b..398a0b8 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -213,7 +213,7 @@
@test.requires_ext(extension='ext-gw-mode', service='network')
def test_update_router_set_gateway_with_snat_explicit(self):
router = self._create_router(data_utils.rand_name('router-'))
- self.admin_routers_client.update_router_with_snat_gw_info(
+ self.admin_routers_client.update_router(
router['id'],
external_gateway_info={
'network_id': CONF.network.public_network_id,
@@ -228,7 +228,7 @@
@test.requires_ext(extension='ext-gw-mode', service='network')
def test_update_router_set_gateway_without_snat(self):
router = self._create_router(data_utils.rand_name('router-'))
- self.admin_routers_client.update_router_with_snat_gw_info(
+ self.admin_routers_client.update_router(
router['id'],
external_gateway_info={
'network_id': CONF.network.public_network_id,
@@ -259,7 +259,7 @@
router = self._create_router(
data_utils.rand_name('router-'),
external_network_id=CONF.network.public_network_id)
- self.admin_routers_client.update_router_with_snat_gw_info(
+ self.admin_routers_client.update_router(
router['id'],
external_gateway_info={
'network_id': CONF.network.public_network_id,
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index b9765c8..a3b0a82 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -153,6 +153,7 @@
# Create rule for icmp protocol with invalid ports
states = [(1, 256, 'Invalid value for ICMP code'),
+ (-1, 25, 'Invalid value'),
(None, 6, 'ICMP type (port-range-min) is missing'),
(300, 1, 'Invalid value for ICMP type')]
for pmin, pmax, msg in states:
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index c6cc8e2..d574d72 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -15,6 +15,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -53,7 +54,9 @@
body = self.subnetpools_client.create_subnetpool(name=subnetpool_name,
prefixes=prefix)
subnetpool_id = body["subnetpool"]["id"]
- self.addCleanup(self._cleanup_subnetpools, subnetpool_id)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.subnetpools_client.delete_subnetpool,
+ subnetpool_id)
self.assertEqual(subnetpool_name, body["subnetpool"]["name"])
# get detail about subnet pool
body = self.subnetpools_client.show_subnetpool(subnetpool_id)
@@ -68,10 +71,3 @@
self.assertRaises(lib_exc.NotFound,
self.subnetpools_client.show_subnetpool,
subnetpool_id)
-
- def _cleanup_subnetpools(self, subnetpool_id):
- # this is used to cleanup the resources
- try:
- self.subnetpools_client.delete_subnetpool(subnetpool_id)
- except lib_exc.NotFound:
- pass
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 044e8c1..97d9eed 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -15,6 +15,7 @@
from tempest.common import custom_matchers
from tempest import config
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -80,10 +81,8 @@
objlist = container_client.list_all_container_objects(cont)
# delete every object in the container
for obj in objlist:
- try:
- object_client.delete_object(cont, obj['name'])
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ object_client.delete_object, cont, obj['name'])
container_client.delete_container(cont)
except lib_exc.NotFound:
pass
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 6bab9b3..5983c1f 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -226,6 +226,17 @@
self.assertEqual(len(container_list),
min(limit, self.containers_count - 2))
+ @test.idempotent_id('365e6fc7-1cfe-463b-a37c-8bd08d47b6aa')
+ def test_list_containers_with_prefix(self):
+ # list containers that have a name that starts with a prefix
+ prefix = '{0}-a'.format(CONF.resources_prefix)
+ params = {'prefix': prefix}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
+ self.assertHeaders(resp, 'Account', 'GET')
+ for container in container_list:
+ self.assertEqual(True, container.startswith(prefix))
+
@test.attr(type='smoke')
@test.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
def test_list_account_metadata(self):
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 752f0b4..867159b 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -19,7 +19,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
from tempest import test
# Each segment, except for the final one, must be at least 1 megabyte
@@ -36,12 +36,9 @@
def tearDown(self):
for obj in self.objects:
- try:
- self.object_client.delete_object(
- self.container_name,
- obj)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ self.object_client.delete_object,
+ self.container_name, obj)
self.container_client.delete_container(self.container_name)
super(ObjectSloTest, self).tearDown()
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index d813263..3701b55 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -16,7 +16,7 @@
from tempest.common.utils import data_utils
from tempest import config
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
import tempest.test
CONF = config.CONF
@@ -83,17 +83,13 @@
@classmethod
def _clear_stacks(cls):
for stack_identifier in cls.stacks:
- try:
- cls.client.delete_stack(stack_identifier)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.client.delete_stack, stack_identifier)
for stack_identifier in cls.stacks:
- try:
- cls.client.wait_for_stack_status(
- stack_identifier, 'DELETE_COMPLETE')
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.client.wait_for_stack_status, stack_identifier,
+ 'DELETE_COMPLETE')
@classmethod
def _create_keypair(cls, name_start='keypair-heat-'):
@@ -124,10 +120,8 @@
@classmethod
def _clear_images(cls):
for image_id in cls.images:
- try:
- cls.images_v2_client.delete_image(image_id)
- except lib_exc.NotFound:
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.images_v2_client.delete_image, image_id)
@classmethod
def read_template(cls, name, ext='yaml'):
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b2e52bb..cf05f5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -30,6 +30,11 @@
super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
+ def _delete_volume(self, volume_id):
+ # Delete the specified volume using admin credentials
+ self.admin_volume_client.delete_volume(volume_id)
+ self.admin_volume_client.wait_for_resource_deletion(volume_id)
+
@test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
quotas = (self.quotas_client.show_quota_set(self.demo_tenant_id)
@@ -83,8 +88,7 @@
self.demo_tenant_id)['quota_set']
volume = self.create_volume()
- self.addCleanup(self.admin_volume_client.delete_volume,
- volume['id'])
+ self.addCleanup(self._delete_volume, volume['id'])
new_quota_usage = self.quotas_client.show_quota_usage(
self.demo_tenant_id)['quota_set']
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index b09cd2c..1289297 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -13,11 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
+import base64
+import six
+
+from oslo_serialization import jsonutils as json
+
from tempest.api.volume import base
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest.lib import decorators
from tempest import test
CONF = config.CONF
@@ -41,6 +45,20 @@
self.backups_adm_client.delete_backup(backup_id)
self.backups_adm_client.wait_for_backup_deletion(backup_id)
+ def _decode_url(self, backup_url):
+ return json.loads(base64.decodestring(backup_url))
+
+ def _encode_backup(self, backup):
+ retval = json.dumps(backup)
+ if six.PY3:
+ retval = retval.encode('utf-8')
+ return base64.encodestring(retval)
+
+ def _modify_backup_url(self, backup_url, changes):
+ backup = self._decode_url(backup_url)
+ backup.update(changes)
+ return self._encode_backup(backup)
+
@test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
def test_volume_backup_create_get_detailed_list_restore_delete(self):
# Create backup
@@ -78,9 +96,13 @@
waiters.wait_for_volume_status(self.admin_volume_client,
restore['volume_id'], 'available')
- @decorators.skip_because(bug='1455043')
@test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
def test_volume_backup_export_import(self):
+ """Test backup export import functionality.
+
+ Cinder allows exporting DB backup information through its API so it can
+ be imported back in case of a DB loss.
+ """
# Create backup
backup_name = data_utils.rand_name('Backup')
backup = (self.backups_adm_client.create_backup(
@@ -99,25 +121,40 @@
'cinder.backup.drivers'))
self.assertIsNotNone(export_backup['backup_url'])
+ # NOTE(geguileo): Backups are imported with the same backup id
+ # (important for incremental backups among other things), so we cannot
+ # import the exported backup information as it is, because that Backup
+ # ID already exists. So we'll fake the data by changing the backup id
+ # in the exported backup DB info we have retrieved before importing it
+ # back.
+ new_id = data_utils.rand_uuid()
+ new_url = self._modify_backup_url(
+ export_backup['backup_url'], {'id': new_id})
+
# Import Backup
import_backup = self.backups_adm_client.import_backup(
backup_service=export_backup['backup_service'],
- backup_url=export_backup['backup_url'])['backup']
- self.addCleanup(self._delete_backup, import_backup['id'])
+ backup_url=new_url)['backup']
+
+ # NOTE(geguileo): We delete both backups, but only one of those
+ # deletions will delete data from the backup back-end because they
+ # were both pointing to the same backend data.
+ self.addCleanup(self._delete_backup, new_id)
self.assertIn("id", import_backup)
+ self.assertEqual(new_id, import_backup['id'])
self.backups_adm_client.wait_for_backup_status(import_backup['id'],
'available')
# Verify Import Backup
backups = self.backups_adm_client.list_backups(detail=True)['backups']
- self.assertIn(import_backup['id'], [b['id'] for b in backups])
+ self.assertIn(new_id, [b['id'] for b in backups])
# Restore backup
- restore = (self.backups_adm_client.restore_backup(import_backup['id'])
- ['restore'])
+ restore = self.backups_adm_client.restore_backup(
+ backup['id'])['restore']
self.addCleanup(self.admin_volume_client.delete_volume,
restore['volume_id'])
- self.assertEqual(import_backup['id'], restore['backup_id'])
+ self.assertEqual(backup['id'], restore['backup_id'])
waiters.wait_for_volume_status(self.admin_volume_client,
restore['volume_id'], 'available')
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 14819e3..6116d72 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -18,7 +18,7 @@
from tempest.common import waiters
from tempest import config
from tempest import exceptions
-from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import test_utils
import tempest.test
CONF = config.CONF
@@ -223,15 +223,9 @@
@classmethod
def clear_qos_specs(cls):
for qos_id in cls.qos_specs:
- try:
- cls.volume_qos_client.delete_qos(qos_id)
- except lib_exc.NotFound:
- # The qos_specs may have already been deleted which is OK.
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.volume_qos_client.delete_qos, qos_id)
for qos_id in cls.qos_specs:
- try:
- cls.volume_qos_client.wait_for_resource_deletion(qos_id)
- except lib_exc.NotFound:
- # The qos_specs may have already been deleted which is OK.
- pass
+ test_utils.call_and_ignore_notfound_exc(
+ cls.volume_qos_client.wait_for_resource_deletion, qos_id)
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 5975231..38913ce 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -12,14 +12,14 @@
# 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.api.volume import base
from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
-from tempest.lib import exceptions
+from tempest.lib.common.utils import test_utils
from tempest import test
-import testtools
CONF = config.CONF
@@ -124,19 +124,13 @@
self.volume['id'], image_name=image_name,
disk_format=CONF.volume.disk_format)['os-volume_upload_image']
image_id = body["image_id"]
- self.addCleanup(self._cleanup_image, image_id)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.image_client.delete_image,
+ image_id)
waiters.wait_for_image_status(self.image_client, image_id, 'active')
waiters.wait_for_volume_status(self.client,
self.volume['id'], 'available')
- def _cleanup_image(self, image_id):
- # Ignores the image deletion
- # in the case that image wasn't created in the first place
- try:
- self.image_client.delete_image(image_id)
- except exceptions.NotFound:
- pass
-
@test.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
# Mark volume as reserved.
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 17ee618..f2a69c7 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -823,7 +823,7 @@
if router['gateway']:
if CONF.network.public_network_id:
ext_net = CONF.network.public_network_id
- client.routers._update_router(
+ client.routers.update_router(
router_id, set_enable_snat=True,
external_gateway_info={"network_id": ext_net})
else:
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index 37c9727..b23bc6f 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -170,6 +170,29 @@
user['id'],
role['id'])
+ def assign_user_role_on_domain(self, user, role_name, domain=None):
+ """Assign the specified role on a domain
+
+ :param user: a user dict
+ :param role_name: name of the role to be assigned
+ :param domain: (optional) The domain to assign the role on. If not
+ specified the default domain of cred_client
+ """
+ # NOTE(andreaf) This method is very specific to the v3 case, and
+ # because of that it's not defined in the parent class.
+ if domain is None:
+ domain = self.creds_domain
+ role = self._check_role_exists(role_name)
+ if not role:
+ msg = 'No "%s" role found' % role_name
+ raise lib_exc.NotFound(msg)
+ try:
+ self.roles_client.assign_user_role_on_domain(
+ domain['id'], user['id'], role['id'])
+ except lib_exc.Conflict:
+ LOG.debug("Role %s already assigned on domain %s for user %s",
+ role['id'], domain['id'], user['id'])
+
def get_creds_client(identity_client,
projects_client,
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index a63af38..3a3e3c2 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -122,7 +122,9 @@
project = self.creds_client.create_project(
name=project_name, description=project_desc)
- username = data_utils.rand_name(root) + suffix
+ # NOTE(andreaf) User and project can be distinguished from the context,
+ # having the same ID in both makes it easier to match them and debug.
+ username = project_name
user_password = data_utils.rand_password()
email = data_utils.rand_name(root) + suffix + "@example.com"
user = self.creds_client.create_user(
@@ -134,6 +136,9 @@
self.creds_client.assign_user_role(user, project,
self.admin_role)
role_assigned = True
+ if self.identity_version == 'v3':
+ self.creds_client.assign_user_role_on_domain(
+ user, CONF.identity.admin_role)
# Add roles specified in config file
for conf_role in CONF.auth.tempest_roles:
self.creds_client.assign_user_role(user, project, conf_role)
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
deleted file mode 100644
index 00062de..0000000
--- a/tempest/common/glance_http.py
+++ /dev/null
@@ -1,361 +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.
-
-# Originally copied from python-glanceclient
-
-import copy
-import hashlib
-import posixpath
-import re
-import socket
-import struct
-
-import OpenSSL
-from oslo_log import log as logging
-import six
-from six import moves
-from six.moves import http_client as httplib
-from six.moves.urllib import parse as urlparse
-
-from tempest import exceptions as exc
-
-LOG = logging.getLogger(__name__)
-USER_AGENT = 'tempest'
-CHUNKSIZE = 1024 * 64 # 64kB
-TOKEN_CHARS_RE = re.compile('^[-A-Za-z0-9+/=]*$')
-
-
-class HTTPClient(object):
-
- def __init__(self, auth_provider, filters, **kwargs):
- self.auth_provider = auth_provider
- self.filters = filters
- self.endpoint = auth_provider.base_url(filters)
- endpoint_parts = urlparse.urlparse(self.endpoint)
- self.endpoint_scheme = endpoint_parts.scheme
- self.endpoint_hostname = endpoint_parts.hostname
- self.endpoint_port = endpoint_parts.port
-
- self.connection_class = self._get_connection_class(
- self.endpoint_scheme)
- self.connection_kwargs = self._get_connection_kwargs(
- self.endpoint_scheme, **kwargs)
-
- @staticmethod
- def _get_connection_class(scheme):
- if scheme == 'https':
- return VerifiedHTTPSConnection
- else:
- return httplib.HTTPConnection
-
- @staticmethod
- def _get_connection_kwargs(scheme, **kwargs):
- _kwargs = {'timeout': float(kwargs.get('timeout', 600))}
-
- if scheme == 'https':
- _kwargs['ca_certs'] = kwargs.get('ca_certs', None)
- _kwargs['cert_file'] = kwargs.get('cert_file', None)
- _kwargs['key_file'] = kwargs.get('key_file', None)
- _kwargs['insecure'] = kwargs.get('insecure', False)
- _kwargs['ssl_compression'] = kwargs.get('ssl_compression', True)
-
- return _kwargs
-
- def _get_connection(self):
- _class = self.connection_class
- try:
- return _class(self.endpoint_hostname, self.endpoint_port,
- **self.connection_kwargs)
- except httplib.InvalidURL:
- raise exc.EndpointNotFound
-
- def _http_request(self, url, method, **kwargs):
- """Send an http request with the specified characteristics.
-
- Wrapper around httplib.HTTP(S)Connection.request to handle tasks such
- as setting headers and error handling.
- """
- # 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)
-
- self._log_request(method, url, kwargs['headers'])
-
- conn = self._get_connection()
-
- try:
- url_parts = urlparse.urlparse(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():
- conn.putheader(header, value)
- conn.endheaders()
- chunk = kwargs['body'].read(CHUNKSIZE)
- # Chunk it, baby...
- while chunk:
- conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
- chunk = kwargs['body'].read(CHUNKSIZE)
- conn.send('0\r\n\r\n')
- else:
- conn.request(method, conn_url, **kwargs)
- resp = conn.getresponse()
- except socket.gaierror as e:
- message = ("Error finding address for %(url)s: %(e)s" %
- {'url': url, 'e': e})
- raise exc.EndpointNotFound(message)
- except (socket.error, socket.timeout) as e:
- message = ("Error communicating with %(endpoint)s %(e)s" %
- {'endpoint': self.endpoint, 'e': e})
- raise exc.TimeoutException(message)
-
- body_iter = ResponseBodyIterator(resp)
- # Read body into string if it isn't obviously image data
- if resp.getheader('content-type', None) != 'application/octet-stream':
- body_str = ''.join([body_chunk for body_chunk in body_iter])
- body_iter = six.StringIO(body_str)
- self._log_response(resp, None)
- else:
- self._log_response(resp, body_iter)
-
- return resp, body_iter
-
- def _log_request(self, method, url, headers):
- LOG.info('Request: ' + method + ' ' + url)
- if headers:
- headers_out = headers
- if 'X-Auth-Token' in headers and headers['X-Auth-Token']:
- token = headers['X-Auth-Token']
- if len(token) > 64 and TOKEN_CHARS_RE.match(token):
- headers_out = headers.copy()
- headers_out['X-Auth-Token'] = "<Token omitted>"
- LOG.info('Request Headers: ' + str(headers_out))
-
- def _log_response(self, resp, body):
- status = str(resp.status)
- LOG.info("Response Status: " + status)
- if resp.getheaders():
- LOG.info('Response Headers: ' + str(resp.getheaders()))
- if body:
- str_body = str(body)
- length = len(body)
- LOG.info('Response Body: ' + str_body[:2048])
- if length >= 2048:
- self.LOG.debug("Large body (%d) md5 summary: %s", length,
- hashlib.md5(str_body).hexdigest())
-
- def raw_request(self, method, url, **kwargs):
- kwargs.setdefault('headers', {})
- kwargs['headers'].setdefault('Content-Type',
- 'application/octet-stream')
- if 'body' in kwargs:
- if (hasattr(kwargs['body'], 'read')
- and method.lower() in ('post', 'put')):
- # We use 'Transfer-Encoding: chunked' because
- # body size may not always be known in advance.
- kwargs['headers']['Transfer-Encoding'] = 'chunked'
-
- # 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):
- """An OpenSSL.SSL.Connection delegator.
-
- Supplies an additional 'makefile' method which httplib requires
- and is not present in OpenSSL.SSL.Connection.
-
- Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
- a delegator must be used.
- """
- def __init__(self, *args, **kwargs):
- self.connection = OpenSSL.SSL.Connection(*args, **kwargs)
-
- def __getattr__(self, name):
- return getattr(self.connection, name)
-
- def makefile(self, *args, **kwargs):
- # Ensure the socket is closed when this file is closed
- kwargs['close'] = True
- return socket._fileobject(self.connection, *args, **kwargs)
-
-
-class VerifiedHTTPSConnection(httplib.HTTPSConnection):
- """Extended HTTPSConnection which uses OpenSSL library for enhanced SSL
-
- Note: Much of this functionality can eventually be replaced
- with native Python 3.3 code.
- """
- def __init__(self, host, port=None, key_file=None, cert_file=None,
- ca_certs=None, timeout=None, insecure=False,
- ssl_compression=True):
- httplib.HTTPSConnection.__init__(self, host, port,
- key_file=key_file,
- cert_file=cert_file)
- self.key_file = key_file
- self.cert_file = cert_file
- self.timeout = timeout
- self.insecure = insecure
- self.ssl_compression = ssl_compression
- self.ca_certs = ca_certs
- self.setcontext()
-
- @staticmethod
- def host_matches_cert(host, x509):
- """Verify that the x509 certificate we have received from 'host'
-
- Identifies the server we are connecting to, ie that the certificate's
- Common Name or a Subject Alternative Name matches 'host'.
- """
- # First see if we can match the CN
- if x509.get_subject().commonName == host:
- return True
-
- # Also try Subject Alternative Names for a match
- san_list = None
- for i in moves.xrange(x509.get_extension_count()):
- ext = x509.get_extension(i)
- if ext.get_short_name() == 'subjectAltName':
- san_list = str(ext)
- for san in ''.join(san_list.split()).split(','):
- if san == "DNS:%s" % host:
- return True
-
- # Server certificate does not match host
- msg = ('Host "%s" does not match x509 certificate contents: '
- 'CommonName "%s"' % (host, x509.get_subject().commonName))
- if san_list is not None:
- msg = msg + ', subjectAltName "%s"' % san_list
- raise exc.SSLCertificateError(msg)
-
- def verify_callback(self, connection, x509, errnum,
- depth, preverify_ok):
- if x509.has_expired():
- msg = "SSL Certificate expired on '%s'" % x509.get_notAfter()
- raise exc.SSLCertificateError(msg)
-
- if depth == 0 and preverify_ok is True:
- # We verify that the host matches against the last
- # certificate in the chain
- return self.host_matches_cert(self.host, x509)
- else:
- # Pass through OpenSSL's default result
- return preverify_ok
-
- def setcontext(self):
- """Set up the OpenSSL context."""
- self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
-
- if self.ssl_compression is False:
- self.context.set_options(0x20000) # SSL_OP_NO_COMPRESSION
-
- if self.insecure is not True:
- self.context.set_verify(OpenSSL.SSL.VERIFY_PEER,
- self.verify_callback)
- else:
- self.context.set_verify(OpenSSL.SSL.VERIFY_NONE,
- self.verify_callback)
-
- if self.cert_file:
- try:
- self.context.use_certificate_file(self.cert_file)
- except Exception as e:
- msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
- raise exc.SSLConfigurationError(msg)
- if self.key_file is None:
- # We support having key and cert in same file
- try:
- self.context.use_privatekey_file(self.cert_file)
- except Exception as e:
- msg = ('No key file specified and unable to load key '
- 'from "%s" %s' % (self.cert_file, e))
- raise exc.SSLConfigurationError(msg)
-
- if self.key_file:
- try:
- self.context.use_privatekey_file(self.key_file)
- except Exception as e:
- msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
- raise exc.SSLConfigurationError(msg)
-
- if self.ca_certs:
- try:
- self.context.load_verify_locations(self.ca_certs)
- except Exception as e:
- msg = 'Unable to load CA from "%s" %s' % (self.ca_certs, e)
- raise exc.SSLConfigurationError(msg)
- else:
- self.context.set_default_verify_paths()
-
- def connect(self):
- """Connect to SSL port and apply per-connection parameters."""
- try:
- addresses = socket.getaddrinfo(self.host,
- self.port,
- socket.AF_UNSPEC,
- socket.SOCK_STREAM)
- except OSError as msg:
- raise exc.RestClientException(msg)
- for res in addresses:
- af, socktype, proto, canonname, sa = res
- sock = socket.socket(af, socket.SOCK_STREAM)
-
- if self.timeout is not None:
- # '0' microseconds
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
- struct.pack('LL', self.timeout, 0))
- self.sock = OpenSSLConnectionDelegator(self.context, sock)
- try:
- self.sock.connect(sa)
- except OSError as msg:
- if self.sock:
- self.sock = None
- continue
- break
- if self.sock is None:
- # Happen only when all results have failed.
- raise exc.RestClientException('Cannot connect to %s' % self.host)
-
- def close(self):
- if self.sock:
- # Remove the reference to the socket but don't close it yet.
- # Response close will close both socket and associated
- # file. Closing socket too soon will cause response
- # reads to fail with socket IO error 'Bad file descriptor'.
- self.sock = None
- httplib.HTTPSConnection.close(self)
-
-
-class ResponseBodyIterator(object):
- """A class that acts as an iterator over an HTTP response."""
-
- def __init__(self, resp):
- self.resp = resp
-
- def __iter__(self):
- while True:
- yield self.next()
-
- def next(self):
- chunk = self.resp.read(CHUNKSIZE)
- if chunk:
- return chunk
- else:
- raise StopIteration()
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 7350222..42c69d5 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -249,6 +249,9 @@
raise lib_exc.InvalidCredentials(
"Account file %s doesn't exist" % self.test_accounts_file)
useable_hashes = self._get_match_hash_list(roles)
+ if len(useable_hashes) == 0:
+ msg = 'No users configured for type/roles %s' % roles
+ raise lib_exc.InvalidCredentials(msg)
free_hash = self._get_free_hash(useable_hashes)
clean_creds = self._sanitize_creds(
self.hash_dict['creds'][free_hash])
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 3f573b7..0a3e8d3 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -21,7 +21,7 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import ssh
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
import tempest.lib.exceptions
CONF = config.CONF
@@ -37,7 +37,7 @@
except tempest.lib.exceptions.SSHTimeout:
try:
original_exception = sys.exc_info()
- caller = misc_utils.find_test_caller() or "not found"
+ caller = test_utils.find_test_caller() or "not found"
if self.server:
msg = 'Caller: %s. Timeout trying to ssh to server %s'
LOG.debug(msg, caller, self.server)
diff --git a/tempest/config.py b/tempest/config.py
index f5125b5..3810ceb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -370,14 +370,6 @@
'encrypted volume to a running server instance? This may '
'depend on the combination of compute_driver in nova and '
'the volume_driver(s) in cinder.'),
- # TODO(mriedem): Remove allow_duplicate_networks once kilo-eol happens
- # since the option was removed from nova in Liberty and is the default
- # behavior starting in Liberty.
- cfg.BoolOpt('allow_duplicate_networks',
- default=False,
- help='Does the test environment support creating instances '
- 'with multiple ports on the same network? This is only '
- 'valid when using Neutron.'),
cfg.BoolOpt('config_drive',
default=True,
help='Enable special configuration drive with metadata.'),
@@ -849,21 +841,6 @@
help="Value must match heat configuration of the same name."),
]
-
-dashboard_group = cfg.OptGroup(name="dashboard",
- title="Dashboard options")
-
-DashboardGroup = [
- cfg.StrOpt('dashboard_url',
- default='http://localhost/',
- help="Where the dashboard can be found"),
- cfg.StrOpt('login_url',
- default='http://localhost/auth/login/',
- help="Login page for the dashboard",
- deprecated_for_removal=True),
-]
-
-
data_processing_group = cfg.OptGroup(name="data-processing",
title="Data Processing options")
@@ -994,9 +971,6 @@
cfg.BoolOpt('heat',
default=False,
help="Whether or not Heat is expected to be available"),
- cfg.BoolOpt('horizon',
- default=True,
- help="Whether or not Horizon is expected to be available"),
cfg.BoolOpt('sahara',
default=False,
help="Whether or not Sahara is expected to be available"),
@@ -1139,7 +1113,6 @@
(object_storage_feature_group, ObjectStoreFeaturesGroup),
(database_group, DatabaseGroup),
(orchestration_group, OrchestrationGroup),
- (dashboard_group, DashboardGroup),
(data_processing_group, DataProcessingGroup),
(data_processing_feature_group, DataProcessingFeaturesGroup),
(stress_group, StressGroup),
@@ -1207,7 +1180,6 @@
'object-storage-feature-enabled']
self.database = _CONF.database
self.orchestration = _CONF.orchestration
- self.dashboard = _CONF.dashboard
self.data_processing = _CONF['data-processing']
self.data_processing_feature_enabled = _CONF[
'data-processing-feature-enabled']
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index aff9dee..09106d1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -256,6 +256,23 @@
yield (0, msg)
+def dont_use_config_in_tempest_lib(logical_line, filename):
+ """Check that tempest.lib doesn't use tempest config
+
+ T114
+ """
+
+ if 'tempest/lib/' not in filename:
+ return
+
+ if ('tempest.config' in logical_line
+ or 'from tempest import config' in logical_line
+ or 'oslo_config' in logical_line):
+ msg = ('T114: tempest.lib can not have any dependency on tempest '
+ 'config.')
+ yield(0, msg)
+
+
def factory(register):
register(import_no_clients_in_api_and_scenario_tests)
register(scenario_tests_need_service_tags)
@@ -268,4 +285,5 @@
register(get_resources_on_service_clients)
register(delete_resources_on_service_clients)
register(dont_import_local_tempest_into_lib)
+ register(dont_use_config_in_tempest_lib)
register(use_rand_uuid_instead_of_uuid4)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/images.py b/tempest/lib/api_schema/response/compute/v2_1/images.py
index daab898..b0f1934 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/images.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/images.py
@@ -77,7 +77,7 @@
'properties': {
'id': {'type': 'string'},
'links': image_links,
- 'name': {'type': 'string'}
+ 'name': {'type': ['string', 'null']}
},
'additionalProperties': False,
'required': ['id', 'links', 'name']
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 20b2a3d..a43d002 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -247,7 +247,7 @@
:param merge_stderr: if True the stderr buffer is merged into stdout
:type merge_stderr: boolean
"""
- flags += ' --os-endpoint-type %s' % endpoint_type
+ flags += ' --endpoint-type %s' % endpoint_type
return self.cmd_with_auth(
'cinder', action, flags, params, fail_ok, merge_stderr)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 179db17..bf107d2 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -26,7 +26,7 @@
import six
from tempest.lib.common import http
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
# redrive rate limited calls at most twice
@@ -221,6 +221,10 @@
:raises exceptions.InvalidHttpSuccessCode: if the read code isn't an
expected http success code
"""
+ if not isinstance(read_code, int):
+ raise TypeError("'read_code' must be an int instead of (%s)"
+ % type(read_code))
+
assert_msg = ("This function only allowed to use for HTTP status"
"codes which explicitly defined in the RFC 7231 & 4918."
"{0} is not a defined Success Code!"
@@ -393,7 +397,7 @@
req_body=None):
if req_headers is None:
req_headers = {}
- caller_name = misc_utils.find_test_caller()
+ caller_name = test_utils.find_test_caller()
if self.trace_requests and re.search(self.trace_requests, caller_name):
self.LOG.debug('Starting Request (%s): %s %s' %
(caller_name, method, req_url))
@@ -432,7 +436,7 @@
# we're going to just provide work around on who is actually
# providing timings by gracefully adding no content if they don't.
# Once we're down to 1 caller, clean this up.
- caller_name = misc_utils.find_test_caller()
+ caller_name = test_utils.find_test_caller()
if secs:
secs = " %.3fs" % secs
self.LOG.info(
@@ -849,7 +853,7 @@
'the required time (%(timeout)s s).' %
{'resource_type': self.resource_type, 'id': id,
'timeout': self.build_timeout})
- caller = misc_utils.find_test_caller()
+ caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
raise exceptions.TimeoutException(message)
diff --git a/tempest/lib/common/utils/misc.py b/tempest/lib/common/utils/misc.py
index b97dd86..f13b4c8 100644
--- a/tempest/lib/common/utils/misc.py
+++ b/tempest/lib/common/utils/misc.py
@@ -12,12 +12,10 @@
# 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 inspect
-import re
-
from oslo_log import log as logging
+from tempest.lib.common.utils import test_utils
+
LOG = logging.getLogger(__name__)
@@ -32,56 +30,8 @@
return getinstance
-def find_test_caller():
- """Find the caller class and test name.
-
- Because we know that the interesting things that call us are
- test_* methods, and various kinds of setUp / tearDown, we
- can look through the call stack to find appropriate methods,
- and the class we were in when those were called.
- """
- caller_name = None
- names = []
- frame = inspect.currentframe()
- is_cleanup = False
- # Start climbing the ladder until we hit a good method
- while True:
- try:
- frame = frame.f_back
- name = frame.f_code.co_name
- names.append(name)
- if re.search("^(test_|setUp|tearDown)", name):
- cname = ""
- if 'self' in frame.f_locals:
- cname = frame.f_locals['self'].__class__.__name__
- if 'cls' in frame.f_locals:
- cname = frame.f_locals['cls'].__name__
- caller_name = cname + ":" + name
- break
- elif re.search("^_run_cleanup", name):
- is_cleanup = True
- elif name == 'main':
- caller_name = 'main'
- break
- else:
- cname = ""
- if 'self' in frame.f_locals:
- cname = frame.f_locals['self'].__class__.__name__
- if 'cls' in frame.f_locals:
- cname = frame.f_locals['cls'].__name__
-
- # the fact that we are running cleanups is indicated pretty
- # deep in the stack, so if we see that we want to just
- # start looking for a real class name, and declare victory
- # once we do.
- if is_cleanup and cname:
- if not re.search("^RunTest", cname):
- caller_name = cname + ":_run_cleanups"
- break
- except Exception:
- break
- # prevents frame leaks
- del frame
- if caller_name is None:
- LOG.debug("Sane call name not found in %s" % names)
- return caller_name
+def find_test_caller(*args, **kwargs):
+ LOG.warning("tempest.lib.common.utils.misc.find_test_caller is deprecated "
+ "in favor of tempest.lib.common.utils.test_utils."
+ "find_test_caller")
+ test_utils.find_test_caller(*args, **kwargs)
diff --git a/tempest/lib/common/utils/test_utils.py b/tempest/lib/common/utils/test_utils.py
new file mode 100644
index 0000000..50a1a7d
--- /dev/null
+++ b/tempest/lib/common/utils/test_utils.py
@@ -0,0 +1,85 @@
+# Copyright 2016 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 inspect
+import re
+
+from oslo_log import log as logging
+
+from tempest.lib import exceptions
+
+LOG = logging.getLogger(__name__)
+
+
+def find_test_caller():
+ """Find the caller class and test name.
+
+ Because we know that the interesting things that call us are
+ test_* methods, and various kinds of setUp / tearDown, we
+ can look through the call stack to find appropriate methods,
+ and the class we were in when those were called.
+ """
+ caller_name = None
+ names = []
+ frame = inspect.currentframe()
+ is_cleanup = False
+ # Start climbing the ladder until we hit a good method
+ while True:
+ try:
+ frame = frame.f_back
+ name = frame.f_code.co_name
+ names.append(name)
+ if re.search("^(test_|setUp|tearDown)", name):
+ cname = ""
+ if 'self' in frame.f_locals:
+ cname = frame.f_locals['self'].__class__.__name__
+ if 'cls' in frame.f_locals:
+ cname = frame.f_locals['cls'].__name__
+ caller_name = cname + ":" + name
+ break
+ elif re.search("^_run_cleanup", name):
+ is_cleanup = True
+ elif name == 'main':
+ caller_name = 'main'
+ break
+ else:
+ cname = ""
+ if 'self' in frame.f_locals:
+ cname = frame.f_locals['self'].__class__.__name__
+ if 'cls' in frame.f_locals:
+ cname = frame.f_locals['cls'].__name__
+
+ # the fact that we are running cleanups is indicated pretty
+ # deep in the stack, so if we see that we want to just
+ # start looking for a real class name, and declare victory
+ # once we do.
+ if is_cleanup and cname:
+ if not re.search("^RunTest", cname):
+ caller_name = cname + ":_run_cleanups"
+ break
+ except Exception:
+ break
+ # prevents frame leaks
+ del frame
+ if caller_name is None:
+ LOG.debug("Sane call name not found in %s" % names)
+ return caller_name
+
+
+def call_and_ignore_notfound_exc(func, *args, **kwargs):
+ """Call the given function and pass if a `NotFound` exception is raised."""
+ try:
+ return func(*args, **kwargs)
+ except exceptions.NotFound:
+ pass
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e78e624..6ed99b4 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -69,12 +69,11 @@
"or False") % attr
def __call__(self, func):
+ @functools.wraps(func)
def _skipper(*args, **kw):
"""Wrapped skipper function."""
testobj = args[0]
if not getattr(testobj, self.attr, False):
raise testtools.TestCase.skipException(self.message)
func(*args, **kw)
- _skipper.__name__ = func.__name__
- _skipper.__doc__ = func.__doc__
return _skipper
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index d753650..8d0f2f6 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -27,7 +27,7 @@
from tempest.common import waiters
from tempest import config
from tempest import exceptions
-from tempest.lib.common.utils import misc as misc_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
from tempest.scenario import network_resources
import tempest.test
@@ -95,21 +95,6 @@
# not at the end of the class
self.addCleanup(self._wait_for_cleanups)
- def delete_wrapper(self, delete_thing, *args, **kwargs):
- """Ignores NotFound exceptions for delete operations.
-
- @param delete_thing: delete method of a resource. method will be
- executed as delete_thing(*args, **kwargs)
-
- """
- try:
- # Tempest clients return dicts, so there is no common delete
- # method available. Using a callable instead
- delete_thing(*args, **kwargs)
- except lib_exc.NotFound:
- # If the resource is already missing, mission accomplished.
- pass
-
def addCleanup_with_wait(self, waiter_callable, thing_id, thing_id_param,
cleanup_callable, cleanup_args=None,
cleanup_kwargs=None, waiter_client=None):
@@ -254,7 +239,7 @@
self.addCleanup_with_wait(
waiter_callable=waiters.wait_for_server_termination,
thing_id=body['id'], thing_id_param='server_id',
- cleanup_callable=self.delete_wrapper,
+ cleanup_callable=test_utils.call_and_ignore_notfound_exc,
cleanup_args=[clients.servers_client.delete_server, body['id']],
waiter_client=clients.servers_client)
server = clients.servers_client.show_server(body['id'])['server']
@@ -274,7 +259,7 @@
self.addCleanup(self.volumes_client.wait_for_resource_deletion,
volume['id'])
- self.addCleanup(self.delete_wrapper,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.volumes_client.delete_volume, volume['id'])
# NOTE(e0ne): Cinder API v2 uses name instead of display_name
@@ -334,7 +319,7 @@
self.assertEqual(secgroup['name'], sg_name)
self.assertEqual(secgroup['description'], sg_desc)
self.addCleanup(
- self.delete_wrapper,
+ test_utils.call_and_ignore_notfound_exc,
self.compute_security_groups_client.delete_security_group,
secgroup['id'])
@@ -373,7 +358,7 @@
message = ('Initializing SSH connection to %(ip)s failed. '
'Error: %(error)s' % {'ip': ip_address,
'error': e})
- caller = misc_utils.find_test_caller()
+ caller = test_utils.find_test_caller()
if caller:
message = '(%s) %s' % (caller, message)
LOG.exception(message)
@@ -463,7 +448,7 @@
self.addCleanup_with_wait(
waiter_callable=_image_client.wait_for_resource_deletion,
thing_id=image_id, thing_id_param='id',
- cleanup_callable=self.delete_wrapper,
+ cleanup_callable=test_utils.call_and_ignore_notfound_exc,
cleanup_args=[_image_client.delete_image, image_id])
snapshot_image = _image_client.check_image(image_id)
@@ -475,12 +460,11 @@
self.addCleanup(
self.snapshots_client.wait_for_resource_deletion,
snapshot_id)
- self.addCleanup(
- self.delete_wrapper, self.snapshots_client.delete_snapshot,
- snapshot_id)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.snapshots_client.delete_snapshot,
+ snapshot_id)
waiters.wait_for_snapshot_status(self.snapshots_client,
snapshot_id, 'available')
-
image_name = snapshot_image['name']
self.assertEqual(name, image_name)
LOG.debug("Created snapshot image %s for server %s",
@@ -537,7 +521,7 @@
return (proc.returncode == 0) == should_succeed
- caller = misc_utils.find_test_caller()
+ caller = test_utils.find_test_caller()
LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
' expected result is %(should_succeed)s' % {
'caller': caller, 'ip': ip_address, 'timeout': timeout,
@@ -606,7 +590,7 @@
pool_name = CONF.network.floating_network_name
floating_ip = (self.compute_floating_ips_client.
create_floating_ip(pool=pool_name)['floating_ip'])
- self.addCleanup(self.delete_wrapper,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.compute_floating_ips_client.delete_floating_ip,
floating_ip['id'])
self.compute_floating_ips_client.associate_floating_ip_to_server(
@@ -701,7 +685,8 @@
networks_client=networks_client, routers_client=routers_client,
**result['network'])
self.assertEqual(network.name, name)
- self.addCleanup(self.delete_wrapper, network.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ network.delete)
return network
def _list_networks(self, *args, **kwargs):
@@ -794,7 +779,7 @@
subnets_client=subnets_client,
routers_client=routers_client, **result['subnet'])
self.assertEqual(subnet.cidr, str_cidr)
- self.addCleanup(self.delete_wrapper, subnet.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnet.delete)
return subnet
def _create_port(self, network_id, client=None, namestart='port-quotatest',
@@ -809,7 +794,7 @@
self.assertIsNotNone(result, 'Unable to allocate port')
port = network_resources.DeletablePort(ports_client=client,
**result['port'])
- self.addCleanup(self.delete_wrapper, port.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc, port.delete)
return port
def _get_server_port_id_and_ip4(self, server, ip_addr=None):
@@ -866,7 +851,8 @@
floating_ip = network_resources.DeletableFloatingIp(
client=client,
**result['floatingip'])
- self.addCleanup(self.delete_wrapper, floating_ip.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ floating_ip.delete)
return floating_ip
def _associate_floating_ip(self, floating_ip, server):
@@ -1004,7 +990,8 @@
self.assertEqual(secgroup.name, sg_name)
self.assertEqual(tenant_id, secgroup.tenant_id)
self.assertEqual(secgroup.description, sg_desc)
- self.addCleanup(self.delete_wrapper, secgroup.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ secgroup.delete)
return secgroup
def _default_security_group(self, client=None, tenant_id=None):
@@ -1164,7 +1151,7 @@
router = network_resources.DeletableRouter(routers_client=client,
**result['router'])
self.assertEqual(router.name, name)
- self.addCleanup(self.delete_wrapper, router.delete)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc, router.delete)
return router
def _update_router_admin_state(self, router, admin_state_up):
@@ -1293,11 +1280,8 @@
"""Waits for a node to be associated with instance_id."""
def _get_node():
- node = None
- try:
- node = self.get_node(instance_id=instance_id)
- except lib_exc.NotFound:
- pass
+ node = test_utils.call_and_ignore_notfound_exc(
+ self.get_node, instance_id=instance_id)
return node is not None
if not tempest.test.call_until_true(
@@ -1444,7 +1428,7 @@
# look for the container to assure it is created
self.list_and_check_container_objects(name)
LOG.debug('Container %s created' % (name))
- self.addCleanup(self.delete_wrapper,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.container_client.delete_container,
name)
return name
@@ -1457,7 +1441,7 @@
obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
obj_data = data_utils.arbitrary_string()
self.object_client.create_object(container_name, obj_name, obj_data)
- self.addCleanup(self.delete_wrapper,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.object_client.delete_object,
container_name,
obj_name)
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
deleted file mode 100644
index 5d4f7b3..0000000
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from six.moves import html_parser as HTMLParser
-from six.moves.urllib import parse
-from six.moves.urllib import request
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-
-class HorizonHTMLParser(HTMLParser.HTMLParser):
- csrf_token = None
- region = None
- login = None
-
- def _find_name(self, attrs, name):
- for attrpair in attrs:
- if attrpair[0] == 'name' and attrpair[1] == name:
- return True
- return False
-
- def _find_value(self, attrs):
- for attrpair in attrs:
- if attrpair[0] == 'value':
- return attrpair[1]
- return None
-
- def _find_attr_value(self, attrs, attr_name):
- for attrpair in attrs:
- if attrpair[0] == attr_name:
- return attrpair[1]
- return None
-
- def handle_starttag(self, tag, attrs):
- if tag == 'input':
- if self._find_name(attrs, 'csrfmiddlewaretoken'):
- self.csrf_token = self._find_value(attrs)
- if self._find_name(attrs, 'region'):
- self.region = self._find_value(attrs)
- if tag == 'form':
- self.login = self._find_attr_value(attrs, 'action')
-
-
-class TestDashboardBasicOps(manager.ScenarioTest):
-
- """The test suite for dashboard basic operations
-
- This is a basic scenario test:
- * checks that the login page is available
- * logs in as a regular user
- * checks that the user home page loads without error
- """
-
- @classmethod
- def skip_checks(cls):
- super(TestDashboardBasicOps, cls).skip_checks()
- if not CONF.service_available.horizon:
- raise cls.skipException("Horizon support is required")
-
- @classmethod
- def setup_credentials(cls):
- cls.set_network_resources()
- super(TestDashboardBasicOps, cls).setup_credentials()
-
- def check_login_page(self):
- response = request.urlopen(CONF.dashboard.dashboard_url)
- self.assertIn("id_username", response.read())
-
- def user_login(self, username, password):
- self.opener = request.build_opener(request.HTTPCookieProcessor())
- response = self.opener.open(CONF.dashboard.dashboard_url).read()
-
- # Grab the CSRF token and default region
- parser = HorizonHTMLParser()
- parser.feed(response)
-
- # construct login url for dashboard, discovery accommodates non-/ web
- # root for dashboard
- login_url = parse.urljoin(CONF.dashboard.dashboard_url, parser.login)
-
- # Prepare login form request
- req = request.Request(login_url)
- req.add_header('Content-type', 'application/x-www-form-urlencoded')
- req.add_header('Referer', CONF.dashboard.dashboard_url)
-
- # Pass the default domain name regardless of the auth version in order
- # to test the scenario of when horizon is running with keystone v3
- params = {'username': username,
- 'password': password,
- 'region': parser.region,
- 'domain': CONF.auth.default_credentials_domain_name,
- 'csrfmiddlewaretoken': parser.csrf_token}
- self.opener.open(req, parse.urlencode(params))
-
- def check_home_page(self):
- response = self.opener.open(CONF.dashboard.dashboard_url)
- self.assertIn('Overview', response.read())
-
- @test.idempotent_id('4f8851b1-0e69-482b-b63b-84c6e76f6c80')
- @test.services('dashboard')
- def test_basic_scenario(self):
- creds = self.os.credentials
- self.check_login_page()
- self.user_login(creds.username, creds.password)
- self.check_home_page()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index f0ae223..f5134e5 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -23,6 +23,7 @@
from tempest.common import waiters
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import test_utils
from tempest.scenario import manager
from tempest.scenario import network_resources
from tempest import test
@@ -252,7 +253,7 @@
net_id=self.new_net.id)['interfaceAttachment']
self.addCleanup(self.ports_client.wait_for_resource_deletion,
interface['port_id'])
- self.addCleanup(self.delete_wrapper,
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.interface_client.delete_interface,
server['id'], interface['port_id'])
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 6e24801..2bdd092 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -111,7 +111,7 @@
uri += "?%s" % urllib.urlencode(kwargs)
resp, body = self.get(uri)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@@ -127,7 +127,7 @@
else:
uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
resp, body = self.get(uri)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@@ -145,7 +145,7 @@
uri = self._get_uri(resource)
resp, body = self.post(uri, body=body)
- self.expected_success(201, resp['status'])
+ self.expected_success(201, resp.status)
return resp, self.deserialize(body)
@@ -160,7 +160,7 @@
uri = self._get_uri(resource, uuid)
resp, body = self.delete(uri)
- self.expected_success(204, resp['status'])
+ self.expected_success(204, resp.status)
return resp, body
def _patch_request(self, resource, uuid, patch_object):
@@ -176,7 +176,7 @@
patch_body = json.dumps(patch_object)
resp, body = self.patch(uri, body=patch_body)
- self.expected_success(200, resp['status'])
+ self.expected_success(200, resp.status)
return resp, self.deserialize(body)
@handle_errors
@@ -201,5 +201,5 @@
put_body = json.dumps(put_object)
resp, body = self.put(uri, body=put_body)
- self.expected_success(202, resp['status'])
+ self.expected_success([202, 204], resp.status)
return resp, body
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/services/image/v1/json/images_client.py
index a36075f..922b626 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/services/image/v1/json/images_client.py
@@ -15,6 +15,7 @@
import copy
import errno
+import functools
import os
from oslo_log import log as logging
@@ -22,11 +23,11 @@
import six
from six.moves.urllib import parse as urllib
-from tempest.common import glance_http
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
LOG = logging.getLogger(__name__)
+CHUNKSIZE = 1024 * 64 # 64kB
class ImagesClient(rest_client.RestClient):
@@ -34,9 +35,6 @@
def __init__(self, auth_provider, catalog_type, region, **kwargs):
super(ImagesClient, self).__init__(
auth_provider, catalog_type, region, **kwargs)
- self._http = None
- self.dscv = kwargs.get("disable_ssl_certificate_validation")
- self.ca_certs = kwargs.get("ca_certs")
def _image_meta_from_headers(self, headers):
meta = {'properties': {}}
@@ -103,27 +101,29 @@
# Cannot determine size of input image
return None
- def _get_http(self):
- return glance_http.HTTPClient(auth_provider=self.auth_provider,
- filters=self.filters,
- insecure=self.dscv,
- ca_certs=self.ca_certs)
-
def _create_with_data(self, headers, data):
- resp, body_iter = self.http.raw_request('POST', '/v1/images',
- headers=headers, body=data)
+ # We are going to do chunked transfert, so split the input data
+ # info fixed-sized chunks.
+ headers['Content-Type'] = 'application/octet-stream'
+ data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+ resp, body = self.request('POST', '/v1/images',
+ headers=headers, body=data, chunked=True)
self._error_checker('POST', '/v1/images', headers, data, resp,
- body_iter)
- body = json.loads(''.join([c for c in body_iter]))
+ body)
+ body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def _update_with_data(self, image_id, headers, data):
+ # We are going to do chunked transfert, so split the input data
+ # info fixed-sized chunks.
+ headers['Content-Type'] = 'application/octet-stream'
+ data = iter(functools.partial(data.read, CHUNKSIZE), b'')
url = '/v1/images/%s' % image_id
- resp, body_iter = self.http.raw_request('PUT', url, headers=headers,
- body=data)
+ resp, body = self.request('PUT', url, headers=headers,
+ body=data, chunked=True)
self._error_checker('PUT', url, headers, data,
- resp, body_iter)
- body = json.loads(''.join([c for c in body_iter]))
+ resp, body)
+ body = json.loads(body)
return rest_client.ResponseBody(resp, body)
@property
diff --git a/tempest/services/image/v2/json/images_client.py b/tempest/services/image/v2/json/images_client.py
index a61f31d..88eafe1 100644
--- a/tempest/services/image/v2/json/images_client.py
+++ b/tempest/services/image/v2/json/images_client.py
@@ -13,34 +13,22 @@
# License for the specific language governing permissions and limitations
# under the License.
+import functools
+
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urllib
-from tempest.common import glance_http
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+CHUNKSIZE = 1024 * 64 # 64kB
+
class ImagesClient(rest_client.RestClient):
def __init__(self, auth_provider, catalog_type, region, **kwargs):
super(ImagesClient, self).__init__(
auth_provider, catalog_type, region, **kwargs)
- self._http = None
- self.dscv = kwargs.get("disable_ssl_certificate_validation")
- self.ca_certs = kwargs.get("ca_certs")
-
- def _get_http(self):
- return glance_http.HTTPClient(auth_provider=self.auth_provider,
- filters=self.filters,
- insecure=self.dscv,
- ca_certs=self.ca_certs)
-
- @property
- def http(self):
- if self._http is None:
- self._http = self._get_http()
- return self._http
def update_image(self, image_id, patch):
"""Update an image.
@@ -118,9 +106,14 @@
def store_image_file(self, image_id, data):
url = 'v2/images/%s/file' % image_id
+
+ # We are going to do chunked transfert, so split the input data
+ # info fixed-sized chunks.
headers = {'Content-Type': 'application/octet-stream'}
- resp, body = self.http.raw_request('PUT', url, headers=headers,
- body=data)
+ data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+
+ resp, body = self.request('PUT', url, headers=headers,
+ body=data, chunked=True)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/network/json/routers_client.py b/tempest/services/network/json/routers_client.py
index 725dd76..22f5a37 100644
--- a/tempest/services/network/json/routers_client.py
+++ b/tempest/services/network/json/routers_client.py
@@ -25,37 +25,10 @@
uri = '/routers'
return self.create_resource(uri, post_body)
- def _update_router(self, router_id, set_enable_snat, **kwargs):
- uri = '/routers/%s' % router_id
- body = self.show_resource(uri)
- update_body = {}
- update_body['name'] = kwargs.get('name', body['router']['name'])
- update_body['admin_state_up'] = kwargs.get(
- 'admin_state_up', body['router']['admin_state_up'])
- cur_gw_info = body['router']['external_gateway_info']
- if cur_gw_info:
- # TODO(kevinbenton): setting the external gateway info is not
- # allowed for a regular tenant. If the ability to update is also
- # merged, a test case for this will need to be added similar to
- # the SNAT case.
- cur_gw_info.pop('external_fixed_ips', None)
- if not set_enable_snat:
- cur_gw_info.pop('enable_snat', None)
- update_body['external_gateway_info'] = kwargs.get(
- 'external_gateway_info', body['router']['external_gateway_info'])
- if 'distributed' in kwargs:
- update_body['distributed'] = kwargs['distributed']
- update_body = dict(router=update_body)
- return self.update_resource(uri, update_body)
-
def update_router(self, router_id, **kwargs):
- """Update a router leaving enable_snat to its default value."""
- # If external_gateway_info contains enable_snat the request will fail
- # with 404 unless executed with admin client, and therefore we instruct
- # _update_router to not set this attribute
- # NOTE(salv-orlando): The above applies as long as Neutron's default
- # policy is to restrict enable_snat usage to admins only.
- return self._update_router(router_id, set_enable_snat=False, **kwargs)
+ uri = '/routers/%s' % router_id
+ update_body = {'router': kwargs}
+ return self.update_resource(uri, update_body)
def show_router(self, router_id, **fields):
uri = '/routers/%s' % router_id
@@ -88,14 +61,6 @@
}
return self.update_resource(uri, put_body)
- def update_router_with_snat_gw_info(self, router_id, **kwargs):
- """Update a router passing also the enable_snat attribute.
-
- This method must be execute with admin credentials, otherwise the API
- call will return a 404 error.
- """
- return self._update_router(router_id, set_enable_snat=True, **kwargs)
-
def add_router_interface(self, router_id, **kwargs):
"""Add router interface.
diff --git a/tempest/test.py b/tempest/test.py
index aefe1a9..d31c509 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -77,7 +77,6 @@
'network': True,
'identity': True,
'object_storage': CONF.service_available.swift,
- 'dashboard': CONF.service_available.horizon,
'data_processing': CONF.service_available.sahara,
'database': CONF.service_available.trove
}
@@ -92,8 +91,8 @@
"""
def decorator(f):
services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
- 'network', 'identity', 'object_storage', 'dashboard',
- 'data_processing', 'database']
+ 'network', 'identity', 'object_storage', 'data_processing',
+ 'database']
for service in args:
if service not in services:
raise exceptions.InvalidServiceTag('%s is not a valid '
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 8d4f33b..f025418 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -21,15 +21,19 @@
from tempest import config
from tempest import exceptions
from tempest.lib.common import rest_client
-from tempest.lib.services.identity.v2 import token_client as json_token_client
-from tempest.services.identity.v2.json import identity_client as \
- json_iden_client
-from tempest.services.identity.v2.json import roles_client as \
- json_roles_client
+from tempest.lib.services.identity.v2 import token_client as v2_token_client
+from tempest.lib.services.identity.v3 import token_client as v3_token_client
+from tempest.services.identity.v2.json import identity_client as v2_iden_client
+from tempest.services.identity.v2.json import roles_client as v2_roles_client
from tempest.services.identity.v2.json import tenants_client as \
- json_tenants_client
-from tempest.services.identity.v2.json import users_client as \
- json_users_client
+ v2_tenants_client
+from tempest.services.identity.v2.json import users_client as v2_users_client
+from tempest.services.identity.v3.json import domains_client
+from tempest.services.identity.v3.json import identity_client as v3_iden_client
+from tempest.services.identity.v3.json import projects_client as \
+ v3_projects_client
+from tempest.services.identity.v3.json import roles_client as v3_roles_client
+from tempest.services.identity.v3.json import users_clients as v3_users_client
from tempest.services.network.json import routers_client
from tempest.tests import base
from tempest.tests import fake_config
@@ -43,13 +47,24 @@
'identity_version': 'v2',
'admin_role': 'admin'}
+ token_client = v2_token_client
+ iden_client = v2_iden_client
+ roles_client = v2_roles_client
+ tenants_client = v2_tenants_client
+ users_client = v2_users_client
+ token_client_class = token_client.TokenClient
+ fake_response = fake_identity._fake_v2_response
+ assign_role_on_project = 'assign_user_role'
+ tenants_client_class = tenants_client.TenantsClient
+ delete_tenant = 'delete_tenant'
+
def setUp(self):
super(TestDynamicCredentialProvider, self).setUp()
self.useFixture(fake_config.ConfigFixture())
self.patchobject(config, 'TempestConfigPrivate',
fake_config.FakePrivate)
- self.patchobject(json_token_client.TokenClient, 'raw_request',
- fake_identity._fake_v2_response)
+ self.patchobject(self.token_client_class, 'raw_request',
+ self.fake_response)
cfg.CONF.set_default('operator_role', 'FakeRole',
group='object-storage')
self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
@@ -59,7 +74,7 @@
def test_tempest_client(self):
creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
self.assertIsInstance(creds.identity_admin_client,
- json_iden_client.IdentityClient)
+ self.iden_client.IdentityClient)
def _get_fake_admin_creds(self):
return credentials.get_credentials(
@@ -70,7 +85,7 @@
def _mock_user_create(self, id, name):
user_fix = self.useFixture(mockpatch.PatchObject(
- json_users_client.UsersClient,
+ self.users_client.UsersClient,
'create_user',
return_value=(rest_client.ResponseBody
(200, {'user': {'id': id, 'name': name}}))))
@@ -78,7 +93,7 @@
def _mock_tenant_create(self, id, name):
tenant_fix = self.useFixture(mockpatch.PatchObject(
- json_tenants_client.TenantsClient,
+ self.tenants_client.TenantsClient,
'create_tenant',
return_value=(rest_client.ResponseBody
(200, {'tenant': {'id': id, 'name': name}}))))
@@ -86,7 +101,7 @@
def _mock_list_roles(self, id, name):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
(200,
@@ -97,7 +112,7 @@
def _mock_list_2_roles(self):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
(200,
@@ -108,24 +123,25 @@
def _mock_assign_user_role(self):
tenant_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
- 'assign_user_role',
+ self.roles_client.RolesClient,
+ self.assign_role_on_project,
return_value=(rest_client.ResponseBody
(200, {}))))
return tenant_fix
def _mock_list_role(self):
roles_fix = self.useFixture(mockpatch.PatchObject(
- json_roles_client.RolesClient,
+ self.roles_client.RolesClient,
'list_roles',
return_value=(rest_client.ResponseBody
- (200, {'roles': [{'id': '1',
- 'name': 'FakeRole'}]}))))
+ (200, {'roles': [
+ {'id': '1', 'name': 'FakeRole'},
+ {'id': '2', 'name': 'Member'}]}))))
return roles_fix
def _mock_list_ec2_credentials(self, user_id, tenant_id):
ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
- json_users_client.UsersClient,
+ self.users_client.UsersClient,
'list_user_ec2_credentials',
return_value=(rest_client.ResponseBody
(200, {'credentials': [{
@@ -180,12 +196,12 @@
self._mock_user_create('1234', 'fake_admin_user')
self._mock_tenant_create('1234', 'fake_admin_tenant')
- user_mock = mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role')
+ user_mock = mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project)
user_mock.start()
self.addCleanup(user_mock.stop)
- with mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role') as user_mock:
+ with mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project) as user_mock:
admin_creds = creds.get_admin_creds()
user_mock.assert_has_calls([
mock.call('1234', '1234', '1234')])
@@ -203,12 +219,12 @@
self._mock_user_create('1234', 'fake_role_user')
self._mock_tenant_create('1234', 'fake_role_tenant')
- user_mock = mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role')
+ user_mock = mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project)
user_mock.start()
self.addCleanup(user_mock.stop)
- with mock.patch.object(json_roles_client.RolesClient,
- 'assign_user_role') as user_mock:
+ with mock.patch.object(self.roles_client.RolesClient,
+ self.assign_role_on_project) as user_mock:
role_creds = creds.get_creds_by_roles(
roles=['role1', 'role2'])
calls = user_mock.mock_calls
@@ -240,12 +256,10 @@
self._mock_user_create('123456', 'fake_admin_user')
self._mock_list_roles('123456', 'admin')
creds.get_admin_creds()
- user_mock = self.patch(
- 'tempest.services.identity.v2.json.users_client.'
- 'UsersClient.delete_user')
- tenant_mock = self.patch(
- 'tempest.services.identity.v2.json.tenants_client.'
- 'TenantsClient.delete_tenant')
+ user_mock = self.patchobject(self.users_client.UsersClient,
+ 'delete_user')
+ tenant_mock = self.patchobject(self.tenants_client_class,
+ self.delete_tenant)
creds.clear_creds()
# Verify user delete calls
calls = user_mock.mock_calls
@@ -374,18 +388,13 @@
self._mock_router_create('123456', 'fake_admin_router')
self._mock_list_roles('123456', 'admin')
creds.get_admin_creds()
- self.patch('tempest.services.identity.v2.json.users_client.'
- 'UsersClient.delete_user')
- self.patch('tempest.services.identity.v2.json.tenants_client.'
- 'TenantsClient.delete_tenant')
- net = mock.patch.object(creds.networks_admin_client,
- 'delete_network')
+ self.patchobject(self.users_client.UsersClient, 'delete_user')
+ self.patchobject(self.tenants_client_class, self.delete_tenant)
+ net = mock.patch.object(creds.networks_admin_client, 'delete_network')
net_mock = net.start()
- subnet = mock.patch.object(creds.subnets_admin_client,
- 'delete_subnet')
+ subnet = mock.patch.object(creds.subnets_admin_client, 'delete_subnet')
subnet_mock = subnet.start()
- router = mock.patch.object(creds.routers_admin_client,
- 'delete_router')
+ router = mock.patch.object(creds.routers_admin_client, 'delete_router')
router_mock = router.start()
remove_router_interface_mock = self.patch(
'tempest.services.network.json.routers_client.RoutersClient.'
@@ -587,3 +596,42 @@
self._mock_tenant_create('1234', 'fake_prim_tenant')
self.assertRaises(exceptions.InvalidConfiguration,
creds.get_primary_creds)
+
+
+class TestDynamicCredentialProviderV3(TestDynamicCredentialProvider):
+
+ fixed_params = {'name': 'test class',
+ 'identity_version': 'v3',
+ 'admin_role': 'admin'}
+
+ token_client = v3_token_client
+ iden_client = v3_iden_client
+ roles_client = v3_roles_client
+ tenants_client = v3_projects_client
+ users_client = v3_users_client
+ token_client_class = token_client.V3TokenClient
+ fake_response = fake_identity._fake_v3_response
+ assign_role_on_project = 'assign_user_role_on_project'
+ tenants_client_class = tenants_client.ProjectsClient
+ delete_tenant = 'delete_project'
+
+ def setUp(self):
+ super(TestDynamicCredentialProviderV3, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.useFixture(mockpatch.PatchObject(
+ domains_client.DomainsClient, 'list_domains',
+ return_value=dict(domains=[dict(id='default',
+ name='Default')])))
+ self.patchobject(self.roles_client.RolesClient,
+ 'assign_user_role_on_domain')
+
+ def _mock_list_ec2_credentials(self, user_id, tenant_id):
+ pass
+
+ def _mock_tenant_create(self, id, name):
+ project_fix = self.useFixture(mockpatch.PatchObject(
+ self.tenants_client.ProjectsClient,
+ 'create_project',
+ return_value=(rest_client.ResponseBody
+ (200, {'project': {'id': id, 'name': name}}))))
+ return project_fix
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index 873c4c4..13d4713 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -14,6 +14,7 @@
import hashlib
import os
+import testtools
import mock
from oslo_concurrency.fixture import lockutils as lockutils_fixtures
@@ -69,10 +70,12 @@
'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
{'username': 'test_user10', 'project_name': 'test_tenant10',
'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
- {'username': 'test_user11', 'tenant_name': 'test_tenant11',
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
'password': 'p', 'roles': [admin_role]},
- {'username': 'test_user12', 'project_name': 'test_tenant12',
- 'password': 'p', 'roles': [admin_role]}]
+ {'username': 'test_admin2', 'project_name': 'test_tenant12',
+ 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin3', 'project_name': 'test_tenant13',
+ 'password': 'p', 'types': ['admin']}]
def setUp(self):
super(TestPreProvisionedCredentials, self).setUp()
@@ -262,9 +265,6 @@
self.assertFalse(test_accounts_class.is_multi_user())
def test__get_creds_by_roles_one_role(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -280,9 +280,6 @@
self.assertIn(i, args)
def test__get_creds_by_roles_list_role(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -300,9 +297,6 @@
self.assertIn(i, args)
def test__get_creds_by_roles_no_admin(self):
- self.useFixture(mockpatch.Patch(
- 'tempest.common.preprov_creds.read_accounts_yaml',
- return_value=self.test_accounts))
test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
**self.fixed_params)
hashes = list(test_accounts_class.hash_dict['creds'].keys())
@@ -346,6 +340,88 @@
self.assertEqual('fake-id', network['id'])
self.assertEqual('network-2', network['name'])
+ def test_get_primary_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ primary_creds = test_accounts_class.get_primary_creds()
+ self.assertNotIn('test_admin', primary_creds.username)
+
+ def test_get_primary_creds_none_available(self):
+ admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_primary_creds()
+
+ def test_get_alt_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ alt_creds = test_accounts_class.get_alt_creds()
+ self.assertNotIn('test_admin', alt_creds.username)
+
+ def test_get_alt_creds_none_available(self):
+ admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_alt_creds()
+
+ def test_get_admin_creds(self):
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_by_type(self):
+ test_accounts = [
+ {'username': 'test_user10', 'project_name': 'test_tenant10',
+ 'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+ 'password': 'p', 'types': ['admin']}]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=test_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_by_role(self):
+ test_accounts = [
+ {'username': 'test_user10', 'project_name': 'test_tenant10',
+ 'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+ {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+ 'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=test_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ admin_creds = test_accounts_class.get_admin_creds()
+ self.assertIn('test_admin', admin_creds.username)
+
+ def test_get_admin_creds_none_available(self):
+ non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
+ not in x['username']]
+ self.useFixture(mockpatch.Patch(
+ 'tempest.common.preprov_creds.read_accounts_yaml',
+ return_value=non_admin_accounts))
+ test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+ **self.fixed_params)
+ with testtools.ExpectedException(lib_exc.InvalidCredentials):
+ # Get one more
+ test_accounts_class.get_admin_creds()
+
class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
@@ -389,9 +465,9 @@
{'username': 'test_user10', 'project_name': 'test_project10',
'domain_name': 'domain', 'password': 'p',
'roles': ['role1', 'role2', 'role3', 'role4']},
- {'username': 'test_user11', 'project_name': 'test_project11',
- 'domain_name': 'domain', 'password': 'p',
- 'roles': [admin_role]},
- {'username': 'test_user12', 'project_name': 'test_project12',
- 'domain_name': 'domain', 'password': 'p',
- 'roles': [admin_role]}]
+ {'username': 'test_admin1', 'project_name': 'test_project11',
+ 'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin2', 'project_name': 'test_project12',
+ 'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+ {'username': 'test_admin3', 'project_name': 'test_tenant13',
+ 'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
diff --git a/tempest/tests/lib/common/utils/test_misc.py b/tempest/tests/lib/common/utils/test_misc.py
index 9597f5b..47e81d1 100644
--- a/tempest/tests/lib/common/utils/test_misc.py
+++ b/tempest/tests/lib/common/utils/test_misc.py
@@ -50,39 +50,3 @@
self.assertEqual(test, test2)
test3 = TestBar()
self.assertNotEqual(test, test3)
-
- def test_find_test_caller_test_case(self):
- # Calling it from here should give us the method we're in.
- self.assertEqual('TestMisc:test_find_test_caller_test_case',
- misc.find_test_caller())
-
- def test_find_test_caller_setup_self(self):
- def setUp(self):
- return misc.find_test_caller()
- self.assertEqual('TestMisc:setUp', setUp(self))
-
- def test_find_test_caller_setup_no_self(self):
- def setUp():
- return misc.find_test_caller()
- self.assertEqual(':setUp', setUp())
-
- def test_find_test_caller_setupclass_cls(self):
- def setUpClass(cls): # noqa
- return misc.find_test_caller()
- self.assertEqual('TestMisc:setUpClass', setUpClass(self.__class__))
-
- def test_find_test_caller_teardown_self(self):
- def tearDown(self):
- return misc.find_test_caller()
- self.assertEqual('TestMisc:tearDown', tearDown(self))
-
- def test_find_test_caller_teardown_no_self(self):
- def tearDown():
- return misc.find_test_caller()
- self.assertEqual(':tearDown', tearDown())
-
- def test_find_test_caller_teardown_class(self):
- def tearDownClass(cls): # noqa
- return misc.find_test_caller()
- self.assertEqual('TestMisc:tearDownClass',
- tearDownClass(self.__class__))
diff --git a/tempest/tests/lib/common/utils/test_test_utils.py b/tempest/tests/lib/common/utils/test_test_utils.py
new file mode 100644
index 0000000..919e219
--- /dev/null
+++ b/tempest/tests/lib/common/utils/test_test_utils.py
@@ -0,0 +1,78 @@
+# Copyright 2016 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 mock
+
+from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
+from tempest.tests import base
+
+
+class TestTestUtils(base.TestCase):
+
+ def test_find_test_caller_test_case(self):
+ # Calling it from here should give us the method we're in.
+ self.assertEqual('TestTestUtils:test_find_test_caller_test_case',
+ test_utils.find_test_caller())
+
+ def test_find_test_caller_setup_self(self):
+ def setUp(self):
+ return test_utils.find_test_caller()
+ self.assertEqual('TestTestUtils:setUp', setUp(self))
+
+ def test_find_test_caller_setup_no_self(self):
+ def setUp():
+ return test_utils.find_test_caller()
+ self.assertEqual(':setUp', setUp())
+
+ def test_find_test_caller_setupclass_cls(self):
+ def setUpClass(cls): # noqa
+ return test_utils.find_test_caller()
+ self.assertEqual('TestTestUtils:setUpClass',
+ setUpClass(self.__class__))
+
+ def test_find_test_caller_teardown_self(self):
+ def tearDown(self):
+ return test_utils.find_test_caller()
+ self.assertEqual('TestTestUtils:tearDown', tearDown(self))
+
+ def test_find_test_caller_teardown_no_self(self):
+ def tearDown():
+ return test_utils.find_test_caller()
+ self.assertEqual(':tearDown', tearDown())
+
+ def test_find_test_caller_teardown_class(self):
+ def tearDownClass(cls): # noqa
+ return test_utils.find_test_caller()
+ self.assertEqual('TestTestUtils:tearDownClass',
+ tearDownClass(self.__class__))
+
+ def test_call_and_ignore_notfound_exc_when_notfound_raised(self):
+ def raise_not_found():
+ raise exceptions.NotFound()
+ self.assertIsNone(
+ test_utils.call_and_ignore_notfound_exc(raise_not_found))
+
+ def test_call_and_ignore_notfound_exc_when_value_error_raised(self):
+ def raise_value_error():
+ raise ValueError()
+ self.assertRaises(ValueError, test_utils.call_and_ignore_notfound_exc,
+ raise_value_error)
+
+ def test_call_and_ignore_notfound_exc(self):
+ m = mock.Mock(return_value=42)
+ args, kwargs = (1,), {'1': None}
+ self.assertEqual(
+ 42, test_utils.call_and_ignore_notfound_exc(m, *args, **kwargs))
+ m.assert_called_once_with(*args, **kwargs)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 2a6fad5..106a1e5 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -693,6 +693,24 @@
self.assertRaises(AssertionError, self.rest_client.expected_success,
expected_code, read_code)
+ def test_non_success_read_code_as_string(self):
+ expected_code = 202
+ read_code = '202'
+ self.assertRaises(TypeError, self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_read_code_as_list(self):
+ expected_code = 202
+ read_code = [202]
+ self.assertRaises(TypeError, self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_expected_code_as_non_int(self):
+ expected_code = ['201', 202]
+ read_code = 202
+ self.assertRaises(AssertionError, self.rest_client.expected_success,
+ expected_code, read_code)
+
class TestResponseBody(base.TestCase):
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
deleted file mode 100644
index 768cd05..0000000
--- a/tempest/tests/test_glance_http.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# Copyright 2014 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 socket
-
-import mock
-from oslotest import mockpatch
-import six
-from six.moves import http_client as httplib
-
-from tempest.common import glance_http
-from tempest import exceptions
-from tempest.tests import base
-from tempest.tests import fake_auth_provider
-from tempest.tests.lib import fake_http
-
-
-class TestGlanceHTTPClient(base.TestCase):
-
- def setUp(self):
- super(TestGlanceHTTPClient, self).setUp()
- self.endpoint = 'http://fake_url.com'
- self.fake_auth = fake_auth_provider.FakeAuthProvider()
-
- self.fake_auth.base_url = mock.MagicMock(return_value=self.endpoint)
-
- self.useFixture(mockpatch.PatchObject(
- httplib.HTTPConnection,
- 'request',
- side_effect=b'fake_body'))
- self.client = glance_http.HTTPClient(self.fake_auth, {})
-
- def _set_response_fixture(self, header, status, resp_body):
- resp = fake_http.fake_http_response(header, status=status,
- body=six.StringIO(resp_body))
- self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
- 'getresponse', return_value=resp))
- return resp
-
- def test_raw_request(self):
- self._set_response_fixture({}, 200, 'fake_response_body')
- resp, body = self.client.raw_request('GET', '/images')
- self.assertEqual(200, resp.status)
- self.assertEqual('fake_response_body', body.read())
-
- def test_raw_request_with_response_chunked(self):
- self._set_response_fixture({}, 200, 'fake_response_body')
- self.useFixture(mockpatch.PatchObject(glance_http,
- 'CHUNKSIZE', 1))
- resp, body = self.client.raw_request('GET', '/images')
- self.assertEqual(200, resp.status)
- self.assertEqual('fake_response_body', body.read())
-
- def test_raw_request_chunked(self):
- self.useFixture(mockpatch.PatchObject(glance_http,
- 'CHUNKSIZE', 1))
- self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
- 'endheaders'))
- self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
- 'send'))
-
- self._set_response_fixture({}, 200, 'fake_response_body')
- req_body = six.StringIO('fake_request_body')
- resp, body = self.client.raw_request('PUT', '/images', body=req_body)
- self.assertEqual(200, resp.status)
- self.assertEqual('fake_response_body', body.read())
- call_count = httplib.HTTPConnection.send.call_count
- self.assertEqual(call_count - 1, req_body.tell())
-
- def test_get_connection_class_for_https(self):
- conn_class = self.client._get_connection_class('https')
- self.assertEqual(glance_http.VerifiedHTTPSConnection, conn_class)
-
- def test_get_connection_class_for_http(self):
- conn_class = (self.client._get_connection_class('http'))
- self.assertEqual(httplib.HTTPConnection, conn_class)
-
- def test_get_connection_http(self):
- self.assertIsInstance(self.client._get_connection(),
- httplib.HTTPConnection)
-
- def test_get_connection_https(self):
- endpoint = 'https://fake_url.com'
- self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
- self.client = glance_http.HTTPClient(self.fake_auth, {})
- self.assertIsInstance(self.client._get_connection(),
- glance_http.VerifiedHTTPSConnection)
-
- def test_get_connection_ipv4_https(self):
- endpoint = 'https://127.0.0.1'
- self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
- self.client = glance_http.HTTPClient(self.fake_auth, {})
- self.assertIsInstance(self.client._get_connection(),
- glance_http.VerifiedHTTPSConnection)
-
- def test_get_connection_ipv6_https(self):
- endpoint = 'https://[::1]'
- self.fake_auth.base_url = mock.MagicMock(return_value=endpoint)
- self.client = glance_http.HTTPClient(self.fake_auth, {})
- self.assertIsInstance(self.client._get_connection(),
- glance_http.VerifiedHTTPSConnection)
-
- def test_get_connection_url_not_fount(self):
- self.useFixture(mockpatch.PatchObject(self.client, 'connection_class',
- side_effect=httplib.InvalidURL()
- ))
- self.assertRaises(exceptions.EndpointNotFound,
- self.client._get_connection)
-
- def test_get_connection_kwargs_default_for_http(self):
- kwargs = self.client._get_connection_kwargs('http')
- self.assertEqual(600, kwargs['timeout'])
- self.assertEqual(1, len(kwargs.keys()))
-
- def test_get_connection_kwargs_set_timeout_for_http(self):
- kwargs = self.client._get_connection_kwargs('http', timeout=10,
- ca_certs='foo')
- self.assertEqual(10, kwargs['timeout'])
- # nothing more than timeout is evaluated for http connections
- self.assertEqual(1, len(kwargs.keys()))
-
- def test_get_connection_kwargs_default_for_https(self):
- kwargs = self.client._get_connection_kwargs('https')
- self.assertEqual(600, kwargs['timeout'])
- self.assertIsNone(kwargs['ca_certs'])
- self.assertIsNone(kwargs['cert_file'])
- self.assertIsNone(kwargs['key_file'])
- self.assertEqual(False, kwargs['insecure'])
- self.assertEqual(True, kwargs['ssl_compression'])
- self.assertEqual(6, len(kwargs.keys()))
-
- def test_get_connection_kwargs_set_params_for_https(self):
- kwargs = self.client._get_connection_kwargs('https', timeout=10,
- ca_certs='foo',
- cert_file='/foo/bar.cert',
- key_file='/foo/key.pem',
- insecure=True,
- ssl_compression=False)
- self.assertEqual(10, kwargs['timeout'])
- self.assertEqual('foo', kwargs['ca_certs'])
- self.assertEqual('/foo/bar.cert', kwargs['cert_file'])
- self.assertEqual('/foo/key.pem', kwargs['key_file'])
- self.assertEqual(True, kwargs['insecure'])
- self.assertEqual(False, kwargs['ssl_compression'])
- self.assertEqual(6, len(kwargs.keys()))
-
-
-class TestVerifiedHTTPSConnection(base.TestCase):
-
- @mock.patch('socket.socket')
- @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
- def test_connect_ipv4(self, mock_delegator, mock_socket):
- connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
- connection.connect()
-
- mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
- mock_delegator.assert_called_once_with(connection.context,
- mock_socket.return_value)
- mock_delegator.return_value.connect.assert_called_once_with(
- (connection.host, 443))
-
- @mock.patch('socket.socket')
- @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
- def test_connect_ipv6(self, mock_delegator, mock_socket):
- connection = glance_http.VerifiedHTTPSConnection('[::1]')
- connection.connect()
-
- mock_socket.assert_called_once_with(socket.AF_INET6,
- socket.SOCK_STREAM)
- mock_delegator.assert_called_once_with(connection.context,
- mock_socket.return_value)
- mock_delegator.return_value.connect.assert_called_once_with(
- (connection.host, 443, 0, 0))
-
- @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
- @mock.patch('socket.getaddrinfo',
- side_effect=OSError('Gettaddrinfo failed'))
- def test_connect_with_address_lookup_failure(self, mock_getaddrinfo,
- mock_delegator):
- connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
- self.assertRaises(exceptions.RestClientException, connection.connect)
-
- mock_getaddrinfo.assert_called_once_with(
- connection.host, connection.port, 0, socket.SOCK_STREAM)
-
- @mock.patch('socket.socket')
- @mock.patch('socket.getaddrinfo',
- return_value=[(2, 1, 6, '', ('127.0.0.1', 443))])
- @mock.patch('tempest.common.glance_http.OpenSSLConnectionDelegator')
- def test_connect_with_socket_failure(self, mock_delegator,
- mock_getaddrinfo,
- mock_socket):
- mock_delegator.return_value.connect.side_effect = \
- OSError('Connect failed')
-
- connection = glance_http.VerifiedHTTPSConnection('127.0.0.1')
- self.assertRaises(exceptions.RestClientException, connection.connect)
-
- mock_getaddrinfo.assert_called_once_with(
- connection.host, connection.port, 0, socket.SOCK_STREAM)
- mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
- mock_delegator.return_value.connect.\
- assert_called_once_with((connection.host, 443))
-
-
-class TestResponseBodyIterator(base.TestCase):
-
- def test_iter_default_chunk_size_64k(self):
- resp = fake_http.fake_http_response({}, six.StringIO(
- 'X' * (glance_http.CHUNKSIZE + 1)))
- iterator = glance_http.ResponseBodyIterator(resp)
- chunks = list(iterator)
- self.assertEqual(chunks, ['X' * glance_http.CHUNKSIZE, 'X'])
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index aba2aab..f005c21 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -167,3 +167,16 @@
self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
"import tempest.exception",
'./tempest/lib/common/compute.py'))))
+
+ def test_dont_use_config_in_tempest_lib(self):
+ self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+ 'from tempest import config', './tempest/common/compute.py')))
+ self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+ 'from oslo_concurrency import lockutils',
+ './tempest/lib/auth.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'from tempest import config', './tempest/lib/auth.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'from oslo_config import cfg', './tempest/lib/decorators.py')))
+ self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+ 'import tempest.config', './tempest/lib/common/rest_client.py')))
diff --git a/test-requirements.txt b/test-requirements.txt
index 9ef956a..763f0ba 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,6 +7,6 @@
python-subunit>=0.0.18 # Apache-2.0/BSD
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
reno>=1.6.2 # Apache2
-mock>=1.2 # BSD
+mock>=2.0 # BSD
coverage>=3.6 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
diff --git a/tools/check_logs.py b/tools/check_logs.py
index e34dec3..caad85c 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -20,8 +20,8 @@
import os
import re
import six
+import six.moves.urllib.request as urlreq
import sys
-import urllib2
import yaml
@@ -67,9 +67,9 @@
logs_with_errors.append(name)
for (name, url) in url_specs:
whitelist = whitelists.get(name, [])
- req = urllib2.Request(url)
+ req = urlreq.Request(url)
req.add_header('Accept-Encoding', 'gzip')
- page = urllib2.urlopen(req)
+ page = urlreq.urlopen(req)
buf = six.StringIO(page.read())
f = gzip.GzipFile(fileobj=buf)
if scan_content(name, f.read().splitlines(), regexp, whitelist):
@@ -95,7 +95,7 @@
def collect_url_logs(url):
- page = urllib2.urlopen(url)
+ page = urlreq.urlopen(url)
content = page.read()
logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
return logs
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 49a42fe..f2da27a 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -19,8 +19,8 @@
import pprint
import re
import six
+import six.moves.urllib.request as urlreq
import sys
-import urllib2
pp = pprint.PrettyPrinter()
@@ -65,9 +65,9 @@
def hunt_for_stacktrace(url):
"""Return TRACE or ERROR lines out of logs."""
- req = urllib2.Request(url)
+ req = urlreq.Request(url)
req.add_header('Accept-Encoding', 'gzip')
- page = urllib2.urlopen(req)
+ page = urlreq.urlopen(req)
buf = six.StringIO(page.read())
f = gzip.GzipFile(fileobj=buf)
content = f.read()
@@ -105,7 +105,7 @@
def collect_logs(url):
- page = urllib2.urlopen(url)
+ page = urlreq.urlopen(url)
content = page.read()
logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
return logs