Merge "Fix test_admin_catalog_list"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 703d92a..d39ef70 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -390,3 +390,11 @@
 heat = false
 # Whether or not horizon is expected to be available
 horizon = True
+
+[stress]
+# Maximum number of instances to create during test
+max_instances = 32
+# Time (in seconds) between log file error checks
+log_check_interval = 60
+# The default number of threads created while stress test
+default_thread_number_per_action=4
diff --git a/run_tests.sh b/run_tests.sh
index f995cde..856ce54 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -11,7 +11,7 @@
   echo "  -u, --update             Update the virtual environment with any newer package versions"
   echo "  -s, --smoke              Only run smoke tests"
   echo "  -w, --whitebox           Only run whitebox tests"
-  echo "  -t, --parallel           Run testr parallel"
+  echo "  -t, --serial             Run testr serially"
   echo "  -c, --nova-coverage      Enable Nova coverage collection"
   echo "  -C, --config             Config file location"
   echo "  -p, --pep8               Just run pep8"
@@ -26,7 +26,7 @@
 just_pep8=0
 venv=.venv
 with_venv=tools/with_venv.sh
-parallel=0
+serial=0
 always_venv=0
 never_venv=0
 no_site_packages=0
@@ -38,7 +38,7 @@
 logging=0
 logging_config=etc/logging.conf
 
-if ! options=$(getopt -o VNnfuswtcphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,parallel,nova-coverage,pep8,help,debug,config:,logging,logging-config: -- "$@")
+if ! options=$(getopt -o VNnfuswtcphdC:lL: -l virtual-env,no-virtual-env,no-site-packages,force,update,smoke,whitebox,serial,nova-coverage,pep8,help,debug,config:,logging,logging-config: -- "$@")
 then
     # parse error
     usage
@@ -61,7 +61,7 @@
     -p|--pep8) let just_pep8=1;;
     -s|--smoke) testrargs="$testrargs smoke";;
     -w|--whitebox) testrargs="$testrargs whitebox";;
-    -t|--parallel) parallel=1;;
+    -t|--serial) serial=1;;
     -l|--logging) logging=1;;
     -L|--logging-config) logging_config=$2; shift;;
     --) [ "yes" == "$first_uu" ] || testrargs="$testrargs $1"; first_uu=no  ;;
@@ -101,10 +101,10 @@
 function run_tests {
   testr_init
   ${wrapper} find . -type f -name "*.pyc" -delete
-  if [ $parallel -eq 1 ]; then
-      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
-  else
+  if [ $serial -eq 1 ]; then
       ${wrapper} testr run --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
+  else
+      ${wrapper} testr run --parallel --subunit $testrargs | ${wrapper} subunit-2to1 | ${wrapper} tools/colorizer.py
   fi
 }
 
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 2a1e2c4..9fa86b6 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -18,6 +18,7 @@
 from tempest.api.volume.base import BaseVolumeTest
 from tempest.common.utils.data_utils import rand_name
 from tempest.test import attr
+from tempest.test import stresstest
 
 
 class VolumesActionsTest(BaseVolumeTest):
@@ -52,6 +53,7 @@
 
         super(VolumesActionsTest, cls).tearDownClass()
 
+    @stresstest(class_setup_per='process')
     @attr(type='smoke')
     def test_attach_detach_volume_to_instance(self):
         # Volume is attached and detached successfully from an instance
@@ -65,6 +67,7 @@
         self.assertEqual(202, resp.status)
         self.client.wait_for_volume_status(self.volume['id'], 'available')
 
+    @stresstest(class_setup_per='process')
     @attr(type='gate')
     def test_get_volume_attachment(self):
         # Verify that a volume's attachment information is retrieved
diff --git a/tempest/config.py b/tempest/config.py
index e0ac843..3b09b5e 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -519,7 +519,10 @@
                help='regexp for list of log files.'),
     cfg.StrOpt('log_check_interval',
                default=60,
-               help='time between log file error checks.')
+               help='time (in seconds) between log file error checks.'),
+    cfg.StrOpt('default_thread_number_per_action',
+               default=4,
+               help='The number of threads created while stress test.')
 ]
 
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 886bf3a..1703248 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -590,7 +590,3 @@
     @classmethod
     def _stack_rand_name(cls):
         return rand_name(cls.__name__ + '-')
-
-    def _create_keypair(self):
-        kp_name = rand_name('keypair-smoke')
-        return self.compute_client.keypairs.create(kp_name)
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index cd959a8..17870a1 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -35,9 +35,8 @@
         if self.config.orchestration.keypair_name:
             self.keypair_name = self.config.orchestration.keypair_name
         else:
-            self.keypair = self._create_keypair()
+            self.keypair = self.create_keypair()
             self.keypair_name = self.keypair.id
