Add handling for inherited stress attributes

In some cases inheritance is used for test cases (like XML/JSON tests).
For many of those cases inherited stress attribute are misleading
since they will execute nearly the same load on the system.

To avoid this issue we skip all inherited stress attributes by default
and add parameters to deactivate this on a global level and function
level.

Change-Id: I4260844ed15917106436d836c939157e32a59554
Fixes: bug #1219402
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..9d30037 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