Merge "Switch to using subunit-trace from tempest-lib"
diff --git a/HACKING.rst b/HACKING.rst
index 83d67a9..fd63d64 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -260,15 +260,15 @@
example of this would be::
class TestVolumeBootPattern(manager.ScenarioTest):
- """
- This test case attempts to reproduce the following steps:
+ """
+ This test case attempts to reproduce the following steps:
- * Create in Cinder some bootable volume importing a Glance image
- * Boot an instance from the bootable volume
- * Write content to the volume
- * Delete an instance and Boot a new instance from the volume
- * Check written content in the instance
- * Create a volume snapshot while the instance is running
- * Boot an additional instance from the new snapshot based volume
- * Check written content in the instance booted from snapshot
- """
+ * Create in Cinder some bootable volume importing a Glance image
+ * Boot an instance from the bootable volume
+ * Write content to the volume
+ * Delete an instance and Boot a new instance from the volume
+ * Check written content in the instance
+ * Create a volume snapshot while the instance is running
+ * Boot an additional instance from the new snapshot based volume
+ * Check written content in the instance booted from snapshot
+ """
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 1cccacc..9a9952d 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -802,6 +802,9 @@
# attributes ipv6_ra_mode and ipv6_address_mode (boolean value)
#ipv6_subnet_attributes = false
+# If false, skip all network api tests with xml (boolean value)
+#xml_api = false
+
[object-storage]
diff --git a/setup.cfg b/setup.cfg
index d010ccc..90ea944 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = tempest
-version = 2
+version = 3
summary = OpenStack Integration Testing
description-file =
README.rst
diff --git a/tempest/api/baremetal/admin/test_nodes.py b/tempest/api/baremetal/admin/test_nodes.py
index 8ccd36b..41c12c6 100644
--- a/tempest/api/baremetal/admin/test_nodes.py
+++ b/tempest/api/baremetal/admin/test_nodes.py
@@ -130,9 +130,7 @@
@test.attr(type='smoke')
def test_set_node_boot_device(self):
- body = self.client.set_node_boot_device(self.node['uuid'], 'pxe')
- # No content
- self.assertEqual('', body)
+ self.client.set_node_boot_device(self.node['uuid'], 'pxe')
@test.attr(type='smoke')
def test_get_node_boot_device(self):
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index d365f3a..3307159 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -296,7 +296,7 @@
flavor_name = data_utils.rand_name(self.flavor_name_prefix)
new_flavor_id = data_utils.rand_int_id(start=1000)
- ram = " 1024 "
+ ram = "1024"
resp, flavor = self.client.create_flavor(flavor_name,
ram, self.vcpus,
self.disk,
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 6507ce1..2f53a0b 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -72,6 +72,8 @@
cls.quotas_client = cls.os.quotas_client
# NOTE(mriedem): os-quota-class-sets is v2 API only
cls.quota_classes_client = cls.os.quota_classes_client
+ # NOTE(mriedem): os-networks is v2 API only
+ cls.networks_client = cls.os.networks_client
cls.limits_client = cls.os.limits_client
cls.volumes_extensions_client = cls.os.volumes_extensions_client
cls.volumes_client = cls.os.volumes_client
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index bc452aa..d954c01 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -128,6 +128,9 @@
@testtools.skipUnless(CONF.service_available.neutron,
'Neutron service must be available.')
def test_verify_multiple_nics_order(self):
+ if getattr(self, '_interface',
+ None) == 'xml' and not CONF.network_feature_enabled.xml_api:
+ raise self.skipException('Neutron XML API is not enabled')
# Verify that the networks order given at the server creation is
# preserved within the server.
name_net1 = data_utils.rand_name(self.__class__.__name__)
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
new file mode 100644
index 0000000..86779b3
--- /dev/null
+++ b/tempest/api/compute/test_networks.py
@@ -0,0 +1,33 @@
+# Copyright 2014 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class NetworksTestJSON(base.BaseV2ComputeTest):
+ @classmethod
+ def resource_setup(cls):
+ if CONF.service_available.neutron:
+ raise cls.skipException('nova-network is not available.')
+ super(NetworksTestJSON, cls).resource_setup()
+ cls.client = cls.os.networks_client
+
+ @test.attr(type='gate')
+ def test_list_networks(self):
+ _, networks = self.client.list_networks()
+ self.assertNotEmpty(networks, "No networks found.")
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index f1f1eb6..676f101 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -81,8 +81,7 @@
fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
self.assertIn(endpoint['id'], fetched_endpoints_id)
# Deleting the endpoint created in this method
- _, body = self.client.delete_endpoint(endpoint['id'])
- self.assertEqual(body, '')
+ self.client.delete_endpoint(endpoint['id'])
# Checking whether endpoint is deleted successfully
resp, fetched_endpoints = self.client.list_endpoints()
fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
diff --git a/tempest/api/messaging/test_queues.py b/tempest/api/messaging/test_queues.py
index 8f9ac20..accbd17 100644
--- a/tempest/api/messaging/test_queues.py
+++ b/tempest/api/messaging/test_queues.py
@@ -20,6 +20,7 @@
from tempest.api.messaging import base
from tempest.common.utils import data_utils
+from tempest import exceptions
from tempest import test
@@ -29,15 +30,22 @@
class TestQueues(base.BaseMessagingTest):
@test.attr(type='smoke')
- def test_create_queue(self):
- # Create Queue
+ def test_create_delete_queue(self):
+ # Create & Delete Queue
queue_name = data_utils.rand_name('test-')
_, body = self.create_queue(queue_name)
self.addCleanup(self.client.delete_queue, queue_name)
-
+ # NOTE(gmann): create_queue returns response status code as 201
+ # so specifically checking the expected empty response body as
+ # this is not going to be checked in response_checker().
self.assertEqual('', body)
+ self.delete_queue(queue_name)
+ self.assertRaises(exceptions.NotFound,
+ self.client.get_queue,
+ queue_name)
+
class TestManageQueue(base.BaseMessagingTest):
_interface = 'json'
@@ -53,25 +61,16 @@
cls.client.create_queue(queue_name)
@test.attr(type='smoke')
- def test_delete_queue(self):
- # Delete Queue
- queue_name = self.queues.pop()
- _, body = self.delete_queue(queue_name)
- self.assertEqual('', body)
-
- @test.attr(type='smoke')
def test_check_queue_existence(self):
# Checking Queue Existence
for queue_name in self.queues:
- _, body = self.check_queue_exists(queue_name)
- self.assertEqual('', body)
+ self.check_queue_exists(queue_name)
@test.attr(type='smoke')
def test_check_queue_head(self):
# Checking Queue Existence by calling HEAD
for queue_name in self.queues:
- _, body = self.check_queue_exists_head(queue_name)
- self.assertEqual('', body)
+ self.check_queue_exists_head(queue_name)
@test.attr(type='smoke')
def test_list_queues(self):
@@ -111,8 +110,8 @@
req_body = dict()
req_body[data_utils.rand_name('key1')] = req_body1
# Set Queue Metadata
- _, body = self.set_queue_metadata(queue_name, req_body)
- self.assertEqual('', body)
+ self.set_queue_metadata(queue_name, req_body)
+
# Get Queue Metadata
_, body = self.get_queue_metadata(queue_name)
self.assertThat(body, matchers.Equals(req_body))
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 91e3e14..c6480a1 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -62,6 +62,9 @@
super(BaseNetworkTest, cls).resource_setup()
if not CONF.service_available.neutron:
raise cls.skipException("Neutron support is required")
+ if getattr(cls, '_interface', None) == 'xml':
+ if not CONF.network_feature_enabled.xml_api:
+ raise cls.skipException('XML API is not enabled')
os = cls.get_client_manager()
diff --git a/tempest/clients.py b/tempest/clients.py
index cf04929..19b4e11 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -447,20 +447,6 @@
self.auth_provider)
-class AltManager(Manager):
-
- """
- Manager object that uses the alt_XXX credentials for its
- managed client objects
- """
-
- def __init__(self, interface='json', service=None):
- super(AltManager, self).__init__(
- credentials=auth.get_default_credentials('alt_user'),
- interface=interface,
- service=service)
-
-
class AdminManager(Manager):
"""
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
old mode 100644
new mode 100755
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 0d3c6c6..8adfbef 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
# Copyright 2014 Dell Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index a3f185c..d21a441 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -102,9 +102,11 @@
call_inherited=ns.call_inherited)
if ns.serial:
+ # Duration is total time
+ duration = ns.duration / len(tests)
for test in tests:
step_result = driver.stress_openstack([test],
- ns.duration,
+ duration,
ns.number,
ns.stop)
# NOTE(mkoderer): we just save the last result code
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index 88e8ced..66285e4 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -65,6 +65,9 @@
else:
return len(self.hash_dict) > 1
+ def is_multi_tenant(self):
+ return self.is_multi_user()
+
def _create_hash_file(self, hash_string):
path = os.path.join(os.path.join(self.accounts_dir, hash_string))
if not os.path.isfile(path):
@@ -149,13 +152,13 @@
to preserve the current behaviour of the serial tempest run.
"""
- def is_multi_user(self):
+ def _unique_creds(self, cred_arg=None):
+ """Verify that the configured credentials are valid and distinct """
if self.use_default_creds:
- # Verify that the configured users are valid and distinct
try:
user = self.get_primary_creds()
alt_user = self.get_alt_creds()
- return user.username != alt_user.username
+ return getattr(user, cred_arg) != getattr(alt_user, cred_arg)
except exceptions.InvalidCredentials as ic:
msg = "At least one of the configured credentials is " \
"not valid: %s" % ic.message
@@ -164,6 +167,12 @@
# TODO(andreaf) Add a uniqueness check here
return len(self.hash_dict) > 1
+ def is_multi_user(self):
+ return self._unique_creds('username')
+
+ def is_multi_tenant(self):
+ return self._unique_creds('tenant_id')
+
def get_creds(self, id):
try:
# No need to sort the dict as within the same python process
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index b09c964..c5be0c0 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -48,3 +48,7 @@
@abc.abstractmethod
def is_multi_user(self):
return
+
+ @abc.abstractmethod
+ def is_multi_tenant(self):
+ return
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 2d16107..228e47c 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -354,3 +354,6 @@
def is_multi_user(self):
return True
+
+ def is_multi_tenant(self):
+ return True
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 42e4f56..c290dad 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -16,6 +16,7 @@
import collections
import json
+import logging as real_logging
import re
import time
@@ -36,8 +37,8 @@
MAX_RECURSION_DEPTH = 2
TOKEN_CHARS_RE = re.compile('^[-A-Za-z0-9+/=]*$')
-# All the successful HTTP status codes from RFC 2616
-HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206)
+# All the successful HTTP status codes from RFC 7231 & 4918
+HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206, 207)
# convert a structure into a string safely
@@ -208,8 +209,9 @@
@classmethod
def expected_success(cls, expected_code, read_code):
assert_msg = ("This function only allowed to use for HTTP status"
- "codes which explicitly defined in the RFC 2616. {0}"
- " is not a defined Success Code!").format(expected_code)
+ "codes which explicitly defined in the RFC 7231 & 4918."
+ "{0} is not a defined Success Code!"
+ ).format(expected_code)
if isinstance(expected_code, list):
for code in expected_code:
assert code in HTTP_SUCCESS, assert_msg
@@ -310,14 +312,15 @@
caller_name = misc_utils.find_test_caller()
if secs:
secs = " %.3fs" % secs
- self.LOG.info(
- 'Request (%s): %s %s %s%s' % (
- caller_name,
- resp['status'],
- method,
- req_url,
- secs),
- extra=extra)
+ if not self.LOG.isEnabledFor(real_logging.DEBUG):
+ self.LOG.info(
+ 'Request (%s): %s %s %s%s' % (
+ caller_name,
+ resp['status'],
+ method,
+ req_url,
+ secs),
+ extra=extra)
# Also look everything at DEBUG if you want to filter this
# out, don't run at debug.
diff --git a/tempest/config.py b/tempest/config.py
index d8f22d4..6e8238a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -481,7 +481,10 @@
help="Allow the execution of IPv6 subnet tests that use "
"the extended IPv6 attributes ipv6_ra_mode "
"and ipv6_address_mode"
- )
+ ),
+ cfg.BoolOpt('xml_api',
+ default=False,
+ help='If false, skip all network api tests with xml')
]
messaging_group = cfg.OptGroup(name='messaging',
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 928a8e1..990a392 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -624,14 +624,20 @@
return floating_ip
def check_floating_ip_status(self, floating_ip, status):
- """Verifies floatingip has reached given status. without waiting
+ """Verifies floatingip reaches the given status
:param floating_ip: net_resources.DeletableFloatingIp floating IP to
to check status
:param status: target status
:raises: AssertionError if status doesn't match
"""
- floating_ip.refresh()
+ def refresh():
+ floating_ip.refresh()
+ return status == floating_ip.status
+
+ tempest.test.call_until_true(refresh,
+ CONF.network.build_timeout,
+ CONF.network.build_interval)
self.assertEqual(status, floating_ip.status,
message="FloatingIP: {fp} is at status: {cst}. "
"failed to reach status: {st}"
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index b111939..91b95a8 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -47,6 +47,8 @@
def _wait_for_server_status(self, status):
for server in self.servers:
+ # Make sure nova list keeps working throughout the build process
+ self.servers_client.list_servers()
self.servers_client.wait_for_server_status(server['id'], status)
def nova_boot(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 5d75b64..ac4f004 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -179,9 +179,6 @@
"""Verifies connectivty to a VM via public network and floating IP,
and verifies floating IP has resource status is correct.
- Floating IP status is verified after connectivity test in order to
- not add extra waiting and mask racing conditions.
-
:param should_connect: bool. determines if connectivity check is
negative or positive.
:param msg: Failure message to add to Error message. Should describe
diff --git a/tempest/thirdparty/boto/test_ec2_network.py b/tempest/thirdparty/boto/test_ec2_network.py
index a75fb7b..132a5a8 100644
--- a/tempest/thirdparty/boto/test_ec2_network.py
+++ b/tempest/thirdparty/boto/test_ec2_network.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest import test
from tempest.thirdparty.boto import test as boto_test
@@ -22,21 +21,22 @@
@classmethod
def resource_setup(cls):
super(EC2NetworkTest, cls).resource_setup()
- cls.client = cls.os.ec2api_client
+ cls.ec2_client = cls.os.ec2api_client
# Note(afazekas): these tests for things duable without an instance
- @test.skip_because(bug="1080406")
def test_disassociate_not_associated_floating_ip(self):
# EC2 disassociate not associated floating ip
ec2_codes = self.ec2_error_code
- address = self.client.allocate_address()
+ address = self.ec2_client.allocate_address()
public_ip = address.public_ip
- rcuk = self.addResourceCleanUp(self.client.release_address, public_ip)
- addresses_get = self.client.get_all_addresses(addresses=(public_ip,))
+ rcuk = self.addResourceCleanUp(self.ec2_client.release_address,
+ public_ip)
+ addresses_get = self.ec2_client.get_all_addresses(
+ addresses=(public_ip,))
self.assertEqual(len(addresses_get), 1)
self.assertEqual(addresses_get[0].public_ip, public_ip)
self.assertBotoError(ec2_codes.client.InvalidAssociationID.NotFound,
address.disassociate)
- self.client.release_address(public_ip)
- self.cancelResourceCleanUp(rcuk)
+ self.ec2_client.release_address(public_ip)
self.assertAddressReleasedWait(address)
+ self.cancelResourceCleanUp(rcuk)