The new Python API is extremely complex.

Ansible’s tagline:
“Ansible is a radically simple IT orchestration engine that makes your applications and systems easier to deploy.”

Radically. Simple.

Now, check out the new Python API in v2.0:
http://docs.ansible.com/ansible/developing_api.html

I have no idea how to just run a normal ad-hoc command. No clue. And I’ve been digging at it for hours now. The old API? With Runner? Radically simple. Love it. Flawless.

Why was Runner removed? Like, I’d re-develop it, and Pull Request it in, gladly, but why was it removed in the first place? It’s so so so much simpler. It does what I need it to.

Further, could we have simple helper classes for all these random loading things? Like, something like:

inventory = Inventory()

Bam, now you’ve got your standard inventory, whatever you’d have if you didn’t specify an inventory on the command line. Is that not viable? Or even:

import defaults from somewhere
inventory = defaults.inventory()

Something to make life easier for those of us who don’t need to reinvent the whole wheel.

The API is not the product, the Ansible command line tools are, the API is there for the tool usage and it not the main interface. This has always been the case and stated in many occasions and in the documentation.

Runner was not simple (no class with a thousand+ line init method is) and was full of errors as many features were ‘bolted in’ and was sorely needing a redesign. It might have been ‘simple to call’ but it was not easy to maintain nor extend.

We removed runner and derived over a dozen classes that now handle the same functions in a much cleaner way internally. This also allowed us to correctly test the code and now validate plays, many previously silent errors are now caught. Adding new features is much simpler and clearly defined, as is the inheritance of such features, things are much more well defined and predictable. We also made many parts of the code more configurable and were able to push features to the plugins that were previously hardcoded.

As much as this has made it easier to maintain and extend Ansible, it has made the API interface more complex, but that, again, is something that we use internally for the command line tools, which have not really changed their interface and maintained their simplicity.

Any chance there will be documentation for the api similar to Ansible modules? (I’m looking for an AWS Lambda example using the 2.0 api after seeing Jose article)

I like the way things have been broken apart with Ansible 2.0 into smaller contained components, especially with how some of the modules have been rewritten… It was slightly painful to begin with but it certainly does allow for flexibility and makes it easier for more complicated use cases.

Admittedly I haven’t figured out how to run a playbook via the new api yet either.

It is ‘on the list’ to document it, but we also have many other things on that list that are much higher priority: documenting other plugins, migration path to 2.0, making plugins work under 1.9 AND 2.0, document testing, and in general bugfixes and features galore …

I have found that if you want to wrap the running of a playbook inside of a python script (so you can make the interface a bit more user friendly and translate cmdline args to --extra-vars, etc), the 2.0 API is much simpler.

`
from ansible.cli.playbook import PlaybookCLI

cliargs = {
‘playbook’:pb,
‘args’: {
‘-i’:host_list,
‘-e’:json.dumps(xvars),
}
}

def _run_playbook_v20(cliargs):
ansargs=[‘ansible-playbook’,cliargs[‘playbook’]]
[ansargs.extend([k,v]) for k,v in cliargs[‘args’].items()]
print “executing: {}”.format(’ '.join(ansargs))
cli = PlaybookCLI(ansargs)
cli.parse()
return cli.run()

`

Any chance there will be documentation for the api similar to Ansible
modules? (I'm looking for an AWS Lambda example using the 2.0 api after
seeing Jose article)

I like the way things have been broken apart with Ansible 2.0 into smaller
contained components, especially with how some of the modules have been
rewritten.. It was slightly painful to begin with but it certainly does
allow for flexibility and makes it easier for more complicated use cases.

Admittedly I haven't figured out how to run a playbook via the new api yet
either.

It is my hope that the 2.0 API will be stable for a good long time,
and documenting it would be a great and useful project for someone
that isn't the core team.

--g

Keep in mind that the API is in service of the CLI tools and that we still might change it as needed, also parts of it are still scheduled for a revamp, like inventory, roles and some other subsystems that we did not have time to finish up for 2.0.

I noticed the "making plugins work under 1.9 AND 2.0" and I wrote my first hybrid lookup plugin today. I consider this an odd thing to have and didn't consider it useful to bring up.

Would it be useful to share what I did ?
It may be a starting point to improve the mechanism and document it in the process.

Dag, yes please! We’ve been meaning to write that but have never had the time.

Not sure how to proceed next, but let me quickly show what I did for the filetree plugin. Since we have a need to run the existing playbooks with v1.9.4, but also test new stuff with v2.0.0.2 we have a need for a hybrid plugin.

The original v2.0 filetree plugin is available from:

     https://github.com/ansible/ansible/pull/14332

And the v1.9 filetree plugin is available from:

     https://github.com/ansible/ansible/pull/14628

The resulting hybrid lookup plugin required the following changes:

   - We need an implementation of LookupBase class on v1, with a custom
     v1 __init__()

   - In v1 we get the basedir from runner through __init__

   - Since we use the warning infrastructure, we need to have it imported
     from the right location (different from v1 and v2)

   - v1 also needs to import specialized functions, and we use the
     existence of these fuctions in globals() as the condition to see
     whether we are doing v1 or v2

   - We modified v1 LookupBase __init__() so it behaves a bit as v2
     LookupBase for our purpose, so the calls would be identical (we could
     have done something similar for other v1 functions, but prefered to
     stick as closely to v2 as possible, and consider v1 the exception as
     much as possible.

Below is the hybrid filetree plugin. Feedback is much appreciated:

I have done similar things to enable my plugins for v1 AND v2. Essentially:

`
from ansible import version as ANSIBLE_VERSION
if ANSIBLE_VERSION.startswith(‘2’):
else:

`

More recently, I am encouraging everyone to upgrade to v2 and trying to remove the v1 support esp as some things that I’m doing now simply aren’t possible in v1.

My concern, however, is that all of this is going to break with v3 or maybe even v2.2, resulting in some plugins requiring more code to deal with the version than actually do the thing it’s actually trying to do.

I wrote a small section about hybrid plugins with some advice.
Feel free to add your thoughts and experiences to it.

Here is the PR:

https://github.com/ansible/ansible/pull/14660

interesting. My hybrid (custom) lookup_plugin:

`
def run(self, terms=‘’, **kwargs):
‘’'Normalize terms and args and perform lookup.

Args:
terms (Optional[str,list]): Comma-delimited string (or python list) of terms.
kwargs (**dict): See Store() object for details. All kwargs are sent
when creating instance.

Caveats:
Ansible 2.0 sends all args as list::

“{{ lookup(‘myplug’, [‘key1’, ‘key2’]) }}” ==> [[‘key1’, ‘key2’]]
“{{ lookup(‘myplug’, ‘key1’, ‘key2’) }}” ==> [‘key1’, ‘key2’]

Ansible 1.9 sends first arg as list::

“{{ lookup(‘myplug’, [‘key1’, ‘key2’]) }}” ==> [‘key1’, ‘key2’]
“{{ lookup(‘myplug’, ‘key1’, ‘key2’) }}” ==> ERROR! Too many args.

‘’’

Normalize terms to be a list

if not isinstance(terms, list):
terms = terms.split()

Ansible 2.0 sends all args as list

Ansible 1.9 sends first arg as list

if len(terms) == 1 and isinstance(terms[0], list):
terms = terms[0]
…etc…

`

Now, I want to switch to use listify_lookup_plugin_terms!