Custom Ansible module issue

800 Views Asked by At

I'm writing a custom Ansible module and the python works fine but I'm having trouble turning it into a module. I've created a dummy module to reduce complexity and I'm getting the same issue. So the problem is with my Ansible boiler plate code. We're using version 2.9.2 for Ansible, python 3.6.9.

I've passed the two required parameters into the module and it should return them in a dictionary with the other two two set as the default.

I've created the AnsibleModule with the parameters, pulled them out used them in the python script then checked the result and exited the module. What have I missed?

Directory structure:

ansible.cfg

inventory.ini

module_test.yml

library/module_test.py

Here is the Ansible test script:


 - name: Testing module
   any_errors_fatal: true
   connection: local
   gather_facts: false
   hosts: localhost

   tasks:
     - name: test the ansible module
       module_test:
         var1: "test var 1"
         var2: "test var 2"
       register: output
     - name: debug output
       debug:
         msg: "{{ output }}"

Here is the python module:

#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule


class AnsibleModuleTest(object):

    def __init__(self, var1, var2, var3, var4):

        self._var1 = var1
        self._var2 = var2
        self._var3 = var3
        self._var4 = var4

    def run(self, check):

        if check:
            return {"result": True, "data": {"var1": self._var1, "var2": self._var2, "var3": self._var3, "var4": self._var4}}
        else:
            return {"result": False, "error": "fail"}


def main():

    module = AnsibleModule(
            argument_spec=dict(
                var1=dict(required=True),
                var2=dict(required=True),
                var3=dict(required=False, default=None),
                var4=dict(required=False, default=None),
                supports_check_mode=False
            )
        )

    var1 = module.params["var1"]
    var2 = module.params["var2"]
    var3 = module.params["var3"]
    var4 = module.params["var4"]

    _test_module = AnsibleModuleTest(var1, var2, var3, var4)
    _returned_data = _test_module.run(True)

    if _returned_data["result"]:
        result = dict(msg=_returned_data["data"], changed=False)
        module.exit_json(**result)
    else:
        module.fail_json(msg=_returned_data["error"])


if __name__ == '__main__':
    main()

Here is the traceback:

TASK [test the ansible module] **************************************************************************************************
task path: /root/test_role/module_test.yml:9
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: root
<127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367 `" && echo ansible-tmp-1594296428.9225461-257853634365367="` echo /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367 `" ) && sleep 0'
Using module file /root/test_role/library/module_test.py
<127.0.0.1> PUT /root/.ansible/tmp/ansible-local-34085udiksek/tmphun0ptk_ TO /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/ /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py", line 102, in <module>
    _ansiballz_main()
  File "/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py", line 94, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py", line 40, in invoke_module
    runpy.run_module(mod_name='ansible.modules.module_test', init_globals=None, run_name='__main__', alter_sys=True)
  File "/usr/lib/python3.6/runpy.py", line 205, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/modules/module_test.py", line 52, in <module>
  File "/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/modules/module_test.py", line 32, in main
  File "/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/module_utils/basic.py", line 639, in __init__
  File "/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/module_utils/basic.py", line 1847, in _set_fallbacks
AttributeError: 'bool' object has no attribute 'get'

fatal: [localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1594296428.9225461-257853634365367/AnsiballZ_module_test.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible.modules.module_test', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib/python3.6/runpy.py\", line 205, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.6/runpy.py\", line 96, in _run_module_code\n    mod_name, mod_spec, pkg_name, script_name)\n  File \"/usr/lib/python3.6/runpy.py\", line 85, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/modules/module_test.py\", line 52, in <module>\n  File \"/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/modules/module_test.py\", line 32, in main\n  File \"/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/module_utils/basic.py\", line 639, in __init__\n  File \"/tmp/ansible_module_test_payload_13zt59h5/ansible_module_test_payload.zip/ansible/module_utils/basic.py\", line 1847, in _set_fallbacks\nAttributeError: 'bool' object has no attribute 'get'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}
1

There are 1 best solutions below

0
On

Fixed, I have my boiler plate code, should've followed the Ansible page rather than other people's solutions.

#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule


class AnsibleModuleTest(object):

    def __init__(self, var1, var2, var3, var4):

        self._var1 = var1
        self._var2 = var2
        self._var3 = var3
        self._var4 = var4

    def run(self, check):

        if check:
            return {"result": True,
                    "data": {"var1": self._var1, "var2": self._var2, "var3": self._var3, "var4": self._var4}
                    }
        else:
            return {"result": False, "error": "fail"}


def run_module():

    module_args = dict(
        var1=dict(type="str", required=True),
        var2=dict(type="str", required=True),
        var3=dict(type="str", required=False, default=None),
        var4=dict(type="str", required=False, default=None)
    )
    module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

    _test_module = AnsibleModuleTest(
        module.params["var1"],
        module.params["var2"],
        module.params["var3"],
        module.params["var4"]
    )
    _returned_data = _test_module.run(True)

    if _returned_data["result"]:
        result = dict(msg=_returned_data["data"], changed=False)
    else:
        result = dict(msg=_returned_data["error"], changed=False)
        module.fail_json(**result)

    if module.check_mode:
        module.exit_json(**result)

    module.exit_json(**result)


def main():

    run_module()


if __name__ == '__main__':
    main()