1

I'm writing a new Ansible Custom Module in Python.

I want to be able to resolve paths in my code (in order to read their content) just as file and copy modules do when receiving a relative path in src argument (the path is relative to root-dir/roles/x/files), for instance.

Is it possible to do so? And if so, how?

As this seems to be impossible at the moment, I've added a feature request here.

U880D
  • 8,601
  • 6
  • 24
  • 40
Eyal Roth
  • 3,895
  • 6
  • 34
  • 45

2 Answers2

2

I've posted this question on "ansible-devel" group, and they gave me a direction which led me to an answer.

You can write a custom action plugin and place it under root-playbook-dir/action_plugins. The file name must be "your_mod_name.py"

The code would be something like this:

#!/usr/bin/python

from ansible.runner.lookup_plugins.file import LookupModule as FilePlugin
from ansible import utils

class ActionModule(object):

    def __init__(self, runner):
        self.runner = runner

    def run(self, conn, tmp_path, module_name, module_args, inject, complex_args=None, **kwargs):

        options = {}
        options.update(utils.parse_kv(module_args))

        file_plugin = FilePlugin()
        files_content = file_plugin.run(terms=options.get('myarg'), inject=inject)
        options['myarg'] = files_content[0]

        return self.runner._execute_module(conn=conn, tmp="/tmp", module_name="mymod", args=utils.serialize_args(options), inject=inject)

The code uses the "file" lookup plugin to read the file, and then executes the custom module and returns its result.

Eventually, your custom module will receive the file contents (not the path) in the "myarg" argument.

Eyal Roth
  • 3,895
  • 6
  • 34
  • 45
0

DEFAULT_ROLES_PATH is defined in Ansible's constants.py file:

DEFAULT_ROLES_PATH        = shell_expand_path(get_config(p, DEFAULTS, 'roles_path',       'ANSIBLE_ROLES_PATH',       '/etc/ansible/roles'))

By importing it from ansible.constant import DEFAULT_ROLES_PATH your module will follow any path changes that might occur in the Ansible project.

utils.py will likely be the next file you should become familiar with when writing modules as it contains the path-lookup functions (see path_dwim) and other Ansible-specific interaction functions.


If you want a plugin to work like the copy module, you can see the logic for the file pathing in the runner's __init__.py:

if items_plugin is not None and items_plugin in utils.plugins.lookup_loader:

    basedir = self.basedir
    if '_original_file' in inject:
        basedir = os.path.dirname(inject['_original_file'])
        filesdir = os.path.join(basedir, '..', 'files')
        if os.path.exists(filesdir):
            basedir = filesdir

where self.basedir is defined as a utils.default(basedir, lambda: os.getcwd()), where basedir is optionally the playbook directory.

By providing the proper argument spec to your module, you should be able to automagically take advantage of these underlying details.

At the bottom of your module, include the basic module_utils to hook into methods expected to exist ('fail_json', etc) and populate other defaults for you:

from ansible.module_utils.basic import *

I believe that with these portions in place, you'll trigger the file search path, but please tell me if I'm incorrect.

  • I'm sorry, but I don't see how this answers my question. – Eyal Roth Apr 30 '15 at 15:05
  • Use the constants.py DEFAULTS_ROLE_PATH variable to get the lookup path, then use it to resolve files in the same way core modules do. –  Apr 30 '15 at 15:06
  • This constant simply resolves to /etc/ansible/roles, which contains nothing. – Eyal Roth Apr 30 '15 at 15:07
  • Which module are you looking to modify? Can you post more details about what you're trying to accomplish? –  Apr 30 '15 at 15:09
  • I'm writing a new module. I want to be able to resolve paths in my code (in order to read their content) just as "file" and "copy" modules do when receiving a relative path in "src" argument (for instance). – Eyal Roth Apr 30 '15 at 15:12
  • Ah, misunderstood. Give me a minute to write it up –  Apr 30 '15 at 15:20
  • I'm not able to test your answer at the moment, but it seems to me that it won't work. a) I don't know the playbook directory (in the module's code), and even if I did, I still need the name of the currently running role. b) `os.getcwd()` is useless here as far as I can tell. c) it seems to me that the "_original_file" entry in "inject" is the path of the role file, which is what I need, but I have no clue how to get it. – Eyal Roth Apr 30 '15 at 16:23
  • I'm reasonably confident that the pathing will get expanded during the running to go back a directory and look for a 'files' dir, but let me know if this is tested and wrong and I'll happily remove it as an answer. –  Apr 30 '15 at 16:26
  • It doesn't work. I think it simply isn't possible. I even tried to use the "copy" core module as a custom module and see if it works then - and it doesn't. I think the core modules are being invoked in a different way which grants more abilities to the module. – Eyal Roth May 03 '15 at 12:43