Merge "Adding scale out templates for ovs_dpdk/sriov using 2 node setup"
[yardstick.git] / yardstick / benchmark / runners / search.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 runs a specific time before it returns
20 """
21
22 from __future__ import absolute_import
23
24 import logging
25 import multiprocessing
26 import time
27 import traceback
28 from contextlib import contextmanager
29 from itertools import takewhile
30
31 import os
32 from collections import Mapping
33 from six.moves import zip
34
35 from yardstick.benchmark.runners import base
36
37 LOG = logging.getLogger(__name__)
38
39
40 class SearchRunnerHelper(object):
41
42     def __init__(self, cls, method_name, scenario_cfg, context_cfg, aborted):
43         super(SearchRunnerHelper, self).__init__()
44         self.cls = cls
45         self.method_name = method_name
46         self.scenario_cfg = scenario_cfg
47         self.context_cfg = context_cfg
48         self.aborted = aborted
49         self.runner_cfg = scenario_cfg['runner']
50         self.run_step = self.runner_cfg.get("run_step", "setup,run,teardown")
51         self.timeout = self.runner_cfg.get("timeout", 60)
52         self.interval = self.runner_cfg.get("interval", 1)
53         self.benchmark = None
54         self.method = None
55
56     def __call__(self, *args, **kwargs):
57         if self.method is None:
58             raise RuntimeError
59         return self.method(*args, **kwargs)
60
61     @contextmanager
62     def get_benchmark_instance(self):
63         self.benchmark = self.cls(self.scenario_cfg, self.context_cfg)
64
65         if 'setup' in self.run_step:
66             self.benchmark.setup()
67
68         self.method = getattr(self.benchmark, self.method_name)
69         LOG.info("worker START, timeout %d sec, class %s", self.timeout, self.cls)
70         try:
71             yield self
72         finally:
73             if 'teardown' in self.run_step:
74                 self.benchmark.teardown()
75
76     def is_not_done(self):
77         if 'run' not in self.run_step:
78             raise StopIteration
79
80         max_time = time.time() + self.timeout
81
82         abort_iter = iter(self.aborted.is_set, True)
83         time_iter = takewhile(lambda t_now: t_now <= max_time, iter(time.time, -1))
84
85         for seq, _ in enumerate(zip(abort_iter, time_iter), 1):
86             yield seq
87             time.sleep(self.interval)
88
89
90 class SearchRunner(base.Runner):
91     """Run a scenario for a certain amount of time
92
93 If the scenario ends before the time has elapsed, it will be started again.
94
95   Parameters
96     timeout - amount of time the scenario will be run for
97         type:    int
98         unit:    seconds
99         default: 1 sec
100     interval - time to wait between each scenario invocation
101         type:    int
102         unit:    seconds
103         default: 1 sec
104     """
105     __execution_type__ = 'Search'
106
107     def __init__(self, config):
108         super(SearchRunner, self).__init__(config)
109         self.runner_cfg = None
110         self.runner_id = None
111         self.sla_action = None
112         self.worker_helper = None
113
114     def _worker_run_once(self, sequence):
115         LOG.debug("runner=%s seq=%s START", self.runner_id, sequence)
116
117         data = {}
118         errors = ""
119
120         try:
121             self.worker_helper(data)
122         except AssertionError as assertion:
123             # SLA validation failed in scenario, determine what to do now
124             if self.sla_action == "assert":
125                 raise
126             elif self.sla_action == "monitor":
127                 LOG.warning("SLA validation failed: %s", assertion.args)
128                 errors = assertion.args
129         except Exception as e:
130             errors = traceback.format_exc()
131             LOG.exception(e)
132
133         record = {
134             'runner_id': self.runner_id,
135             'benchmark': {
136                 'timestamp': time.time(),
137                 'sequence': sequence,
138                 'data': data,
139                 'errors': errors,
140             },
141         }
142
143         self.result_queue.put(record)
144
145         LOG.debug("runner=%s seq=%s END", self.runner_id, sequence)
146
147         # Have to search through all the VNF KPIs
148         kpi_done = any(kpi.get('done') for kpi in data.values() if isinstance(kpi, Mapping))
149
150         return kpi_done or (errors and self.sla_action is None)
151
152     def _worker_run(self, cls, method_name, scenario_cfg, context_cfg):
153         self.runner_cfg = scenario_cfg['runner']
154         self.runner_id = self.runner_cfg['runner_id'] = os.getpid()
155
156         self.worker_helper = SearchRunnerHelper(cls, method_name, scenario_cfg,
157                                                 context_cfg, self.aborted)
158
159         try:
160             self.sla_action = scenario_cfg['sla'].get('action', 'assert')
161         except KeyError:
162             self.sla_action = None
163
164         self.result_queue.put({
165             'runner_id': self.runner_id,
166             'scenario_cfg': scenario_cfg,
167             'context_cfg': context_cfg
168         })
169
170         with self.worker_helper.get_benchmark_instance():
171             for sequence in self.worker_helper.is_not_done():
172                 if self._worker_run_once(sequence):
173                     LOG.info("worker END")
174                     break
175
176     def _run_benchmark(self, cls, method, scenario_cfg, context_cfg):
177         name = "{}-{}-{}".format(self.__execution_type__, scenario_cfg.get("type"), os.getpid())
178         self.process = multiprocessing.Process(
179             name=name,
180             target=self._worker_run,
181             args=(cls, method, scenario_cfg, context_cfg))
182         self.process.start()