I presented this slides at the Ansible Munich Meetup on Feb 22cd 2016. You can find the recording of the talk here: https://www.youtube.com/watch?v=B7K1ETPyzoQ (starts at 1:18). This talk is giving a 101 level introduction on developing Ansible Module in Python.
2. Who is standing in front of you?
• I’m working with VMware’s network virtualization product called NSX
• I’m the co-organizer for this Meetup & the OpenStack Munich Meetup group
• I’m living in Munich since 14 years
• I’ve spend 3 years working at VMware as Systems Engineer & Solution Architect,
7 years as a Systems Engineer at Cisco, and I was a networking / OS consultant and
developer before
• Topics I love to discuss and work with:
Configuration Management, Automation, Containers / ‘Cloud’, OpenStack, Networking, ...
Yves Fauser
Technical Product Manager @ VMware
3. Why developing your own Modules
• There are tons of great build-in core and extra modules for Ansible – So why build your own?
• Generally if you only work with Files, Packages and Services on standard OSs you are
covered, and there is no need to develop your own stuff
• But sometimes you want to do ‘strange stuff’, and then making this idempotent is hard, e.g.
• You might want to interact with APIs of products and ‘cloud services’
• You might want to use commands / CLIs that were not build to work well with configuration
management (e.g. that don’t implement granular return codes but give you lots of stdout garbage)
• I had to do both, interact with an API and use CLI tools with a lot of stdout that I needed to
interpret
• Working with the VMware NSX API
• Using VMware’s OVFTool to deploy VMs into vCenter *
* We will show the use the OVFTool Module as an example later
4. Why using Python to develop Modules
• Ansible generally supports writing Modules in any language, e.g Python, C, Ruby, Shell, Go, …
• The only requirements are:
• The language needs to be able to implement File I/O
• The language needs to be able write JSON formatted output to STDOUT
• Why I am using Python:
• First and foremost, I’m a Pythonist – I really like this language
• Ansible itself and all its core Modules are written in Python – You have a lot to read and learn from!
• There are very easy and ready to use ‘boilerplates’ and ‘helper functions’ for Ansible written for Python
• Besides Python, the second most often used language is shell script
5. Getting started – a few important bits of knowledge
• When writing a custom module
you call it from a Play like this:
• Alternatively you can use ‘action’
• In this case ‘test_module’ can be
test_module.py, or test_module.sh,
test_module.<you_name_it> in the
library path
• Ansible searches for modules the path specified by ANSIBLE_LIBRARY or the --module-path
command line option
• The directory ”./library”, alongside your
top level playbooks, is also automatically
added as a search directory
• I personally always use the “./library”
option when developing modules
6. Getting started – The ‘boilerplate’ code of every module
• You always start with a boilerplate code like this:
Uuuhh … that’s ugly, an
import statement at the
bottom of the file, and
importing *, that’s against all
python style guides!
Why’s that?
7. Ansible code creation
• You module code is not the final code that gets executed
• Ansible takes the code and constructs a single Python script that gets copied to the remote
system and then executed. The statement ‘from ansible.module_utils.basic import *’
imports ~1600 lines of code into this final script
8. Ansible code creation
Here’s the place were it
inserts the Ansible Module
code after your code
And the function main() is
called right at the end of the
code
9. Getting started – The Module argument_spec dictionary
mandatory variable
mandatory variable, do no log (e.g. passwords)
mandatory variable, with fixed possible values
optional variable taking any value
optional variables checked for existence later
variable having a default value
variable with a strict type
variable with an alias
checks if at least one variable in the list is present
ensures that only one of two variables are set
10. Getting started – The Module argument_spec dictionary
• You can access the modules parameters dictionary with ‘module.params[‘key’]’
11. Exiting the module
• Exiting the module is done using
‘module.exit_json’ for successful returns,
and ‘module.fail_json’ to fail the task
Exit the module with the changed flag set and
with additional variables returned to the Play
Exit the module successfully, but
signaling that there was no change
Exit the module with a failure
12. Putting it to work – Example Module
• I love to go running, but not when it’s too cold. I will let Ansible decide for me
You can find the code here: https://gist.github.com/yfauser/90c0955827c74d83d6a2
If the temperature returned by Open Weather
Map in my city is bellow my threshold –
I’ll stay at home (keep my current state)
If the temperature returned by Open Weather
Map in my city is above my threshold – I’ll
leave the house (change my state)
14. Putting it to work – Catching errors
• OK, but what about failed states?
Let’s enhance our code a bit
Using try/except as well as http status codes
as indications of failed conditions
15. Putting it to work – Catching errors
Here we are passing the module
to the sub-function to call
module.fail_json inside of it
16. Use OS commands with Python based Modules
• The python Ansible base code has a great method to interact with the OS
• The difference from running this inside of your python based module instead of ‘command’ in
your Play is that you can interpret the output returned by the OS command more flexibly
• The response object will be a list with two items:
• [0] holds the return code of the command as an integer, so you can react based on the return code
• [1] holds the stdout of the command as a string – So you can do things like:
• Pass it to parsers like ‘json.loads’ to convert it to dictionaries
• Search in it with ‘.find’
• Count, sort, pass it through regexp, etc.
17. Use OS commands with Python based Modules
• Lets change our example to
use OS commands (curl)
format the owm url with all mandatory
url parameters
use the OS command curl to send
the request
check if curl gave a positive return code
check if we received a positive ret. code
convert curl stdout to a python dict
extract temperature value from dict
You can find the code here:
https://gist.github.com/yfauser/b2377e
ee842a1cd2a899
18. Use OS commands with Python based Modules
• Here’s another real life example of using the OS command option
• This code uses
module.run_command
to invoke the
ovftool to deploy
a VM into
vCenter
You can find the code here:
https://github.com/vmware/nsxansible/blob/master/library/nsx_deploy_ova.py
19. Debugging your module
• What is the #1 tool for debugging Python code? => ‘print’ ;-)
• Unfortunately sending non-JSON
formatted output to stdout will not
result in any output on the console
when running your Play:
• Modules must also not output anything on standard error, because the system will merge
standard out with standard error and prevent the JSON from parsing. Capturing standard
error and returning it as a variable in the JSON on standard out is fine, and is, in fact, how the
command module is implemented
• In short => Never use ‘print’
20. Debugging your module code
• Instead of printing to stdout, send your debug data into a variable (e.g. a list) and
pass it to the module.exit_json or
module.fail_json method:
21. What’s this ‘supports_check_mode’ about?
• Ansible Playbooks can run in check-mode
using the --check option
• If ‘supports_check_mode’ is set to:
• ‘True’: It’s up to you to make sure your module doesn’t
apply changes, but only checks if it would have made
changes if Ansible runs in check mode
• ‘False’ or unset: The task using your module will
be skipped if Ansible runs in check mode
22. Inline Documentation
• Ansible supports the generation of module
documentation on their Web-Site from
inline Documentation in the module code
• You can read about all the details here:
http://docs.ansible.com/ansible/developing_
modules.html#documenting-your-module
23. Developing Ansible Modules on a MAC
• When developing a python based module on a MAC you might run into this:
• When you run the module it fails to import
libraries because it doesn’t use the right
interpreter and path
• Solution 1, use the ‘#!/usr/bin/env python’ shebang instead of ‘#!/usr/bin/python’:
• Don’t use this! As it’s an anti-pattern if you want to get this into ansible-modules-extra or core
Solution 2, define the interpreter path in the hosts file
This is the proper solution as this solves it on a per host level for those systems that need it