WARNING: exciting but uber-technical internals discussion. If you're just using the app and not hacking on it, you may ignore this.
I have implemented a method for Ansible to reuse code in modules without first having to transfer any dependencies and not have a build time step, both things
I really don't want to have. This is pretty cool actually.
For a preliminary example, see here:
https://github.com/ansible/ansible/blob/devel/library/slurp
(See this is really short!)
What this means is that when the string #<<INCLUDE_ANSIBLE_MODULE_COMMON>> occurs in a Python file, it will be automatically substituted by Ansible. No code generation is required, which means we aren't going to have a build step and people can just continue to run things from checkout.
The code behind this lives in https://github.com/ansible/ansible/blob/devel/lib/ansible/module_common.py
and that is what is automatically injected into each module.
You can do all sorts of things to specify required variables, valid choices for variable values, and aliases for variables.
For instance, this works:
module = AnsibleModule(
argument_spec = dict(
alpha = dict(required=True),
beta = dict(required=True, choices=['a','b','c']),
gamma = dict(required=True, aliases=['delta', 'epsilon']),
omega = dict(default='last')
)
)
module.exit_json(
alpha=module.params.get('alpha','?'),
beta=module.params.get('beta','?'),
gamma=module.params.get('gamma', '?'),
omega=module.params.get('omega', '?')
)
There is also a fail_json function. module.params is considered "supported" in the API, and returns the argument dictionary.
There's not much else. There may be a few more things added later, like md5 helper functions and so forth, but we'll consider that carefully.
Given that the base class is responsible for required arguments, aliases, and so forth, each module then only has to do the basic checks for things like file existence, ensuring that messaging is consistent.
I'd like those who develop modules in core to take a look at see if these classes look reasonably acceptable. If they are, we can begin simplifying existing modules to all use these functions.
Once this stabilizes, and 0.6 is released, this will allow for cleaning up the module authoring documentation, explaining both this and how to do it the
hard way.
Note that the "MODULE_COMMON" stuff is going to become a rather strict API. Any method that is not private (does not start with "_", can never be removed or change in signature, other than adding optional key=value arguments on the end of the call chain. This means we need to get it mostly right now, so if you can think of any capabilities this can't do, or anything that needs to be added to the argument_spec, we should do it now.
The command module is obviously a bit different in argument parsing. I see the command module subclassing the AnsibleModule class. Actually, that's exactly what it will do.
The "file" module is currently written such that it does not require python_simplejson, such that ansible can be used to install python-simplejson by way of adding an EPEL configuration file for yum, etc. This is primarily a thing for RHEL and CentOS 4, and I honestly don't think we need to keep that up.
The raw module can still be used to install RPMs and bootstrap python-simplejson in fringe cases.
If you want to test library/slurp now, hacking/test-module has been adapted to understand this, as has ansible proper. If you run hacking/env-setup it will essentially code-generate on the fly, and if you need to dig inside the file it generates, it will tell you where it saves it.
Comments welcome.
--Michael