Merge "Fix index link in footer bar"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index db6a7bd..0af8e9b 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -229,6 +229,10 @@
multi_backend_enabled = false
backend1_name = BACKEND_1
backend2_name = BACKEND_2
+# Protocol and vendor of volume backend to target when testing volume-types.
+# You should update to reflect those exported by configured backend driver.
+storage_protocol = iSCSI
+vendor_name = Open Source
[object-storage]
# This section contains configuration options used when executing tests
@@ -332,8 +336,8 @@
# ssh username for the image file
ssh_user = cirros
-[CLI]
+[cli]
# Enable cli tests
enabled = True
# directory where python client binaries are located
-cli_dir = /usr/local/bin/
+cli_dir = /usr/local/bin
diff --git a/requirements.txt b/requirements.txt
index df9951d..606d7ae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,4 +19,3 @@
oslo.config>=1.1.0
# Needed for whitebox testing
sqlalchemy
-MySQL-python
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index f201cf7..34f96ba 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -15,7 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
+from tempest import config
from tempest import exceptions
from tempest.test import attr
@@ -51,6 +54,10 @@
class FixedIPsTestJson(FixedIPsBase):
_interface = 'json'
+ CONF = config.TempestConfig()
+
+ @testtools.skipIf(CONF.network.quantum_available, "This feature is not" +
+ "implemented by Quantum. See bug: #1194569")
@attr(type='gate')
def test_list_fixed_ip_details(self):
resp, fixed_ip = self.client.get_fixed_ip_details(self.ip)
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
new file mode 100644
index 0000000..cb47066
--- /dev/null
+++ b/tempest/api/compute/admin/test_servers.py
@@ -0,0 +1,64 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.compute import base
+from tempest.common.utils.data_utils import rand_name
+from tempest.test import attr
+
+
+class ServersAdminTestJSON(base.BaseComputeAdminTest):
+
+ """
+ Tests Servers API using admin privileges
+ """
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(ServersAdminTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.servers_client
+
+ cls.s1_name = rand_name('server')
+ resp, server = cls.create_server(name=cls.s1_name,
+ wait_until='ACTIVE')
+ cls.s2_name = rand_name('server')
+ resp, server = cls.create_server(name=cls.s2_name,
+ wait_until='ACTIVE')
+
+ @attr(type='gate')
+ def test_list_servers_by_admin(self):
+ # Listing servers by admin user returns empty list by default
+ resp, body = self.client.list_servers_with_detail()
+ servers = body['servers']
+ self.assertEqual('200', resp['status'])
+ self.assertEqual([], servers)
+
+ @attr(type='gate')
+ def test_list_servers_by_admin_with_all_tenants(self):
+ # Listing servers by admin user with all tenants parameter
+ # Here should be listed all servers
+ params = {'all_tenants': ''}
+ resp, body = self.client.list_servers_with_detail(params)
+ servers = body['servers']
+ servers_name = map(lambda x: x['name'], servers)
+
+ self.assertIn(self.s1_name, servers_name)
+ self.assertIn(self.s2_name, servers_name)
+
+
+class ServersAdminTestXML(ServersAdminTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index f960ca4..12c646d 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -15,8 +15,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest.common.utils.data_utils import rand_name
+from tempest import config
from tempest import exceptions
from tempest.test import attr
@@ -155,6 +158,8 @@
self.client.create_security_group, s_name,
s_description)
+ @testtools.skipIf(config.TempestConfig().network.quantum_available,
+ "Quantum allows duplicate names for security groups")
@attr(type=['negative', 'gate'])
def test_security_group_create_with_duplicate_name(self):
# Negative test:Security Group with duplicate name should not
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index bbe489c..5f53080 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -236,11 +236,7 @@
# Create a server with a nonexistent security group
security_groups = [{'name': 'does_not_exist'}]
- if self.config.network.quantum_available:
- expected_exception = exceptions.NotFound
- else:
- expected_exception = exceptions.BadRequest
- self.assertRaises(expected_exception,
+ self.assertRaises(exceptions.BadRequest,
self.create_server,
security_groups=security_groups)
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 3119643..9073aeb 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -16,9 +16,11 @@
# under the License.
import netaddr
+import testtools
from tempest.api.compute import base
from tempest.common.utils.data_utils import rand_name
+from tempest import config
from tempest import exceptions
from tempest.test import attr
@@ -26,6 +28,8 @@
class VirtualInterfacesTestJSON(base.BaseComputeTest):
_interface = 'json'
+ CONF = config.TempestConfig()
+
@classmethod
def setUpClass(cls):
super(VirtualInterfacesTestJSON, cls).setUpClass()
@@ -33,6 +37,8 @@
resp, server = cls.create_server(wait_until='ACTIVE')
cls.server_id = server['id']
+ @testtools.skipIf(CONF.network.quantum_available, "This feature is not " +
+ "implemented by Quantum. See bug: #1183436")
@attr(type='gate')
def test_list_virtual_interfaces(self):
# Positive test:Should be able to GET the virtual interfaces list
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 544558e..fa8190a 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -36,8 +36,10 @@
cls.os = os
cls.orchestration_client = os.orchestration_client
+ cls.servers_client = os.servers_client
cls.keypairs_client = os.keypairs_client
cls.stacks = []
+ cls.keypairs = []
@classmethod
def _get_identity_admin_client(cls):
@@ -88,11 +90,21 @@
kp_name = rand_name(namestart)
resp, body = self.keypairs_client.create_keypair(kp_name)
self.assertEqual(body['name'], kp_name)
+ self.keypairs.append(kp_name)
return body
@classmethod
+ def clear_keypairs(cls):
+ for kp_name in cls.keypairs:
+ try:
+ cls.keypairs_client.delete_keypair(kp_name)
+ except Exception:
+ pass
+
+ @classmethod
def tearDownClass(cls):
cls.clear_stacks()
+ cls.clear_keypairs()
def wait_for(self, condition):
"""Repeatedly calls condition() until a timeout."""
@@ -108,3 +120,9 @@
condition()
return
time.sleep(self.build_interval)
+
+ @staticmethod
+ def stack_output(stack, output_key):
+ """Return a stack output value for a give key."""
+ return next((o['output_value'] for o in stack['outputs']
+ if o['output_key'] == output_key), None)
diff --git a/tempest/api/orchestration/stacks/test_instance_cfn_init.py b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
index 2349830..e3b8162 100644
--- a/tempest/api/orchestration/stacks/test_instance_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_instance_cfn_init.py
@@ -14,9 +14,12 @@
import json
import 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.test import attr
@@ -25,6 +28,8 @@
class InstanceCfnInitTestJSON(base.BaseOrchestrationTest):
_interface = 'json'
+ existing_keypair = (tempest.config.TempestConfig().
+ orchestration.keypair_name is not None)
template = """
HeatTemplateFormatVersion: '2012-12-12'
@@ -101,6 +106,10 @@
Description: Contents of /tmp/smoke-status on SmokeServer
Value:
Fn::GetAtt: [WaitCondition, Data]
+ SmokeServerIp:
+ Description: IP address of server
+ Value:
+ Fn::GetAtt: [SmokeServer, PublicIp]
"""
@classmethod
@@ -113,8 +122,11 @@
def setUp(self):
super(InstanceCfnInitTestJSON, self).setUp()
stack_name = rand_name('heat')
- keypair_name = (self.orchestration_cfg.keypair_name or
- self._create_keypair()['name'])
+ if self.orchestration_cfg.keypair_name:
+ keypair_name = self.orchestration_cfg.keypair_name
+ else:
+ self.keypair = self._create_keypair()
+ keypair_name = self.keypair['name']
# create the stack
self.stack_identifier = self.create_stack(
@@ -127,6 +139,29 @@
})
@attr(type='gate')
+ @testtools.skipIf(existing_keypair, 'Server ssh tests are disabled.')
+ def test_can_log_into_created_server(self):
+
+ sid = self.stack_identifier
+ rid = 'SmokeServer'
+
+ # wait for server resource create to complete.
+ self.client.wait_for_resource_status(sid, rid, 'CREATE_COMPLETE')
+
+ resp, body = self.client.get_resource(sid, rid)
+ self.assertEqual('CREATE_COMPLETE', body['resource_status'])
+
+ # fetch the ip address from servers client, since we can't get it
+ # from the stack until stack create is complete
+ resp, server = self.servers_client.get_server(
+ body['physical_resource_id'])
+
+ # Check that the user can authenticate with the generated password
+ linux_client = RemoteClient(
+ server, 'ec2-user', pkey=self.keypair['private_key'])
+ self.assertTrue(linux_client.can_authenticate())
+
+ @attr(type='gate')
def test_stack_wait_condition_data(self):
sid = self.stack_identifier
@@ -148,5 +183,6 @@
# - 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'])
+ wait_status = json.loads(
+ self.stack_output(body, 'WaitConditionStatus'))
self.assertEqual('smoke test complete', wait_status['00000'])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 4131d3e..3c4b5d8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -55,8 +55,10 @@
volume = {}
vol_name = rand_name("volume-")
vol_type_name = rand_name("volume-type-")
- extra_specs = {"storage_protocol": "iSCSI",
- "vendor_name": "Open Source"}
+ proto = self.config.volume.storage_protocol
+ vendor = self.config.volume.vendor_name
+ extra_specs = {"storage_protocol": proto,
+ "vendor_name": vendor}
body = {}
resp, body = self.client.create_volume_type(
vol_type_name,
diff --git a/tempest/cli/README.rst b/tempest/cli/README.rst
index 76b05a3..3eae492 100644
--- a/tempest/cli/README.rst
+++ b/tempest/cli/README.rst
@@ -36,7 +36,7 @@
If a test is validating the cli for bad data, it should do it with
assertRaises.
-A reasonable example of an existing test is as follows:
+A reasonable example of an existing test is as follows::
def test_admin_list(self):
self.nova('list')
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 413990d..5bbedfd 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -16,6 +16,7 @@
# under the License.
import logging
+import os
import shlex
import subprocess
@@ -99,7 +100,7 @@
def cmd(self, cmd, action, flags='', params='', fail_ok=False,
merge_stderr=False):
"""Executes specified command for the given action."""
- cmd = ' '.join([CONF.cli.cli_dir + cmd,
+ cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
flags, action, params])
LOG.info("running: '%s'" % cmd)
cmd = shlex.split(cmd)
@@ -109,7 +110,7 @@
else:
with open('/dev/null', 'w') as devnull:
result = self.check_output(cmd, stderr=devnull)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
LOG.error("command output:\n%s" % e.output)
raise
return result
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index d19d216..cd33a22 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -304,14 +304,14 @@
if self.cert_file:
try:
self.context.use_certificate_file(self.cert_file)
- except Exception, e:
+ except Exception as e:
msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
raise exc.SSLConfigurationError(msg)
if self.key_file is None:
# We support having key and cert in same file
try:
self.context.use_privatekey_file(self.cert_file)
- except Exception, e:
+ except Exception as e:
msg = ('No key file specified and unable to load key '
'from "%s" %s' % (self.cert_file, e))
raise exc.SSLConfigurationError(msg)
@@ -319,14 +319,14 @@
if self.key_file:
try:
self.context.use_privatekey_file(self.key_file)
- except Exception, e:
+ except Exception as e:
msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
raise exc.SSLConfigurationError(msg)
if self.cacert:
try:
self.context.load_verify_locations(self.cacert)
- except Exception, e:
+ except Exception as e:
msg = 'Unable to load CA from "%s"' % (self.cacert, e)
raise exc.SSLConfigurationError(msg)
else:
diff --git a/tempest/common/log.py b/tempest/common/log.py
index 9b35723..2159bfe 100644
--- a/tempest/common/log.py
+++ b/tempest/common/log.py
@@ -55,7 +55,7 @@
log_config = os.path.join(conf_dir, conf_file)
try:
logging.config.fileConfig(log_config)
- except ConfigParser.Error, exc:
+ except ConfigParser.Error as exc:
raise cfg.ConfigFileParseError(log_config, str(exc))
return True
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 531dfc8..e94455d 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -144,8 +144,8 @@
try:
auth_data = json.loads(resp_body)['access']
token = auth_data['token']['id']
- except Exception, e:
- print "Failed to obtain token for user: %s" % e
+ except Exception as e:
+ print("Failed to obtain token for user: %s" % e)
raise
mgmt_url = None
diff --git a/tempest/config.py b/tempest/config.py
index 7196078..8795b33 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -330,6 +330,12 @@
cfg.StrOpt('backend2_name',
default='BACKEND_2',
help="Name of the backend2 (must be declared in cinder.conf)"),
+ cfg.StrOpt('storage_protocol',
+ default='iSCSI',
+ help='Backend protocol to target when creating volume types'),
+ cfg.StrOpt('vendor_name',
+ default='Open Source',
+ help='Backend vendor to target when creating volume types'),
]
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
new file mode 100644
index 0000000..7725421
--- /dev/null
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -0,0 +1,134 @@
+# 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 logging
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.common.utils.linux.remote_client import RemoteClient
+from tempest.scenario import manager
+
+
+LOG = logging.getLogger(__name__)
+
+
+class TestSnapshotPattern(manager.OfficialClientTest):
+ """
+ This test is for snapshotting an instance and booting with it.
+ The following is the scenario outline:
+ * boot a instance and create a timestamp file in it
+ * snapshot the instance
+ * boot a second instance from the snapshot
+ * check the existence of the timestamp file in the second instance
+
+ """
+
+ def _wait_for_server_status(self, server, status):
+ self.status_timeout(self.compute_client.servers,
+ server.id,
+ status)
+
+ def _wait_for_image_status(self, image_id, status):
+ self.status_timeout(self.image_client.images, image_id, status)
+
+ def _boot_image(self, image_id):
+ name = rand_name('scenario-server-')
+ client = self.compute_client
+ flavor_id = self.config.compute.flavor_ref
+ LOG.debug("name:%s, image:%s" % (name, image_id))
+ server = client.servers.create(name=name,
+ image=image_id,
+ flavor=flavor_id,
+ key_name=self.keypair.name)
+ self.addCleanup(self.compute_client.servers.delete, server)
+ self.assertEqual(name, server.name)
+ self._wait_for_server_status(server, 'ACTIVE')
+ server = client.servers.get(server) # getting network information
+ LOG.debug("server:%s" % server)
+ return server
+
+ def _add_keypair(self):
+ name = rand_name('scenario-keypair-')
+ self.keypair = self.compute_client.keypairs.create(name=name)
+ self.addCleanup(self.compute_client.keypairs.delete, self.keypair)
+ self.assertEqual(name, self.keypair.name)
+
+ def _create_security_group_rule(self):
+ sgs = self.compute_client.security_groups.list()
+ for sg in sgs:
+ if sg.name == 'default':
+ secgroup = sg
+
+ ruleset = {
+ # ssh
+ 'ip_protocol': 'tcp',
+ 'from_port': 22,
+ 'to_port': 22,
+ 'cidr': '0.0.0.0/0',
+ 'group_id': None
+ }
+ sg_rule = self.compute_client.security_group_rules.create(secgroup.id,
+ **ruleset)
+ self.addCleanup(self.compute_client.security_group_rules.delete,
+ sg_rule.id)
+
+ def _ssh_to_server(self, server):
+ username = self.config.scenario.ssh_user
+ ip = server.networks[self.config.compute.network_for_ssh][0]
+ linux_client = RemoteClient(ip,
+ username,
+ pkey=self.keypair.private_key)
+
+ return linux_client.ssh_client
+
+ def _write_timestamp(self, server):
+ ssh_client = self._ssh_to_server(server)
+ ssh_client.exec_command('date > /tmp/timestamp; sync')
+ self.timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+
+ def _create_image(self, server):
+ snapshot_name = rand_name('scenario-snapshot-')
+ create_image_client = self.compute_client.servers.create_image
+ image_id = create_image_client(server, snapshot_name)
+ self.addCleanup(self.image_client.images.delete, image_id)
+ self._wait_for_server_status(server, 'ACTIVE')
+ self._wait_for_image_status(image_id, 'active')
+ snapshot_image = self.image_client.images.get(image_id)
+ self.assertEquals(snapshot_name, snapshot_image.name)
+ return image_id
+
+ def _check_timestamp(self, server):
+ ssh_client = self._ssh_to_server(server)
+ got_timestamp = ssh_client.exec_command('cat /tmp/timestamp')
+ self.assertEqual(self.timestamp, got_timestamp)
+
+ def test_snapshot_pattern(self):
+ # prepare for booting a instance
+ self._add_keypair()
+ self._create_security_group_rule()
+
+ # boot a instance and create a timestamp file in it
+ server = self._boot_image(self.config.compute.image_ref)
+ self._write_timestamp(server)
+
+ # snapshot the instance
+ snapshot_image_id = self._create_image(server)
+
+ # boot a second instance from the snapshot
+ server_from_snapshot = self._boot_image(snapshot_image_id)
+
+ # check the existence of the timestamp file in the second instance
+ self._check_timestamp(server_from_snapshot)
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index d4822da..6906610 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -332,15 +332,6 @@
req_body, self.headers)
return resp, body
- def list_servers_for_all_tenants(self):
-
- url = self.base_url + '/servers?all_tenants=1'
- resp = self.requests.get(url)
- resp, body = self.get('servers', self.headers)
-
- body = json.loads(body)
- return resp, body['servers']
-
def migrate_server(self, server_id, **kwargs):
"""Migrates a server to a new host."""
return self.action(server_id, 'migrate', None, **kwargs)
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index f0b1c28..dac77a2 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -88,7 +88,7 @@
obj_size = obj.tell()
obj.seek(0)
return obj_size
- except IOError, e:
+ except IOError as e:
if e.errno == errno.ESPIPE:
# Illegal seek. This means the user is trying
# to pipe image data to the client, e.g.
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 81162df..6b0e7e3 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -16,6 +16,7 @@
# under the License.
import json
+import re
import time
import urllib
@@ -68,24 +69,69 @@
body = json.loads(body)
return resp, body['stack']
+ def list_resources(self, stack_identifier):
+ """Returns the details of a single resource."""
+ url = "stacks/%s/resources" % stack_identifier
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['resources']
+
+ def get_resource(self, stack_identifier, resource_name):
+ """Returns the details of a single resource."""
+ url = "stacks/%s/resources/%s" % (stack_identifier, resource_name)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body['resource']
+
def delete_stack(self, stack_identifier):
"""Deletes the specified Stack."""
return self.delete("stacks/%s" % str(stack_identifier))
- def wait_for_stack_status(self, stack_identifier, status, failure_status=(
- 'CREATE_FAILED',
- 'DELETE_FAILED',
- 'UPDATE_FAILED',
- 'ROLLBACK_FAILED')):
- """Waits for a Volume to reach a given status."""
- stack_status = None
+ def wait_for_resource_status(self, stack_identifier, resource_name,
+ status, failure_pattern='^.*_FAILED$'):
+ """Waits for a Resource to reach a given status."""
start = int(time.time())
+ fail_regexp = re.compile(failure_pattern)
- while stack_status != status:
+ while True:
+ try:
+ resp, body = self.get_resource(
+ stack_identifier, resource_name)
+ except exceptions.NotFound:
+ # ignore this, as the resource may not have
+ # been created yet
+ pass
+ else:
+ resource_name = body['logical_resource_id']
+ resource_status = body['resource_status']
+ if resource_status == status:
+ return
+ if fail_regexp.search(resource_status):
+ raise exceptions.StackBuildErrorException(
+ stack_identifier=stack_identifier,
+ resource_status=resource_status,
+ resource_status_reason=body['resource_status_reason'])
+
+ if int(time.time()) - start >= self.build_timeout:
+ message = ('Resource %s failed to reach %s status within '
+ 'the required time (%s s).' %
+ (resource_name, status, self.build_timeout))
+ raise exceptions.TimeoutException(message)
+ time.sleep(self.build_interval)
+
+ def wait_for_stack_status(self, stack_identifier, status,
+ failure_pattern='^.*_FAILED$'):
+ """Waits for a Stack to reach a given status."""
+ start = int(time.time())
+ fail_regexp = re.compile(failure_pattern)
+
+ while True:
resp, body = self.get_stack(stack_identifier)
stack_name = body['stack_name']
stack_status = body['stack_status']
- if stack_status in failure_status:
+ if stack_status == status:
+ return
+ if fail_regexp.search(stack_status):
raise exceptions.StackBuildErrorException(
stack_identifier=stack_identifier,
stack_status=stack_status,
diff --git a/tempest/whitebox/manager.py b/tempest/whitebox/manager.py
index aa58ab6..3bd057c 100644
--- a/tempest/whitebox/manager.py
+++ b/tempest/whitebox/manager.py
@@ -128,7 +128,7 @@
meta = MetaData()
meta.reflect(bind=engine)
- except Exception, e:
+ except Exception as e:
raise exceptions.SQLException(message=e)
return connection, meta
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 3129484..0ce1500 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -110,7 +110,7 @@
def usage():
- print """
+ print("""
Usage: find_stack_traces.py <logurl>
Hunts for stack traces in a devstack run. Must provide it a base log url
@@ -118,20 +118,20 @@
Returns a report listing stack traces out of the various files where
they are found.
-"""
+""")
sys.exit(0)
def print_stats(items, fname, verbose=False):
errors = len(filter(lambda x: x.level == "ERROR", items))
traces = len(filter(lambda x: x.level == "TRACE", items))
- print "%d ERRORS found in %s" % (errors, fname)
- print "%d TRACES found in %s" % (traces, fname)
+ print("%d ERRORS found in %s" % (errors, fname))
+ print("%d TRACES found in %s" % (traces, fname))
if verbose:
for item in items:
- print item
- print "\n\n"
+ print(item)
+ print("\n\n")
def main():
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index c7b0033..1ed6961 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -118,8 +118,8 @@
unskips = sorted(set(unskips))
if unskips:
- print "The following bugs have been fixed and the corresponding skips"
- print "should be removed from the test cases:"
- print
+ print("The following bugs have been fixed and the corresponding skips")
+ print("should be removed from the test cases:")
+ print()
for bug in unskips:
- print " %7s" % bug
+ print(" %7s" % bug)
diff --git a/tools/tempest_coverage.py b/tools/tempest_coverage.py
index 5b926f9..ef2eacd 100755
--- a/tools/tempest_coverage.py
+++ b/tools/tempest_coverage.py
@@ -12,7 +12,7 @@
# 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
+# under the License.
import json
import os
@@ -151,14 +151,14 @@
elif CLI.command == 'stop':
resp, body = coverage_client.stop_coverage()
if not resp['status'] == '200':
- print 'coverage stop failed with: %s:' % (resp['status'] + ': '
- + body)
+ print('coverage stop failed with: %s:' % (resp['status'] + ': '
+ + body))
exit(int(resp['status']))
path = body['path']
if CLI.output:
shutil.copytree(path, CLI.output)
else:
- print "Data files located at: %s" % path
+ print("Data files located at: %s" % path)
elif CLI.command == 'report':
if CLI.xml:
@@ -169,8 +169,8 @@
else:
resp, body = coverage_client.report_coverage(file=CLI.filename)
if not resp['status'] == '200':
- print 'coverage report failed with: %s:' % (resp['status'] + ': '
- + body)
+ print('coverage report failed with: %s:' % (resp['status'] + ': '
+ + body))
exit(int(resp['status']))
path = body['path']
if CLI.output:
@@ -182,10 +182,10 @@
else:
if not CLI.html:
path = os.path.dirname(path)
- print 'Report files located at: %s' % path
+ print('Report files located at: %s' % path)
else:
- print 'Invalid command'
+ print('Invalid command')
exit(1)