Plugins¶
In infrared 2.0, plugins are self contained Ansible projects. They can still also depend on common items provided by the core project. Any ansible project can become an`infrared` plugin by adhering to the following structure (see tests/example for an example plugin):
tests/example
├── main.yml # Main playbook. All execution starts here
├── plugin.spec # Plugin definition
├── roles # Add here roles for the project to use
│ └── example_role
│ └── tasks
│ └── main.yml
Note
This structure will work without any ansible.cfg
file provided (unless common resources are used),
as Ansible will search for references in the
relative paths described above. To use an ansible.cfg
config file, use absolute paths to the plugin directory.
Plugin structure¶
Main entry¶
infrared will look for a playbook called main.yml
to start the execution from.
Note
If you want to use other playbook to start from - simply add it into
config section in plugin.spec
:
config:
plugin_type: other
entry_point: your-playbook.yml
...
Plugins are regular Ansible projects, and as such, they might include or reference any item
(files, roles, var files, ansible plugins, modules, templates, etc…) using relative paths
to current playbook.
They can also use roles, callback and filter plugins defined in the common/
directory
provided by infrared core.
An example of plugin_dir/main.yml
:
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 | - name: Main Play
hosts: all
vars_files:
- vars/some_var_file.yml
roles:
- role: example_role
tasks:
- name: fail if no vars dict
when: "provision is not defined"
fail:
- name: fail if input calls for it
when: "provision.foo.bar == 'fail'"
fail:
- debug:
var: inventory_dir
tags: only_this
- name: Test output
vars:
output_file: output.example
file:
path: "{{ inventory_dir }}/{{ output_file }}"
state: touch
when: "provision is defined"
|
Plugin Specification¶
infrared gets all plugin info from plugin.spec
file. Following YAML format.
This file defines the CLI flags this plugin exposes, its name and its type.
config:
plugin_type: provision
entry_point: main.yml
# roles_path: ../ # Optional, contains relative path to a role
subparsers:
example:
description: Example provisioner plugin
include_groups: ["Ansible options", "Inventory", "Common options", "Answers file"]
groups:
- title: Group A
options:
foo-bar:
type: Value
help: "foo.bar option"
default: "default string"
flag:
type: Flag
help: "flag option"
dictionary-val:
type: KeyValueList
help: "dictionary-val option"
- title: Group B
options:
iniopt:
type: IniType
help: "Help for '--iniopt'"
action: append
ansible_variable: 'ini_file'
nestedlist:
type: NestedList
help: "Help for '--nestedlist'"
action: append
- title: Group C
options:
uni-dep:
type: Value
help: "Help for --uni-dep"
required_when: "req-arg-a == yes"
multi-dep:
type: Value
help: "Help for --multi-dep"
required_when:
- "req-arg-a == yes"
- "req-arg-b == yes"
either-dep:
type: Value
help: "Help for --either-dep"
required_when:
- "req-arg-a == yes or req-arg-b == yes"
req-arg-a:
type: Bool
help: "Help for --req-arg-a"
req-arg-b:
type: Bool
help: "Help for --req-arg-b"
- title: Group D
options:
deprecated-way:
type: Value
help: "Deprecated way to do it"
new-way:
deprecates: deprecated-way
type: Value
help: "New way to do it"
- title: Group E
options:
tasks:
type: ListOfFileNames
help: |
This is example for option which is with type "ListOfFileNames" and has
auto propagation of "Allowed Values" in help. When we ask for --help it
will look in plugin folder for directory name as 'lookup_dir' value, and
will add all file names to "Allowed Values"
lookup_dir: 'post_tasks'
- Config section:
Plugin type can be one of the following:
provision
,install
,test
,other
.- Entry point is the main playbook for the plugin. by default this will refer to main.yml file
but can be changed to ant other file.
roles_path
:This is optional and it might be used when the plugin refers to a role which is introduced by that plugin. The
roles_path
then relatively points (from plugin.spec location) to the role. Let’s take a look at the following example of an ansible role (can be as a standalone project) which is also an infrared pluginmy_role ├── defaults │ └── main.yml ├── infrared_plugin │ ├── main.yml # Main playbook. All execution starts here │ └── plugin.spec # Plugin definition ├── tasks # Tasks of the role │ └── main.yml
In the above example main.yml
calls my_role
. In order to help infrared
find the role, roles_path
within config section needs to be set to ../
to point to the ansible role called from infrared_plugin/main.yml
To access the options defined in the spec from your playbooks and roles use
the plugin type with the option name.
For example, to access dictionary-val
use {{ provision.dictionary.val }}
.
Note
the vars-dict defined by Complex option types is nested under plugin_type
root key, and passed
to Ansible using --extra-vars
meaning that any vars file that has plugin_type
as a root key, will be
overriden by that vars-dict. See Ansible variable precidence for more details.
Include Groups¶
A plugin can reference preset control arguments to be included in its CLI
- Answers File:
- Instead of explicitly listing all CLI options every time, infrared plugins
can read their input from
INI
answers file, using--from-file
switch. use--generate-answers-file
switch to generate such file. It will list all input arguments a plugin accepts, with their help and defaults. CLI options still take precedence if explicitly listed, even when--from-file
is used. - Common Options:
--dry-run
: Don’t execute Ansible playbook. Only write generated vars dict to stdout--output
: Redirect generated vars dict from stdout to an explicit file (YAML format).--extra-vars
: Inject custom input into the vars dict
- Inventory:
Load a new inventory to active workspace. The file is copied to workspace directory so all
{{ inventory_dir }}
references in playbooks still point to workspace directory (and not to the input file’s directory).Note
This file permanently becomes the workspace’s inventory. To revert to original workspace the workspace must be cleaned.
- Ansible options:
--verbose
: Set ansible verbosity level--ansible-args
: Pass all subsequent input to Ansible as raw arguments. This is for power-users wishing to access Ansible functionality not exposed by infrared:infrared [...] --ansible-args step;tags=tag1,tag2;forks=500
Is the equivalent of:
ansible-playbook [...] --step --tags=tag1,tag2 --forks 500
Complex option types¶
Infrared extends argparse with the following option types. These options are nested into the vars dict that is later passed to Ansible as extra-vars.
- Value:
String value.
- Bool:
Boolean value. Accepts any form of YAML boolean:
yes
/no
,true
/false
on
/off
. Will fail if the string can’t be resolved to this type.
- Flag:
Acts as a flag, doesn’t parse any value. Will always return
true
.
- IniType:
Value is in
section.option=value
format.append
is the default action for this type, so users can provide multiple args for the same parameter. .. warning:: The IniType option is deprecated, use NestedDict instead of.
- NestedDict:
Value is in
section.option=value
format.append
is the default action for this type, so users can provide multiple args for the same parameter. Example:infrared example --foo option1=value1 --foo option2=value2
{"foo": {"option1": "value1", "option2": "value2"}}
- NestedList:
The NestedList option inherits NestedDict attributes and differs from NestedDict by value format. It composes value as list of dictionaries. Example:
infrared example --foo option1=value1 --foo option1=value2
{"foo": [{"option1": "value1"}, {"option1": "value2"}]}
- KeyValueList:
String representation of a flat dict
--options option1:value1,option2:value2
becomes:{"options": {"option1": "value1", "option2": "value2"}}
The nesting is done in the following manner: option name is split by -
delimiter and each part is
a key of a dict nested in side the previous one, starting with “plugin_type”. Then value is nested at the
inner-most level. Example:
infrared example --foo-bar=value1 --foo-another-bar=value2 --also_foo=value3
{
"provision": {
"foo": {
"bar": "value1",
"another": {
"bar": "value2"
}
},
"also_foo": "value3"
}
}
- FileValue
The absolute or relative path to a file. Infrared validates whether file exists and transform the path to the absolute.
- VarFile
- Same as the
FileValue
type but additionally Infrared will check the following locations for a file: argument/name/option_value
<spec_root>/defaults/argument/name/option_value
<spec_root>/var/argument/name/option_value
In the example above the CLI option name is
--argument-name
. The VarFile suites very well to describe options which point to the file with variables.For example, user can describe network topologies parameters in separate files. In that case, all these files can be put to the
<spec_root>/defaults/network
folder, and plugin specification can look like:config: plugin_type: provision entry_point: main.yml subparsers: my_plugin: description: Provisioner virtual machines on a single Hypervisor using libvirt groups: - title: topology options: network: type: VarFile help: | Network configuration to be used __LISTYAMLS__ default: defautl_3_nets
Then, the cli call can looks simply like:
infrared my_plugin --network=my_file
Here, the ‘my_file’ file should be present in the
/{defaults|var}/network
folder, otherwise an error will be displayed by the Infrared. Infrared will transform that option to the absolute path and will put it to the provision.network variable:provision.network: /home/user/..../my_plugin/defaults/my_file
That variable is later can be used in Ansible playbooks to load the appropriate network parameters.
Note
Infrared automatically checks for files with .yml extension. So the
my_file
andmy_file.yml
will be validated.- Same as the
- ListOfVarFiles
The list of files. Same as
VarFile
but represents the list of files delimited by comma (,
).
- VarDir
The absolute or relative path to a directory. Same as
VarFile
but points to the directory instead of file
Placeholders¶
Placeholders allow users to add a level of sophistication in options help field.
__LISTYAMLS__
:Will be replaced with a list of available YAML (
.yml
) file from the option’s settings dir. | Assume a plugin with the following directory tree is installed:plugin_dir ├── main.yml # Main playbook. All execution starts here ├── plugin.spec # Plugin definition └── vars # Add here variable files ├── yamlsopt │ ├── file_A1.yml # This file will be listed for yamlsopt │ └── file_A2.yml # This file will be listed also for yamlsopt └── another └──yamlsopt ├── file_B1.yml # This file will be listed for another-yamlsopt └── file_B2.yml # This file will be listed also for another-yamlsopt
Content of
plugin_dir/plugin.spec
:plugin_type: provision description: Example provisioner plugin subparsers: example: groups: - title: GroupA yamlsopt: type: Value help: | help of yamlsopt option __LISTYAMLS__ another-yamlsopt: type: Value help: | help of another-yamlsopt option __LISTYAMLS__
Execution of help command (
infrared example --help
) for the ‘example’ plugin, will produce the following help screen:usage: infrared example [-h] [--another-yamlsopt ANOTHER-YAMLSOPT] [--yamlsopt YAMLSOPT] optional arguments: -h, --help show this help message and exit GroupA: --another-yamlsopt ANOTHER-YAMLSOPT help of another-yamlsopt option Available values: ['file_B1', 'file_B2'] --yamlsopt YAMLSOPT help of yamlsopt option Available values: ['file_A1', 'file_A2']
Required Arguments¶
InfraRed provides the ability to mark an argument in a specification file as ‘required’ using two flags:
- ‘required’ - A boolean value tell whether the arguments required or not. (default is ‘False’)
- ‘required_when’ - Makes this argument required only when the mentioned argument is given and the condition is True.
- More than one condition is allowed with YAML list style. In this case the argument will be required if all the conditions are True.
For example, take a look on the plugin.spec
(‘Group C’) in Plugin Specification
Roles Dependencies¶
If a plugin depended on one or more Ansible role, it’s possible to mention them in a file. InfraRed makes use of Ansible Galaxy to install the roles file, therefore, the requirements for it should be exactly the same like in Ansible Galaxy:
- The file need to be in YAML format.
- The name of the file should be either
requirements.yml
orrequirements.yaml
For more details, please take a look on Ansible Galaxy documentation.
Argument Deprecation¶
To deprecate an argument in InfraRed, you need to add flag ‘deprecates’ in newer argument
When we use a deprecated argument, InfraRed will warn you about that and it will add the new argument in Ansible parameters with the value of the deprecated
For example, take a look on the plugin.spec
(‘Group D’) in Plugin Specification
Plugin Manager¶
The following commands are used to manage infrared plugins
- Add:
infrared will look for a plugin.spec file in each given source and register the plugin under the given plugin-type (when source is ‘all’, all available plugins will be installed):
infrared plugin add tests/example infrared plugin add example example2 infrared plugin add <git_url> [--revision <branch/tag/revision>] infrared plugin add all
Note
“–revision” works with one plugin source only.
- List:
List all available plugins, by type:
infrared plugin list ┌───────────┬─────────┐ │ Type │ Name │ ├───────────┼─────────┤ │ provision │ example │ ├───────────┼─────────┤ │ install │ │ ├───────────┼─────────┤ │ test │ │ └───────────┴─────────┘ infrared plugin list --available ┌───────────┬────────────────────┬───────────┐ │ Type │ Name │ Installed │ ├───────────┼────────────────────┼───────────┤ │ provision │ example │ * │ │ │ foreman │ │ │ │ openstack │ │ │ │ virsh │ │ ├───────────┼────────────────────┼───────────┤ │ install │ collect-logs │ │ │ │ packstack │ │ │ │ tripleo-overcloud │ │ │ │ tripleo-undercloud │ │ ├───────────┼────────────────────┼───────────┤ │ test │ rally │ │ │ │ tempest │ │ └───────────┴────────────────────┴───────────┘
Note
Supported plugin types are defined in plugin settings file which is auto generated. Check the Infrared Configuration for details.
- Remove:
Remove the given plugins (when name is ‘all’, all plugins will be removed):
infrared plugin remove example example2 infrared plugin remove all
- Freeze:
Output installed plugins with their revisions in a registry file format. When you need to be able to install somewhere else the exact same versions of plugins use
freeze
command:infrared plugin freeze > registry.yaml
- Import:
Installs all plugins from the given registry file. The registry file can be either path to local file or to URL:
infrared plugin import plugins/registry.yaml infrared plugin import https://url/to/registry.yaml
- Update:
Update a given Git-based plugin to a specific revision. The update process pulls the latest changes from the remote and checks out a specific revision if given, otherwise, it will point to the tip of the updated branch. If the “–skip_reqs” switch is set, the requirements installation will be skipped:
ir plugin update [--skip_reqs] [--hard-reset] name [revision]
- Execute:
Plugins are added as subparsers under
plugin type
and will execute the main playbook:infrared example
Registry Files¶
Registry files are files containing a list of plugins to be installed using the infrared plugin import. These files are used to hold the result from infrared plugin freeze for the purpose of achieving repeatable installations. The Registry file contains a pinned version of everything that was installed when infrared plugin freeze was run.
Registry File Format¶
The registry file is following the YAML format. Each section of the registry file contains an object which specifies the plugin to be installed:
src
: The path to the plugin. It can be either local path or git urlsrc_path
: (optional) Relative path within the repository where infrared plugin can be found.rev
: (optional) If the plugin source is git, this allows to specify the revision to pull.desc
: The plugin description.type
: Plugin type can be one of the following:provision
,install
,test
,other
.
Example of a registry file:
---
plugin_name:
src: path/to/plugin/directory
rev: some_revision_hash
src_path: /path/to/plugin/in/repo
desc: Some plugin description
type: provision/test/install/other