Execution plugins

Plugins system allows you to execute any external command from Polemarch. To create a plugin you need to

  • create a python class describing the argument processing and API talking logic;

  • configure this plugin in your settings.ini.

Quick start

All built-in execution backends, such as ansible playbook or module execution, use plugins (starting from 2.2.0).

To get started let’s create a simple plugin allows you to run echo command.

from polemarch.plugins.base import BasePlugin
from rest_framework.fields import BooleanField, empty
from vstutils.api.fields import VSTCharField


# Any plugin must be inherited from BasePlugin
class TestEcho(BasePlugin):
    # Define fields which will be used to execute this plugin and create template with
    serializer_fields = {
        # You can use fields either from rest_framework or vstutils
        'string': VSTCharField(),
        'n': BooleanField(default=empty, required=False, label='No trailing newlines'),
        'e': BooleanField(default=empty, required=False, label='Interpret backslash escapes'),
    }
    # Value of this field will be shown in history detail page as Mode
    arg_shown_on_history_as_mode = 'string'

    # This is our binary from which any command starts
    @property
    def base_command(self):
        return ['echo']

    # This method is called by get_args method of BasePlugin for each argument received from API. As we defined
    # 'string', 'n', and 'e' arguments in serializer_fields, we can get their keys and values here.
    def _process_arg(self, key, value):
        # As 'string' argument in this case is a positional argument, let's return just it's value, so the final
        # command will be something like ['echo', 'string to output', ...]
        if key == 'string':
            return value
        # As this guys are just boolean flags, let's return them as '-n' or '-e' accordingly
        if key in ('n', 'e') and value:
            return f'-{key}'
        # Note, that if we received for example `n` argument with False value, we are returning None,
        # means that it won't be included to execution command. But of course, you may override this behavior
        # in get_args method.

Supposing that described plugin is located at polemarch.plugins.custom.Echo, let’s connect it to Polemarch. In your /etc/polemarch/settings.ini add following section:

[plugins.echo]
backend = polemarch.plugins.custom.Echo

Also you may want to provide additional options directly to plugin:

[plugins.echo.options]
some_option = 'some_option'

In this example some_option will be available in any plugin’s method as self.config['some_option'], as all options are initialized in the constructor.

So it’s all done! After restarting your polemarch server and resetting schema, you can check /project/<your_project_id>/execute_echo/. Here you should be able to execute echo plugin as any built-in one. Also you should be able to create a template with it at /project/<your_project_id>/execution_templates/new/.

If you tried executing echo plugin with flags, you may see that this flags are being outputted too. This is because they goes after string argument. To fix this issue, we may do something like this:

...

class TestEcho(BasePlugin):
    ...

    def get_args(self, raw_args):
        # We know that 'string' is required so no default for .pop() is needed
        string_value = raw_args.pop('string')
        args = super().get_args(raw_args)
        # Push our string to the end of command
        args += [string_value]
        return args

    @property
    def base_command(self):
        return ['echo']

    def _process_arg(self, key, value):
        if key in ('n', 'e') and value:
            return f'-{key}'

Now if you are passing flags to execution, they should work the same except not being outputted.

To learn more about what plugins are provide, please check API reference.

API reference

class polemarch.plugins.base.BasePlugin(config, project_data, output_handler)

Plugin class from which any other plugin should inherit. The plugin itself is an entity which, on the one hand provides appropriate fields for the API, and on the other hand, processes arguments received from it to build execution command.

For each configured plugin an endpoint will be generated allows you to choose arguments and execute it. Also, this plugin will be available to create template with.

Parameters
  • config (dict) – settings.ini options mapping for this plugin.

  • project_data – proxy of the project instance, allows you to access it’s readonly properties, such as config vars, env_vars etc.

  • output_handler (typing.Callable) – executor’s function which handles logging and outputting to history. Used by verbose_output method.

classmethod _get_serializer_fields(exclude_fields=())

