Merge "add create_image_from_server to base class for auto cleanup"
diff --git a/HACKING.rst b/HACKING.rst
index eafa81b..1db1e26 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -1,3 +1,7 @@
+Tempest Coding Guide
+====================
+
+
Test Data/Configuration
-----------------------
- Assume nothing about existing test data
diff --git a/doc/source/HACKING.rst b/doc/source/HACKING.rst
new file mode 120000
index 0000000..a2f06b7
--- /dev/null
+++ b/doc/source/HACKING.rst
@@ -0,0 +1 @@
+../../HACKING.rst
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 1ca7344..e8fdf2c 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -3,9 +3,9 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-===================================
+=======================
Tempest Testing Project
-===================================
+=======================
Contents:
@@ -13,11 +13,11 @@
:maxdepth: 2
overview
+ HACKING
-
--------------------------------
+------------
Field Guides
--------------------------------
+------------
Tempest contains tests of many different types, the field guides
attempt to explain these in a way that makes it easy to understand
where your test contributions should go.
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 282e455..617c016 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -14,9 +14,6 @@
uri = http://127.0.0.1:5000/v2.0/
# URL for where to find the OpenStack V3 Identity API endpoint (Keystone)
uri_v3 = http://127.0.0.1:5000/v3/
-# Should typically be left as keystone unless you have a non-Keystone
-# authentication API service
-strategy = keystone
# The identity region
region = RegionOne
@@ -226,12 +223,12 @@
# Number of seconds to time out on waiting for a volume
# to be available or reach an expected status
build_timeout = 300
-# Runs Cinder multi-backend tests (requires 2 backend declared in cinder.conf)
+# Runs Cinder multi-backend tests (requires 2 backends declared in cinder.conf)
# They must have different volume_backend_name (backend1_name and backend2_name
# have to be different)
multi_backend_enabled = false
-backend1_name = LVM_iSCSI
-backend2_name = LVM_iSCSI_1
+backend1_name = BACKEND_1
+backend2_name = BACKEND_2
[object-storage]
# This section contains configuration options used when executing tests
@@ -309,7 +306,7 @@
# Instance type for tests. Needs to be big enough for a
# full OS plus the test workload
-instance_type = m1.tiny
+instance_type = m1.micro
# Name of heat-cfntools enabled image to use when launching test instances
# If not specified, tests that spawn instances will not run
diff --git a/requirements.txt b/requirements.txt
index 4873d75..19d6e0b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,7 @@
python-keystoneclient>=0.2.0
python-novaclient>=2.10.0
python-quantumclient>=2.1
+python-cinderclient>=1.0.4,<2
testresources
keyring
testrepository
diff --git a/tempest/README.rst b/tempest/README.rst
index fa29fe2..8f07a07 100644
--- a/tempest/README.rst
+++ b/tempest/README.rst
@@ -12,13 +12,13 @@
and guidelines. Below is the proposed Havana restructuring for Tempest
to make this clear.
-tempest/
- api/ - API tests
- cli/ - CLI tests
- scenario/ - complex scenario tests
- stress/ - stress tests
- thirdparty/ - 3rd party api tests
- whitebox/ - white box testing
+| tempest/
+| api/ - API tests
+| cli/ - CLI tests
+| scenario/ - complex scenario tests
+| stress/ - stress tests
+| thirdparty/ - 3rd party api tests
+| whitebox/ - white box testing
Each of these directories contains different types of tests. What
belongs in each directory, the rules and examples for good tests, are
@@ -75,8 +75,8 @@
------------
Many openstack components include 3rdparty API support. It is
-completely legitmate for Tempest to include tests of 3rdparty APIs,
-but those should be kept seperate from the normal OpenStack
+completely legitimate for Tempest to include tests of 3rdparty APIs,
+but those should be kept separate from the normal OpenStack
validation.
@@ -84,5 +84,5 @@
----------
Whitebox tests are tests which require access to the database of the
-target OpenStack machine to verify internal state after opperations
+target OpenStack machine to verify internal state after operations
are made. White box tests are allowed to use the python clients.
diff --git a/tempest/api/README.rst b/tempest/api/README.rst
index cf0aac7..617fda4 100644
--- a/tempest/api/README.rst
+++ b/tempest/api/README.rst
@@ -1,9 +1,9 @@
Tempest Guide to API tests
-========
+==========================
What are these tests?
---------
+---------------------
One of Tempest's prime function is to ensure that your OpenStack cloud
works with the OpenStack API as documented. The current largest
@@ -21,7 +21,7 @@
Why are these tests in tempest?
---------
+-------------------------------
This is one of the core missions for the Tempest project, and where it
started. Many people use this bit of function in Tempest to ensure
@@ -34,7 +34,7 @@
Scope of these tests
---------
+--------------------
API tests should always use the Tempest implementation of the
OpenStack API, as we want to ensure that bugs aren't hidden by the
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
new file mode 100644
index 0000000..00a5955
--- /dev/null
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -0,0 +1,112 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class HypervisorAdminTestJSON(base.BaseComputeAdminTest):
+
+ """
+ Tests Hypervisors API that require admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(HypervisorAdminTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.hypervisor_client
+ cls.non_adm_client = cls.hypervisor_client
+
+ def _list_hypervisors(self):
+ # List of hypervisors
+ resp, hypers = self.client.get_hypervisor_list()
+ self.assertEqual(200, resp.status)
+ return hypers
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_list(self):
+ # List of hypervisor and available hypervisors hostname
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_list_details(self):
+ # Display the details of the all hypervisor
+ resp, hypers = self.client.get_hypervisor_list_details()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(hypers) > 0)
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_show_details(self):
+ # Display the details of the specified hypervisor
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ resp, details = (self.client.
+ get_hypervisor_show_details(hypers[0]['id']))
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(details) > 0)
+ self.assertEqual(details['hypervisor_hostname'],
+ hypers[0]['hypervisor_hostname'])
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_show_servers(self):
+ # Show instances about the specific hypervisors
+ hypers = self._list_hypervisors()
+ self.assertTrue(len(hypers) > 0)
+
+ hostname = hypers[0]['hypervisor_hostname']
+ resp, hypervisors = self.client.get_hypervisor_servers(hostname)
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(hypervisors) > 0)
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_stats(self):
+ # Verify the stats of the all hypervisor
+ resp, stats = self.client.get_hypervisor_stats()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(stats) > 0)
+
+ @attr(type=['positive', 'gate'])
+ def test_get_hypervisor_uptime(self):
+ # Verify that GET shows the specified hypervisor uptime
+ hypers = self._list_hypervisors()
+
+ resp, uptime = self.client.get_hypervisor_uptime(hypers[0]['id'])
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(uptime) > 0)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_list_with_non_admin_user(self):
+ # List of hypervisor and available services with non admin user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_list)
+
+ @attr(type=['negative', 'gate'])
+ def test_get_hypervisor_list_details_with_non_admin_user(self):
+ # List of hypervisor details and available services with non admin user
+ self.assertRaises(
+ exceptions.Unauthorized,
+ self.non_adm_client.get_hypervisor_list_details)
+
+
+class HypervisorAdminTestXML(HypervisorAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 1266405..a6b4e31 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -19,6 +19,7 @@
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.test import attr
+import testtools
class QuotasAdminTestJSON(base.BaseComputeAdminTest):
@@ -70,12 +71,9 @@
self.assertEqual(200, resp.status)
self.assertEqual(expected_quota_set, quota_set)
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type='gate')
def test_update_all_quota_resources_for_tenant(self):
- self.skipTest("This test require the change in nova component "
- "https://review.openstack.org/#/c/25887/, the related "
- "bug is https://bugs.launchpad.net/nova/+bug/1160749 "
- "once the change is merged I will enable this testcase")
# Admin can update all the resource quota limits for a tenant
new_quota_set = {'force': True,
'injected_file_content_bytes': 20480,
@@ -125,12 +123,9 @@
self.assertEqual(200, resp.status, "Failed to reset quota "
"defaults")
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type='gate')
def test_create_server_when_cpu_quota_is_full(self):
- self.skipTest("This test require the change in nova component "
- "https://review.openstack.org/#/c/25887/, the related "
- "bug is https://bugs.launchpad.net/nova/+bug/1160749 "
- "once the change is merged I will enable this testcase")
# Disallow server creation when tenant's vcpu quota is full
resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
default_vcpu_quota = quota_set['cores']
@@ -144,12 +139,9 @@
cores=default_vcpu_quota)
self.assertRaises(exceptions.OverLimit, self.create_server)
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type='gate')
def test_create_server_when_memory_quota_is_full(self):
- self.skipTest("This test require the change in nova component "
- "https://review.openstack.org/#/c/25887/, the related "
- "bug is https://bugs.launchpad.net/nova/+bug/1160749 "
- "once the change is merged I will enable this testcase")
# Disallow server creation when tenant's memory quota is full
resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
default_mem_quota = quota_set['ram']
@@ -165,12 +157,9 @@
#TODO(afazekas): Add test that tried to update the quota_set as a regular user
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type=['negative', 'gate'])
def test_create_server_when_instances_quota_is_full(self):
- self.skipTest("This test require the change in nova component "
- "https://review.openstack.org/#/c/25887/, the related "
- "bug is https://bugs.launchpad.net/nova/+bug/1160749 "
- "once the change is merged I will enable this testcase")
#Once instances quota limit is reached, disallow server creation
resp, quota_set = self.client.get_quota_set(self.demo_tenant_id)
default_instances_quota = quota_set['instances']
@@ -183,6 +172,7 @@
instances=default_instances_quota)
self.assertRaises(exceptions.OverLimit, self.create_server)
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type=['negative', 'gate'])
def test_security_groups_exceed_limit(self):
# Negative test: Creation Security Groups over limit should FAIL
@@ -204,6 +194,7 @@
self.sg_client.create_security_group,
"sg-overlimit", "sg-desc")
+ @testtools.skip("Skipped until the Bug #1160749 is resolved")
@attr(type=['negative', 'gate'])
def test_security_groups_rules_exceed_limit(self):
# Negative test: Creation of Security Group Rules should FAIL
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d315fcb..abc5899 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -65,6 +65,7 @@
cls.availability_zone_client = os.availability_zone_client
cls.aggregates_client = os.aggregates_client
cls.services_client = os.services_client
+ cls.hypervisor_client = os.hypervisor_client
cls.build_interval = cls.config.compute.build_interval
cls.build_timeout = cls.config.compute.build_timeout
cls.ssh_user = cls.config.compute.ssh_user
@@ -199,15 +200,24 @@
flavor = kwargs.get('flavor', cls.flavor_ref)
image_id = kwargs.get('image_id', cls.image_ref)
- resp, server = cls.servers_client.create_server(
+ resp, body = cls.servers_client.create_server(
name, image_id, flavor, **kwargs)
- cls.servers.append(server)
+
+ # handle the case of multiple servers
+ servers = [body]
+ if 'min_count' in kwargs or 'max_count' in kwargs:
+ # Get servers created which name match with name param.
+ r, b = cls.servers_client.list_servers()
+ servers = [s for s in b['servers'] if s['name'].startswith(name)]
+
+ cls.servers.extend(servers)
if 'wait_until' in kwargs:
- cls.servers_client.wait_for_server_status(
- server['id'], kwargs['wait_until'])
+ for server in servers:
+ cls.servers_client.wait_for_server_status(
+ server['id'], kwargs['wait_until'])
- return resp, server
+ return resp, body
@classmethod
def create_image_from_server(cls, server_id, **kwargs):
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index 1ef30d4..a74bb68 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -15,8 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api import compute
from tempest.api.compute import base
from tempest import clients
@@ -100,11 +98,10 @@
self.assertRaises(exceptions.Duplicate, self.client.create_image,
server['id'], snapshot_name)
- @testtools.skip("Until Bug #1039739 is fixed")
@attr(type=['negative', 'gate'])
def test_create_image_when_server_is_rebooting(self):
# Return error when creating an image of server that is rebooting
- resp, server = self.create_server()
+ resp, server = self.create_server(wait_until='ACTIVE')
self.servers_client.reboot(server['id'], 'HARD')
snapshot_name = rand_name('test-snap-')
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index 63bb86d..9fde618 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -25,16 +25,6 @@
_interface = 'json'
_name = 'multiple-create-test'
- def _get_created_servers(self, name):
- """Get servers created which name match with name param."""
- resp, body = self.servers_client.list_servers()
- servers = body['servers']
- servers_created = []
- for server in servers:
- if server['name'].startswith(name):
- servers_created.append(server)
- return servers_created
-
def _generate_name(self):
return rand_name(self._name)
@@ -45,18 +35,6 @@
"""
kwargs['name'] = kwargs.get('name', self._generate_name())
resp, body = self.create_server(**kwargs)
- created_servers = self._get_created_servers(kwargs['name'])
- # NOTE(maurosr): append it to cls.servers list from base.BaseCompute
- # class.
- self.servers.extend(created_servers)
- # NOTE(maurosr): get a server list, check status of the ones with names
- # that match and wait for them become active. At a first look, since
- # they are building in parallel, wait inside the for doesn't seem be
- # harmful to the performance
- if wait_until is not None:
- for server in created_servers:
- self.servers_client.wait_for_server_status(server['id'],
- wait_until)
return resp, body
diff --git a/tempest/tests/compute/test_auth_token.py b/tempest/api/compute/test_auth_token.py
similarity index 97%
rename from tempest/tests/compute/test_auth_token.py
rename to tempest/api/compute/test_auth_token.py
index ca319a1..bbe92ef 100644
--- a/tempest/tests/compute/test_auth_token.py
+++ b/tempest/api/compute/test_auth_token.py
@@ -15,8 +15,8 @@
import testtools
+from tempest.api.compute import base
import tempest.config as config
-from tempest.tests.compute import base
class AuthTokenTestJSON(base.BaseComputeTest):
diff --git a/tempest/api/identity/admin/test_users.py b/tempest/api/identity/admin/test_users.py
index bcc49aa..c029300 100644
--- a/tempest/api/identity/admin/test_users.py
+++ b/tempest/api/identity/admin/test_users.py
@@ -44,7 +44,7 @@
self.assertEqual('200', resp['status'])
self.assertEqual(self.alt_user, user['name'])
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_by_unauthorized_user(self):
# Non-admin should not be authorized to create a user
self.data.setup_test_tenant()
@@ -53,7 +53,7 @@
self.alt_password, self.data.tenant['id'],
self.alt_email)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_with_empty_name(self):
# User with an empty name should not be created
self.data.setup_test_tenant()
@@ -61,15 +61,15 @@
self.alt_password, self.data.tenant['id'],
self.alt_email)
- @attr(type='gate')
- def test_create_user_with_name_length_over_64(self):
- # Length of user name filed should be restricted to 64 characters
+ @attr(type=['negative', 'gate'])
+ def test_create_user_with_name_length_over_255(self):
+ # Length of user name filed should be restricted to 255 characters
self.data.setup_test_tenant()
self.assertRaises(exceptions.BadRequest, self.client.create_user,
- 'a' * 65, self.alt_password,
+ 'a' * 256, self.alt_password,
self.data.tenant['id'], self.alt_email)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_with_duplicate_name(self):
# Duplicate user should not be created
self.data.setup_test_user()
@@ -78,7 +78,7 @@
self.data.tenant['id'], self.data.test_email)
@testtools.skip("Until Bug #999084 is fixed")
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_with_empty_password(self):
# User with an empty password should not be created
self.data.setup_test_tenant()
@@ -87,7 +87,7 @@
self.alt_email)
@testtools.skip("Until Bug #999084 is fixed")
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_with_long_password(self):
# User having password exceeding max length should not be created
self.data.setup_test_tenant()
@@ -96,21 +96,21 @@
self.alt_email)
@testtools.skip("Until Bug #999084 is fixed")
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_with_invalid_email_format(self):
# Email format should be validated while creating a user
self.data.setup_test_tenant()
self.assertRaises(exceptions.BadRequest, self.client.create_user,
self.alt_user, '', self.data.tenant['id'], '12345')
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_for_non_existant_tenant(self):
# Attempt to create a user in a non-existent tenant should fail
self.assertRaises(exceptions.NotFound, self.client.create_user,
self.alt_user, self.alt_password, '49ffgg99999',
self.alt_email)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_create_user_request_without_a_token(self):
# Request to create a user without a valid token should fail
self.data.setup_test_tenant()
@@ -136,7 +136,7 @@
resp, body = self.client.delete_user(user['id'])
self.assertEquals('204', resp['status'])
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_delete_users_by_unauthorized_user(self):
# Non admin user should not be authorized to delete a user
self.data.setup_test_user()
@@ -144,7 +144,7 @@
self.non_admin_client.delete_user,
self.data.user['id'])
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_delete_non_existant_user(self):
# Attempt to delete a non-existent user should fail
self.assertRaises(exceptions.NotFound, self.client.delete_user,
@@ -163,7 +163,7 @@
self.data.test_tenant)
self.assertEqual('200', resp['status'])
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_authentication_for_disabled_user(self):
# Disabled user's token should not get authenticated
self.data.setup_test_user()
@@ -173,7 +173,7 @@
self.data.test_password,
self.data.test_tenant)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_authentication_when_tenant_is_disabled(self):
# User's token for a disabled tenant should not be authenticated
self.data.setup_test_user()
@@ -183,7 +183,7 @@
self.data.test_password,
self.data.test_tenant)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_authentication_with_invalid_tenant(self):
# User's token for an invalid tenant should not be authenticated
self.data.setup_test_user()
@@ -192,7 +192,7 @@
self.data.test_password,
'junktenant1234')
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_authentication_with_invalid_username(self):
# Non-existent user's token should not get authenticated
self.data.setup_test_user()
@@ -200,7 +200,7 @@
'junkuser123', self.data.test_password,
self.data.test_tenant)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_authentication_with_invalid_password(self):
# User's token with invalid password should not be authenticated
self.data.setup_test_user()
@@ -234,14 +234,14 @@
Contains(self.data.test_user),
"Could not find %s" % self.data.test_user)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_get_users_by_unauthorized_user(self):
# Non admin user should not be authorized to get user list
self.data.setup_test_user()
self.assertRaises(exceptions.Unauthorized,
self.non_admin_client.get_users)
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_get_users_request_without_token(self):
# Request to get list of users without a valid token should fail
token = self.client.get_auth()
@@ -316,7 +316,7 @@
"Failed to find user %s in fetched list" %
', '.join(m_user for m_user in missing_users))
- @attr(type='gate')
+ @attr(type=['negative', 'gate'])
def test_list_users_with_invalid_tenant(self):
# Should not be able to return a list of all
# users for a nonexistant tenant
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index e0e40cb..8068284 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -73,6 +73,7 @@
cidr = netaddr.IPNetwork(cls.network_cfg.tenant_network_cidr)
mask_bits = cls.network_cfg.tenant_network_mask_bits
# Find a cidr that is not in use yet and create a subnet with it
+ failure = None
for subnet_cidr in cidr.subnet(mask_bits):
try:
resp, body = cls.client.create_subnet(network['id'],
@@ -82,6 +83,12 @@
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
if not is_overlapping_cidr:
raise
+ # save the failure in case all of the CIDRs are overlapping
+ failure = e
+
+ if not body and failure:
+ raise failure
+
subnet = body['subnet']
cls.subnets.append(subnet)
return subnet
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 2b8feef..a83e92c 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -15,10 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import hashlib
import time
-import testtools
-
from tempest.api.object_storage import base
from tempest.common.utils.data_utils import arbitrary_string
from tempest.common.utils.data_utils import rand_name
@@ -365,6 +364,30 @@
self.container_name, object_name)
self.assertEqual(data * segments, body)
+ @attr(type='gate')
+ def test_get_object_if_different(self):
+ # http://en.wikipedia.org/wiki/HTTP_ETag
+ # Make a conditional request for an object using the If-None-Match
+ # header, it should get downloaded only if the local file is different,
+ # otherwise the response code should be 304 Not Modified
+ object_name = rand_name(name='TestObject')
+ data = arbitrary_string()
+ self.object_client.create_object(self.container_name,
+ object_name, data)
+ # local copy is identical, no download
+ md5 = hashlib.md5(data).hexdigest()
+ headers = {'If-None-Match': md5}
+ url = "%s/%s" % (self.container_name, object_name)
+ resp, _ = self.object_client.get(url, headers=headers)
+ self.assertEqual(resp['status'], '304')
+
+ # local copy is different, download
+ local_data = "something different"
+ md5 = hashlib.md5(local_data).hexdigest()
+ headers = {'If-None-Match': md5}
+ resp, body = self.object_client.get(url, headers=headers)
+ self.assertEqual(resp['status'], '200')
+
class PublicObjectTest(base.BaseObjectTest):
def setUp(self):
@@ -443,71 +466,3 @@
except Exception as e:
self.fail("Failed to get public readable object with another"
" user creds raised exception is %s" % e)
-
- @testtools.skip('Until Bug #1020722 is resolved.')
- @attr(type='smoke')
- def test_write_public_object_without_using_creds(self):
- # make container public-writable, and create object anonymously, e.g.
- # without using credentials
- try:
- # update container metadata to make publicly writable
- cont_headers = {'X-Container-Write': '-*'}
- resp_meta, body = self.container_client.update_container_metadata(
- self.container_name, metadata=cont_headers, metadata_prefix='')
- self.assertEqual(resp_meta['status'], '204')
- # list container metadata
- resp, _ = self.container_client.list_container_metadata(
- self.container_name)
- self.assertEqual(resp['status'], '204')
- self.assertIn('x-container-write', resp)
- self.assertEqual(resp['x-container-write'], '-*')
-
- object_name = rand_name(name='Object')
- data = arbitrary_string(size=len(object_name),
- base_text=object_name)
- headers = {'Content-Type': 'application/json',
- 'Accept': 'application/json'}
- # create object as anonymous user
- resp, body = self.custom_object_client.create_object(
- self.container_name, object_name, data, metadata=headers)
- self.assertEqual(resp['status'], '201')
-
- except Exception as e:
- self.fail("Failed to create public writable object without using"
- " creds raised exception is %s" % e)
-
- @testtools.skip('Until Bug #1020722 is resolved.')
- @attr(type='smoke')
- def test_write_public_with_another_user_creds(self):
- # make container public-writable, and create object with another user's
- # credentials
- try:
- # update container metadata to make it publicly writable
- cont_headers = {'X-Container-Write': '-*'}
- resp_meta, body = self.container_client.update_container_metadata(
- self.container_name, metadata=cont_headers,
- metadata_prefix='')
- self.assertEqual(resp_meta['status'], '204')
- # list container metadata
- resp, _ = self.container_client.list_container_metadata(
- self.container_name)
- self.assertEqual(resp['status'], '204')
- self.assertIn('x-container-write', resp)
- self.assertEqual(resp['x-container-write'], '-*')
-
- # trying to get auth token of alternative user
- token = self.identity_client_alt.get_auth()
- headers = {'Content-Type': 'application/json',
- 'Accept': 'application/json',
- 'X-Auth-Token': token}
-
- # trying to create an object with another user's creds
- object_name = rand_name(name='Object')
- data = arbitrary_string(size=len(object_name),
- base_text=object_name)
- resp, body = self.custom_object_client.create_object(
- self.container_name, object_name, data, metadata=headers)
- self.assertEqual(resp['status'], '201')
- except Exception as e:
- self.fail("Failed to create public writable object with another"
- " user creds raised exception is %s" % e)
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
new file mode 100644
index 0000000..2349830
--- /dev/null
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -0,0 +1,152 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+import logging
+
+from tempest.api.orchestration import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+LOG = logging.getLogger(__name__)
+
+
+class InstanceCfnInitTestJSON(base.BaseOrchestrationTest):
+ _interface = 'json'
+
+ template = """
+HeatTemplateFormatVersion: '2012-12-12'
+Description: |
+ Template which uses a wait condition to confirm that a minimal
+ cfn-init and cfn-signal has worked
+Parameters:
+ KeyName:
+ Type: String
+ InstanceType:
+ Type: String
+ ImageId:
+ Type: String
+Resources:
+ CfnUser:
+ Type: AWS::IAM::User
+ SmokeKeys:
+ Type: AWS::IAM::AccessKey
+ Properties:
+ UserName: {Ref: CfnUser}
+ SmokeServer:
+ Type: AWS::EC2::Instance
+ Metadata:
+ AWS::CloudFormation::Init:
+ config:
+ files:
+ /tmp/smoke-status:
+ content: smoke test complete
+ /etc/cfn/cfn-credentials:
+ content:
+ Fn::Join:
+ - ''
+ - - AWSAccessKeyId=
+ - {Ref: SmokeKeys}
+ - '
+
+ '
+ - AWSSecretKey=
+ - Fn::GetAtt: [SmokeKeys, SecretAccessKey]
+ - '
+
+ '
+ mode: '000400'
+ owner: root
+ group: root
+ Properties:
+ ImageId: {Ref: ImageId}
+ InstanceType: {Ref: InstanceType}
+ KeyName: {Ref: KeyName}
+ UserData:
+ Fn::Base64:
+ Fn::Join:
+ - ''
+ - - |-
+ #!/bin/bash -v
+ /opt/aws/bin/cfn-init
+ - |-
+ || error_exit ''Failed to run cfn-init''
+ /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" '
+ - {Ref: WaitHandle}
+ - '''
+
+ '
+ WaitHandle:
+ Type: AWS::CloudFormation::WaitConditionHandle
+ WaitCondition:
+ Type: AWS::CloudFormation::WaitCondition
+ DependsOn: SmokeServer
+ Properties:
+ Handle: {Ref: WaitHandle}
+ Timeout: '600'
+Outputs:
+ WaitConditionStatus:
+ Description: Contents of /tmp/smoke-status on SmokeServer
+ Value:
+ Fn::GetAtt: [WaitCondition, Data]
+"""
+
+ @classmethod
+ def setUpClass(cls):
+ super(InstanceCfnInitTestJSON, cls).setUpClass()
+ if not cls.orchestration_cfg.image_ref:
+ raise cls.skipException("No image available to test")
+ cls.client = cls.orchestration_client
+
+ def setUp(self):
+ super(InstanceCfnInitTestJSON, self).setUp()
+ stack_name = rand_name('heat')
+ keypair_name = (self.orchestration_cfg.keypair_name or
+ self._create_keypair()['name'])
+
+ # create the stack
+ self.stack_identifier = self.create_stack(
+ stack_name,
+ self.template,
+ parameters={
+ 'KeyName': keypair_name,
+ 'InstanceType': self.orchestration_cfg.instance_type,
+ 'ImageId': self.orchestration_cfg.image_ref
+ })
+
+ @attr(type='gate')
+ def test_stack_wait_condition_data(self):
+
+ sid = self.stack_identifier
+
+ # wait for create to complete.
+ self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
+
+ # fetch the stack
+ resp, body = self.client.get_stack(sid)
+ self.assertEqual('CREATE_COMPLETE', body['stack_status'])
+
+ # fetch the stack
+ resp, body = self.client.get_stack(sid)
+ self.assertEqual('CREATE_COMPLETE', body['stack_status'])
+
+ # This is an assert of great significance, as it means the following
+ # has happened:
+ # - cfn-init read the provided metadata and wrote out a file
+ # - a user was created and credentials written to the instance
+ # - a cfn-signal was built which was signed with provided credentials
+ # - the wait condition was fulfilled and the stack has changed state
+ wait_status = json.loads(body['outputs'][0]['output_value'])
+ self.assertEqual('smoke test complete', wait_status['00000'])
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index e278f59..086b981 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -1,8 +1,5 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright 2013 OpenStack Foundation
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -15,12 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.volume import base
from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
-from tempest import config
from tempest.services.volume.json.admin import volume_types_client
from tempest.services.volume.json import volumes_client
from tempest.test import attr
@@ -31,66 +25,62 @@
class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
_interface = "json"
- multi_backend_enabled = config.TempestConfig().volume.multi_backend_enabled
- backend1_name = config.TempestConfig().volume.backend1_name
- backend2_name = config.TempestConfig().volume.backend2_name
- backend_names_equal = False
- if (backend1_name == backend2_name):
- backend_names_equal = True
-
@classmethod
- @testtools.skipIf(not multi_backend_enabled,
- "Cinder multi-backend feature is not available")
def setUpClass(cls):
super(VolumeMultiBackendTest, cls).setUpClass()
+ if not cls.config.volume.multi_backend_enabled:
+ raise cls.skipException("Cinder multi-backend feature disabled")
+
+ cls.backend1_name = cls.config.volume.backend1_name
+ cls.backend2_name = cls.config.volume.backend2_name
adm_user = cls.config.identity.admin_username
adm_pass = cls.config.identity.admin_password
adm_tenant = cls.config.identity.admin_tenant_name
auth_url = cls.config.identity.uri
- cls.client = volumes_client.VolumesClientJSON(cls.config,
- adm_user,
- adm_pass,
- auth_url,
- adm_tenant)
- cls.client2 = volume_types_client.VolumeTypesClientJSON(cls.config,
- adm_user,
- adm_pass,
- auth_url,
- adm_tenant)
+ cls.volume_client = volumes_client.VolumesClientJSON(cls.config,
+ adm_user,
+ adm_pass,
+ auth_url,
+ adm_tenant)
+ cls.type_client = volume_types_client.VolumeTypesClientJSON(cls.config,
+ adm_user,
+ adm_pass,
+ auth_url,
+ adm_tenant)
- ## variables initialization
- type_name1 = rand_name('type-')
- type_name2 = rand_name('type-')
- cls.volume_type_list = []
-
- vol_name1 = rand_name('Volume-')
- vol_name2 = rand_name('Volume-')
+ cls.volume_type_id_list = []
cls.volume_id_list = []
-
try:
- ## Volume types creation
+ # Volume/Type creation (uses backend1_name)
+ type1_name = rand_name('Type-')
+ vol1_name = rand_name('Volume-')
extra_specs1 = {"volume_backend_name": cls.backend1_name}
- resp, cls.body1 = cls.client2.create_volume_type(
- type_name1, extra_specs=extra_specs1)
- cls.volume_type_list.append(cls.body1)
+ resp, cls.type1 = cls.type_client.create_volume_type(
+ type1_name, extra_specs=extra_specs1)
+ cls.volume_type_id_list.append(cls.type1['id'])
- extra_specs2 = {"volume_backend_name": cls.backend2_name}
- resp, cls.body2 = cls.client2.create_volume_type(
- type_name2, extra_specs=extra_specs2)
- cls.volume_type_list.append(cls.body2)
-
- ## Volumes creation
- resp, cls.volume1 = cls.client.create_volume(
- size=1, display_name=vol_name1, volume_type=type_name1)
- cls.client.wait_for_volume_status(cls.volume1['id'], 'available')
+ resp, cls.volume1 = cls.volume_client.create_volume(
+ size=1, display_name=vol1_name, volume_type=type1_name)
cls.volume_id_list.append(cls.volume1['id'])
+ cls.volume_client.wait_for_volume_status(cls.volume1['id'],
+ 'available')
- resp, cls.volume2 = cls.client.create_volume(
- size=1, display_name=vol_name2, volume_type=type_name2)
- cls.client.wait_for_volume_status(cls.volume2['id'], 'available')
- cls.volume_id_list.append(cls.volume2['id'])
+ if cls.backend1_name != cls.backend2_name:
+ # Volume/Type creation (uses backend2_name)
+ type2_name = rand_name('Type-')
+ vol2_name = rand_name('Volume-')
+ extra_specs2 = {"volume_backend_name": cls.backend2_name}
+ resp, cls.type2 = cls.type_client.create_volume_type(
+ type2_name, extra_specs=extra_specs2)
+ cls.volume_type_id_list.append(cls.type2['id'])
+
+ resp, cls.volume2 = cls.volume_client.create_volume(
+ size=1, display_name=vol2_name, volume_type=type2_name)
+ cls.volume_id_list.append(cls.volume2['id'])
+ cls.volume_client.wait_for_volume_status(cls.volume2['id'],
+ 'available')
except Exception:
LOG.exception("setup failed")
cls.tearDownClass()
@@ -100,60 +90,43 @@
def tearDownClass(cls):
## volumes deletion
for volume_id in cls.volume_id_list:
- cls.client.delete_volume(volume_id)
- cls.client.wait_for_resource_deletion(volume_id)
+ cls.volume_client.delete_volume(volume_id)
+ cls.volume_client.wait_for_resource_deletion(volume_id)
## volume types deletion
- for volume_type in cls.volume_type_list:
- cls.client2.delete_volume_type(volume_type)
+ for volume_type_id in cls.volume_type_id_list:
+ cls.type_client.delete_volume_type(volume_type_id)
super(VolumeMultiBackendTest, cls).tearDownClass()
@attr(type='smoke')
- def test_multi_backend_enabled(self):
- # this test checks that multi backend is enabled for at least the
- # computes where the volumes created in setUp were made
+ def test_backend_name_reporting(self):
+ # this test checks if os-vol-attr:host is populated correctly after
+ # the multi backend feature has been enabled
# if multi-backend is enabled: os-vol-attr:host should be like:
# host@backend_name
- # this test fails if:
- # - multi backend is not enabled
- resp, fetched_volume = self.client.get_volume(self.volume1['id'])
+ resp, volume = self.volume_client.get_volume(self.volume1['id'])
self.assertEqual(200, resp.status)
- volume_host1 = fetched_volume['os-vol-host-attr:host']
- msg = ("Multi-backend is not available for at least host "
- "%(volume_host1)s") % locals()
- self.assertTrue(len(volume_host1.split("@")) > 1, msg)
-
- resp, fetched_volume = self.client.get_volume(self.volume2['id'])
- self.assertEqual(200, resp.status)
-
- volume_host2 = fetched_volume['os-vol-host-attr:host']
- msg = ("Multi-backend is not available for at least host "
- "%(volume_host2)s") % locals()
- self.assertTrue(len(volume_host2.split("@")) > 1, msg)
+ volume1_host = volume['os-vol-host-attr:host']
+ msg = ("multi-backend reporting incorrect values for volume %s" %
+ self.volume1['id'])
+ self.assertTrue(len(volume1_host.split("@")) > 1, msg)
@attr(type='gate')
def test_backend_name_distinction(self):
- # this test checks that the two volumes created at setUp doesn't
- # belong to the same backend (if they are in the same backend, that
- # means, volume_backend_name distinction is not working properly)
- # this test fails if:
- # - tempest.conf is not well configured
- # - the two volumes belongs to the same backend
+ # this test checks that the two volumes created at setUp don't
+ # belong to the same backend (if they are, than the
+ # volume backend distinction is not working properly)
+ if self.backend1_name == self.backend2_name:
+ raise self.skipException("backends configured with same name")
- # checks tempest.conf
- msg = ("tempest.conf is not well configured, "
- "backend1_name and backend2_name are equal")
- self.assertEqual(self.backend_names_equal, False, msg)
+ resp, volume = self.volume_client.get_volume(self.volume1['id'])
+ volume1_host = volume['os-vol-host-attr:host']
- # checks the two volumes belongs to different backend
- resp, fetched_volume = self.client.get_volume(self.volume1['id'])
- volume_host1 = fetched_volume['os-vol-host-attr:host']
+ resp, volume = self.volume_client.get_volume(self.volume2['id'])
+ volume2_host = volume['os-vol-host-attr:host']
- resp, fetched_volume = self.client.get_volume(self.volume2['id'])
- volume_host2 = fetched_volume['os-vol-host-attr:host']
-
- msg = ("volume2 was created in the same backend as volume1: "
- "%(volume_host2)s.") % locals()
- self.assertNotEqual(volume_host2, volume_host1, msg)
+ msg = ("volumes %s and %s were created in the same backend" %
+ (self.volume1['id'], self.volume2['id']))
+ self.assertNotEqual(volume1_host, volume2_host, msg)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 2839da4..fc510cb 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -128,9 +128,9 @@
resp, snapshot = cls.snapshots_client.create_snapshot(volume_id,
**kwargs)
assert 200 == resp.status
+ cls.snapshots.append(snapshot)
cls.snapshots_client.wait_for_snapshot_status(snapshot['id'],
'available')
- cls.snapshots.append(snapshot)
return snapshot
#NOTE(afazekas): these create_* and clean_* could be defined
@@ -141,8 +141,8 @@
"""Wrapper utility that returns a test volume."""
resp, volume = cls.volumes_client.create_volume(size, **kwargs)
assert 200 == resp.status
- cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
cls.volumes.append(volume)
+ cls.volumes_client.wait_for_volume_status(volume['id'], 'available')
return volume
@classmethod
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 68ab745..eda7153 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -21,7 +21,6 @@
class VolumesGetTest(base.BaseVolumeTest):
-
_interface = "json"
@classmethod
@@ -29,22 +28,17 @@
super(VolumesGetTest, cls).setUpClass()
cls.client = cls.volumes_client
- def _volume_create_get_delete(self, image_ref=None):
+ def _volume_create_get_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
try:
volume = {}
- v_name = rand_name('Volume-')
- metadata = {'Type': 'work'}
+ v_name = rand_name('Volume')
+ metadata = {'Type': 'Test'}
#Create a volume
- if not image_ref:
- resp, volume = self.client.create_volume(size=1,
- display_name=v_name,
- metadata=metadata)
- else:
- resp, volume = self.client.create_volume(size=1,
- display_name=v_name,
- metadata=metadata,
- imageRef=image_ref)
+ resp, volume = self.client.create_volume(size=1,
+ display_name=v_name,
+ metadata=metadata,
+ **kwargs)
self.assertEqual(200, resp.status)
self.assertTrue('id' in volume)
self.assertTrue('display_name' in volume)
@@ -107,11 +101,17 @@
@attr(type='smoke')
def test_volume_create_get_delete(self):
- self._volume_create_get_delete(image_ref=None)
+ self._volume_create_get_delete()
@attr(type='smoke')
- def test_volume_from_image(self):
- self._volume_create_get_delete(image_ref=self.config.compute.image_ref)
+ def test_volume_create_get_delete_from_image(self):
+ self._volume_create_get_delete(imageRef=self.config.compute.image_ref)
+
+ @attr(type='gate')
+ def test_volume_create_get_delete_as_clone(self):
+ origin = self.create_volume(size=1,
+ display_name="Volume Origin")
+ self._volume_create_get_delete(source_volid=origin['id'])
class VolumesGetTestXML(VolumesGetTest):
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 3a0c802..602209a 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.volume import base
from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
@@ -82,6 +80,5 @@
self.clear_snapshots()
-@testtools.skip("Until Bug #1177610 is resolved.")
class VolumesSnapshotTestXML(VolumesSnapshotTest):
_interface = "xml"
diff --git a/tempest/cli/README.rst b/tempest/cli/README.rst
index 4742d4a..76b05a3 100644
--- a/tempest/cli/README.rst
+++ b/tempest/cli/README.rst
@@ -1,16 +1,16 @@
Tempest Guide to CLI tests
-========
+==========================
What are these tests?
----------
+---------------------
The cli tests test the various OpenStack command line interface tools
to ensure that they minimally function. The current scope is read only
operations on a cloud that are hard to test via unit tests.
Why are these tests in tempest?
----------
+-------------------------------
These tests exist here because it is extremely difficult to build a
functional enough environment in the python-*client unit tests to
provide this kind of testing. Because we already put up a cloud in the
@@ -20,14 +20,14 @@
Scope of these tests
----------
+--------------------
This should stay limited to the scope of testing the cli. Functional
testing of the cloud should be elsewhere, this is about exercising the
cli code.
Example of a good test
----------
+----------------------
Tests should be isolated to a single command in one of the python
clients.
diff --git a/tempest/clients.py b/tempest/clients.py
index e85809f..a5c7b4d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -30,6 +30,8 @@
from tempest.services.compute.json.floating_ips_client import \
FloatingIPsClientJSON
from tempest.services.compute.json.hosts_client import HostsClientJSON
+from tempest.services.compute.json.hypervisor_client import \
+ HypervisorClientJSON
from tempest.services.compute.json.images_client import ImagesClientJSON
from tempest.services.compute.json.interfaces_client import \
InterfacesClientJSON
@@ -52,6 +54,7 @@
from tempest.services.compute.xml.flavors_client import FlavorsClientXML
from tempest.services.compute.xml.floating_ips_client import \
FloatingIPsClientXML
+from tempest.services.compute.xml.hypervisor_client import HypervisorClientXML
from tempest.services.compute.xml.images_client import ImagesClientXML
from tempest.services.compute.xml.interfaces_client import \
InterfacesClientXML
@@ -231,6 +234,11 @@
"xml": PolicyClientXML,
}
+HYPERVISOR_CLIENT = {
+ "json": HypervisorClientJSON,
+ "xml": HypervisorClientXML,
+}
+
class Manager(object):
@@ -266,22 +274,15 @@
self.auth_url = self.config.identity.uri
self.auth_url_v3 = self.config.identity.uri_v3
- if self.config.identity.strategy == 'keystone':
- client_args = (self.config, self.username, self.password,
- self.auth_url, self.tenant_name)
+ client_args = (self.config, self.username, self.password,
+ self.auth_url, self.tenant_name)
- if self.auth_url_v3:
- auth_version = 'v3'
- client_args_v3_auth = (self.config, self.username,
- self.password, self.auth_url_v3,
- self.tenant_name, auth_version)
- else:
- client_args_v3_auth = None
-
+ if self.auth_url_v3:
+ auth_version = 'v3'
+ client_args_v3_auth = (self.config, self.username,
+ self.password, self.auth_url_v3,
+ self.tenant_name, auth_version)
else:
- client_args = (self.config, self.username, self.password,
- self.auth_url)
-
client_args_v3_auth = None
try:
@@ -317,6 +318,7 @@
self.tenant_usages_client = \
TENANT_USAGES_CLIENT[interface](*client_args)
self.policy_client = POLICY_CLIENT[interface](*client_args)
+ self.hypervisor_client = HYPERVISOR_CLIENT[interface](*client_args)
if client_args_v3_auth:
self.servers_client_v3_auth = SERVERS_CLIENTS[interface](
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index baa3c03..531dfc8 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -51,7 +51,6 @@
self.base_url = None
self.region = {'compute': self.config.identity.region}
self.endpoint_url = 'publicURL'
- self.strategy = self.config.identity.strategy
self.headers = {'Content-Type': 'application/%s' % self.TYPE,
'Accept': 'application/%s' % self.TYPE}
self.build_interval = config.compute.build_interval
@@ -72,21 +71,14 @@
Sets the token and base_url used in requests based on the strategy type
"""
- if self.strategy == 'keystone':
-
- if self.auth_version == 'v3':
- auth_func = self.identity_auth_v3
- else:
- auth_func = self.keystone_auth
-
- self.token, self.base_url = (
- auth_func(self.user, self.password, self.auth_url,
- self.service, self.tenant_name))
-
+ if self.auth_version == 'v3':
+ auth_func = self.identity_auth_v3
else:
- self.token, self.base_url = self.basic_auth(self.user,
- self.password,
- self.auth_url)
+ auth_func = self.keystone_auth
+
+ self.token, self.base_url = (
+ auth_func(self.user, self.password, self.auth_url,
+ self.service, self.tenant_name))
def clear_auth(self):
"""
diff --git a/tempest/config.py b/tempest/config.py
index 89a3614..7852eba 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -40,10 +40,6 @@
help="Full URI of the OpenStack Identity API (Keystone), v2"),
cfg.StrOpt('uri_v3',
help='Full URI of the OpenStack Identity API (Keystone), v3'),
- cfg.StrOpt('strategy',
- default='keystone',
- help="Which auth method does the environment use? "
- "(basic|keystone)"),
cfg.StrOpt('region',
default='RegionOne',
help="The identity region name to use."),
@@ -325,12 +321,12 @@
help="Catalog type of the Volume Service"),
cfg.BoolOpt('multi_backend_enabled',
default=False,
- help="Runs Cinder multi-backend test (requires 2 backend)"),
+ help="Runs Cinder multi-backend test (requires 2 backends)"),
cfg.StrOpt('backend1_name',
- default='LVM_iSCSI',
+ default='BACKEND_1',
help="Name of the backend1 (must be declared in cinder.conf)"),
cfg.StrOpt('backend2_name',
- default='LVM_iSCSI_1',
+ default='BACKEND_2',
help="Name of the backend2 (must be declared in cinder.conf)"),
]
@@ -388,7 +384,7 @@
default=False,
help="Whether or not Heat is expected to be available"),
cfg.StrOpt('instance_type',
- default='m1.tiny',
+ default='m1.micro',
help="Instance type for tests. Needs to be big enough for a "
"full OS plus the test workload"),
cfg.StrOpt('image_ref',
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 353a9ac..5e941da 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -53,6 +53,17 @@
" in tempest/api/* tests"))
+def import_no_files_in_tests(physical_line, filename):
+ """Check for merges that try to land into tempest/tests
+
+ T103: tempest/tests directory is deprecated
+ """
+
+ if "tempest/tests" in filename:
+ return (0, ("T103: tempest/tests is deprecated"))
+
+
def factory(register):
register(skip_bugs)
register(import_no_clients_in_api)
+ register(import_no_files_in_tests)
diff --git a/tempest/manager.py b/tempest/manager.py
index 25e80ad..4a447f3 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -22,6 +22,7 @@
from tempest.services.compute.json import extensions_client
from tempest.services.compute.json import flavors_client
from tempest.services.compute.json import floating_ips_client
+from tempest.services.compute.json import hypervisor_client
from tempest.services.compute.json import images_client
from tempest.services.compute.json import keypairs_client
from tempest.services.compute.json import limits_client
@@ -46,6 +47,7 @@
VolumesClient = volumes_client.VolumesClientJSON
SnapshotsClient = snapshots_client.SnapshotsClientJSON
QuotasClient = quotas_client.QuotasClientJSON
+HypervisorClient = hypervisor_client.HypervisorClientJSON
LOG = logging.getLogger(__name__)
@@ -113,11 +115,8 @@
if 'tokens' not in auth_url:
auth_url = auth_url.rstrip('/') + '/tokens'
- if self.config.identity.strategy == 'keystone':
- client_args = (self.config, username, password, auth_url,
- tenant_name)
- else:
- client_args = (self.config, username, password, auth_url)
+ client_args = (self.config, username, password, auth_url,
+ tenant_name)
self.servers_client = ServersClient(*client_args)
self.flavors_client = FlavorsClient(*client_args)
@@ -132,6 +131,7 @@
self.snapshots_client = SnapshotsClient(*client_args)
self.quotas_client = QuotasClient(*client_args)
self.network_client = NetworkClient(*client_args)
+ self.hypervisor_client = HypervisorClient(*client_args)
class ComputeFuzzClientAltManager(Manager):
diff --git a/tempest/scenario/README.rst b/tempest/scenario/README.rst
index c5fa0d3..98b74e4 100644
--- a/tempest/scenario/README.rst
+++ b/tempest/scenario/README.rst
@@ -1,9 +1,9 @@
Tempest Guide to Scenario tests
-========
+===============================
What are these tests?
---------
+---------------------
Scenario tests are "through path" tests of OpenStack
function. Complicated setups where one part might depend on completion
@@ -17,13 +17,13 @@
Why are these tests in tempest?
---------
+-------------------------------
This is one of tempests core purposes, testing the integration between
projects.
Scope of these tests
---------
+--------------------
Scenario tests should always test at least 2 services in
interaction. They should use the official python client libraries for
OpenStack, as they provide a more realistic approach in how people
@@ -34,7 +34,7 @@
Example of a good test
---------
+----------------------
While we are looking for interaction of 2 or more services, be
specific in your interactions. A giant "this is my data center" smoke
test is hard to debug when it goes wrong.
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index b62e8bb..366ff43 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -25,13 +25,9 @@
import keystoneclient.v2_0.client
import netaddr
import novaclient.client
-try:
- # TODO(sdague): is there are reason this is still optional
- from quantumclient.common import exceptions as exc
- import quantumclient.v2_0.client
+from quantumclient.common import exceptions as exc
+import quantumclient.v2_0.client
-except ImportError:
- pass
from tempest.api.network import common as net_common
from tempest.common import ssh
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 109aaa5..6202e91 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -45,11 +45,6 @@
msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
raise cls.skipException(msg)
- @classmethod
- def tearDownClass(cls):
- for thing in cls.resources:
- thing.delete()
-
def test_resize_server_confirm(self):
# We create an instance for use in this test
i_name = rand_name('instance')
diff --git a/tempest/services/compute/json/hypervisor_client.py b/tempest/services/compute/json/hypervisor_client.py
new file mode 100644
index 0000000..e2e5c7b
--- /dev/null
+++ b/tempest/services/compute/json/hypervisor_client.py
@@ -0,0 +1,65 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common.rest_client import RestClient
+
+
+class HypervisorClientJSON(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(HypervisorClientJSON, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def get_hypervisor_list(self):
+ """List hypervisors information."""
+ resp, body = self.get('os-hypervisors')
+ body = json.loads(body)
+ return resp, body['hypervisors']
+
+ def get_hypervisor_list_details(self):
+ """Show detailed hypervisors information."""
+ resp, body = self.get('os-hypervisors/detail')
+ body = json.loads(body)
+ return resp, body['hypervisors']
+
+ def get_hypervisor_show_details(self, hyper_id):
+ """Display the details of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s' % hyper_id)
+ body = json.loads(body)
+ return resp, body['hypervisor']
+
+ def get_hypervisor_servers(self, hyper_name):
+ """List instances belonging to the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/servers' % hyper_name)
+ body = json.loads(body)
+ return resp, body['hypervisors']
+
+ def get_hypervisor_stats(self):
+ """Get hypervisor statistics over all compute nodes."""
+ resp, body = self.get('os-hypervisors/statistics')
+ body = json.loads(body)
+ return resp, body['hypervisor_statistics']
+
+ def get_hypervisor_uptime(self, hyper_id):
+ """Display the uptime of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id)
+ body = json.loads(body)
+ return resp, body['hypervisor']
diff --git a/tempest/services/compute/xml/hypervisor_client.py b/tempest/services/compute/xml/hypervisor_client.py
new file mode 100644
index 0000000..3c4f2b8
--- /dev/null
+++ b/tempest/services/compute/xml/hypervisor_client.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class HypervisorClientXML(RestClientXML):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(HypervisorClientXML, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ self.service = self.config.compute.catalog_type
+
+ def _parse_array(self, node):
+ return [xml_to_json(x) for x in node]
+
+ def get_hypervisor_list(self):
+ """List hypervisors information."""
+ resp, body = self.get('os-hypervisors', self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_list_details(self):
+ """Show detailed hypervisors information."""
+ resp, body = self.get('os-hypervisors/detail', self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_show_details(self, hyper_id):
+ """Display the details of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s' % hyper_id,
+ self.headers)
+ hypervisor = xml_to_json(etree.fromstring(body))
+ return resp, hypervisor
+
+ def get_hypervisor_servers(self, hyper_name):
+ """List instances belonging to the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/servers' % hyper_name,
+ self.headers)
+ hypervisors = self._parse_array(etree.fromstring(body))
+ return resp, hypervisors
+
+ def get_hypervisor_stats(self):
+ """Get hypervisor statistics over all compute nodes."""
+ resp, body = self.get('os-hypervisors/statistics', self.headers)
+ stats = xml_to_json(etree.fromstring(body))
+ return resp, stats
+
+ def get_hypervisor_uptime(self, hyper_id):
+ """Display the uptime of the specified hypervisor."""
+ resp, body = self.get('os-hypervisors/%s/uptime' % hyper_id,
+ self.headers)
+ uptime = xml_to_json(etree.fromstring(body))
+ return resp, uptime
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 08b381c..4f9d347 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -97,29 +97,18 @@
group_id : ID of the Source group
"""
group_rule = Element("security_group_rule")
- parent_group = Element("parent_group_id")
- parent_group.append(Text(content=parent_group_id))
- ip_protocol = Element("ip_protocol")
- ip_protocol.append(Text(content=ip_proto))
- from_port_num = Element("from_port")
- from_port_num.append(Text(content=str(from_port)))
- to_port_num = Element("to_port")
- to_port_num.append(Text(content=str(to_port)))
- cidr = kwargs.get('cidr')
- if cidr is not None:
- cidr_num = Element("cidr")
- cidr_num.append(Text(content=cidr))
+ elements = {k: kwargs.get(k) for k in ('cidr', 'group_id')}
+ elements['parent_group_id'] = parent_group_id
+ elements['ip_protocol'] = ip_proto
+ elements['from_port'] = from_port
+ elements['to_port'] = to_port
- group_id = kwargs.get('group_id')
- if group_id is not None:
- group_id_num = Element("group_id")
- group_id_num.append(Text(content=group_id))
-
- group_rule.append(parent_group)
- group_rule.append(ip_protocol)
- group_rule.append(from_port_num)
- group_rule.append(to_port_num)
+ for k, v in elements.items():
+ if v is not None:
+ element = Element(k)
+ element.append(Text(content=str(v)))
+ group_rule.append(element)
url = 'os-security-group-rules'
resp, body = self.post(url, str(Document(group_rule)), self.headers)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 69df472..c894612 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -34,8 +34,11 @@
def create_object(self, container, object_name, data):
"""Create storage object."""
+ headers = dict(self.headers)
+ if not data:
+ headers['content-length'] = '0'
url = "%s/%s" % (str(container), str(object_name))
- resp, body = self.put(url, data, self.headers)
+ resp, body = self.put(url, data, headers)
return resp, body
def update_object(self, container, object_name, data):
@@ -194,6 +197,8 @@
for key in metadata:
headers[str(key)] = metadata[key]
+ if not data:
+ headers['content-length'] = '0'
url = "%s/%s" % (str(container), str(object_name))
resp, body = self.put(url, data, headers=headers)
return resp, body
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index 410ed3e..b35c43e 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -48,7 +48,10 @@
resp, body = self.get(url, self.headers)
body = etree.fromstring(body)
- return resp, xml_to_json(body)
+ snapshots = []
+ for snap in body:
+ snapshots.append(xml_to_json(snap))
+ return resp, snapshots
def list_snapshots_with_detail(self, params=None):
"""List all the details of snapshot."""
@@ -60,7 +63,9 @@
resp, body = self.get(url, self.headers)
body = etree.fromstring(body)
snapshots = []
- return resp, snapshots(xml_to_json(body))
+ for snap in body:
+ snapshots.append(xml_to_json(snap))
+ return resp, snapshots
def get_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot."""
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
index 2fcdf2e..661763c 100644
--- a/tempest/stress/README.rst
+++ b/tempest/stress/README.rst
@@ -1,5 +1,5 @@
Tempest Field Guide to Stress Tests
-======================================================
+===================================
Nova is a distributed, asynchronous system that is prone to race condition
bugs. These bugs will not be easily found during
@@ -9,7 +9,7 @@
Environment
-------------
+-----------
This particular framework assumes your working Nova cluster understands Nova
API 2.0. The stress tests can read the logs from the cluster. To enable this
you have to provide the hostname to call 'nova-manage' and
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 7480833..89891d2 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -139,6 +139,7 @@
#NOTE(afazekas): doctored test case,
# with normal validation it would fail
+ @testtools.skip("Until Bug #1182679 is fixed")
@attr(type='smoke')
def test_integration_1(self):
# EC2 1. integration test (not strict)
diff --git a/tempest/whitebox/README.rst b/tempest/whitebox/README.rst
index dabf758..0e45421 100644
--- a/tempest/whitebox/README.rst
+++ b/tempest/whitebox/README.rst
@@ -1,9 +1,9 @@
Tempest Guide to Whitebox tests
-========
+===============================
What are these tests?
---------
+---------------------
When you hit the OpenStack API, this causes internal state changes in
the system. This might be database transitions, vm modifications,
@@ -20,7 +20,7 @@
Why are these tests in tempest?
---------
+-------------------------------
Especially when it comes to something like VM state changing, which is
a coordination of numerous running daemons, and a functioning VM, it's
@@ -28,7 +28,7 @@
Scope of these tests
---------
+--------------------
White box tests should be limitted to tests where black box testing
(using the OpenStack API to verify results) isn't sufficient.
@@ -40,7 +40,7 @@
Example of a good test
---------
+----------------------
Pushing VMs through a series of state transitions, and ensuring along
the way the database state transitions match what's expected.
diff --git a/tempest/whitebox/test_images_whitebox.py b/tempest/whitebox/test_images_whitebox.py
index 096d3bf..dc68336 100644
--- a/tempest/whitebox/test_images_whitebox.py
+++ b/tempest/whitebox/test_images_whitebox.py
@@ -20,6 +20,10 @@
from tempest import exceptions
from tempest.whitebox import manager
+#TODO(afazekas): The whitebox tests are using complex testclass/manager
+# hierarchy, without a real need. It is difficult to maintain.
+# They could share more code with scenario tests.
+
class ImagesWhiteboxTest(manager.ComputeWhiteboxTest, base.BaseComputeTest):
_interface = 'json'
@@ -34,7 +38,8 @@
@classmethod
def tearDownClass(cls):
- """Delete images after a test is executed."""
+ """Delete images and server after a test is executed."""
+ cls.servers_client.delete_server(cls.shared_server['id'])
for image_id in cls.image_ids:
cls.client.delete_image(image_id)
cls.image_ids.remove(image_id)
diff --git a/tempest/whitebox/test_servers_whitebox.py b/tempest/whitebox/test_servers_whitebox.py
index cd3c026..2694b95 100644
--- a/tempest/whitebox/test_servers_whitebox.py
+++ b/tempest/whitebox/test_servers_whitebox.py
@@ -50,70 +50,6 @@
except exceptions.NotFound:
continue
- def test_create_server_vcpu_quota_full(self):
- # Disallow server creation when tenant's vcpu quota is full
- quotas = self.meta.tables['quotas']
- stmt = (quotas.select().
- where(quotas.c.project_id == self.tenant_id).
- where(quotas.c.resource == 'cores'))
- result = self.connection.execute(stmt).first()
-
- # Set vcpu quota for tenant if not already set
- if not result:
- cores_hard_limit = 2
- stmt = quotas.insert().values(deleted=0,
- project_id=self.tenant_id,
- resource='cores',
- hard_limit=cores_hard_limit)
-
- self.connection.execute(stmt, autocommit=True)
- else:
- cores_hard_limit = result.hard_limit
-
- # Create servers assuming 1 VCPU per instance i.e flavor_id=1
- try:
- for count in range(cores_hard_limit + 1):
- self.create_server()
- except exceptions.OverLimit:
- pass
- else:
- self.fail("Could create servers over the VCPU quota limit")
- finally:
- stmt = quotas.delete()
- self.connection.execute(stmt, autocommit=True)
-
- def test_create_server_memory_quota_full(self):
- # Disallow server creation when tenant's memory quota is full
- quotas = self.meta.tables['quotas']
- stmt = (quotas.select().
- where(quotas.c.project_id == self.tenant_id).
- where(quotas.c.resource == 'ram'))
- result = self.connection.execute(stmt).first()
-
- # Set memory quota for tenant if not already set
- if not result:
- ram_hard_limit = 1024
- stmt = quotas.insert().values(deleted=0,
- project_id=self.tenant_id,
- resource='ram',
- hard_limit=ram_hard_limit)
-
- self.connection.execute(stmt, autocommit=True)
- else:
- ram_hard_limit = result.hard_limit
-
- try:
- # Set a hard range of 3 servers for reaching the RAM quota
- for count in range(3):
- self.create_server()
- except exceptions.OverLimit:
- pass
- else:
- self.fail("Could create servers over the RAM quota limit")
- finally:
- stmt = quotas.delete()
- self.connection.execute(stmt, autocommit=True)
-
def update_state(self, server_id, vm_state, task_state, deleted=0):
"""Update states of an instance in database for validation."""
if not task_state:
diff --git a/test-requirements.txt b/test-requirements.txt
index 27851da..3912695 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,8 +3,6 @@
pyflakes==0.7.2
flake8==2.0
hacking>=0.5.3,<0.6
-#
-#TODO(afazekas): ensure pg_config installed
psycopg2
# needed for doc build
sphinx>=1.1.2
diff --git a/tox.ini b/tox.ini
index e31e43d..caa9403 100644
--- a/tox.ini
+++ b/tox.ini
@@ -10,6 +10,18 @@
NOSE_OPENSTACK_SHOW_ELAPSED=1
NOSE_OPENSTACK_STDOUT=1
+[testenv:all]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+ NOSE_WITH_OPENSTACK=1
+ NOSE_OPENSTACK_COLOR=1
+ NOSE_OPENSTACK_RED=15
+ NOSE_OPENSTACK_YELLOW=3
+ NOSE_OPENSTACK_SHOW_ELAPSED=1
+ NOSE_OPENSTACK_STDOUT=1
+commands =
+ nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-all.xml -sv tempest
+
[testenv:full]
sitepackages = True
setenv = VIRTUAL_ENV={envdir}
@@ -47,7 +59,7 @@
commands =
python -m tools/tempest_coverage -c start --combine
nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
- python -m tools/tempest_coverage -c report --html
+ python -m tools/tempest_coverage -c report --html {posargs}
[testenv:venv]
commands = {posargs}