Merge "add image tests v2"
diff --git a/.testr.conf b/.testr.conf
index 9a72d29..510f4c9 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -1,5 +1,8 @@
[DEFAULT]
-test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
+test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
+ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
+ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-250} \
+ ${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=([^\.]*\.)*
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 033bc82..3aa0497 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -1,3 +1,15 @@
+[DEFAULT]
+# log_config = /opt/stack/tempest/etc/logging.conf.sample
+
+# disable logging to the stderr
+use_stderr = False
+
+# log file
+log_file = tempest.log
+
+# lock/semaphore base directory
+lock_path=/tmp
+
[identity]
# This section contains configuration options that a variety of Tempest
# test clients use when authenticating with different user/tenant
diff --git a/openstack-common.conf b/openstack-common.conf
index 8568f22..ff84404 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -4,6 +4,7 @@
module=install_venv_common
module=lockutils
module=log
+module=importlib
# The base module to hold the copy of openstack.common
base=tempest
diff --git a/requirements.txt b/requirements.txt
index 06aa9f3..cc61b01 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,3 +19,4 @@
oslo.config>=1.1.0
# Needed for whitebox testing
sqlalchemy
+eventlet>=0.12.0
diff --git a/tempest/api/compute/__init__.py b/tempest/api/compute/__init__.py
index fb96b4a..fd26081 100644
--- a/tempest/api/compute/__init__.py
+++ b/tempest/api/compute/__init__.py
@@ -15,9 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
from tempest import config
from tempest.exceptions import InvalidConfiguration
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index b66bd7e..107d635 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,6 +18,7 @@
from tempest.api.compute import base
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
+from tempest.openstack.common import lockutils
from tempest.test import attr
@@ -197,6 +198,7 @@
self.assertIn(self.host, body['hosts'])
@attr(type='gate')
+ @lockutils.synchronized('availability_zone', 'tempest-', True)
def test_aggregate_add_host_create_server_with_az(self):
# Add an host to the given aggregate and create a server.
aggregate_name = rand_name(self.aggregate_name_prefix)
@@ -205,7 +207,6 @@
self.addCleanup(self.client.delete_aggregate, aggregate['id'])
self.client.add_host(aggregate['id'], self.host)
self.addCleanup(self.client.remove_host, aggregate['id'], self.host)
-
server_name = rand_name('test_server_')
servers_client = self.servers_client
admin_servers_client = self.os_adm.servers_client
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index ce16353..dc960db 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -69,7 +69,10 @@
# lookups, so only compare binary lists.
s1 = map(lambda x: x['binary'], services)
s2 = map(lambda x: x['binary'], services_on_host)
- self.assertEqual(s1, s2)
+
+ #sort the lists before comparing, to take out dependency
+ #on order.
+ self.assertEqual(sorted(s1), sorted(s2))
@attr(type=['negative', 'gate'])
def test_get_service_by_invalid_params(self):
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 8ba074e..d40b0e0 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,9 +19,9 @@
from tempest.api import compute
from tempest import clients
-from tempest.common import log as logging
from tempest.common.utils.data_utils import parse_image_id
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
import tempest.test
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index b27d710..fb2906a 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -16,9 +16,9 @@
# under the License.
from tempest.api.compute import base
-from tempest.common import log as logging
from tempest.common.utils.data_utils import parse_image_id
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.test import attr
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 13c2f74..b35b1b2 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -110,6 +110,11 @@
self.assertEqual(202, resp.status)
self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+ def _unpause(self, server_id):
+ resp, body = self.servers_client.unpause_server(server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+
@attr(type='smoke')
def test_rescue_unrescue_instance(self):
resp, body = self.servers_client.rescue_server(
@@ -121,6 +126,18 @@
self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
@attr(type=['negative', 'gate'])
+ def test_rescue_paused_instance(self):
+ #Rescue a paused server
+ resp, body = self.servers_client.pause_server(
+ self.server_id)
+ self.addCleanup(self._unpause, self.server_id)
+ self.assertEqual(202, resp.status)
+ self.servers_client.wait_for_server_status(self.server_id, 'PAUSED')
+ self.assertRaises(exceptions.Duplicate,
+ self.servers_client.rescue_server,
+ self.server_id)
+
+ @attr(type=['negative', 'gate'])
def test_rescued_vm_reboot(self):
self.assertRaises(exceptions.Duplicate, self.servers_client.reboot,
self.rescue_id, 'HARD')
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index af58b5f..703f143 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -221,13 +221,10 @@
@attr(type=['negative', 'gate'])
def test_delete_a_server_of_another_tenant(self):
# Delete a server that belongs to another tenant
- try:
- resp, server = self.create_server(wait_until='ACTIVE')
- self.assertRaises(exceptions.NotFound,
- self.alt_client.delete_server,
- server['id'])
- finally:
- self.client.delete_server(server['id'])
+ resp, server = self.create_server(wait_until='ACTIVE')
+ self.assertRaises(exceptions.NotFound,
+ self.alt_client.delete_server,
+ server['id'])
@attr(type=['negative', 'gate'])
def test_delete_server_pass_negative_id(self):
diff --git a/tempest/api/identity/__init__.py b/tempest/api/identity/__init__.py
index 718aa15..0ed47f5 100644
--- a/tempest/api/identity/__init__.py
+++ b/tempest/api/identity/__init__.py
@@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 062d63e..086c50e 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,9 +15,9 @@
# under the License.
from tempest import clients
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
+from tempest.openstack.common import log as logging
import tempest.test
LOG = logging.getLogger(__name__)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 142ad7d..8785e31 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -38,6 +38,11 @@
tenant_network_mask_bits with the mask bits to be used to partition the
block defined by tenant-network_cidr
+
+ Finally, it is assumed that the following option is defined in the
+ [service_available] section of etc/tempest.conf
+
+ neutron as True
"""
@classmethod
diff --git a/tempest/api/network/test_quotas.py b/tempest/api/network/test_quotas.py
new file mode 100644
index 0000000..ba70f34
--- /dev/null
+++ b/tempest/api/network/test_quotas.py
@@ -0,0 +1,91 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# 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.network import base
+from tempest import clients
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class QuotasTest(base.BaseNetworkTest):
+
+ """
+ Tests the following operations in the Neutron API using the REST client for
+ Neutron:
+
+ list quotas for tenants who have non-default quota values
+ show quotas for a specified tenant
+ update quotas for a specified tenant
+ reset quotas to default values for a specified tenant
+
+ v2.0 of the API is assumed. It is also assumed that the following
+ option is defined in the [service_available] section of etc/tempest.conf:
+
+ neutron as True
+
+ Finally, it is assumed that the per-tenant quota extension API is
+ configured in /etc/neutron/neutron.conf as follows:
+
+ quota_driver = neutron.db.quota_db.DbQuotaDriver
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(QuotasTest, cls).setUpClass()
+ admin_manager = clients.AdminManager()
+ cls.admin_client = admin_manager.network_client
+ cls.identity_admin_client = admin_manager.identity_client
+
+ @attr(type='gate')
+ def test_quotas(self):
+ # Add a tenant to conduct the test
+ test_tenant = rand_name('test_tenant_')
+ test_description = rand_name('desc_')
+ _, tenant = self.identity_admin_client.create_tenant(
+ name=test_tenant,
+ description=test_description)
+ tenant_id = tenant['id']
+ self.addCleanup(self.identity_admin_client.delete_tenant, tenant_id)
+ # Change quotas for tenant
+ new_quotas = {'network': 0, 'security_group': 0}
+ resp, quota_set = self.admin_client.update_quotas(tenant_id,
+ **new_quotas)
+ self.assertEqual('200', resp['status'])
+ self.addCleanup(self.admin_client.reset_quotas, tenant_id)
+ self.assertEqual(0, quota_set['network'])
+ self.assertEqual(0, quota_set['security_group'])
+ # Confirm our tenant is listed among tenants with non default quotas
+ resp, non_default_quotas = self.admin_client.list_quotas()
+ self.assertEqual('200', resp['status'])
+ found = False
+ for qs in non_default_quotas:
+ if qs['tenant_id'] == tenant_id:
+ found = True
+ self.assertTrue(found)
+ # Confirm from APi quotas were changed as requested for tenant
+ resp, quota_set = self.admin_client.show_quotas(tenant_id)
+ self.assertEqual('200', resp['status'])
+ self.assertEqual(0, quota_set['network'])
+ self.assertEqual(0, quota_set['security_group'])
+ # Reset quotas to default and confirm
+ resp, body = self.admin_client.reset_quotas(tenant_id)
+ self.assertEqual('204', resp['status'])
+ resp, non_default_quotas = self.admin_client.list_quotas()
+ self.assertEqual('200', resp['status'])
+ for q in non_default_quotas:
+ self.assertNotEqual(tenant_id, q['tenant_id'])
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index a0b248c..d06d942 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -12,11 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
import time
from tempest import clients
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
import tempest.test
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
index 4f22158..7897b70 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -13,13 +13,13 @@
# under the License.
import json
-from tempest.common import log as logging
import testtools
from tempest.api.orchestration import base
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
import tempest.config
+from tempest.openstack.common import log as logging
from tempest.test import attr
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 15979ed..f1f1f7e 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -12,10 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
-
from tempest.api.orchestration import base
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.test import attr
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 086b981..13d0d48 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.volume import base
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.services.volume.json.admin import volume_types_client
from tempest.services.volume.json import volumes_client
from tempest.test import attr
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index a84f9e8..bb0047d 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -18,7 +18,7 @@
import time
from tempest import clients
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
import tempest.test
LOG = logging.getLogger(__name__)
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 91b44da..5861497 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -106,3 +106,4 @@
self.addCleanup(self.image_client.delete_image, image_id)
self.assertEqual(202, resp.status)
self.image_client.wait_for_image_status(image_id, 'active')
+ self.client.wait_for_volume_status(self.volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 602209a..0328b44 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.volume import base
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.test import attr
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index f696180..00e025d 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -22,7 +22,7 @@
from oslo.config import cfg
import tempest.cli.output_parser
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
import tempest.test
diff --git a/tempest/cli/output_parser.py b/tempest/cli/output_parser.py
index 3ee3098..bfd7f9e 100644
--- a/tempest/cli/output_parser.py
+++ b/tempest/cli/output_parser.py
@@ -17,11 +17,10 @@
"""Collection of utilities for parsing CLI clients output."""
-
-from tempest.common import log as logging
-
import re
+from tempest.openstack.common import log as logging
+
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_compute.py b/tempest/cli/simple_read_only/test_compute.py
index 5dadbeb..e60e238 100644
--- a/tempest/cli/simple_read_only/test_compute.py
+++ b/tempest/cli/simple_read_only/test_compute.py
@@ -21,7 +21,7 @@
import testtools
import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
CONF = cfg.CONF
diff --git a/tempest/cli/simple_read_only/test_compute_manage.py b/tempest/cli/simple_read_only/test_compute_manage.py
index 802a206..1848827 100644
--- a/tempest/cli/simple_read_only/test_compute_manage.py
+++ b/tempest/cli/simple_read_only/test_compute_manage.py
@@ -18,7 +18,7 @@
import subprocess
import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_glance.py b/tempest/cli/simple_read_only/test_glance.py
index fa77e8a..3d58451 100644
--- a/tempest/cli/simple_read_only/test_glance.py
+++ b/tempest/cli/simple_read_only/test_glance.py
@@ -19,7 +19,7 @@
import subprocess
import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_keystone.py b/tempest/cli/simple_read_only/test_keystone.py
index 3bc8b3e..4002081 100644
--- a/tempest/cli/simple_read_only/test_keystone.py
+++ b/tempest/cli/simple_read_only/test_keystone.py
@@ -19,7 +19,7 @@
import subprocess
import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 3b93696..7b8340d 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -19,10 +19,9 @@
import subprocess
from oslo.config import cfg
-import testtools
import tempest.cli
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
CONF = cfg.CONF
@@ -36,9 +35,13 @@
These tests do not presume any content, nor do they create
their own. They only verify the structure of output if present.
"""
- if (not CONF.service_available.neutron):
- msg = "Skiping all Neutron cli tests because it is not available"
- raise testtools.TestCase.skipException(msg)
+
+ @classmethod
+ def setUpClass(cls):
+ if (not CONF.service_available.neutron):
+ msg = "Skiping all Neutron cli tests because it is not available"
+ raise cls.skipException(msg)
+ super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
def test_neutron_fake_action(self):
self.assertRaises(subprocess.CalledProcessError,
@@ -53,7 +56,8 @@
self.assertTableStruct(ext, ['alias', 'name'])
def test_neutron_dhcp_agent_list_hosting_net(self):
- self.neutron('dhcp-agent-list-hosting-net', params="private")
+ self.neutron('dhcp-agent-list-hosting-net',
+ params=CONF.compute.fixed_network_name)
def test_neutron_agent_list(self):
agents = self.parser.listing(self.neutron('agent-list'))
diff --git a/tempest/clients.py b/tempest/clients.py
index 02ab711..195cb89 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -15,9 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
from tempest import config
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.services import botoclients
from tempest.services.compute.json.aggregates_client import \
AggregatesClientJSON
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 4045430..831874d 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -34,9 +34,8 @@
import OpenSSL
-from tempest.common import log as logging
from tempest import exceptions as exc
-
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
USER_AGENT = 'tempest'
diff --git a/tempest/common/log.py b/tempest/common/log.py
deleted file mode 100644
index 2159bfe..0000000
--- a/tempest/common/log.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2013 NEC Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import ConfigParser
-import inspect
-import logging
-import logging.config
-import os
-import re
-
-from oslo.config import cfg
-
-
-_DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
-_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
-
-_loggers = {}
-
-
-def getLogger(name='unknown'):
- if len(_loggers) == 0:
- loaded = _load_log_config()
- getLogger.adapter = TestsAdapter if loaded else None
-
- if name not in _loggers:
- logger = logging.getLogger(name)
- if getLogger.adapter:
- _loggers[name] = getLogger.adapter(logger, name)
- else:
- _loggers[name] = logger
-
- return _loggers[name]
-
-
-def _load_log_config():
- conf_dir = os.environ.get('TEMPEST_LOG_CONFIG_DIR', None)
- conf_file = os.environ.get('TEMPEST_LOG_CONFIG', None)
- if not conf_dir or not conf_file:
- return False
-
- log_config = os.path.join(conf_dir, conf_file)
- try:
- logging.config.fileConfig(log_config)
- except ConfigParser.Error as exc:
- raise cfg.ConfigFileParseError(log_config, str(exc))
- return True
-
-
-class TestsAdapter(logging.LoggerAdapter):
-
- def __init__(self, logger, project_name):
- self.logger = logger
- self.project = project_name
- self.regexp = re.compile(r"test_\w+\.py")
-
- def __getattr__(self, key):
- return getattr(self.logger, key)
-
- def _get_test_name(self):
- frames = inspect.stack()
- for frame in frames:
- binary_name = frame[1]
- if self.regexp.search(binary_name) and 'self' in frame[0].f_locals:
- return frame[0].f_locals.get('self').id()
- elif frame[3] == '_run_cleanups':
- #NOTE(myamazaki): method calling addCleanup
- return frame[0].f_locals.get('self').case.id()
- elif frame[3] in ['setUpClass', 'tearDownClass']:
- #NOTE(myamazaki): setUpClass or tearDownClass
- return "%s.%s.%s" % (frame[0].f_locals['cls'].__module__,
- frame[0].f_locals['cls'].__name__,
- frame[3])
- return None
-
- def process(self, msg, kwargs):
- if 'extra' not in kwargs:
- kwargs['extra'] = {}
- extra = kwargs['extra']
-
- test_name = self._get_test_name()
- if test_name:
- extra.update({'testname': test_name})
- extra['extra'] = extra.copy()
-
- return msg, kwargs
-
-
-class TestsFormatter(logging.Formatter):
- def __init__(self, fmt=None, datefmt=None):
- super(TestsFormatter, self).__init__()
- self.default_format = _DEFAULT_LOG_FORMAT
- self.testname_format =\
- "%(asctime)s %(levelname)8s [%(testname)s] %(message)s"
- self.datefmt = _DEFAULT_LOG_DATE_FORMAT
-
- def format(self, record):
- extra = record.__dict__.get('extra', None)
- if extra and 'testname' in extra:
- self._fmt = self.testname_format
- else:
- self._fmt = self.default_format
- return logging.Formatter.format(self, record)
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index e94455d..09b87b2 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -24,8 +24,8 @@
import re
import time
-from tempest.common import log as logging
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.services.compute.xml.common import xml_to_json
# redrive rate limited calls at most twice
diff --git a/tempest/config.py b/tempest/config.py
index a918d0b..19170ae 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -23,10 +23,9 @@
from oslo.config import cfg
-from tempest.common import log as logging
from tempest.common.utils.misc import singleton
+from tempest.openstack.common import log as logging
-LOG = logging.getLogger(__name__)
identity_group = cfg.OptGroup(name='identity',
title="Keystone Configuration Options")
@@ -603,7 +602,6 @@
def __init__(self):
"""Initialize a configuration from a conf directory and conf file."""
config_files = []
-
failsafe_path = "/etc/tempest/" + self.DEFAULT_CONFIG_FILE
# Environment variables override defaults...
@@ -618,8 +616,6 @@
'TEMPEST_CONFIG' in os.environ):
path = failsafe_path
- LOG.info("Using tempest config file %s" % path)
-
if not os.path.exists(path):
msg = "Config file %s not found" % path
print(RuntimeError(msg), file=sys.stderr)
@@ -627,6 +623,9 @@
config_files.append(path)
cfg.CONF([], project='tempest', default_config_files=config_files)
+ logging.setup('tempest')
+ LOG = logging.getLogger('tempest')
+ LOG.info("Using tempest config file %s" % path)
register_compute_opts(cfg.CONF)
register_identity_opts(cfg.CONF)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8b24b2e..e785299 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -29,10 +29,10 @@
from tempest.api.network import common as net_common
-from tempest.common import log as logging
from tempest.common import ssh
from tempest.common.utils.data_utils import rand_name
import tempest.manager
+from tempest.openstack.common import log as logging
import tempest.test
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 1f75e2f..39b1e10 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -15,8 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 12227f6..13b31ec 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -15,10 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
-
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 6202e91..8ee740e 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -15,8 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
LOG = logging.getLogger(__name__)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index d318dd9..0ec3a1d 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -15,8 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
LOG = logging.getLogger(__name__)
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 76fac82..6e305c1 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,10 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common import log as logging
-
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index c7721b6..4434604 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -20,10 +20,10 @@
from cinderclient import exceptions as cinder_exceptions
import testtools
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
import tempest.test
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index ea8b0e0..12e7034 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -21,9 +21,9 @@
from lxml import etree
-from tempest.common import log as logging
from tempest.common.rest_client import RestClientXML
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
from tempest.services.compute.xml.common import Text
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index dac77a2..bd48068 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -23,9 +23,9 @@
import urllib
from tempest.common import glance_http
-from tempest.common import log as logging
from tempest.common.rest_client import RestClient
from tempest import exceptions
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 446a674..f96ed91 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -23,13 +23,11 @@
Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
V1 API has been removed from the code base.
- Implements the following operations for each one of the basic Neutron
+ Implements create, delete, list and show for the basic Neutron
abstractions (networks, sub-networks and ports):
- create
- delete
- list
- show
+ It also implements list, show, update and reset for OpenStack Networking
+ quotas
"""
def __init__(self, config, username, password, auth_url, tenant_name=None):
@@ -128,3 +126,28 @@
resp, body = self.get(uri, self.headers)
body = json.loads(body)
return resp, body
+
+ def update_quotas(self, tenant_id, **kwargs):
+ put_body = {'quota': kwargs}
+ body = json.dumps(put_body)
+ uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+ resp, body = self.put(uri, body, self.headers)
+ body = json.loads(body)
+ return resp, body['quota']
+
+ def show_quotas(self, tenant_id):
+ uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+ resp, body = self.get(uri, self.headers)
+ body = json.loads(body)
+ return resp, body['quota']
+
+ def reset_quotas(self, tenant_id):
+ uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
+ resp, body = self.delete(uri, self.headers)
+ return resp, body
+
+ def list_quotas(self):
+ uri = '%s/quotas' % (self.uri_prefix)
+ resp, body = self.get(uri, self.headers)
+ body = json.loads(body)
+ return resp, body['quotas']
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 17f6cba..034b452 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -16,9 +16,9 @@
import time
import urllib
-from tempest.common import log as logging
from tempest.common.rest_client import RestClient
from tempest import exceptions
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index b35c43e..017ca95 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -17,9 +17,9 @@
from lxml import etree
-from tempest.common import log as logging
from tempest.common.rest_client import RestClientXML
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
from tempest.services.compute.xml.common import xml_to_json
diff --git a/tempest/stress/README.rst b/tempest/stress/README.rst
index 661763c..31642b0 100644
--- a/tempest/stress/README.rst
+++ b/tempest/stress/README.rst
@@ -30,7 +30,7 @@
To test installation, do the following (from the tempest/stress directory):
- ./run_stress.py etc/sample-test.json -d 30
+ ./run_stress.py etc/server-create-destroy-test.json -d 30
This sample test tries to create a few VMs and kill a few VMs.
diff --git a/tempest/stress/actions/create_destroy_server.py b/tempest/stress/actions/server_create_destroy.py
similarity index 96%
rename from tempest/stress/actions/create_destroy_server.py
rename to tempest/stress/actions/server_create_destroy.py
index 68dc148..1a1e30b 100644
--- a/tempest/stress/actions/create_destroy_server.py
+++ b/tempest/stress/actions/server_create_destroy.py
@@ -16,7 +16,7 @@
import tempest.stress.stressaction as stressaction
-class CreateDestroyServerTest(stressaction.StressAction):
+class ServerCreateDestroyTest(stressaction.StressAction):
def setUp(self, **kwargs):
self.image = self.manager.config.compute.image_ref
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
new file mode 100644
index 0000000..a7b872f
--- /dev/null
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -0,0 +1,70 @@
+# (c) 2013 Deutsche Telekom AG
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from tempest.common.utils.data_utils import rand_name
+import tempest.stress.stressaction as stressaction
+
+
+class VolumeAttachDeleteTest(stressaction.StressAction):
+
+ def setUp(self, **kwargs):
+ self.image = self.manager.config.compute.image_ref
+ self.flavor = self.manager.config.compute.flavor_ref
+
+ def run(self):
+ # Step 1: create volume
+ name = rand_name("volume")
+ self.logger.info("creating volume: %s" % name)
+ resp, volume = self.manager.volumes_client.create_volume(size=1,
+ display_name=
+ name)
+ assert(resp.status == 200)
+ self.manager.volumes_client.wait_for_volume_status(volume['id'],
+ 'available')
+ self.logger.info("created volume: %s" % volume['id'])
+
+ # Step 2: create vm instance
+ vm_name = rand_name("instance")
+ self.logger.info("creating vm: %s" % vm_name)
+ resp, server = self.manager.servers_client.create_server(
+ vm_name, self.image, self.flavor)
+ server_id = server['id']
+ assert(resp.status == 202)
+ self.manager.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+ self.logger.info("created vm %s" % server_id)
+
+ # Step 3: attach volume to vm
+ self.logger.info("attach volume (%s) to vm %s" %
+ (volume['id'], server_id))
+ resp, body = self.manager.servers_client.attach_volume(server_id,
+ volume['id'],
+ '/dev/vdc')
+ assert(resp.status == 200)
+ self.manager.volumes_client.wait_for_volume_status(volume['id'],
+ 'in-use')
+ self.logger.info("volume (%s) attached to vm %s" %
+ (volume['id'], server_id))
+
+ # Step 4: delete vm
+ self.logger.info("deleting vm: %s" % vm_name)
+ resp, _ = self.manager.servers_client.delete_server(server_id)
+ assert(resp.status == 204)
+ self.manager.servers_client.wait_for_server_termination(server_id)
+ self.logger.info("deleted vm: %s" % server_id)
+
+ # Step 5: delete volume
+ self.logger.info("deleting volume: %s" % volume['id'])
+ resp, _ = self.manager.volumes_client.delete_volume(volume['id'])
+ assert(resp.status == 202)
+ self.manager.volumes_client.wait_for_resource_deletion(volume['id'])
+ self.logger.info("deleted volume: %s" % volume['id'])
diff --git a/tempest/stress/actions/volume_create_delete.py b/tempest/stress/actions/volume_create_delete.py
index 184f870..e29d9c4 100644
--- a/tempest/stress/actions/volume_create_delete.py
+++ b/tempest/stress/actions/volume_create_delete.py
@@ -14,20 +14,20 @@
import tempest.stress.stressaction as stressaction
-class CreateDeleteTest(stressaction.StressAction):
+class VolumeCreateDeleteTest(stressaction.StressAction):
def run(self):
name = rand_name("volume")
self.logger.info("creating %s" % name)
- resp, volume = self.manager.volumes_client.\
- create_volume(size=1, display_name=name)
+ volumes_client = self.manager.volumes_client
+ resp, volume = volumes_client.create_volume(size=1,
+ display_name=name)
assert(resp.status == 200)
vol_id = volume['id']
- status = 'available'
- self.manager.volumes_client.wait_for_volume_status(vol_id, status)
+ volumes_client.wait_for_volume_status(vol_id, 'available')
self.logger.info("created %s" % volume['id'])
self.logger.info("deleting %s" % name)
- resp, _ = self.manager.volumes_client.delete_volume(vol_id)
+ resp, _ = volumes_client.delete_volume(vol_id)
assert(resp.status == 202)
- self.manager.volumes_client.wait_for_resource_deletion(vol_id)
+ volumes_client.wait_for_resource_deletion(vol_id)
self.logger.info("deleted %s" % vol_id)
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index d170eb8..d9b95e0 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -12,15 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import importlib
import logging
import multiprocessing
+import signal
import time
from tempest import clients
from tempest.common import ssh
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
+from tempest.openstack.common import importutils
from tempest.stress import cleanup
admin_manager = clients.AdminManager()
@@ -45,6 +46,7 @@
# add the handler to the root logger
logger = logging.getLogger('tempest.stress')
logger.addHandler(_console)
+processes = []
def do_ssh(command, host):
@@ -93,15 +95,29 @@
return None
-def get_action_object(path):
- (module_part, _, obj_name) = path.rpartition('.')
- return getattr(importlib.import_module(module_part), obj_name)
+def sigchld_handler(signal, frame):
+ """
+ Signal handler (only active if stop_on_error is True).
+ """
+ terminate_all_processes()
-def stress_openstack(tests, duration, max_runs=None):
+def terminate_all_processes():
+ """
+ Goes through the process list and terminates all child processes.
+ """
+ for process in processes:
+ if process['process'].is_alive():
+ try:
+ process['process'].terminate()
+ except Exception:
+ pass
+ process['process'].join()
+
+
+def stress_openstack(tests, duration, max_runs=None, stop_on_error=False):
"""
Workload driver. Executes an action function against a nova-cluster.
-
"""
logfiles = admin_manager.config.stress.target_logfiles
log_check_interval = int(admin_manager.config.stress.log_check_interval)
@@ -110,7 +126,6 @@
computes = _get_compute_nodes(controller)
for node in computes:
do_ssh("rm -f %s" % logfiles, node)
- processes = []
for test in tests:
if test.get('use_admin', False):
manager = admin_manager
@@ -131,8 +146,8 @@
password="pass",
tenant_name=tenant_name)
- test_obj = get_action_object(test['action'])
- test_run = test_obj(manager, logger, max_runs)
+ test_obj = importutils.import_class(test['action'])
+ test_run = test_obj(manager, logger, max_runs, stop_on_error)
kwargs = test.get('kwargs', {})
test_run.setUp(**dict(kwargs.iteritems()))
@@ -155,6 +170,9 @@
processes.append(process)
p.start()
+ if stop_on_error:
+ # NOTE(mkoderer): only the parent should register the handler
+ signal.signal(signal.SIGCHLD, sigchld_handler)
end_time = time.time() + duration
had_errors = False
while True:
@@ -173,6 +191,11 @@
break
time.sleep(min(remaining, log_check_interval))
+ if stop_on_error:
+ for process in processes:
+ if process['statistic']['fails'] > 0:
+ break
+
if not logfiles:
continue
errors = _error_in_logs(logfiles, computes)
@@ -180,10 +203,7 @@
had_errors = True
break
- for process in processes:
- if process['process'].is_alive():
- process['process'].terminate()
- process['process'].join()
+ terminate_all_processes()
sum_fails = 0
sum_runs = 0
diff --git a/tempest/stress/etc/sample-test.json b/tempest/stress/etc/sample-test.json
deleted file mode 100644
index 494c823..0000000
--- a/tempest/stress/etc/sample-test.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[{"action": "tempest.stress.actions.create_destroy_server.CreateDestroyServerTest",
- "threads": 8,
- "use_admin": false,
- "use_isolated_tenants": false,
- "kwargs": {}
- }
-]
diff --git a/tempest/stress/etc/server-create-destroy-test.json b/tempest/stress/etc/server-create-destroy-test.json
new file mode 100644
index 0000000..17d5e1a
--- /dev/null
+++ b/tempest/stress/etc/server-create-destroy-test.json
@@ -0,0 +1,7 @@
+[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
+ "threads": 8,
+ "use_admin": false,
+ "use_isolated_tenants": false,
+ "kwargs": {}
+ }
+]
diff --git a/tempest/stress/etc/stress-tox-job.json b/tempest/stress/etc/stress-tox-job.json
index 159794b..dffc469 100644
--- a/tempest/stress/etc/stress-tox-job.json
+++ b/tempest/stress/etc/stress-tox-job.json
@@ -1,13 +1,19 @@
-[{"action": "tempest.stress.actions.create_destroy_server.CreateDestroyServerTest",
+[{"action": "tempest.stress.actions.server_create_destroy.ServerCreateDestroyTest",
"threads": 8,
"use_admin": false,
"use_isolated_tenants": false,
"kwargs": {}
},
- {"action": "tempest.stress.actions.volume_create_delete.CreateDeleteTest",
+ {"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
"threads": 4,
"use_admin": false,
"use_isolated_tenants": false,
"kwargs": {}
+ },
+ {"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
+ "threads": 2,
+ "use_admin": false,
+ "use_isolated_tenants": false,
+ "kwargs": {}
}
]
diff --git a/tempest/stress/etc/volume-attach-delete-test.json b/tempest/stress/etc/volume-attach-delete-test.json
new file mode 100644
index 0000000..4553ff8
--- /dev/null
+++ b/tempest/stress/etc/volume-attach-delete-test.json
@@ -0,0 +1,7 @@
+[{"action": "tempest.stress.actions.volume_attach_delete.VolumeAttachDeleteTest",
+ "threads": 4,
+ "use_admin": false,
+ "use_isolated_tenants": false,
+ "kwargs": {}
+ }
+]
diff --git a/tempest/stress/etc/volume-create-delete-test.json b/tempest/stress/etc/volume-create-delete-test.json
index 6325bdc..e8a58f7 100644
--- a/tempest/stress/etc/volume-create-delete-test.json
+++ b/tempest/stress/etc/volume-create-delete-test.json
@@ -1,4 +1,4 @@
-[{"action": "tempest.stress.actions.volume_create_delete.CreateDeleteTest",
+[{"action": "tempest.stress.actions.volume_create_delete.VolumeCreateDeleteTest",
"threads": 4,
"use_admin": false,
"use_isolated_tenants": false,
diff --git a/tempest/stress/run_stress.py b/tempest/stress/run_stress.py
index 106049d..32e3ae0 100755
--- a/tempest/stress/run_stress.py
+++ b/tempest/stress/run_stress.py
@@ -22,7 +22,7 @@
def main(ns):
- #NOTE(kodererm): moved import to make "-h" possible without OpenStack
+ # NOTE(mkoderer): moved import to make "-h" possible without OpenStack
from tempest.stress import driver
result = 0
tests = json.load(open(ns.tests, 'r'))
@@ -30,12 +30,13 @@
for test in tests:
step_result = driver.stress_openstack([test],
ns.duration,
- ns.number)
- #NOTE(kodererm): we just save the last result code
+ ns.number,
+ ns.stop)
+ # NOTE(mkoderer): we just save the last result code
if (step_result != 0):
result = step_result
else:
- driver.stress_openstack(tests, ns.duration, ns.number)
+ driver.stress_openstack(tests, ns.duration, ns.number, ns.stop)
return result
@@ -44,6 +45,8 @@
help="Duration of test in secs.")
parser.add_argument('-s', '--serial', action='store_true',
help="Trigger running tests serially.")
+parser.add_argument('-S', '--stop', action='store_true',
+ default=False, help="Stop on first error.")
parser.add_argument('-n', '--number', type=int,
help="How often an action is executed for each process.")
parser.add_argument('tests', help="Name of the file with test description.")
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
index 77ddd1c..ab09adc 100644
--- a/tempest/stress/stressaction.py
+++ b/tempest/stress/stressaction.py
@@ -20,10 +20,11 @@
class StressAction(object):
- def __init__(self, manager, logger, max_runs=None):
+ def __init__(self, manager, logger, max_runs=None, stop_on_error=False):
self.manager = manager
self.logger = logger
self.max_runs = max_runs
+ self.stop_on_error = stop_on_error
def _shutdown_handler(self, signal, frame):
self.tearDown()
@@ -63,6 +64,11 @@
self.logger.exception("Failure in run")
finally:
shared_statistic['runs'] += 1
+ if self.stop_on_error and (shared_statistic['fails'] > 1):
+ self.logger.warn("Stop process due to"
+ "\"stop-on-error\" argument")
+ self.tearDown()
+ sys.exit(1)
def run(self):
"""This method is where the stress test code runs."""
diff --git a/tempest/test.py b/tempest/test.py
index 7ba63cd..6c304c3 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -18,15 +18,16 @@
import os
import time
+import fixtures
import nose.plugins.attrib
import testresources
import testtools
from tempest import clients
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
from tempest import config
from tempest import exceptions
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
@@ -104,6 +105,25 @@
if hasattr(super(BaseTestCase, cls), 'setUpClass'):
super(BaseTestCase, cls).setUpClass()
+ def setUp(cls):
+ super(BaseTestCase, cls).setUp()
+ test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
+ try:
+ test_timeout = int(test_timeout)
+ except ValueError:
+ test_timeout = 0
+ if test_timeout > 0:
+ cls.useFixture(fixtures.Timeout(test_timeout, gentle=True))
+
+ if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
+ os.environ.get('OS_STDOUT_CAPTURE') == '1'):
+ stdout = cls.useFixture(fixtures.StringStream('stdout')).stream
+ cls.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
+ if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
+ os.environ.get('OS_STDERR_CAPTURE') == '1'):
+ stderr = cls.useFixture(fixtures.StringStream('stderr')).stream
+ cls.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
+
@classmethod
def _get_identity_admin_client(cls):
"""
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 9ff628c..ba627e3 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -28,10 +28,10 @@
import keystoneclient.exceptions
import tempest.clients
-from tempest.common import log as logging
from tempest.common.utils.file_utils import have_effective_read_access
import tempest.config
from tempest import exceptions
+from tempest.openstack.common import log as logging
import tempest.test
from tempest.thirdparty.boto.utils.wait import re_search_wait
from tempest.thirdparty.boto.utils.wait import state_wait
@@ -58,8 +58,9 @@
A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
boto_logger = logging.getLogger('boto')
- level = boto_logger.level
- boto_logger.setLevel(orig_logging.CRITICAL) # suppress logging for these
+ level = boto_logger.logger.level
+ boto_logger.logger.setLevel(orig_logging.CRITICAL) # suppress logging
+ # for these
def _cred_sub_check(connection_data):
if not id_matcher.match(connection_data["aws_access_key_id"]):
@@ -99,7 +100,7 @@
except keystoneclient.exceptions.Unauthorized:
S3_CAN_CONNECT_ERROR = "AWS credentials not set," +\
" faild to get them even by keystoneclient"
- boto_logger.setLevel(level)
+ boto_logger.logger.setLevel(level)
return {'A_I_IMAGES_READY': A_I_IMAGES_READY,
'S3_CAN_CONNECT_ERROR': S3_CAN_CONNECT_ERROR,
'EC2_CAN_CONNECT_ERROR': EC2_CAN_CONNECT_ERROR}
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 1201866..df2ff6a 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -19,10 +19,10 @@
import testtools
from tempest import clients
-from tempest.common import log as logging
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.test import attr
from tempest.thirdparty.boto.test import BotoTestCase
from tempest.thirdparty.boto.utils.s3 import s3_upload_dir
@@ -88,6 +88,53 @@
image["image_id"])
@attr(type='smoke')
+ def test_run_idempotent_instances(self):
+ # EC2 run instances idempotently
+
+ def _run_instance(client_token):
+ reservation = self.ec2_client.run_instances(
+ image_id=self.images["ami"]["image_id"],
+ kernel_id=self.images["aki"]["image_id"],
+ ramdisk_id=self.images["ari"]["image_id"],
+ instance_type=self.instance_type,
+ client_token=client_token)
+ rcuk = self.addResourceCleanUp(self.destroy_reservation,
+ reservation)
+ return (reservation, rcuk)
+
+ def _terminate_reservation(reservation, rcuk):
+ for instance in reservation.instances:
+ instance.terminate()
+ self.cancelResourceCleanUp(rcuk)
+
+ reservation_1, rcuk_1 = _run_instance('token_1')
+ reservation_2, rcuk_2 = _run_instance('token_2')
+ reservation_1a, rcuk_1a = _run_instance('token_1')
+
+ self.assertIsNotNone(reservation_1)
+ self.assertIsNotNone(reservation_2)
+ self.assertIsNotNone(reservation_1a)
+
+ # same reservation for token_1
+ self.assertEqual(reservation_1.id, reservation_1a.id)
+
+ # Cancel cleanup -- since it's a duplicate, it's
+ # handled by rcuk1
+ self.cancelResourceCleanUp(rcuk_1a)
+
+ _terminate_reservation(reservation_1, rcuk_1)
+ _terminate_reservation(reservation_2, rcuk_2)
+
+ reservation_3, rcuk_3 = _run_instance('token_1')
+ self.assertIsNotNone(reservation_3)
+
+ # make sure we don't get the old reservation back
+ self.assertNotEqual(reservation_1.id, reservation_3.id)
+
+ # clean up
+ _terminate_reservation(reservation_3, rcuk_3)
+
+ @attr(type='smoke')
def test_run_stop_terminate_instance(self):
# EC2 run, stop and terminate instance
image_ami = self.ec2_client.get_image(self.images["ami"]
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index c90c586..dbb3104 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -16,7 +16,7 @@
# under the License.
from tempest import clients
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
from tempest.test import attr
from tempest.thirdparty.boto.test import BotoTestCase
diff --git a/tempest/thirdparty/boto/test_s3_ec2_images.py b/tempest/thirdparty/boto/test_s3_ec2_images.py
index 5e1e2cb..e2ca15f 100644
--- a/tempest/thirdparty/boto/test_s3_ec2_images.py
+++ b/tempest/thirdparty/boto/test_s3_ec2_images.py
@@ -22,7 +22,6 @@
from tempest.test import attr
from tempest.thirdparty.boto.test import BotoTestCase
from tempest.thirdparty.boto.utils.s3 import s3_upload_dir
-from tempest.thirdparty.boto.utils.wait import state_wait
class S3ImagesTest(BotoTestCase):
@@ -51,8 +50,6 @@
cls.bucket_name)
s3_upload_dir(bucket, cls.materials_path)
- #Note(afazekas): Without the normal status change test!
- # otherwise I would skip it too
@attr(type='smoke')
def test_register_get_deregister_ami_image(self):
# Register and deregister ami image
@@ -70,13 +67,8 @@
retrieved_image = self.images_client.get_image(image["image_id"])
self.assertTrue(retrieved_image.name == image["name"])
self.assertTrue(retrieved_image.id == image["image_id"])
- state = retrieved_image.state
- if state != "available":
- def _state():
- retr = self.images_client.get_image(image["image_id"])
- return retr.state
- state = state_wait(_state, "available")
- self.assertEqual("available", state)
+ if retrieved_image.state != "available":
+ self.assertImageStateWait(retrieved_image, "available")
self.images_client.deregister_image(image["image_id"])
self.assertNotIn(image["image_id"], str(
self.images_client.get_all_images()))
diff --git a/tempest/thirdparty/boto/utils/s3.py b/tempest/thirdparty/boto/utils/s3.py
index a309a12..f8fa61b 100644
--- a/tempest/thirdparty/boto/utils/s3.py
+++ b/tempest/thirdparty/boto/utils/s3.py
@@ -22,7 +22,7 @@
import boto
import boto.s3.key
-from tempest.common import log as logging
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/thirdparty/boto/utils/wait.py b/tempest/thirdparty/boto/utils/wait.py
index 6b3ef27..d8fca3b 100644
--- a/tempest/thirdparty/boto/utils/wait.py
+++ b/tempest/thirdparty/boto/utils/wait.py
@@ -21,8 +21,8 @@
import boto.exception
from testtools import TestCase
-from tempest.common import log as logging
import tempest.config
+from tempest.openstack.common import log as logging
LOG = logging.getLogger(__name__)
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index 471d8b4..b2632f1 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -21,10 +21,11 @@
import sys
from sqlalchemy import create_engine, MetaData
-from tempest.common import log as logging
+
from tempest.common.ssh import Client
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
+from tempest.openstack.common import log as logging
from tempest.scenario import manager
LOG = logging.getLogger(__name__)
diff --git a/tools/pretty_tox_serial.sh b/tools/pretty_tox_serial.sh
new file mode 100755
index 0000000..490d263
--- /dev/null
+++ b/tools/pretty_tox_serial.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+TESTRARGS=$@
+
+if [ ! -d .testrepository ]; then
+ testr init
+fi
+testr run --subunit $TESTRARGS | subunit-2to1 | tools/colorizer.py
+testr slowest
diff --git a/tox.ini b/tox.ini
index 93a53ac..7eae948 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,6 +26,12 @@
commands =
nosetests --logging-format '%(asctime)-15s %(message)s' --with-xunit --xunit-file=nosetests-full.xml -sv tempest/api tempest/scenario tempest/thirdparty tempest/cli
+[testenv:testr-serial]
+sitepackages = True
+setenv = VIRTUAL_ENV={envdir}
+commands =
+ sh tools/pretty_tox_serial.sh '{posargs} tempest.api tempest.scenario tempest.thirdparty tempest.cli'
+
[testenv:testr-full]
sitepackages = True
setenv = VIRTUAL_ENV={envdir}