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