Merge "Add more detailed message about the volumes missing"
diff --git a/.testr.conf b/.testr.conf
index 510f4c9..05b12c4 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -1,7 +1,7 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
- OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-250} \
+ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index d744e3d..8dfff6e 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -307,6 +307,12 @@
self.LOG.info("Response Status: " + status)
headers = resp.copy()
del headers['status']
+ if headers.get('x-compute-request-id'):
+ self.LOG.info("Nova request id: %s" %
+ headers.pop('x-compute-request-id'))
+ elif headers.get('x-openstack-request-id'):
+ self.LOG.info("Glance request id %s" %
+ headers.pop('x-openstack-request-id'))
if len(headers):
self.LOG.debug('Response Headers: ' + str(headers))
if resp_body:
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index 17870a1..78025ee 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -12,16 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
from tempest.scenario import manager
from tempest.test import attr
from tempest.test import call_until_true
import time
-LOG = logging.getLogger(__name__)
-
-
class AutoScalingTest(manager.OrchestrationScenarioTest):
def setUp(self):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 95d2862..003c264 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -15,13 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
from tempest.scenario import manager
-LOG = logging.getLogger(__name__)
-
-
class TestSnapshotPattern(manager.OfficialClientTest):
"""
This test is for snapshotting an instance and booting with it.
diff --git a/tempest/scenario/test_volume_snapshot_pattern.py b/tempest/scenario/test_volume_snapshot_pattern.py
index 8fa177e..d873d30 100644
--- a/tempest/scenario/test_volume_snapshot_pattern.py
+++ b/tempest/scenario/test_volume_snapshot_pattern.py
@@ -12,13 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.openstack.common import log as logging
-
from tempest.common.utils.data_utils import rand_name
from tempest.scenario import manager
-LOG = logging.getLogger(__name__)
-
class TestVolumeSnapshotPattern(manager.OfficialClientTest):
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
new file mode 100644
index 0000000..36ef023
--- /dev/null
+++ b/tempest/stress/actions/ssh_floating.py
@@ -0,0 +1,189 @@
+# 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 socket
+import subprocess
+
+from tempest.common.utils.data_utils import rand_name
+import tempest.stress.stressaction as stressaction
+import tempest.test
+
+
+class FloatingStress(stressaction.StressAction):
+
+ # from the scenario manager
+ def ping_ip_address(self, ip_address):
+ cmd = ['ping', '-c1', '-w1', ip_address]
+
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.wait()
+ success = proc.returncode == 0
+ self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
+ "pong!" if success else "no pong :(")
+ return success
+
+ def tcp_connect_scan(self, addr, port):
+ # like tcp
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect((addr, port))
+ except socket.error as exc:
+ self.logger.info("%s(%s): %s", self.server_id, self.floating['ip'],
+ str(exc))
+ return False
+ self.logger.info("%s(%s): Connected :)", self.server_id,
+ self.floating['ip'])
+ s.close()
+ return True
+
+ def check_port_ssh(self):
+ def func():
+ return self.tcp_connect_scan(self.floating['ip'], 22)
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("Cannot connect to the ssh port.")
+
+ def check_icmp_echo(self):
+ def func():
+ return self.ping_ip_address(self.floating['ip'])
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("Cannot ping the machine.")
+
+ def _create_vm(self):
+ self.name = name = rand_name("instance")
+ servers_client = self.manager.servers_client
+ self.logger.info("creating %s" % name)
+ vm_args = self.vm_extra_args.copy()
+ vm_args['security_groups'] = [{'name': self.sec_grp}]
+ resp, server = servers_client.create_server(name, self.image,
+ self.flavor,
+ **vm_args)
+ self.server_id = server['id']
+ assert(resp.status == 202)
+ if self.wait_after_vm_create:
+ self.manager.servers_client.wait_for_server_status(self.server_id,
+ 'ACTIVE')
+
+ def _destroy_vm(self):
+ self.logger.info("deleting %s" % self.server_id)
+ resp, _ = self.manager.servers_client.delete_server(self.server_id)
+ assert(resp.status == 204) # It cannot be 204 if I had to wait..
+ self.manager.servers_client.wait_for_server_termination(self.server_id)
+ self.logger.info("deleted %s" % self.server_id)
+
+ def _create_sec_group(self):
+ sec_grp_cli = self.manager.security_groups_client
+ s_name = rand_name('sec_grp-')
+ s_description = rand_name('desc-')
+ _, _sec_grp = sec_grp_cli.create_security_group(s_name,
+ s_description)
+ self.sec_grp = _sec_grp['id']
+ create_rule = sec_grp_cli.create_security_group_rule
+ create_rule(self.sec_grp, 'tcp', 22, 22)
+ create_rule(self.sec_grp, 'icmp', -1, -1)
+
+ def _destroy_sec_grp(self):
+ sec_grp_cli = self.manager.security_groups_client
+ sec_grp_cli.delete_security_group(self.sec_grp)
+
+ def _create_floating_ip(self):
+ floating_cli = self.manager.floating_ips_client
+ _, self.floating = floating_cli.create_floating_ip(self.floating_pool)
+
+ def _destroy_floating_ip(self):
+ cli = self.manager.floating_ips_client
+ cli.delete_floating_ip(self.floating['id'])
+ cli.wait_for_resource_deletion(self.floating['id'])
+ self.logger.info("Deleted Floating IP %s", str(self.floating['ip']))
+
+ def setUp(self, **kwargs):
+ self.image = self.manager.config.compute.image_ref
+ self.flavor = self.manager.config.compute.flavor_ref
+ self.vm_extra_args = kwargs.get('vm_extra_args', {})
+ self.wait_after_vm_create = kwargs.get('wait_after_vm_create',
+ True)
+ self.new_vm = kwargs.get('new_vm', False)
+ self.new_sec_grp = kwargs.get('new_sec_group', False)
+ self.new_floating = kwargs.get('new_floating', False)
+ self.reboot = kwargs.get('reboot', False)
+ self.floating_pool = kwargs.get('floating_pool', None)
+ self.verify = kwargs.get('verify', ('check_port_ssh',
+ 'check_icmp_echo'))
+ self.check_timeout = kwargs.get('check_timeout', 120)
+ self.check_interval = kwargs.get('check_interval', 1)
+ self.wait_for_disassociate = kwargs.get('wait_for_disassociate',
+ True)
+
+ # allocate floating
+ if not self.new_floating:
+ self._create_floating_ip()
+ # add security group
+ if not self.new_sec_grp:
+ self._create_sec_group()
+ # create vm
+ if not self.new_vm:
+ self._create_vm()
+
+ def wait_disassociate(self):
+ cli = self.manager.floating_ips_client
+
+ def func():
+ _, floating = cli.get_floating_ip_details(self.floating['id'])
+ return floating['instance_id'] is None
+
+ if not tempest.test.call_until_true(func, self.check_timeout,
+ self.check_interval):
+ raise RuntimeError("IP disassociate timeout!")
+
+ def run_core(self):
+ cli = self.manager.floating_ips_client
+ cli.associate_floating_ip_to_server(self.floating['ip'],
+ self.server_id)
+ for method in self.verify:
+ m = getattr(self, method)
+ m()
+ cli.disassociate_floating_ip_from_server(self.floating['ip'],
+ self.server_id)
+ if self.wait_for_disassociate:
+ self.wait_disassociate()
+
+ def run(self):
+ if self.new_sec_grp:
+ self._create_sec_group()
+ if self.new_floating:
+ self._create_floating_ip()
+ if self.new_vm:
+ self._create_vm()
+ if self.reboot:
+ self.manager.servers_client.reboot(self.server_id, 'HARD')
+
+ self.run_core()
+
+ if self.new_vm:
+ self._destroy_vm()
+ if self.new_floating:
+ self._destroy_floating_ip()
+ if self.new_sec_grp:
+ self._destroy_sec_grp()
+
+ def tearDown(self):
+ if not self.new_vm:
+ self._destroy_vm()
+ if not self.new_floating:
+ self._destroy_floating_ip()
+ if not self.new_sec_grp:
+ self._destroy_sec_grp()
diff --git a/tempest/stress/etc/ssh_floating.json b/tempest/stress/etc/ssh_floating.json
new file mode 100644
index 0000000..0cb6776
--- /dev/null
+++ b/tempest/stress/etc/ssh_floating.json
@@ -0,0 +1,16 @@
+[{"action": "tempest.stress.actions.ssh_floating.FloatingStress",
+ "threads": 8,
+ "use_admin": false,
+ "use_isolated_tenants": false,
+ "kwargs": {"vm_extra_args": {},
+ "new_vm": true,
+ "new_sec_group": true,
+ "new_floating": true,
+ "verify": ["check_icmp_echo", "check_port_ssh"],
+ "check_timeout": 120,
+ "check_inerval": 1,
+ "wait_after_vm_create": true,
+ "wait_for_disassociate": true,
+ "reboot": false}
+}
+]
diff --git a/tempest/stress/run_stress.py b/tempest/stress/run_stress.py
index aab2afd..886d94b 100755
--- a/tempest/stress/run_stress.py
+++ b/tempest/stress/run_stress.py
@@ -17,16 +17,16 @@
# limitations under the License.
import argparse
+import inspect
import json
import sys
from testtools.testsuite import iterate_tests
from unittest import loader
-def discover_stress_tests(path="./", filter_attr=None):
+def discover_stress_tests(path="./", filter_attr=None, call_inherited=False):
"""Discovers all tempest tests and create action out of them
"""
-
tests = []
testloader = loader.TestLoader()
list = testloader.discover(path)
@@ -52,6 +52,11 @@
"class_setup_per": class_setup_per
}
}
+ if (not call_inherited and
+ getattr(test_func, "st_allow_inheritance") is not True):
+ class_structure = inspect.getmro(test_func.im_class)
+ if test_func.__name__ not in class_structure[0].__dict__:
+ continue
tests.append(action)
return tests
@@ -63,7 +68,8 @@
if not ns.all:
tests = json.load(open(ns.tests, 'r'))
else:
- tests = discover_stress_tests(filter_attr=ns.type)
+ tests = discover_stress_tests(filter_attr=ns.type,
+ call_inherited=ns.call_inherited)
if ns.serial:
for test in tests:
@@ -79,22 +85,25 @@
return result
-parser = argparse.ArgumentParser(description='Run stress tests. ')
+parser = argparse.ArgumentParser(description='Run stress tests')
parser.add_argument('-d', '--duration', default=300, type=int,
- help="Duration of test in secs.")
+ help="Duration of test in secs")
parser.add_argument('-s', '--serial', action='store_true',
- help="Trigger running tests serially.")
+ help="Trigger running tests serially")
parser.add_argument('-S', '--stop', action='store_true',
- default=False, help="Stop on first error.")
+ default=False, help="Stop on first error")
parser.add_argument('-n', '--number', type=int,
- help="How often an action is executed for each process.")
+ help="How often an action is executed for each process")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a', '--all', action='store_true',
help="Execute all stress tests")
parser.add_argument('-T', '--type',
help="Filters tests of a certain type (e.g. gate)")
+parser.add_argument('-i', '--call-inherited', action='store_true',
+ default=False,
+ help="Call also inherited function with stress attribute")
group.add_argument('-t', "--tests", nargs='?',
- help="Name of the file with test description.")
+ help="Name of the file with test description")
if __name__ == "__main__":
sys.exit(main(parser.parse_args()))
diff --git a/tempest/test.py b/tempest/test.py
index ccb985a..decae94 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -67,12 +67,17 @@
``application``: once in the stress job lifetime
``process``: once in the worker process lifetime
``action``: on each action
+ @param allow_inheritance: allows inheritance of this attribute
"""
def decorator(f):
if 'class_setup_per' in kwargs:
setattr(f, "st_class_setup_per", kwargs['class_setup_per'])
else:
setattr(f, "st_class_setup_per", 'process')
+ if 'allow_inheritance' in kwargs:
+ setattr(f, "st_allow_inheritance", kwargs['allow_inheritance'])
+ else:
+ setattr(f, "st_allow_inheritance", False)
attr(type='stress')(f)
return f
return decorator
@@ -141,7 +146,7 @@
@classmethod
def tearDownClass(cls):
- at_exit_set.remove(cls)
+ at_exit_set.discard(cls)
if hasattr(super(BaseTestCase, cls), 'tearDownClass'):
super(BaseTestCase, cls).tearDownClass()