Merge "[docs][5] Re-design docs to cover all user-groups"
This commit is contained in:
commit
8b9496374d
@ -42,8 +42,7 @@ Contents
|
||||
cli/cli_reference
|
||||
components/task
|
||||
components/verification
|
||||
plugins
|
||||
plugin/plugin_reference
|
||||
plugins/index
|
||||
contribute
|
||||
feature_requests
|
||||
project_info
|
||||
|
143
doc/source/plugins/implementation/context_plugin.rst
Normal file
143
doc/source/plugins/implementation/context_plugin.rst
Normal file
@ -0,0 +1,143 @@
|
||||
..
|
||||
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.
|
||||
|
||||
.. _plugins_context_plugin:
|
||||
|
||||
|
||||
Context as a plugin
|
||||
===================
|
||||
|
||||
So what are contexts doing? These plugins will be executed before
|
||||
scenario iteration starts. For example, a context plugin could create
|
||||
resources (e.g., download 10 images) that will be used by the
|
||||
scenarios. All created objects must be put into the *self.context*
|
||||
dict, through which they will be available in the scenarios. Let's
|
||||
create a simple context plugin that adds a flavor to the environment
|
||||
before the benchmark task starts and deletes it after it finishes.
|
||||
|
||||
Creation
|
||||
^^^^^^^^
|
||||
|
||||
Inherit a class for your plugin from the base *Context* class. Then,
|
||||
implement the Context API: the *setup()* method that creates a flavor and the
|
||||
*cleanup()* method that deletes it.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rally.task import context
|
||||
from rally.common import logging
|
||||
from rally import consts
|
||||
from rally import osclients
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@context.configure(name="create_flavor", order=1000)
|
||||
class CreateFlavorContext(context.Context):
|
||||
"""This sample creates a flavor with specified options before task starts
|
||||
and deletes it after task completion.
|
||||
|
||||
To create your own context plugin, inherit it from
|
||||
rally.task.context.Context
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"flavor_name": {
|
||||
"type": "string",
|
||||
},
|
||||
"ram": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"vcpus": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
"""This method is called before the task starts."""
|
||||
try:
|
||||
# use rally.osclients to get necessary client instance
|
||||
nova = osclients.Clients(self.context["admin"]["credential"]).nova()
|
||||
# and than do what you need with this client
|
||||
self.context["flavor"] = nova.flavors.create(
|
||||
# context settings are stored in self.config
|
||||
name=self.config.get("flavor_name", "rally_test_flavor"),
|
||||
ram=self.config.get("ram", 1),
|
||||
vcpus=self.config.get("vcpus", 1),
|
||||
disk=self.config.get("disk", 1)).to_dict()
|
||||
LOG.debug("Flavor with id '%s'" % self.context["flavor"]["id"])
|
||||
except Exception as e:
|
||||
msg = "Can't create flavor: %s" % e.message
|
||||
if logging.is_debug():
|
||||
LOG.exception(msg)
|
||||
else:
|
||||
LOG.warning(msg)
|
||||
|
||||
def cleanup(self):
|
||||
"""This method is called after the task finishes."""
|
||||
try:
|
||||
nova = osclients.Clients(self.context["admin"]["credential"]).nova()
|
||||
nova.flavors.delete(self.context["flavor"]["id"])
|
||||
LOG.debug("Flavor '%s' deleted" % self.context["flavor"]["id"])
|
||||
except Exception as e:
|
||||
msg = "Can't delete flavor: %s" % e.message
|
||||
if logging.is_debug():
|
||||
LOG.exception(msg)
|
||||
else:
|
||||
LOG.warning(msg)
|
||||
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
You can refer to your plugin context in the benchmark task configuration
|
||||
files in the same way as any other contexts:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"Dummy.dummy": [
|
||||
{
|
||||
"args": {
|
||||
"sleep": 0.01
|
||||
},
|
||||
"runner": {
|
||||
"type": "constant",
|
||||
"times": 5,
|
||||
"concurrency": 1
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1
|
||||
},
|
||||
"create_flavor": {
|
||||
"ram": 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
109
doc/source/plugins/implementation/runner_plugin.rst
Normal file
109
doc/source/plugins/implementation/runner_plugin.rst
Normal file
@ -0,0 +1,109 @@
|
||||
..
|
||||
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.
|
||||
|
||||
.. _plugins_runner_plugin:
|
||||
|
||||
Scenario runner as a plugin
|
||||
===========================
|
||||
|
||||
Let's create a scenario runner plugin that runs a given benchmark
|
||||
scenario a random number of times (chosen at random from a given
|
||||
range).
|
||||
|
||||
Creation
|
||||
^^^^^^^^
|
||||
|
||||
Inherit a class for your plugin from the base *ScenarioRunner* class
|
||||
and implement its API (the *_run_scenario()* method):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import random
|
||||
|
||||
from rally.task import runner
|
||||
from rally import consts
|
||||
|
||||
|
||||
@runner.configure(name="random_times")
|
||||
class RandomTimesScenarioRunner(runner.ScenarioRunner):
|
||||
"""Sample scenario runner plugin.
|
||||
|
||||
Run scenario random number of times, which is chosen between min_times and
|
||||
max_times.
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "object",
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"min_times": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"max_times": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": True
|
||||
}
|
||||
|
||||
def _run_scenario(self, cls, method_name, context, args):
|
||||
# runners settings are stored in self.config
|
||||
min_times = self.config.get('min_times', 1)
|
||||
max_times = self.config.get('max_times', 1)
|
||||
|
||||
for i in range(random.randrange(min_times, max_times)):
|
||||
run_args = (i, cls, method_name,
|
||||
runner._get_scenario_context(context), args)
|
||||
result = runner._run_scenario_once(run_args)
|
||||
# use self.send_result for result of each iteration
|
||||
self._send_result(result)
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
You can refer to your scenario runner in the benchmark task
|
||||
configuration files in the same way as any other runners. Don't forget
|
||||
to put your runner-specific parameters in the configuration as well
|
||||
(*"min_times"* and *"max_times"* in our example):
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"Dummy.dummy": [
|
||||
{
|
||||
"runner": {
|
||||
"type": "random_times",
|
||||
"min_times": 10,
|
||||
"max_times": 20,
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Different plugin samples are available `here <https://github.com/openstack/rally/tree/master/samples/plugins>`_.
|
86
doc/source/plugins/implementation/scenario_plugin.rst
Normal file
86
doc/source/plugins/implementation/scenario_plugin.rst
Normal file
@ -0,0 +1,86 @@
|
||||
..
|
||||
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.
|
||||
|
||||
.. _plugins_scenario_plugin:
|
||||
|
||||
|
||||
Scenario as a plugin
|
||||
====================
|
||||
|
||||
Let's create a simple scenario plugin that list flavors.
|
||||
|
||||
Creation
|
||||
^^^^^^^^
|
||||
|
||||
Inherit a class for your plugin from the base *Scenario* class and
|
||||
implement a scenario method inside it. In our scenario, we'll first
|
||||
list flavors as an ordinary user, and then repeat the same using admin
|
||||
clients:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rally.task import atomic
|
||||
from rally.task import scenario
|
||||
|
||||
|
||||
class ScenarioPlugin(scenario.Scenario):
|
||||
"""Sample plugin which lists flavors."""
|
||||
|
||||
@atomic.action_timer("list_flavors")
|
||||
def _list_flavors(self):
|
||||
"""Sample of usage clients - list flavors
|
||||
|
||||
You can use self.context, self.admin_clients and self.clients which are
|
||||
initialized on scenario instance creation"""
|
||||
self.clients("nova").flavors.list()
|
||||
|
||||
@atomic.action_timer("list_flavors_as_admin")
|
||||
def _list_flavors_as_admin(self):
|
||||
"""The same with admin clients"""
|
||||
self.admin_clients("nova").flavors.list()
|
||||
|
||||
@scenario.configure()
|
||||
def list_flavors(self):
|
||||
"""List flavors."""
|
||||
self._list_flavors()
|
||||
self._list_flavors_as_admin()
|
||||
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
You can refer to your plugin scenario in the benchmark task
|
||||
configuration files in the same way as any other scenarios:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"ScenarioPlugin.list_flavors": [
|
||||
{
|
||||
"runner": {
|
||||
"type": "serial",
|
||||
"times": 5,
|
||||
},
|
||||
"context": {
|
||||
"create_flavor": {
|
||||
"ram": 512,
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
This configuration file uses the *"create_flavor"* context which we
|
||||
created in :ref:`plugins_context_plugin`.
|
99
doc/source/plugins/implementation/sla_plugin.rst
Normal file
99
doc/source/plugins/implementation/sla_plugin.rst
Normal file
@ -0,0 +1,99 @@
|
||||
..
|
||||
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.
|
||||
|
||||
.. _plugins_sla_plugin:
|
||||
|
||||
|
||||
SLA as a plugin
|
||||
===============
|
||||
|
||||
Let's create an SLA (success criterion) plugin that checks whether the
|
||||
range of the observed performance measurements does not exceed the
|
||||
allowed maximum value.
|
||||
|
||||
Creation
|
||||
^^^^^^^^
|
||||
|
||||
Inherit a class for your plugin from the base *SLA* class and implement its API
|
||||
(the *add_iteration(iteration)*, the *details()* method):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rally.task import sla
|
||||
from rally.common.i18n import _
|
||||
|
||||
@sla.configure(name="max_duration_range")
|
||||
class MaxDurationRange(sla.SLA):
|
||||
"""Maximum allowed duration range in seconds."""
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
}
|
||||
|
||||
def __init__(self, criterion_value):
|
||||
super(MaxDurationRange, self).__init__(criterion_value)
|
||||
self._min = 0
|
||||
self._max = 0
|
||||
|
||||
def add_iteration(self, iteration):
|
||||
# Skipping failed iterations (that raised exceptions)
|
||||
if iteration.get("error"):
|
||||
return self.success # This field is defined in base class
|
||||
|
||||
# Updating _min and _max values
|
||||
self._max = max(self._max, iteration["duration"])
|
||||
self._min = min(self._min, iteration["duration"])
|
||||
|
||||
# Updating successfulness based on new max and min values
|
||||
self.success = self._max - self._min <= self.criterion_value
|
||||
return self.success
|
||||
|
||||
def details(self):
|
||||
return (_("%s - Maximum allowed duration range: %.2f%% <= %.2f%%") %
|
||||
(self.status(), self._max - self._min, self.criterion_value))
|
||||
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
You can refer to your SLA in the benchmark task configuration files in
|
||||
the same way as any other SLA:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"Dummy.dummy": [
|
||||
{
|
||||
"args": {
|
||||
"sleep": 0.01
|
||||
},
|
||||
"runner": {
|
||||
"type": "constant",
|
||||
"times": 5,
|
||||
"concurrency": 1
|
||||
},
|
||||
"context": {
|
||||
"users": {
|
||||
"tenants": 1,
|
||||
"users_per_tenant": 1
|
||||
}
|
||||
},
|
||||
"sla": {
|
||||
"max_duration_range": 2.5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
83
doc/source/plugins/index.rst
Normal file
83
doc/source/plugins/index.rst
Normal file
@ -0,0 +1,83 @@
|
||||
..
|
||||
Copyright 2015 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.
|
||||
|
||||
.. _plugins:
|
||||
|
||||
Rally Plugins
|
||||
=============
|
||||
|
||||
|
||||
Rally Plugin Reference
|
||||
----------------------
|
||||
|
||||
Rally has a plugin oriented architecture - in other words Rally team is trying
|
||||
to make all places of code pluggable. Such architecture leads to the big amount
|
||||
of plugins. :ref:`Rally Plugins Reference page <plugin_reference>` contains
|
||||
a full list with detailed descriptions of all official Rally plugins.
|
||||
|
||||
|
||||
How plugins work
|
||||
----------------
|
||||
|
||||
Rally provides an opportunity to create and use a **custom benchmark
|
||||
scenario, runner, SLA, deployment or context** as a **plugin**:
|
||||
|
||||
.. image:: ../images/Rally-Plugins.png
|
||||
:align: center
|
||||
|
||||
Placement
|
||||
---------
|
||||
|
||||
Plugins can be quickly written and used, with no need to contribute
|
||||
them to the actual Rally code. Just place a Python module with your
|
||||
plugin class into the ``/opt/rally/plugins`` or ``~/.rally/plugins``
|
||||
directory (or its subdirectories), and it will be
|
||||
automatically loaded. Additional paths can be specified with the
|
||||
``--plugin-paths`` argument, or with the ``RALLY_PLUGIN_PATHS``
|
||||
environment variable, both of which accept comma-delimited
|
||||
lists. Both ``--plugin-paths`` and ``RALLY_PLUGIN_PATHS`` can list
|
||||
either plugin module files, or directories containing plugins. For
|
||||
instance, both of these are valid:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
rally --plugin-paths /rally/plugins ...
|
||||
rally --plugin-paths /rally/plugins/foo.py,/rally/plugins/bar.py ...
|
||||
|
||||
You can also use a script ``unpack_plugins_samples.sh`` from
|
||||
``samples/plugins`` which will automatically create the
|
||||
``~/.rally/plugins`` directory.
|
||||
|
||||
How to create a plugin
|
||||
----------------------
|
||||
|
||||
To create your own plugin you need to inherit your plugin class from
|
||||
plugin.Plugin class or its subclasses. Also you need to decorate your class
|
||||
with ``rally.task.scenario.configure``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from rally.task import scenario
|
||||
|
||||
@scenario.configure(name="my_new_plugin_name")
|
||||
class MyNewPlugin(plugin.Plugin):
|
||||
pass
|
||||
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 1
|
||||
|
||||
implementation/**
|
12
doc/source/plugins/plugin_reference.rst
Normal file
12
doc/source/plugins/plugin_reference.rst
Normal file
@ -0,0 +1,12 @@
|
||||
:tocdepth: 1
|
||||
.. _plugin_reference:
|
||||
|
||||
|
||||
Rally Plugins Reference
|
||||
=======================
|
||||
|
||||
.. contents::
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
.. generate_plugin_reference::
|
Loading…
x
Reference in New Issue
Block a user