1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
|
..
Copyright 2016 Mirantis Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
.. _hook_and_trigger_plugins:
Hooks. Hook trigger plugins
===========================
Why Hooks?
----------
All Rally workloads repeat their actions as many times as it is configured by
runner. Once run, there is no way to interrupt the runner to evaluate any
change or restart event on the stability of the cloud under test.
For example we would like to test how configuration change or cloud component
restart would affect performance and stability.
Task hooks were added to fill this gap and allow to use Rally for reliability
and high availability testing. Generally, hooks allow to perform any
actions on specified iteration or specified time since the workload has been
started.
Also, task html-report provides results of hook execution. They can contain
graphical or textual information with timing and statistics.
Hooks & Triggers Overview
-------------------------
Architecture
^^^^^^^^^^^^
Rally uses runners to specify how many times the workload should be executed.
Hooks do not use runners, instead they rely on trigger plugins to specify when
and how many times hook should be called. Therefore hooks are isolated from
workload runners and do not affect them because each hook is executed in
separate thread.
Sample of usage
^^^^^^^^^^^^^^^
Hooks can be added to the task configuration. Lets take a look at hook
configuration:
.. code-block:: json
{
"name": "sys_call",
"args": "/bin/echo 123",
"trigger": {
"name": "event",
"args": {
"unit": "iteration",
"at": [5, 50, 200, 1000]
}
}
}
It specifies hook plugin with name "sys_call". "args" field contains string
that will be used by sys_call plugin, but in case of any other hook plugin it
can contain any other Python object, that is assumed to be passed to the hook.
"trigger" field specifies which trigger plugin should be used to run this hook.
"trigger" contains similar fields "name" and "args" which represent trigger
plugin name and arguments for trigger plugin. In this example "event" trigger
is specified and configured to run the hook at 5th, 50th, 200th and 1000th
iterations.
Here is a full task config that contains previous hook configuration:
.. code-block:: json
{
"Dummy.dummy": [
{
"args": {
"sleep": 0.01
},
"runner": {
"type": "constant",
"times": 1500,
"concurrency": 1
},
"hooks": [
{
"name": "sys_call",
"args": "/bin/echo 123",
"trigger": {
"name": "event",
"args": {
"unit": "iteration",
"at": [5, 50, 200, 1000]
}
}
}
]
}
]
}
.. note::
In this example, runner is configured to run workload 1500 times. So there
is a limit for iterations and hook will be triggered only if certain
iteration is started by runner. In other words, if trigger specifies
iteration out of runner iterations scope then such trigger will not be
called.
Task report for this example will contain minimal information about hook
execution: duration of each hook call and its status(success of failure).
Let's take a look at more complicated config that can produce graphical
and textual information.
.. code-block:: yaml
---
Dummy.dummy:
-
args:
sleep: 0.75
runner:
type: "constant"
times: 20
concurrency: 2
hooks:
- name: sys_call
description: Run script
args: sh rally/rally-jobs/extra/hook_example_script.sh
trigger:
name: event
args:
unit: iteration
at: [2, 5, 8, 13, 17]
- name: sys_call
description: Show time
args: date +%Y-%m-%dT%H:%M:%S
trigger:
name: event
args:
unit: time
at: [0, 2, 5, 6, 9]
- name: sys_call
description: Show system name
args: uname -a
trigger:
name: event
args:
unit: iteration
at: [2, 3, 4, 5, 6, 8, 10, 12, 13, 15, 17, 18]
sla:
failure_rate:
max: 0
hook_example_script.sh generates dummy output in JSON format. Graphical
information format is the same as for workloads and the same types of
charts are supported for the hooks.
Here is a report that shows aggregated table and chart with hook results:
.. image:: ../../images/Hook-Aggregated-Report.png
Here is report that shows lines chart and pie chart for first hook on
the second iteration:
.. image:: ../../images/Hook-Per-Hook-Report.png
Browse existing Hooks_ and Triggers_.
Writing your own Hook plugin
----------------------------
Problem description
^^^^^^^^^^^^^^^^^^^
Hook plugin should implement custom action that can be done one or multiple
times during the workload. Examples of such actions might be the following:
- Destructive action inside cloud (`Fault Injection`_)
- Getting information about current state of cloud (load/health)
- Upgrading/downgrading a component of cloud
- Changing configuration of cloud
- etc.
Plugin code
^^^^^^^^^^^
The following example shows simple hook code that performs system call.
It is inherited from the base *Hook* class and contains implemented ``run()``
method:
.. code-block:: python
import shlex
import subprocess
from rally import consts
from rally.task import hook
@hook.configure(name="simple_sys_call")
class SimpleSysCallHook(hook.Hook):
"""Performs system call."""
CONFIG_SCHEMA = {
"$schema": consts.JSON_SCHEMA,
"type": "string",
}
def run(self):
proc = subprocess.Popen(shlex.split(self.config),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
proc.wait()
if proc.returncode:
self.set_error(
exception_name="n/a", # no exception class
description="Subprocess returned {}".format(proc.returncode),
details=proc.stdout.read(),
)
Any exceptions risen during execution of ``run`` method will be caught by Hook
base class and saved as a result. Although hook should manually call
``Hook.set_error()`` to indicate logical error in case if there is no exception
raised.
Also there is a method for saving charts data: ``Hook.add_output()``.
Plugin Placement
^^^^^^^^^^^^^^^^
There are two folders for hook plugins:
- `OpenStack Hooks`_
- `Common Hooks`_
Sample of task that uses Hook
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: json
{
"Dummy.dummy": [
{
"args": {
"sleep": 0.01
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 1
},
"hooks": [
{
"name": "simple_sys_call",
"args": "/bin/echo 123",
"trigger": {
"name": "event",
"args": {
"unit": "iteration",
"at": [3, 6]
}
}
}
]
}
]
}
Results of task execution
^^^^^^^^^^^^^^^^^^^^^^^^^
Result of previous task example:
.. image:: ../../images/Hook-Results.png
Writing your own Trigger plugin
-------------------------------
Problem description
^^^^^^^^^^^^^^^^^^^
Trigger plugin should implement an event processor that decides whether to
start hook or not. Rally has two basic triggers that should cover most cases:
- `Event Trigger`_
- `Periodic Trigger`_
Plugin code
^^^^^^^^^^^
This example shows the code of the existing Event trigger:
.. code-block:: python
from rally import consts
from rally.task import trigger
@trigger.configure(name="event")
class EventTrigger(trigger.Trigger):
"""Triggers hook on specified event and list of values."""
CONFIG_SCHEMA = {
"type": "object",
"$schema": consts.JSON_SCHEMA,
"oneOf": [
{
"properties": {
"unit": {"enum": ["time"]},
"at": {
"type": "array",
"minItems": 1,
"uniqueItems": True,
"items": {
"type": "integer",
"minimum": 0,
}
},
},
"required": ["unit", "at"],
"additionalProperties": False,
},
{
"properties": {
"unit": {"enum": ["iteration"]},
"at": {
"type": "array",
"minItems": 1,
"uniqueItems": True,
"items": {
"type": "integer",
"minimum": 1,
}
},
},
"required": ["unit", "at"],
"additionalProperties": False,
},
]
}
def get_listening_event(self):
return self.config["unit"]
def on_event(self, event_type, value=None):
if not (event_type == self.get_listening_event()
and value in self.config["at"]):
# do nothing
return
super(EventTrigger, self).on_event(event_type, value)
Trigger plugins must override two methods:
- ``get_listening_event`` - this method should return currently configured
event name. (So far Rally supports only "time" and "iteration")
- ``on_event`` - this method is called each time certain events occur.
It calls base method when the hook is triggered on specified event.
Plugin Placement
^^^^^^^^^^^^^^^^
All trigger plugins should be placed in `Trigger folder`_.
.. references:
.. _Hooks: ../plugin_reference.html#task-hooks
.. _Triggers: ../plugin_reference.html#task-hook-triggers
.. _Fault Injection: ../plugin_reference.html#fault-injection-hook
.. _Event Trigger: ../plugin_reference.html#event-hook-trigger
.. _Periodic Trigger: ../plugin_reference.html#periodic-hook-trigger
.. _Common Hooks: https://github.com/openstack/rally/tree/master/rally/plugins/common/hook
.. _OpenStack Hooks: https://github.com/openstack/rally/tree/master/rally/plugins/openstack/hook
.. _Trigger folder: https://github.com/openstack/rally/tree/master/rally/plugins/common/trigger
|