I am developing an Ansible module and I have a problem with the architecture of my module. I would like to have something that looks like this:
- name: test my new module
hosts: localhost
tasks:
- name: run the new module
my_module:
instance:
name: test
state: present
other: mano
My module is called my_module. The idea is to ask for required fields in my "instance" dict depending on the value of "state". I tried to do it with required_if see Ansible documentation: https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec-dependencies
Below is my module in python :
#!/usr/bin/python
# Copyright: (c) 2018, Terry Jones <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: my_test
short_description: This is my test module
# If this is part of a collection, you need to use semantic versioning,
# i.e. the version is of the form "2.5.0" and not "2.4".
version_added: "1.0.0"
description: This is my longer description explaining my test module.
options:
name:
description: This is the message to send to the test module.
required: true
type: str
new:
description:
- Control to demo if the result of this module is changed or not.
- Parameter description can be a list as well.
required: false
type: bool
# Specify this value according to your collection
# in format of namespace.collection.doc_fragment_name
extends_documentation_fragment:
- my_namespace.my_collection.my_doc_fragment_name
author:
- Your Name (@yourGitHubHandle)
'''
EXAMPLES = r'''
# Pass in a message
- name: Test with a message
my_namespace.my_collection.my_test:
name: hello world
# pass in a message and have changed true
- name: Test with a message and changed output
my_namespace.my_collection.my_test:
name: hello world
new: true
# fail the module
- name: Test failure of the module
my_namespace.my_collection.my_test:
name: fail me
'''
RETURN = r'''
# These are examples of possible return values, and in general should use other names for return values.
original_message:
description: The original name param that was passed in.
type: str
returned: always
sample: 'hello world'
message:
description: The output message that the test module generates.
type: str
returned: always
sample: 'goodbye'
'''
from ansible.module_utils.basic import AnsibleModule
def run_module():
instance=dict(type='dict',
name=dict(type='dict'),
state=dict(type='dict'),
other=dict(type='dict')
)
required_if=[('state', 'present', ('name', 'other'))
]
argument_spec = dict(
instance=dict(type='dict', elements='dict', options=instance)
)
module = AnsibleModule(
argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True
)
module.exit_json(name="test")
def main():
run_module()
if __name__ == '__main__':
main()
The errors that it returns to me are the following :
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'str' object has no attribute 'get'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last)
AttributeError: 'str' object has no attribute 'get'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
Or if I try to modify and do otherwise :
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Invalid type dict for option '{'name': 'test', 'state': 'present', 'other': 'mano'}', elements value check is supported only with 'list' type"}
This error is I think due to the type and the element when defining the instance in argument_spec.
My goal is the following: I would like to have different sub-modules here for example "instance" we could have had other options and to be able to ask for mandatory fields depending on the sub-module here "instance" and its "state".
I saw that normally it should be possible to do it with "required_if" and "options" from AnsibleModule
There are a couple of problem here.
First,
required_if
can only refer to top-level options. By settingrequired_if=('state', 'present', ('name', 'other'))
, you are describing a condition that looks like:But that doesn't match the structure of your
argument_spec
.Second, your argument spec doesn't match your example usage, which is the source of the
AttributeError: 'str' object has no attribute 'get'
error. Your spec says that the values ofname
,state
, andother
should be dictionaries:But you're passing in strings. You also appear to be erroneously setting
type
ininstance
, andelements
, according to the documentation, is only useful withlist
options. I think you want:The above will support calling the module like this: