Add support for action hooks in runner config 39/939/3
authorHans Feldt <hans.feldt@ericsson.com>
Mon, 29 Jun 2015 13:27:55 +0000 (15:27 +0200)
committerHans Feldt <hans.feldt@ericsson.com>
Wed, 8 Jul 2015 07:02:45 +0000 (07:02 +0000)
pre-start and post-stop intention is to be used to gather
information about the target system.

single-shot and periodic-action intention is to perform
actions on the infrastructure or cloud resources. For example
server live migration or network interface down.

Example of what can be added in the runner section:

    pre-start-action:
        command: "heat stack-show demo"
    periodic-action:
        interval: 10
        command: "ifconfig vboxnet1"
    single-shot-action:
        after: 30
        command: "nova show goofy.demo"
    post-stop-action:
        command: "nova list"

pre-start and post-stop data are added into the output file.
periodic and single-shot are not because that would interfere with
the actual sampled data. Besides the intention is not to log statistics
but do things with the infrastructure such as server live migration.

TODO: add sections to the output file, something like pre, data & post

JIRA: YARDSTICK-46
Change-Id: Ia059813fb74733f86368aea9c7a20e5afb71d228
Signed-off-by: Hans Feldt <hans.feldt@ericsson.com>
samples/ping-ext-stimuli.yaml [new file with mode: 0644]
yardstick/benchmark/runners/base.py

diff --git a/samples/ping-ext-stimuli.yaml b/samples/ping-ext-stimuli.yaml
new file mode 100644 (file)
index 0000000..cfe7915
--- /dev/null
@@ -0,0 +1,49 @@
+---
+# Sample benchmark task config file
+# Measure network latency using ping, destination is an external server
+# Make sure servers have internet access before running this test.
+# For example using virtual MOS do something this on the host:
+# sudo iptables -t nat -A POSTROUTING -s 172.16.0.0/24 \! -d 172.16.0.0/24 -j MASQUERADE
+#
+# This sample demonstrates the use of runner actions - hooks inserted in
+# diffrent places of the runner execution.
+#
+
+schema: "yardstick:task:0.1"
+
+scenarios:
+-
+  type: Ping
+  host: goofy.demo
+  target: 8.8.8.8
+  runner:
+    type: Duration
+    duration: 60
+    interval: 1
+    pre-start-action:
+        command: "heat stack-show demo"
+    periodic-action:
+        interval: 10
+        command: "ifconfig vboxnet1"
+    single-shot-action:
+        after: 30
+        command: "nova show goofy.demo"
+    post-stop-action:
+        command: "nova list"
+  sla:
+    max_rtt: 10
+    action: monitor
+
+context:
+  name: demo
+  image: cirros-0.3.3
+  flavor: m1.tiny
+  user: cirros
+  servers:
+    goofy:
+      floating_ip: true
+  networks:
+    test:
+      cidr: '10.0.1.0/24'
+      external_network: "net04_ext"
+
index 08117c6..badc335 100644 (file)
@@ -8,9 +8,11 @@
 ##############################################################################
 
 import importlib
-import multiprocessing
 import json
 import logging
+import multiprocessing
+import subprocess
+import time
 
 log = logging.getLogger(__name__)
 
@@ -35,6 +37,27 @@ def _output_serializer_main(filename, queue):
                 outfile.write('\n')
 
 
+def _single_action(seconds, command, queue):
+    '''entrypoint for the single action process'''
+    log.debug("single action, fires after %d seconds (from now)", seconds)
+    time.sleep(seconds)
+    log.debug("single action: executing command: '%s'", command)
+    data = subprocess.check_output(command, shell=True)
+    log.debug("\n%s" % data)
+
+
+def _periodic_action(interval, command, queue):
+    '''entrypoint for the periodic action process'''
+    log.debug("periodic action, fires every: %d seconds", interval)
+    time_spent = 0
+    while True:
+        time.sleep(interval)
+        time_spent += interval
+        log.debug("periodic action, executing command: '%s'", command)
+        data = subprocess.check_output(command, shell=True)
+        log.debug("\n%s" % data)
+
+
 class Runner(object):
     queue = None
     dump_process = None
@@ -88,6 +111,10 @@ class Runner(object):
         '''Terminate all runners (subprocesses)'''
         log.debug("Terminating all runners")
         for runner in Runner.runners:
+            if runner.periodic_action_process:
+                log.debug("Terminating periodic action process")
+                runner.periodic_action_process.terminate()
+                runner.periodic_action_process = None
             runner.process.terminate()
             runner.process.join()
             Runner.release(runner)
@@ -95,9 +122,30 @@ class Runner(object):
     def __init__(self, config, queue):
         self.context = {}
         self.config = config
+        self.periodic_action_process = None
         self.result_queue = queue
         Runner.runners.append(self)
 
+    def run_pre_start_action(self):
+        '''run a potentially configured pre-start action'''
+        if "pre-start-action" in self.config:
+            command = self.config["pre-start-action"]["command"]
+            log.debug("pre start action: command: '%s'" % command)
+            data = subprocess.check_output(command, shell=True)
+            log.debug("pre-start data: \n%s" % data)
+            output = "{'pre-start-action-data': %s}" % data
+            self.result_queue.put(output)
+
+    def run_post_stop_action(self):
+        '''run a potentially configured post-stop action'''
+        if "post-stop-action" in self.config:
+            command = self.config["post-stop-action"]["command"]
+            log.debug("post stop action: command: '%s'" % command)
+            data = subprocess.check_output(command, shell=True)
+            log.debug("post-stop data: \n%s" % data)
+            output = "{'post-stop-action-data': %s}" % data
+            self.result_queue.put(output)
+
     def run(self, scenario_type, scenario_args):
         class_name = base_scenario.Scenario.get(scenario_type)
         path_split = class_name.split(".")
@@ -106,8 +154,33 @@ class Runner(object):
         cls = getattr(module, path_split[-1])
 
         self.config['object'] = class_name
+
+        self.run_pre_start_action()
+
+        if "single-shot-action" in self.config:
+            single_action_process = multiprocessing.Process(
+                target=_single_action,
+                name="single-shot-action",
+                args=(self.config["single-shot-action"]["after"],
+                      self.config["single-shot-action"]["command"],
+                      self.result_queue))
+            single_action_process.start()
+
+        if "periodic-action" in self.config:
+            self.periodic_action_process = multiprocessing.Process(
+                target=_periodic_action,
+                name="periodic-action",
+                args=(self.config["periodic-action"]["interval"],
+                      self.config["periodic-action"]["command"],
+                      self.result_queue))
+            self.periodic_action_process.start()
+
         self._run_benchmark(cls, "run", scenario_args)
 
     def join(self):
         self.process.join()
+        if self.periodic_action_process:
+            self.periodic_action_process.terminate()
+            self.periodic_action_process = None
+        self.run_post_stop_action()
         return self.process.exitcode