-            self.set_resource('keypair', self.keypair)
 
     def launch_stack(self):
         self.parameters = {
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 22f3f26..ef88eed 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -135,7 +135,7 @@
                 # been created yet
                 pass
             else:
-                resource_name = body['logical_resource_id']
+                resource_name = body['resource_name']
                 resource_status = body['resource_status']
                 if resource_status == status:
                     return
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index efc57a9..5171da2 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -102,6 +102,8 @@
     """
     logfiles = admin_manager.config.stress.target_logfiles
     log_check_interval = int(admin_manager.config.stress.log_check_interval)
+    default_thread_num = int(admin_manager.config.stress.
+                             default_thread_number_per_action)
     if logfiles:
         controller = admin_manager.config.stress.target_controller
         computes = _get_compute_nodes(controller)
@@ -112,7 +114,7 @@
             manager = admin_manager
         else:
             manager = clients.Manager()
-        for p_number in xrange(test.get('threads', 1)):
+        for p_number in xrange(test.get('threads', default_thread_num)):
             if test.get('use_isolated_tenants', False):
                 username = rand_name("stress_user")
                 tenant_name = rand_name("stress_tenant")
diff --git a/tempest/stress/run_stress.py b/tempest/stress/run_stress.py
index 32e3ae0..aab2afd 100755
--- a/tempest/stress/run_stress.py
+++ b/tempest/stress/run_stress.py
@@ -19,13 +19,52 @@
 import argparse
 import json
 import sys
+from testtools.testsuite import iterate_tests
+from unittest import loader
+
+
+def discover_stress_tests(path="./", filter_attr=None):
+    """Discovers all tempest tests and create action out of them
+    """
+
+    tests = []
+    testloader = loader.TestLoader()
+    list = testloader.discover(path)
+    for func in (iterate_tests(list)):
+        try:
+            method_name = getattr(func, '_testMethodName')
+            full_name = "%s.%s.%s" % (func.__module__,
+                                      func.__class__.__name__,
+                                      method_name)
+            test_func = getattr(func, method_name)
+            # NOTE(mkoderer): this contains a list of all type attributes
+            attrs = getattr(test_func, "__testtools_attrs")
+        except Exception:
+            next
+        if 'stress' in attrs:
+            if filter_attr is not None and not filter_attr in attrs:
+                continue
+            class_setup_per = getattr(test_func, "st_class_setup_per")
+
+            action = {'action':
+                      "tempest.stress.actions.unit_test.UnitTest",
+                      'kwargs': {"test_method": full_name,
+                                 "class_setup_per": class_setup_per
+                                 }
+                      }
+            tests.append(action)
+    return tests
 
 
 def main(ns):
     # NOTE(mkoderer): moved import to make "-h" possible without OpenStack
     from tempest.stress import driver
     result = 0
-    tests = json.load(open(ns.tests, 'r'))
+    if not ns.all:
+        tests = json.load(open(ns.tests, 'r'))
+    else:
+        tests = discover_stress_tests(filter_attr=ns.type)
+
     if ns.serial:
         for test in tests:
             step_result = driver.stress_openstack([test],
@@ -49,7 +88,13 @@
                     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.")
+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)")
+group.add_argument('-t', "--tests", nargs='?',
+                   help="Name of the file with test description.")
 
 if __name__ == "__main__":
     sys.exit(main(parser.parse_args()))
diff --git a/tempest/stress/stressaction.py b/tempest/stress/stressaction.py
index 3719841..6284cef 100644
--- a/tempest/stress/stressaction.py
+++ b/tempest/stress/stressaction.py
@@ -60,6 +60,8 @@
 
         while self.max_runs is None or (shared_statistic['runs'] <
                                         self.max_runs):
+            self.logger.debug("Trigger new run (run %d)" %
+                              shared_statistic['runs'])
             try:
                 self.run()
             except Exception:
diff --git a/tempest/test.py b/tempest/test.py
index 68cedf0..8b3cfa6 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -57,6 +57,27 @@
     return decorator
 
 
+def stresstest(*args, **kwargs):
+    """Add stress test decorator
+
+    For all functions with this decorator a attr stress will be
+    set automatically.
+
+    @param class_setup_per: allowed values are application, process, action
+           ``application``: once in the stress job lifetime
+           ``process``: once in the worker process lifetime
+           ``action``: on each action
+    """
+    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')
+        attr(type='stress')(f)
+        return f
+    return decorator
+
+
 # there is a mis-match between nose and testtools for older pythons.
 # testtools will set skipException to be either
 # unittest.case.SkipTest, unittest2.case.SkipTest or an internal skip
diff --git a/tox.ini b/tox.ini
index a101a9e..c60e19e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -78,7 +78,7 @@
 sitepackages = True
 setenv = VIRTUAL_ENV={envdir}
 commands =
-    python -m tempest/stress/run_stress tempest/stress/etc/stress-tox-job.json -d 3600
+    python -m tempest/stress/run_stress -a -d 3600
 
 [testenv:venv]
 commands = {posargs}