Returns dict with field names and field instances used to generate fields for serializer.

Parameters

exclude_fields (tuple) – field names that should not be presented in serializer.

Return type

dict

classmethod _get_serializer_metaclass(exclude_fields=())

Returns serializer metaclass used to generate fields in serializer.

Parameters

exclude_fields (tuple) – field names that should not be presented in serializer.

Return type

typing.Type[typing.Type[vstutils.api.serializers.BaseSerializer]]

_process_arg(key, value)

Returns single argument with value for get_args method. Should return None if argument must not be included to the execution command.

Parameters
  • key (str) – argument key (e.g. verbose).

  • value (typing.Any) – argument value (e.g. 2).

Return type

typing.Optional[str]

arg_shown_on_history_as_mode: Optional[str] = None

Name of argument presented in generated serializer which will be shown on detail history page as Mode. For example, if you are executing some module with additional arguments and fields contains module field, than you can set ‘module’ here, and it’s value will be shown after execution. If not set, Mode in the history will show [<plugin name> plugin] string.

base_command: List[str]

Base command (usually binary) from which execution command starts, e.g. ['echo']. You may also override this attribute as a property if more complex logic needs to be performed.

error_codes: Mapping[int, str] = {}

This mapping will be looked up to choose an appropriate error message for history output if execution finished with errors. If no code found, then just “ERROR” string outputs.

get_args(raw_args)

Returns list of processed arguments which will be substituted into execution command.

Parameters

raw_args (dict) – argument name-value mapping which should be processed.

Return type

typing.List[str]

get_env_vars()

Returns env variables which will be used in execution, project’s env variables by default.

Return type

typing.Mapping[str, str]

get_execution_data(execution_dir, raw_args)

Returns tuple of execution command, env variables and raw inventory string. This method will be called directly by executor.

Parameters
  • execution_dir (pathlib.Path) – path to execution directory in which project copy located. All additional files that should be generated (e.g. inventory file) must be placed here.

  • raw_args (dict) – argument name-value mapping which should be processed.

Return type

typing.Tuple[typing.List[str], dict, str]

get_inventory(inventory)

Returns tuple of inventory argument for execution command and raw inventory string used for representation in history. If no inventory presented, should return (None, '').

Parameters

inventory (typing.Union[polemarch.main.models.hosts.Inventory, str, int, None]) – inventory, received from API, which can None if there is no inventory, polemarch.main.models.Inventory instance, int or str.

Return type

typing.Tuple[typing.Optional[str], str]

classmethod get_serializer_class(exclude_fields=())

Returns serializer class which will be used to generate fields for arguments. Uses metaclass returned by _get_serializer_metaclass method.

Parameters

exclude_fields (tuple) – field names that should not be presented in serializer.

Return type

typing.Type[vstutils.api.serializers.BaseSerializer]

get_verbose_level(raw_args)

Returns verbose level used for history output and logging. Should be taken from execution arguments (usually from verbose argument). This method will be called directly by executor.

Parameters

raw_args (dict) – argument name-value mapping which should be processed.

Return type

int

property inventory_filename: str

Returns name of file with inventory content.

Return type

str

prepare_execution_dir(dir)

Gets execution directory with copied project. All files needed for execution (e.g. generated inventory file) should be here.

Parameters

dir (pathlib.Path) – path to execution directory in which project copy located. All additional files that should be generated (e.g. inventory file) must be placed here.

Return type

None

serializer_fields: Mapping[str, Field] = {}

Fields mapping used to generate serializer. By default returned by _get_serializer_fields method.

supports_inventory: bool = False

Flag shows if the plugin supports working with inventory. For now it’s only used to show or hide inventory field in execution template create page.

verbose_output(message, level=3)

Logs value with logger and outputs it to history.

Parameters
  • message (str) – message to output.

  • level (int) – verbosity level from which message should be outputted.

Return type

None