First of all, please have a look at the Ansible module development guide and get familiar with the general Ansible module layout.
When looking at actual modules in this repository (foreman_domain
is a nice short example), you will notice a few differences to a "regular" Ansible module:
- Instead of
AnsibleModule
, we useForemanEntityAnsibleModule
(and a few others, seeplugins/module_utils/foreman_helper.py
) which provides an abstraction layer for talking with the Foreman API - Instead of Ansible's
argument_spec
, we provide an enhanced version calledentity_spec
. It handles the translation from Ansible module arguments to Foreman API parameters, as nobody wants to writeorganization_ids
in their playbook when they can writeorganizations
The rest of the module is usually very minimalistic:
- Create a Sub class of
ForemanEntityAnsibleModule
for your module calledForemanMyEntityModule
to work withMyEntity
foreman resource and use this one for your module definition. Eg: If the foreman entity is namedArchitecture
:[...] class ForemanArchitectureModule(ForemanEntityAnsibleModule): pass [...]
- Connect to the API and run the module
Eg: Like previous example, if the foreman entity is named
Architecture
:[...] def main(): module = ForemanArchitectureModule( argument_spec=dict( [...] ), entity_spec=dict( [...] ), ) with module.api_connection(): module.run() if __name__ == '__main__': main()
You can see a complete example of simple module in foreman_architecture
In some cases, you will have to handle some custom workflows/validations, you can see some examples in foreman_bookmark
, foreman_compute_attribute
, foreman_hostgroup
, foreman_provisioning_template
...
The entity_spec
can be seen as an extended version of Ansible's argument_spec
. It understands more parameters (e.g. flat_name
) and supports more type
s than the original version. An argument_spec
will be generated from an entity_spec
. Any parameters not directly known or consumed by entity_spec
will be passed directly to the argument_spec
.
In addition to Ansible's argument_spec
, entity_spec
understands the following types:
type='entity'
The referenced value is another Foreman entity. This is usually combined withflat_name=<entity>_id
. If no flat_name is provided, fallback to<entity>_id
where entity is the entity_spec key. egdefault_organization=dict(type='entity')
=>flat_name=default_organization_id
.type='entity_list'
The referenced value is a list of Foreman entities. This is usually combined withflat_name=<entity>_ids
. If no flat_name is provided, fallback tosingularize(<entity>)_ids
where entity is the entity_spec key. egorganizations=dict(type='entity_list')
=>flat_name=organization_ids
.type='nested_list'
The referenced value is a list of Foreman entities that are not included in the main API call. The module must handle the entities separately. See domain parameters inforeman_domain
for an example. The sub entities must be described byentity_spec=<sub_entity>_spec
.type='invisible'
The parameter is available to the API call, but it will be excluded from Ansible'sargument_spec
.search_by='login'
: Used withtype='entity'
ortype='entity_list'
. Field used to search the sub entity. Defaults to value provided byENTITY_KEYS
or 'name' if no value found.search_operator='~'
: Used withtype='entity'
ortype='entity_list'
. Operator used to search the sub entity. Defaults to '='. For fuzzy search use '~'.resource_type='organizations'
: Used withtype='entity'
ortype='entity_list'
. Resource type used to build API resource PATH. Defaults to pluralized entity key.resolve=False
: Defaults to 'True'. If set to false, the sub entity will not be resolved automatically.ensure=False
: Defaults to 'True'. If set to false, it will be removed before sending data to the foreman server.
flat_name
provides a way to translate the name of a module argument as known to Ansible to the name understood by the Foreman API.
You can add new or override generated Ansible module arguments, by specifying them in the argument_spec
as usual.