b1446e01c687aaa51f7105910d46adb16bab470d
[yardstick.git] / yardstick / benchmark / runners / arithmetic.py
1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 '''A runner that every run arithmetically steps specified input value(s) to
11 the scenario. This just means step value(s) is added to the previous value(s).
12 It is possible to combine several named input values and run with those either
13 as nested for loops or combine each i:th index of each "input value list"
14 until the end of the shortest list is reached (optimally all lists should be
15 defined with the same number of values when using such iter_type).
16 '''
17
18 import os
19 import multiprocessing
20 import logging
21 import traceback
22 import time
23 import itertools
24
25 from yardstick.benchmark.runners import base
26
27 LOG = logging.getLogger(__name__)
28
29
30 def _worker_process(queue, cls, method_name, scenario_cfg,
31                     context_cfg, aborted):
32
33     sequence = 1
34
35     runner_cfg = scenario_cfg['runner']
36
37     interval = runner_cfg.get("interval", 1)
38     if 'options' in scenario_cfg:
39         options = scenario_cfg['options']
40     else:  # options must be instatiated if not present in yaml
41         options = {}
42         scenario_cfg['options'] = options
43
44     runner_cfg['runner_id'] = os.getpid()
45
46     LOG.info("worker START, class %s", cls)
47
48     benchmark = cls(scenario_cfg, context_cfg)
49     benchmark.setup()
50     method = getattr(benchmark, method_name)
51
52     queue.put({'runner_id': runner_cfg['runner_id'],
53                'scenario_cfg': scenario_cfg,
54                'context_cfg': context_cfg})
55
56     sla_action = None
57     if "sla" in scenario_cfg:
58         sla_action = scenario_cfg["sla"].get("action", "assert")
59
60     # To both be able to include the stop value and handle backwards stepping
61     margin = lambda start, stop: -1 if start > stop else 1
62
63     param_iters = \
64         [xrange(d['start'], d['stop'] + margin(d['start'], d['stop']),
65                 d['step']) for d in runner_cfg['iterators']]
66     param_names = [d['name'] for d in runner_cfg['iterators']]
67
68     iter_type = runner_cfg.get("iter_type", "nested_for_loops")
69
70     if iter_type == 'nested_for_loops':
71         # Create a complete combination set of all parameter lists
72         loop_iter = itertools.product(*param_iters)
73     elif iter_type == 'tuple_loops':
74         # Combine each i;th index of respective parameter list
75         loop_iter = itertools.izip(*param_iters)
76     else:
77         LOG.warning("iter_type unrecognized: %s", iter_type)
78         raise
79
80     # Populate options and run the requested method for each value combination
81     for comb_values in loop_iter:
82
83         if aborted.is_set():
84             break
85
86         LOG.debug("runner=%(runner)s seq=%(sequence)s START" %
87                   {"runner": runner_cfg["runner_id"], "sequence": sequence})
88
89         for i, value in enumerate(comb_values):
90             options[param_names[i]] = value
91
92         data = {}
93         errors = ""
94
95         try:
96             method(data)
97         except AssertionError as assertion:
98             # SLA validation failed in scenario, determine what to do now
99             if sla_action == "assert":
100                 raise
101             elif sla_action == "monitor":
102                 LOG.warning("SLA validation failed: %s" % assertion.args)
103                 errors = assertion.args
104         except Exception as e:
105             errors = traceback.format_exc()
106             LOG.exception(e)
107
108         time.sleep(interval)
109
110         benchmark_output = {
111             'timestamp': time.time(),
112             'sequence': sequence,
113             'data': data,
114             'errors': errors
115         }
116
117         record = {'runner_id': runner_cfg['runner_id'],
118                   'benchmark': benchmark_output}
119
120         queue.put(record)
121
122         LOG.debug("runner=%(runner)s seq=%(sequence)s END" %
123                   {"runner": runner_cfg["runner_id"], "sequence": sequence})
124
125         sequence += 1
126
127         if errors:
128             break
129
130     benchmark.teardown()
131     LOG.info("worker END")
132
133
134 class ArithmeticRunner(base.Runner):
135     '''Run a scenario arithmetically stepping input value(s)
136
137   Parameters
138     interval - time to wait between each scenario invocation
139         type:    int
140         unit:    seconds
141         default: 1 sec
142     iter_type: - Iteration type of input parameter(s): nested_for_loops
143                  or tuple_loops
144         type:    string
145         unit:    na
146         default: nested_for_loops
147     -
148       name - name of scenario option that will be increased for each invocation
149           type:    string
150           unit:    na
151           default: na
152       start - value to use in first invocation of scenario
153           type:    int
154           unit:    na
155           default: none
156       stop - value indicating end of invocation. Can be set to same
157              value as start for one single value.
158           type:    int
159           unit:    na
160           default: none
161       step - value added to start value in next invocation of scenario.
162              Must not be set to zero. Can be set negative if start > stop
163           type:    int
164           unit:    na
165           default: none
166     -
167       name - and so on......
168     '''
169
170     __execution_type__ = 'Arithmetic'
171
172     def _run_benchmark(self, cls, method, scenario_cfg, context_cfg):
173         self.process = multiprocessing.Process(
174             target=_worker_process,
175             args=(self.result_queue, cls, method, scenario_cfg,
176                   context_cfg, self.aborted))
177         self.process.start()