Platform as a plugin

A platform plugin teaches Rally about one kind of target. It knows how to read a piece of the environment spec, how to check that the target is alive, and how to clean up after a task.

Rally itself ships no platform plugins. Every platform comes from a plugin package, like existing@openstack from rally-openstack.

Creation

Inherit from rally.env.platform.Platform and register the class with @platform.configure(). It takes two names:

  • name is the name of the plugin

  • platform is the thing the plugin talks to

Together they make the full name that goes into the spec, in the form name@platform. The example below is used as existing@myservice.

Put the schema of your part of the spec into CONFIG_SCHEMA. Rally validates the spec against it before it creates anything.

import requests

from rally.env import platform


@platform.configure(name="existing", platform="myservice")
class ExistingMyService(platform.Platform):
    """Describes an already deployed MyService instance."""

    CONFIG_SCHEMA = {
        "type": "object",
        "properties": {
            "url": {"type": "string"},
            "token": {"type": "string"}
        },
        "required": ["url"],
        "additionalProperties": False
    }

    def create(self):
        # nothing to deploy, the service is already there
        return self.spec, {}

    def destroy(self):
        # and nothing to tear down
        pass

    def check_health(self):
        try:
            resp = requests.get("%s/healthz" % self.spec["url"])
        except Exception as e:
            return {"available": False, "message": str(e)}
        if resp.status_code != 200:
            return {
                "available": False,
                "message": "MyService answered with %s" % resp.status_code
            }
        return {"available": True}

    def info(self):
        resp = requests.get("%s/version" % self.spec["url"])
        return {"info": {"version": resp.json()["version"]}}

The spec of the plugin is available as self.spec.

An environment with this plugin looks like this:

{
    "existing@myservice": {
        "url": "http://example.net:8080"
    }
}

The API

class rally.env.platform.Platform(spec: dict[str, Any], uuid: str | None = None, plugin_data: dict[str, Any] | None = None, platform_data: dict[str, Any] | None = None, status: str | None = None)[source]

Base class for platform plugins.

A platform plugin teaches Rally about one kind of target. The part of the environment spec that belongs to the plugin is validated against its CONFIG_SCHEMA and is available as self.spec.

Every method is optional, implement the ones that make sense for the target. The data returned by create() is available as self.platform_data and self.plugin_data.

create() tuple[dict[str, Any], dict[str, Any]][source]

Make the target usable.

Called once, when the environment is created. A plugin that only describes an already existing target has nothing to do here.

Platforms of an environment are created one by one. If this method raises, the platform and the whole environment get the FAILED TO CREATE status, and the platforms that were not created yet are marked as SKIPPED.

Returns:

a tuple of two dicts, platform_data and plugin_data. Both are stored in the database.

destroy() None[source]

Undo what create() did.

Called by rally env destroy, after cleanup() unless the cleanup is skipped. If this method raises, the platform gets the FAILED TO DESTROY status and the destroy can be retried.

update(new_spec: dict[str, Any]) dict[str, Any][source]

Apply a new spec to an existing platform.

Reserved for the future: Rally does not call it yet.

Parameters:

new_spec – the new spec of the plugin

Returns:

the new platform data

cleanup(task_uuid: str | None = None) CleanupInfo[source]

Find and delete the resources that tasks left behind.

Called by rally env cleanup and by rally env destroy. A plugin that does not support it is reported as “Not implemented”.

Parameters:

task_uuid – clean up only the resources of this task

Returns:

a dict with the number of discovered, deleted and failed resources, the same numbers per resource type in resources, and a list of errors, each with a message and optional resource_id, resource_type and traceback. An optional message summarizes the result.

check_health() HealthInfo[source]

Check whether the target is alive and usable.

Called by rally env check.

Returns:

a dict with a boolean available and an optional message, e.g. the reason why the target is not available

info() PlatformInfo[source]

Describe what the target provides.

Called by rally env info, which prints the result as it is.

Returns:

a dict with info of any shape, e.g. the list of services of a cloud

_get_validation_context() dict[str, Any][source]

Return the context that validators of the platform need.

Called before a task is validated. The contexts of all platforms of the environment are merged into one. Most plugins need nothing here.

classmethod create_spec_from_sys_environ(sys_environ: Mapping[str, str]) SysEnvSpec[source]

Build a spec from credentials found in environment variables.

Called by rally env create --from-sysenv. The base implementation reports that nothing was found, which fits a target that has no such convention.

Parameters:

sys_environ – a copy of the environment variables

Returns:

a dict with a boolean available, the spec of the plugin when credentials were found, and an optional message that tells what was found or why nothing was

If check_health(), info(), cleanup() or create_spec_from_sys_environ() raises or returns something that does not match the described format, Rally reports that the plugin is broken. It does not crash the whole run.

Usage

Once the package with your plugin is installed, the platform can be used in a spec right away:

$ rally env create --name=my-service --spec myservice.json
$ rally env check
$ rally env info

See Environment Component for the rest of the environment workflow.