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.

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 define the CLI this plugin exposes, its name and its type.

plugin_type: provision
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"

                  dictionary-val:
                      type: KeyValueList
                      help: "dictionary-val option"

            - title: Group B
              options:
                  iniopt:
                      type: IniType
                      help: "Help for '--iniopt'"
                      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"

                  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"

Plugin type can be one of the following: provision, install, test, other.

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.

  • 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.

  • 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:

    plugin_type: provision
    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 and my_file.yml will be validated.

  • 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:

  1. ‘required’ - A boolean value tell whether the arguments required or not. (default is ‘False’)
  2. ‘required_when’ - Makes this argument required only when the mentioned argument is given and has the exact mentioned value. (More than one condition is allowed with YAML list style)

For example, take a look on the plugin.spec (‘Group C’) in Plugin Specification

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 the 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 <git_url>
infrared plugin add all
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 plugin (when name is ‘all’, all plugins will be removed):

infrared plugin remove example
infrared plugin remove all
Execute:

Plugins are added as subparsers under plugin type and will execute the main.yml playbook:

infrared example