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