Writing Ansible Modules (CLT'19)

Martin Schütte
Martin SchütteSystems Engineer at Hamburg
Writing Ansible Modules
Martin Schütte
Outline
Concepts
Writing Python Modules
Simple Example
Patterns
Module Execution – In-Depth
Low Level Module Execution
AnsiballZ
Debugging
Beyond Python
Conclusion
Martin Schütte | Ansible Modules | CLT’19 2/50
Concepts
Assumptions
You …
• configure servers or other IT systems
• have already used (or tried) Ansible
• can write shell or Python scripts
• have some “special” device or API
or a CLI that does not fit into a simple command
This talk …
• is no Ansible introduction
• has too many slides, I will skip some
Martin Schütte | Ansible Modules | CLT’19 3/50
Ansible – Concepts and Naming
Ansible is a radically simple IT automation platform.
• controller
• target host
• playbook
• role
• task
• module
Martin Schütte | Ansible Modules | CLT’19 4/50
Example: Simple Playbook
---
- hosts: webserver
vars:
apache_version: latest
tasks:
- name: ensure apache is at given version
yum:
name: httpd
state: ”{{ apache_version }}”
- hosts: dbserver
roles:
- ansible-role-postgresql
Martin Schütte | Ansible Modules | CLT’19 5/50
What is a Module?
some code snippet to run on the (remote) host
executable with input and output
Martin Schütte | Ansible Modules | CLT’19 6/50
Minimal Module
#!/bin/sh
echo ’{”foo”: ”bar”}’
exit 0
#!/usr/bin/python
if __name__ == ’__main__’:
print(’{”foo”: ”bar”}’)
exit(0)
Martin Schütte | Ansible Modules | CLT’19 7/50
File Locations: library and module_utils
my_role/
meta
defaults
tasks
library
my_module.py
module_utils
util.py
• role can use Ansible module my_module
• import * from util
finds Python module in module_utils
• for “larger” libraries use packages (pip/rpm/dpkg)
Martin Schütte | Ansible Modules | CLT’19 8/50
Action Plugins call Modules
• plugins run on the controller
• may prepare input for modules
• may handle “special” connections (non SSH or WinRM)
• may implement actions on the controller, e. g. debug
• defaults to normal to run module on target host
Martin Schütte | Ansible Modules | CLT’19 9/50
Writing Python Modules
Avoid Writing Own Code
• get_url – Downloads files
• uri – Interacts with webservices
• wait_for – Waits for a condition before continuing
• set_fact – Set host facts from a task
- name: Wait for port 8000 to become open on the host
wait_for:
port: 8000
delay: 10
- name: wait for service to become available
uri:
url: ’https://{{ inventory_hostname }}:{{ svc_port }}/service’
return_content: yes
register: content
until: content.status == 200
retries: 60
delay: 10
when: not ansible_check_mode
Martin Schütte | Ansible Modules | CLT’19 10/50
Documentation
ANSIBLE_METADATA = {’metadata_version’: ’1.1’,
’status’: [’stableinterface’],
’supported_by’: ’core’}
DOCUMENTATION = ’’’
---
module: ping
version_added: historical
short_description: Try to connect to host, verify a usable
python and return C(pong) on success
...
’’’
EXAMPLES = ’’’
# Induce an exception to see what happens
- ping:
data: crash
’’’
RETURN = ’’’
ping:
description: value provided with the data parameter
returned: success
type: string
sample: pong
’’’
Martin Schütte | Ansible Modules | CLT’19 11/50
ansible-doc
[mschuett@work:/home/mschuett] $ ansible-doc --snippet ping
- name: Try to connect to host, verify a usable python and return ‘pong’ on success
ping:
data: # Data to return for the ‘ping’ return value. If this
parameter is set to ‘crash’, the module will cause an
exception.
[mschuett@work:/home/mschuett] $ ansible-doc ping
> PING (.../site-packages/ansible/modules/system/ping.py)
A trivial test module, this module always returns ‘pong’ on
successful contact. It does not make sense in playbooks, but
it is useful from ‘/usr/bin/ansible’ to verify the ability to
login and that a usable Python is configured. This is NOT ICMP
ping, this is just a trivial test module that requires Python
on the remote-node. For Windows targets, use the [win_ping]
module instead. For Network targets, use the [net_ping] module
instead.
OPTIONS (= is mandatory):
- data
Data to return for the ‘ping’ return value.
...
Martin Schütte | Ansible Modules | CLT’19 12/50
ping.py
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
data=dict(type=’str’, default=’pong’),
),
supports_check_mode=True
)
if module.params[’data’] == ’crash’:
raise Exception(”boom”)
result = dict(
ping=module.params[’data’],
)
module.exit_json(**result)
if __name__ == ’__main__’:
main()
Martin Schütte | Ansible Modules | CLT’19 13/50
AnsibleModule
useful common methods:
• argument_spec for parameters
• supports_check_mode
• exit_json(), fail_json()
• atomic_move(), run_command()
• bytes_to_human(), human_to_bytes()
Other module_utils:
• api: function/decorator @rate_limit()
• timeout: function/decorator @timeout(secs)
• _text: new and unstable to_text()
Martin Schütte | Ansible Modules | CLT’19 14/50
Pattern: Idempotency
• Playbooks can run many times
• As few changes as possible
• Only perform required actions
1. Get spec parameters
2. Check actual state of system
if =: done, do nothing
if ̸=: action to change state
Martin Schütte | Ansible Modules | CLT’19 15/50
Pattern: Check Dependencies
try:
import psycopg2
import psycopg2.extras
except ImportError:
HAS_PSYCOPG2 = False
else:
HAS_PSYCOPG2 = True
def main():
module = AnsibleModule()
# ...
if not HAS_PSYCOPG2:
module.fail_json(
msg=”the python psycopg2 module is required”)
Martin Schütte | Ansible Modules | CLT’19 16/50
Check Mode/“Dry Run”
• Return information but never apply changes
• Optional but recommended for modules
• Interface provided by AnsibleModule
Example without support:
m = AnsibleModule(
argument_spec=...,
supports_check_mode=False
)
$ ansible-playbook -v --check playbook.yml
...
TASK [role-pymod : role_pymod | get_release_py] **************
skipping: [server] => {”changed”: false, ”msg”: ”remote module
(get_release_py) does not support check mode”}
Martin Schütte | Ansible Modules | CLT’19 17/50
Check Mode/“Dry Run”
def update_permanent_hostname(self):
name = self.module.params[’name’]
permanent_name = self.get_permanent_hostname()
if permanent_name != name:
if not self.module.check_mode:
self.set_permanent_hostname(name)
self.changed = True
Important: Modules without AnsibleModule (or non-Python)
have to handle this on their own!
⇒ test the _ansible_check_mode parameter
Martin Schütte | Ansible Modules | CLT’19 18/50
Common Return Values/Result Attributes
Common
• changed
• failed
• rc
• msg
• results
• invocation
• skipped
• stderr, stderr_lines
• stdout, stdout_lines
• backup_file
Internal use
• ansible_facts
• exception
• warnings
• deprecations
Martin Schütte | Ansible Modules | CLT’19 19/50
Other Common Return Value: Diff
Example from hostname:
if changed:
kw[’diff’] = {’after’: ’hostname = ’ + name + ’n’,
’before’: ’hostname = ’ + name_before + ’n’}
Example output, sample module:
TASK [role-minimal : role_minimal | py_sample_08] ************
task path: /vagrant/roles/role-minimal/tasks/main.yml:23
--- before
+++ after
@@ -1,3 +1,3 @@
common line
-old value
+new vale
common line
changed: [server] => {”changed”: ”true”, ”foo”: ”bar”}
Martin Schütte | Ansible Modules | CLT’19 20/50
Example: Set Facts
In a playbook:
- do_something:
# ...
register: result_var
- set_fact:
foo: ”{{ result_var.results | list }}”
In a module (from hostname):
kw = dict(changed=changed, name=name,
ansible_facts=dict(ansible_hostname=name.split(’.’)[0],
ansible_nodename=name,
ansible_fqdn=socket.getfqdn(),
ansible_domain=’.’.join(
socket.getfqdn().split(’.’)[1:])))
module.exit_json(**kw)
Martin Schütte | Ansible Modules | CLT’19 21/50
Example: String Formatting
When Jinja2 is not enough for variables and formatting …
Random example:
- name: calculate cluster config lines
calc_some_cluster_config:
hostname: ”{{ ansible_fqdn }}”
port: ”{{ application_port }}”
group: ”{{ groups[’appserver’] }}”
register: cluster_config
- name: cluster config
lineinfile:
path: ”{{ basedir }}/cluster/config”
line: ”{{ item }}”
with_items:
- ”hosts={{ cluster_config.tcp_hostlist | join(’,’) }}”
- ”exclude={{ cluster_config.exclude_roles | join(’,’) }}”
notify: appserver restart
Martin Schütte | Ansible Modules | CLT’19 22/50
Pattern: Provider Dict
- name: apply IOS config
ios_config:
provider: ”{{ ios_creds }}”
src: my_config2b.txt
- name: Import Zabbix json template configuration
local_action:
module: zabbix_template
server_url: http://127.0.0.1
login_user: username
login_password: password
template_name: Apache2
template_json: ”{{ lookup(’file’, ’apache2.json’) }}”
template_groups:
- Webservers
Martin Schütte | Ansible Modules | CLT’19 23/50
Common Module Pattern
class Controller(object):
def __init__(module)
def do_something()
def main():
module = AnsibleModule(...)
ctl = Controller(module)
result = ctl.do_something()
module.exit_json(**result)
if __name__ == ’__main__’:
main()
• simple access to input parameters
• access to util functions (e. g. module.run_command())
• difficult to unit test
Martin Schütte | Ansible Modules | CLT’19 24/50
Common Module Pattern II
class Controller(object):
def __init__(many_variables, ...)
def do_something()
def main():
module = AnsibleModule(...)
ctl = Controller(many_variables, ...)
result = ctl.do_something()
module.exit_json(**result)
if __name__ == ’__main__’:
main()
• good isolation
• re-use and testability
• often more complex
Martin Schütte | Ansible Modules | CLT’19 25/50
Module Execution – In-Depth
Minimal Module – Verbose Output
TASK [role-minimal : role_minimal | bash_sample_01] ****************************
task path: /vagrant/roles/role-minimal/tasks/main.yml:5
<192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC
<192.168.56.202> (0, ’/home/vagrantn’, ’’)
<192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC
<192.168.56.202> (0, ’ansible-tmp-1548772995.78-225807547469627=/home/vagrant/.ansible/tmp/ansi
Using module file /vagrant/roles/role-minimal/library/bash_sample_01
<192.168.56.202> PUT /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpnrj9rd TO /home/va
<192.168.56.202> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHo
<192.168.56.202> (0, ’sftp> put /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpnrj9rd
<192.168.56.202> PUT /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpgN3ZKr TO /home/va
<192.168.56.202> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHo
<192.168.56.202> (0, ’sftp> put /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpgN3ZKr
<192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC
<192.168.56.202> (0, ’’, ’’)
<192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC
Escalation succeeded
<192.168.56.202> (0, ’{”foo”:”bar”}rn’, ’Shared connection to 192.168.56.202 closed.rn’)
ok: [server] => {
”changed”: false,
”foo”: ”bar”
}
TASK [role-minimal : role_minimal | bash_sample_01] ****************************
task path: /vagrant/roles/role-minimal/tasks/main.yml:5
Martin Schütte | Ansible Modules | CLT’19 26/50
Argument File Format
Old-style default: key=value
[vagrant@server ~]$ cat $TMPDIR/args
_ansible_version=2.7.5
_ansible_selinux_special_fs=’[’”’”’fuse’”’”’,
’”’”’nfs’”’”’, ’”’”’vboxsf’”’”’, ’”’”’ramfs’”’”’,
’”’”’9p’”’”’]’ _ansible_no_log=False
_ansible_module_name=py_sample_01
_ansible_tmpdir=/home/vagrant/.ansible/tmp/ansible-tmp-
1548756932.8-240778677026680/ _ansible_verbosity=3
_ansible_keep_remote_files=True
_ansible_syslog_facility=LOG_USER _ansible_socket=None
_ansible_remote_tmp=’~/.ansible/tmp’ _ansible_diff=False
_ansible_debug=False _ansible_shell_executable=/bin/sh
_ansible_check_mode=False foo=baz
→
→
→
→
→
→
→
→
→
→
→
Martin Schütte | Ansible Modules | CLT’19 27/50
Want JSON Option
#!/bin/sh
# WANT_JSON
echo ’{”foo”:”bar”}’
exit 0
#!/usr/bin/python
# WANT_JSON
if __name__ == ’__main__’:
print(’{”foo”:”bar”}’)
exit(0)
Martin Schütte | Ansible Modules | CLT’19 28/50
Argument File Format
With WANT_JSON flag:
[vagrant@server ~]$ cat $TMPDIR/args
{”_ansible_version”: ”2.7.5”, ”_ansible_selinux_special_fs”:
[”fuse”, ”nfs”, ”vboxsf”, ”ramfs”, ”9p”],
”_ansible_no_log”: false, ”_ansible_module_name”:
”bash_sample_02”, ”_ansible_tmpdir”:
”/home/vagrant/.ansible/tmp/ansible-tmp-1548756933.19-
248002152304605/”, ”_ansible_verbosity”: 3,
”_ansible_keep_remote_files”: true,
”_ansible_syslog_facility”: ”LOG_USER”, ”_ansible_socket”:
null, ”_ansible_remote_tmp”: ”~/.ansible/tmp”,
”_ansible_diff”: false, ”_ansible_debug”: false,
”_ansible_shell_executable”: ”/bin/sh”,
”_ansible_check_mode”: false, ”foo”: ”baz”}
→
→
→
→
→
→
→
→
→
→
→
Martin Schütte | Ansible Modules | CLT’19 29/50
Remove Argument File: JSONARGS
#!/bin/sh
ARGS=’<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>’
echo ”arguments: $ARGS” | logger --tag $(basename ”$0”)
echo ’{”foo”:”bar”}’
exit 0
#!/usr/bin/python
import syslog
args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>”””
if __name__ == ’__main__’:
syslog.openlog()
syslog.syslog(args)
print(’{”foo”:”bar”}’)
exit(0)
Martin Schütte | Ansible Modules | CLT’19 30/50
JSONARGS – Verbose Output
<192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant
<192.168.56.202> SSH: EXEC ssh $SSH_OPTS -tt 192.168.56.202 +
’/bin/sh -c ’”’”’/bin/sh $TMPDIR/AnsiballZ_bash_sample_03 +
&& sleep 0’”’”’’
<192.168.56.202> (0, ’{”foo”:”bar”}rn’, +
’Shared connection to 192.168.56.202 closed.rn’)
ok: [server] => {
”changed”: false,
”foo”: ”bar”
}
Martin Schütte | Ansible Modules | CLT’19 31/50
JSONARGS – Argument in Script File
[vagrant@server ~]$ cat $TMPDIR/AnsiballZ_bash_sample_03
#!/bin/sh
ARGS=’{”_ansible_version”: ”2.7.5”,
”_ansible_selinux_special_fs”: [”fuse”, ”nfs”, ”vboxsf”,
”ramfs”, ”9p”], ”_ansible_no_log”: false,
”_ansible_module_name”: ”bash_sample_03”,
”_ansible_tmpdir”: ”/home/vagrant/.ansible/tmp/ansible-tmp-
1548797622.11-222413844288764/”, ”_ansible_verbosity”: 3,
”_ansible_keep_remote_files”: true,
”_ansible_syslog_facility”: ”LOG_USER”, ”_ansible_socket”:
null, ”_ansible_remote_tmp”: ”~/.ansible/tmp”,
”_ansible_diff”: false, ”_ansible_debug”: false,
”_ansible_shell_executable”: ”/bin/sh”,
”_ansible_check_mode”: false, ”foo”: ”baz”}’
→
→
→
→
→
→
→
→
→
→
→
echo ”arguments: $ARGS” | logger --tag $(basename ”$0”)
echo ’{”foo”:”bar”}’
exit 0
Martin Schütte | Ansible Modules | CLT’19 32/50
Import AnsibleModule
#!/usr/bin/python
args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>”””
from ansible.module_utils.basic import AnsibleModule
if __name__ == ’__main__’:
print(’{”foo”:”bar”}’)
exit(0)
Martin Schütte | Ansible Modules | CLT’19 33/50
Import AnsibleModule – Verbose Output
[vagrant@server ~]$ ls -hl $TMPDIR
-rwx------. 1 vagrant vagrant 75K Jan 29 14:53 AnsiballZ_py_sample_04.py
[vagrant@server ~]$ head $TMPDIR/AnsiballZ_py_sample_04.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
_ANSIBALLZ_WRAPPER = True # For test-module script to tell this is a ANSIBALLZ_WRAPPER
# This code is part of Ansible, but is an independent component.
# The code in this particular templatable string, and this templatable string
# only, is BSD licensed. Modules which end up using this snippet, which is
# dynamically combined together by Ansible still belong to the author of the
# module, and they may assign their own license to the complete work.
#
# Copyright (c), James Cammarata, 2016
[vagrant@server ~]$
Martin Schütte | Ansible Modules | CLT’19 34/50
AnsiballZ Wrapper
Python template script for
• helper functions (execute, explode)
• zipped data of
• module text
• JSON arguments
• all module_utils imports
Martin Schütte | Ansible Modules | CLT’19 35/50
Debugging Tools and Tips
Dev environment:
• Vagrant
• keep_remote_files = True
• ansible -vvv
• AnsiballZ code expand
• “print to output”
• AnsibleModule.log()
• q
Martin Schütte | Ansible Modules | CLT’19 36/50
Debugging – AnsiballZ Explode
[vagrant@server ~]$ ls -hl $TMPDIR
-rwx------. 1 vagrant vagrant 75K Jan 29 14:53 AnsiballZ_py_sample_04.py
[vagrant@server ~]$ $TMPDIR/AnsiballZ_py_sample_04.py explode
Module expanded into:
$TMPDIR/debug_dir
[vagrant@server ~]$ cd $TMPDIR/debug_dir; find .
.
./ansible
./ansible/__init__.py
./ansible/module_utils
./ansible/module_utils/__init__.py
./ansible/module_utils/basic.py
./ansible/module_utils/parsing
./ansible/module_utils/parsing/convert_bool.py
./ansible/module_utils/parsing/__init__.py
./ansible/module_utils/common
./ansible/module_utils/common/_collections_compat.py
./ansible/module_utils/common/process.py
./ansible/module_utils/common/__init__.py
./ansible/module_utils/common/file.py
./ansible/module_utils/six
./ansible/module_utils/six/__init__.py
./ansible/module_utils/_text.py
./ansible/module_utils/pycompat24.py
./__main__.py
./args
Martin Schütte | Ansible Modules | CLT’19 37/50
Debugging – AnsiballZ Explode
[vagrant@server debug_dir]$ cat __main__.py
#!/usr/bin/python
args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>”””
from ansible.module_utils.basic import AnsibleModule
if __name__ == ’__main__’:
print(’{”foo”:”bar”}’)
exit(0)
[vagrant@server debug_dir]$ $TMPDIR/AnsiballZ_py_sample_04.py execute
{”foo”:”bar”}
Martin Schütte | Ansible Modules | CLT’19 38/50
Debugging – printf
• Ansible reads stdin and stdout, expects JSON
⇒ cannot use print() to debug
• Use output values instead
# ...
debug_msg = ”some_func({}) returned {}”.format(bar, foo)
# ...
module.exit_json(result=foo, debug_msg=debug_msg)
ok: [server] => {
”changed”: false,
”debug_msg”: ”some_func(bar) returned foo”,
...
}
Martin Schütte | Ansible Modules | CLT’19 39/50
Debugging – AnsibleModule log()
• AnsibleModule includes method log()
with variants debug() and warn()
• Writes to journald or Syslog
module.log(”Hello World”)
# tail /var/log/messages
Feb 9 15:02:59 server ansible-my_module: Invoked with param=...
Feb 9 15:02:59 server ansible-my_module: Hello World
Martin Schütte | Ansible Modules | CLT’19 40/50
Debugging – q
• PyPI q or zestyping/q 
• Always writes to /tmp/q
• function decorators
try:
import q
except ImportError:
def q(x):
return x
@q
def my_func(params):
q(special_var)
# ...
$ tail /tmp/q
0.0s my_func(’VERSION’)
0.0s my_func: ’special_value’
0.0s -> {’failed’: False, ’msg’: ’...’}
Martin Schütte | Ansible Modules | CLT’19 41/50
Beyond Python
Ansible Modules in Other Languages
• Python: the default choice, best tools and support
• PowerShell: officially supported, but not covered here
• Scripting Languages: can use JSON_ARGS
• Binary Executables: can use WANT_JSON
Martin Schütte | Ansible Modules | CLT’19 42/50
Binary Executables, e. g. Go Tools
possible, but not recommended:
• binary in Ansible git repository
• should have own build chain and versions
• architecture dependent (all the world’s a x86_64?)
better:
• separate packaging and deployment (.deb and .rpm)
• thin wrapper module to execute installed file or library
• same for binary Python libraries (e. g. DB connectors)
Martin Schütte | Ansible Modules | CLT’19 43/50
Java
• “executable” JAR file
• not architecture dependent
• otherwise the same as binaries
Martin Schütte | Ansible Modules | CLT’19 44/50
Groovy
• JVM scripting language
• compromise between Ansible scripts and Java apps
• maybe best way to access/use Java libraries with Ansible
• may need workarounds to use custom classpath
Martin Schütte | Ansible Modules | CLT’19 45/50
Groovy – Small Example
#!/usr/bin/env groovy
import groovy.json.*
def jsonArgs=’’’<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>’’’
def args_object = new JsonSlurper().parseText(jsonArgs)
def checkMode = (boolean) args_object[’_ansible_check_mode’]
// do something useful
def result = [
foo: ’bar’,
code: 42,
did_check_mode: checkMode
]
print JsonOutput.toJson(result)
Martin Schütte | Ansible Modules | CLT’19 46/50
Scripting Languages
• Perl, Ruby, etc.
• need JSON library
• works
• question: why?
Martin Schütte | Ansible Modules | CLT’19 47/50
Conclusion
Useful Software Design Principles
Not only for Ansible modules …
• KISS
• YAGNI
• readability!
Martin Schütte | Ansible Modules | CLT’19 48/50
Links
• Ansible Docs on Modules:
Conventions, tips, and pitfalls
• ansible/ansible 
• Ansible: Up & Running, 2nd ed
by Lorin Hochstein & René Moser
Martin Schütte | Ansible Modules | CLT’19 49/50
The End
Thank You! — Questions?
Martin Schütte
@m_schuett 
info@martin-schuette.de 
slideshare.net/mschuett/ 
noti.st/mschuett/
Martin Schütte | Ansible Modules | CLT’19 50/50
1 of 55

Recommended

Writing Ansible Modules (DENOG11) by
Writing Ansible Modules (DENOG11)Writing Ansible Modules (DENOG11)
Writing Ansible Modules (DENOG11)Martin Schütte
465 views45 slides
Terraform – Infrastructure as Code (Kielux'18) by
Terraform – Infrastructure as Code (Kielux'18)Terraform – Infrastructure as Code (Kielux'18)
Terraform – Infrastructure as Code (Kielux'18)Martin Schütte
586 views45 slides
Infrastructure as Code with Terraform by
Infrastructure as Code with TerraformInfrastructure as Code with Terraform
Infrastructure as Code with TerraformMario IC
927 views25 slides
Infrastructure as Code: Introduction to Terraform by
Infrastructure as Code: Introduction to TerraformInfrastructure as Code: Introduction to Terraform
Infrastructure as Code: Introduction to TerraformAlexander Popov
685 views29 slides
Terraform modules and best-practices - September 2018 by
Terraform modules and best-practices - September 2018Terraform modules and best-practices - September 2018
Terraform modules and best-practices - September 2018Anton Babenko
3.6K views34 slides
Refactoring terraform by
Refactoring terraformRefactoring terraform
Refactoring terraformNell Shamrell-Harrington
3K views191 slides

More Related Content

What's hot

Intro to Terraform by
Intro to TerraformIntro to Terraform
Intro to TerraformJosh Michielsen
225 views20 slides
Terraform Modules and Continuous Deployment by
Terraform Modules and Continuous DeploymentTerraform Modules and Continuous Deployment
Terraform Modules and Continuous DeploymentZane Williamson
1.9K views29 slides
Terraform introduction by
Terraform introductionTerraform introduction
Terraform introductionJason Vance
755 views57 slides
Declarative & workflow based infrastructure with Terraform by
Declarative & workflow based infrastructure with TerraformDeclarative & workflow based infrastructure with Terraform
Declarative & workflow based infrastructure with TerraformRadek Simko
1.1K views66 slides
Comprehensive Terraform Training by
Comprehensive Terraform TrainingComprehensive Terraform Training
Comprehensive Terraform TrainingYevgeniy Brikman
23K views148 slides
Terraform at Scale by
Terraform at ScaleTerraform at Scale
Terraform at ScaleCalvin French-Owen
1.6K views85 slides

What's hot(20)

Terraform Modules and Continuous Deployment by Zane Williamson
Terraform Modules and Continuous DeploymentTerraform Modules and Continuous Deployment
Terraform Modules and Continuous Deployment
Zane Williamson1.9K views
Terraform introduction by Jason Vance
Terraform introductionTerraform introduction
Terraform introduction
Jason Vance755 views
Declarative & workflow based infrastructure with Terraform by Radek Simko
Declarative & workflow based infrastructure with TerraformDeclarative & workflow based infrastructure with Terraform
Declarative & workflow based infrastructure with Terraform
Radek Simko1.1K views
Terraform at Scale - All Day DevOps 2017 by Jonathon Brouse
Terraform at Scale - All Day DevOps 2017Terraform at Scale - All Day DevOps 2017
Terraform at Scale - All Day DevOps 2017
Jonathon Brouse596 views
Terraforming the Kubernetes Land by Radek Simko
Terraforming the Kubernetes LandTerraforming the Kubernetes Land
Terraforming the Kubernetes Land
Radek Simko1.3K views
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests by DECK36
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
DECK362.5K views
Infrastructure as Code in Google Cloud by Radek Simko
Infrastructure as Code in Google CloudInfrastructure as Code in Google Cloud
Infrastructure as Code in Google Cloud
Radek Simko769 views
Case Study: Using Terraform and Packer to deploy go applications to AWS by Patrick Bolduan
Case Study: Using Terraform and Packer to deploy go applications to AWSCase Study: Using Terraform and Packer to deploy go applications to AWS
Case Study: Using Terraform and Packer to deploy go applications to AWS
Patrick Bolduan3.7K views
Puppet Node Classifiers Talk - Patrick Buckley by Christian Mague
Puppet Node Classifiers Talk - Patrick BuckleyPuppet Node Classifiers Talk - Patrick Buckley
Puppet Node Classifiers Talk - Patrick Buckley
Christian Mague6.5K views
Terraform modules and some of best-practices - March 2019 by Anton Babenko
Terraform modules and some of best-practices - March 2019Terraform modules and some of best-practices - March 2019
Terraform modules and some of best-practices - March 2019
Anton Babenko3.9K views
Ansible fest Presentation slides by Aaron Carey
Ansible fest Presentation slidesAnsible fest Presentation slides
Ansible fest Presentation slides
Aaron Carey463 views
Agiles Peru 2019 - Infrastructure As Code by Mario IC
Agiles Peru 2019 - Infrastructure As CodeAgiles Peru 2019 - Infrastructure As Code
Agiles Peru 2019 - Infrastructure As Code
Mario IC208 views
Scaling terraform by Paolo Tonin
Scaling terraformScaling terraform
Scaling terraform
Paolo Tonin272 views

Similar to Writing Ansible Modules (CLT'19)

TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07 by
TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07
TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07garrett honeycutt
868 views64 slides
Automation with Ansible and Containers by
Automation with Ansible and ContainersAutomation with Ansible and Containers
Automation with Ansible and ContainersRodolfo Carvalho
453 views23 slides
Mock cli with Python unittest by
Mock cli with Python unittestMock cli with Python unittest
Mock cli with Python unittestSong Jin
139 views14 slides
friends functionToshu by
friends functionToshufriends functionToshu
friends functionToshuSidd Singh
751 views52 slides
Scala to assembly by
Scala to assemblyScala to assembly
Scala to assemblyJarek Ratajski
186 views124 slides
Portland Puppet User Group June 2014: Writing and publishing puppet modules by
Portland Puppet User Group June 2014: Writing and publishing puppet modulesPortland Puppet User Group June 2014: Writing and publishing puppet modules
Portland Puppet User Group June 2014: Writing and publishing puppet modulesPuppet
1.9K views39 slides

Similar to Writing Ansible Modules (CLT'19)(20)

TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07 by garrett honeycutt
TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07
TDD with Puppet Tutorial presented at Cascadia IT Conference 2014-03-07
garrett honeycutt868 views
Automation with Ansible and Containers by Rodolfo Carvalho
Automation with Ansible and ContainersAutomation with Ansible and Containers
Automation with Ansible and Containers
Rodolfo Carvalho453 views
Mock cli with Python unittest by Song Jin
Mock cli with Python unittestMock cli with Python unittest
Mock cli with Python unittest
Song Jin139 views
friends functionToshu by Sidd Singh
friends functionToshufriends functionToshu
friends functionToshu
Sidd Singh751 views
Portland Puppet User Group June 2014: Writing and publishing puppet modules by Puppet
Portland Puppet User Group June 2014: Writing and publishing puppet modulesPortland Puppet User Group June 2014: Writing and publishing puppet modules
Portland Puppet User Group June 2014: Writing and publishing puppet modules
Puppet1.9K views
June 2014 PDX PUG: Writing and Publishing Puppet Modules by Puppet
June 2014 PDX PUG: Writing and Publishing Puppet Modules June 2014 PDX PUG: Writing and Publishing Puppet Modules
June 2014 PDX PUG: Writing and Publishing Puppet Modules
Puppet1.2K views
External Language Stored Procedures for MySQL by Antony T Curtis
External Language Stored Procedures for MySQLExternal Language Stored Procedures for MySQL
External Language Stored Procedures for MySQL
Antony T Curtis3.9K views
Zabbix LLD from a C Module by Jan-Piet Mens by NETWAYS
Zabbix LLD from a C Module by Jan-Piet MensZabbix LLD from a C Module by Jan-Piet Mens
Zabbix LLD from a C Module by Jan-Piet Mens
NETWAYS172 views
Troubleshooting the Puppet Enterprise Stack by Puppet
Troubleshooting the Puppet Enterprise StackTroubleshooting the Puppet Enterprise Stack
Troubleshooting the Puppet Enterprise Stack
Puppet11.7K views
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2 by PVS-Studio
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
PVS-Studio238 views
Preparse Query Rewrite Plugins by Sveta Smirnova
Preparse Query Rewrite PluginsPreparse Query Rewrite Plugins
Preparse Query Rewrite Plugins
Sveta Smirnova645 views
PowerShell Fundamentals by mozdzen
PowerShell FundamentalsPowerShell Fundamentals
PowerShell Fundamentals
mozdzen218 views
Treasure Data Summer Internship 2016 by Yuta Iwama
Treasure Data Summer Internship 2016Treasure Data Summer Internship 2016
Treasure Data Summer Internship 2016
Yuta Iwama5.6K views
C# 7 development by Fisnik Doko
C# 7 developmentC# 7 development
C# 7 development
Fisnik Doko871 views

More from Martin Schütte

Terraform: Cloud Configuration Management (WTC/IPC'16) by
Terraform: Cloud Configuration Management (WTC/IPC'16)Terraform: Cloud Configuration Management (WTC/IPC'16)
Terraform: Cloud Configuration Management (WTC/IPC'16)Martin Schütte
779 views44 slides
Terraform: Configuration Management for Cloud Services by
Terraform: Configuration Management for Cloud ServicesTerraform: Configuration Management for Cloud Services
Terraform: Configuration Management for Cloud ServicesMartin Schütte
1.8K views34 slides
The IPv6 Snort Plugin (at DeepSec 2014) by
The IPv6 Snort Plugin (at DeepSec 2014)The IPv6 Snort Plugin (at DeepSec 2014)
The IPv6 Snort Plugin (at DeepSec 2014)Martin Schütte
1.7K views43 slides
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit) by
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)Martin Schütte
1.7K views45 slides
Short Introduction to IPv6 by
Short Introduction to IPv6Short Introduction to IPv6
Short Introduction to IPv6Martin Schütte
1.9K views34 slides
Software Testing on the Web by
Software Testing on the WebSoftware Testing on the Web
Software Testing on the WebMartin Schütte
1.1K views51 slides

More from Martin Schütte(10)

Terraform: Cloud Configuration Management (WTC/IPC'16) by Martin Schütte
Terraform: Cloud Configuration Management (WTC/IPC'16)Terraform: Cloud Configuration Management (WTC/IPC'16)
Terraform: Cloud Configuration Management (WTC/IPC'16)
Martin Schütte779 views
Terraform: Configuration Management for Cloud Services by Martin Schütte
Terraform: Configuration Management for Cloud ServicesTerraform: Configuration Management for Cloud Services
Terraform: Configuration Management for Cloud Services
Martin Schütte1.8K views
The IPv6 Snort Plugin (at DeepSec 2014) by Martin Schütte
The IPv6 Snort Plugin (at DeepSec 2014)The IPv6 Snort Plugin (at DeepSec 2014)
The IPv6 Snort Plugin (at DeepSec 2014)
Martin Schütte1.7K views
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit) by Martin Schütte
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)
The IPv6 Snort Plugin (at Troopers 14 IPv6 Security Summit)
Martin Schütte1.7K views
NetBSD syslogd with IETF Syslog Protocols by Martin Schütte
NetBSD syslogd with IETF Syslog ProtocolsNetBSD syslogd with IETF Syslog Protocols
NetBSD syslogd with IETF Syslog Protocols
Martin Schütte2K views
Design and Implementation of an IPv6 Plugin for the Snort Intrusion Detection... by Martin Schütte
Design and Implementation of an IPv6 Plugin for the Snort Intrusion Detection...Design and Implementation of an IPv6 Plugin for the Snort Intrusion Detection...
Design and Implementation of an IPv6 Plugin for the Snort Intrusion Detection...
Martin Schütte901 views

Recently uploaded

[2023] Putting the R! in R&D.pdf by
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdfEleanor McHugh
38 views127 slides
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum... by
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...NUS-ISS
28 views35 slides
MemVerge: Gismo (Global IO-free Shared Memory Objects) by
MemVerge: Gismo (Global IO-free Shared Memory Objects)MemVerge: Gismo (Global IO-free Shared Memory Objects)
MemVerge: Gismo (Global IO-free Shared Memory Objects)CXL Forum
112 views16 slides
"Fast Start to Building on AWS", Igor Ivaniuk by
"Fast Start to Building on AWS", Igor Ivaniuk"Fast Start to Building on AWS", Igor Ivaniuk
"Fast Start to Building on AWS", Igor IvaniukFwdays
36 views76 slides
TE Connectivity: Card Edge Interconnects by
TE Connectivity: Card Edge InterconnectsTE Connectivity: Card Edge Interconnects
TE Connectivity: Card Edge InterconnectsCXL Forum
96 views12 slides
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad... by
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad..."Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad...
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad...Fwdays
40 views30 slides

Recently uploaded(20)

[2023] Putting the R! in R&D.pdf by Eleanor McHugh
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
Eleanor McHugh38 views
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum... by NUS-ISS
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...
Beyond the Hype: What Generative AI Means for the Future of Work - Damien Cum...
NUS-ISS28 views
MemVerge: Gismo (Global IO-free Shared Memory Objects) by CXL Forum
MemVerge: Gismo (Global IO-free Shared Memory Objects)MemVerge: Gismo (Global IO-free Shared Memory Objects)
MemVerge: Gismo (Global IO-free Shared Memory Objects)
CXL Forum112 views
"Fast Start to Building on AWS", Igor Ivaniuk by Fwdays
"Fast Start to Building on AWS", Igor Ivaniuk"Fast Start to Building on AWS", Igor Ivaniuk
"Fast Start to Building on AWS", Igor Ivaniuk
Fwdays36 views
TE Connectivity: Card Edge Interconnects by CXL Forum
TE Connectivity: Card Edge InterconnectsTE Connectivity: Card Edge Interconnects
TE Connectivity: Card Edge Interconnects
CXL Forum96 views
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad... by Fwdays
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad..."Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad...
"Ukrainian Mobile Banking Scaling in Practice. From 0 to 100 and beyond", Vad...
Fwdays40 views
Astera Labs: Intelligent Connectivity for Cloud and AI Infrastructure by CXL Forum
Astera Labs:  Intelligent Connectivity for Cloud and AI InfrastructureAstera Labs:  Intelligent Connectivity for Cloud and AI Infrastructure
Astera Labs: Intelligent Connectivity for Cloud and AI Infrastructure
CXL Forum125 views
AMD: 4th Generation EPYC CXL Demo by CXL Forum
AMD: 4th Generation EPYC CXL DemoAMD: 4th Generation EPYC CXL Demo
AMD: 4th Generation EPYC CXL Demo
CXL Forum126 views
.conf Go 2023 - Data analysis as a routine by Splunk
.conf Go 2023 - Data analysis as a routine.conf Go 2023 - Data analysis as a routine
.conf Go 2023 - Data analysis as a routine
Splunk90 views
Understanding GenAI/LLM and What is Google Offering - Felix Goh by NUS-ISS
Understanding GenAI/LLM and What is Google Offering - Felix GohUnderstanding GenAI/LLM and What is Google Offering - Felix Goh
Understanding GenAI/LLM and What is Google Offering - Felix Goh
NUS-ISS39 views
"Quality Assurance: Achieving Excellence in startup without a Dedicated QA", ... by Fwdays
"Quality Assurance: Achieving Excellence in startup without a Dedicated QA", ..."Quality Assurance: Achieving Excellence in startup without a Dedicated QA", ...
"Quality Assurance: Achieving Excellence in startup without a Dedicated QA", ...
Fwdays33 views
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi by Fwdays
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi
"AI Startup Growth from Idea to 1M ARR", Oleksandr Uspenskyi
Fwdays26 views
MemVerge: Past Present and Future of CXL by CXL Forum
MemVerge: Past Present and Future of CXLMemVerge: Past Present and Future of CXL
MemVerge: Past Present and Future of CXL
CXL Forum110 views
Future of Learning - Khoong Chan Meng by NUS-ISS
Future of Learning - Khoong Chan MengFuture of Learning - Khoong Chan Meng
Future of Learning - Khoong Chan Meng
NUS-ISS31 views
JCon Live 2023 - Lice coding some integration problems by Bernd Ruecker
JCon Live 2023 - Lice coding some integration problemsJCon Live 2023 - Lice coding some integration problems
JCon Live 2023 - Lice coding some integration problems
Bernd Ruecker67 views
Future of Learning - Yap Aye Wee.pdf by NUS-ISS
Future of Learning - Yap Aye Wee.pdfFuture of Learning - Yap Aye Wee.pdf
Future of Learning - Yap Aye Wee.pdf
NUS-ISS38 views
GigaIO: The March of Composability Onward to Memory with CXL by CXL Forum
GigaIO: The March of Composability Onward to Memory with CXLGigaIO: The March of Composability Onward to Memory with CXL
GigaIO: The March of Composability Onward to Memory with CXL
CXL Forum126 views
MemVerge: Memory Viewer Software by CXL Forum
MemVerge: Memory Viewer SoftwareMemVerge: Memory Viewer Software
MemVerge: Memory Viewer Software
CXL Forum118 views
Upskilling the Evolving Workforce with Digital Fluency for Tomorrow's Challen... by NUS-ISS
Upskilling the Evolving Workforce with Digital Fluency for Tomorrow's Challen...Upskilling the Evolving Workforce with Digital Fluency for Tomorrow's Challen...
Upskilling the Evolving Workforce with Digital Fluency for Tomorrow's Challen...
NUS-ISS23 views

Writing Ansible Modules (CLT'19)

  • 2. Outline Concepts Writing Python Modules Simple Example Patterns Module Execution – In-Depth Low Level Module Execution AnsiballZ Debugging Beyond Python Conclusion Martin Schütte | Ansible Modules | CLT’19 2/50
  • 4. Assumptions You … • configure servers or other IT systems • have already used (or tried) Ansible • can write shell or Python scripts • have some “special” device or API or a CLI that does not fit into a simple command This talk … • is no Ansible introduction • has too many slides, I will skip some Martin Schütte | Ansible Modules | CLT’19 3/50
  • 5. Ansible – Concepts and Naming Ansible is a radically simple IT automation platform. • controller • target host • playbook • role • task • module Martin Schütte | Ansible Modules | CLT’19 4/50
  • 6. Example: Simple Playbook --- - hosts: webserver vars: apache_version: latest tasks: - name: ensure apache is at given version yum: name: httpd state: ”{{ apache_version }}” - hosts: dbserver roles: - ansible-role-postgresql Martin Schütte | Ansible Modules | CLT’19 5/50
  • 7. What is a Module? some code snippet to run on the (remote) host executable with input and output Martin Schütte | Ansible Modules | CLT’19 6/50
  • 8. Minimal Module #!/bin/sh echo ’{”foo”: ”bar”}’ exit 0 #!/usr/bin/python if __name__ == ’__main__’: print(’{”foo”: ”bar”}’) exit(0) Martin Schütte | Ansible Modules | CLT’19 7/50
  • 9. File Locations: library and module_utils my_role/ meta defaults tasks library my_module.py module_utils util.py • role can use Ansible module my_module • import * from util finds Python module in module_utils • for “larger” libraries use packages (pip/rpm/dpkg) Martin Schütte | Ansible Modules | CLT’19 8/50
  • 10. Action Plugins call Modules • plugins run on the controller • may prepare input for modules • may handle “special” connections (non SSH or WinRM) • may implement actions on the controller, e. g. debug • defaults to normal to run module on target host Martin Schütte | Ansible Modules | CLT’19 9/50
  • 12. Avoid Writing Own Code • get_url – Downloads files • uri – Interacts with webservices • wait_for – Waits for a condition before continuing • set_fact – Set host facts from a task - name: Wait for port 8000 to become open on the host wait_for: port: 8000 delay: 10 - name: wait for service to become available uri: url: ’https://{{ inventory_hostname }}:{{ svc_port }}/service’ return_content: yes register: content until: content.status == 200 retries: 60 delay: 10 when: not ansible_check_mode Martin Schütte | Ansible Modules | CLT’19 10/50
  • 13. Documentation ANSIBLE_METADATA = {’metadata_version’: ’1.1’, ’status’: [’stableinterface’], ’supported_by’: ’core’} DOCUMENTATION = ’’’ --- module: ping version_added: historical short_description: Try to connect to host, verify a usable python and return C(pong) on success ... ’’’ EXAMPLES = ’’’ # Induce an exception to see what happens - ping: data: crash ’’’ RETURN = ’’’ ping: description: value provided with the data parameter returned: success type: string sample: pong ’’’ Martin Schütte | Ansible Modules | CLT’19 11/50
  • 14. ansible-doc [mschuett@work:/home/mschuett] $ ansible-doc --snippet ping - name: Try to connect to host, verify a usable python and return ‘pong’ on success ping: data: # Data to return for the ‘ping’ return value. If this parameter is set to ‘crash’, the module will cause an exception. [mschuett@work:/home/mschuett] $ ansible-doc ping > PING (.../site-packages/ansible/modules/system/ping.py) A trivial test module, this module always returns ‘pong’ on successful contact. It does not make sense in playbooks, but it is useful from ‘/usr/bin/ansible’ to verify the ability to login and that a usable Python is configured. This is NOT ICMP ping, this is just a trivial test module that requires Python on the remote-node. For Windows targets, use the [win_ping] module instead. For Network targets, use the [net_ping] module instead. OPTIONS (= is mandatory): - data Data to return for the ‘ping’ return value. ... Martin Schütte | Ansible Modules | CLT’19 12/50
  • 15. ping.py from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule( argument_spec=dict( data=dict(type=’str’, default=’pong’), ), supports_check_mode=True ) if module.params[’data’] == ’crash’: raise Exception(”boom”) result = dict( ping=module.params[’data’], ) module.exit_json(**result) if __name__ == ’__main__’: main() Martin Schütte | Ansible Modules | CLT’19 13/50
  • 16. AnsibleModule useful common methods: • argument_spec for parameters • supports_check_mode • exit_json(), fail_json() • atomic_move(), run_command() • bytes_to_human(), human_to_bytes() Other module_utils: • api: function/decorator @rate_limit() • timeout: function/decorator @timeout(secs) • _text: new and unstable to_text() Martin Schütte | Ansible Modules | CLT’19 14/50
  • 17. Pattern: Idempotency • Playbooks can run many times • As few changes as possible • Only perform required actions 1. Get spec parameters 2. Check actual state of system if =: done, do nothing if ̸=: action to change state Martin Schütte | Ansible Modules | CLT’19 15/50
  • 18. Pattern: Check Dependencies try: import psycopg2 import psycopg2.extras except ImportError: HAS_PSYCOPG2 = False else: HAS_PSYCOPG2 = True def main(): module = AnsibleModule() # ... if not HAS_PSYCOPG2: module.fail_json( msg=”the python psycopg2 module is required”) Martin Schütte | Ansible Modules | CLT’19 16/50
  • 19. Check Mode/“Dry Run” • Return information but never apply changes • Optional but recommended for modules • Interface provided by AnsibleModule Example without support: m = AnsibleModule( argument_spec=..., supports_check_mode=False ) $ ansible-playbook -v --check playbook.yml ... TASK [role-pymod : role_pymod | get_release_py] ************** skipping: [server] => {”changed”: false, ”msg”: ”remote module (get_release_py) does not support check mode”} Martin Schütte | Ansible Modules | CLT’19 17/50
  • 20. Check Mode/“Dry Run” def update_permanent_hostname(self): name = self.module.params[’name’] permanent_name = self.get_permanent_hostname() if permanent_name != name: if not self.module.check_mode: self.set_permanent_hostname(name) self.changed = True Important: Modules without AnsibleModule (or non-Python) have to handle this on their own! ⇒ test the _ansible_check_mode parameter Martin Schütte | Ansible Modules | CLT’19 18/50
  • 21. Common Return Values/Result Attributes Common • changed • failed • rc • msg • results • invocation • skipped • stderr, stderr_lines • stdout, stdout_lines • backup_file Internal use • ansible_facts • exception • warnings • deprecations Martin Schütte | Ansible Modules | CLT’19 19/50
  • 22. Other Common Return Value: Diff Example from hostname: if changed: kw[’diff’] = {’after’: ’hostname = ’ + name + ’n’, ’before’: ’hostname = ’ + name_before + ’n’} Example output, sample module: TASK [role-minimal : role_minimal | py_sample_08] ************ task path: /vagrant/roles/role-minimal/tasks/main.yml:23 --- before +++ after @@ -1,3 +1,3 @@ common line -old value +new vale common line changed: [server] => {”changed”: ”true”, ”foo”: ”bar”} Martin Schütte | Ansible Modules | CLT’19 20/50
  • 23. Example: Set Facts In a playbook: - do_something: # ... register: result_var - set_fact: foo: ”{{ result_var.results | list }}” In a module (from hostname): kw = dict(changed=changed, name=name, ansible_facts=dict(ansible_hostname=name.split(’.’)[0], ansible_nodename=name, ansible_fqdn=socket.getfqdn(), ansible_domain=’.’.join( socket.getfqdn().split(’.’)[1:]))) module.exit_json(**kw) Martin Schütte | Ansible Modules | CLT’19 21/50
  • 24. Example: String Formatting When Jinja2 is not enough for variables and formatting … Random example: - name: calculate cluster config lines calc_some_cluster_config: hostname: ”{{ ansible_fqdn }}” port: ”{{ application_port }}” group: ”{{ groups[’appserver’] }}” register: cluster_config - name: cluster config lineinfile: path: ”{{ basedir }}/cluster/config” line: ”{{ item }}” with_items: - ”hosts={{ cluster_config.tcp_hostlist | join(’,’) }}” - ”exclude={{ cluster_config.exclude_roles | join(’,’) }}” notify: appserver restart Martin Schütte | Ansible Modules | CLT’19 22/50
  • 25. Pattern: Provider Dict - name: apply IOS config ios_config: provider: ”{{ ios_creds }}” src: my_config2b.txt - name: Import Zabbix json template configuration local_action: module: zabbix_template server_url: http://127.0.0.1 login_user: username login_password: password template_name: Apache2 template_json: ”{{ lookup(’file’, ’apache2.json’) }}” template_groups: - Webservers Martin Schütte | Ansible Modules | CLT’19 23/50
  • 26. Common Module Pattern class Controller(object): def __init__(module) def do_something() def main(): module = AnsibleModule(...) ctl = Controller(module) result = ctl.do_something() module.exit_json(**result) if __name__ == ’__main__’: main() • simple access to input parameters • access to util functions (e. g. module.run_command()) • difficult to unit test Martin Schütte | Ansible Modules | CLT’19 24/50
  • 27. Common Module Pattern II class Controller(object): def __init__(many_variables, ...) def do_something() def main(): module = AnsibleModule(...) ctl = Controller(many_variables, ...) result = ctl.do_something() module.exit_json(**result) if __name__ == ’__main__’: main() • good isolation • re-use and testability • often more complex Martin Schütte | Ansible Modules | CLT’19 25/50
  • 29. Minimal Module – Verbose Output TASK [role-minimal : role_minimal | bash_sample_01] **************************** task path: /vagrant/roles/role-minimal/tasks/main.yml:5 <192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant <192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC <192.168.56.202> (0, ’/home/vagrantn’, ’’) <192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant <192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC <192.168.56.202> (0, ’ansible-tmp-1548772995.78-225807547469627=/home/vagrant/.ansible/tmp/ansi Using module file /vagrant/roles/role-minimal/library/bash_sample_01 <192.168.56.202> PUT /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpnrj9rd TO /home/va <192.168.56.202> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHo <192.168.56.202> (0, ’sftp> put /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpnrj9rd <192.168.56.202> PUT /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpgN3ZKr TO /home/va <192.168.56.202> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHo <192.168.56.202> (0, ’sftp> put /home/vagrant/.ansible/tmp/ansible-local-32044a_dXdg/tmpgN3ZKr <192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant <192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC <192.168.56.202> (0, ’’, ’’) <192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant <192.168.56.202> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyC Escalation succeeded <192.168.56.202> (0, ’{”foo”:”bar”}rn’, ’Shared connection to 192.168.56.202 closed.rn’) ok: [server] => { ”changed”: false, ”foo”: ”bar” } TASK [role-minimal : role_minimal | bash_sample_01] **************************** task path: /vagrant/roles/role-minimal/tasks/main.yml:5 Martin Schütte | Ansible Modules | CLT’19 26/50
  • 30. Argument File Format Old-style default: key=value [vagrant@server ~]$ cat $TMPDIR/args _ansible_version=2.7.5 _ansible_selinux_special_fs=’[’”’”’fuse’”’”’, ’”’”’nfs’”’”’, ’”’”’vboxsf’”’”’, ’”’”’ramfs’”’”’, ’”’”’9p’”’”’]’ _ansible_no_log=False _ansible_module_name=py_sample_01 _ansible_tmpdir=/home/vagrant/.ansible/tmp/ansible-tmp- 1548756932.8-240778677026680/ _ansible_verbosity=3 _ansible_keep_remote_files=True _ansible_syslog_facility=LOG_USER _ansible_socket=None _ansible_remote_tmp=’~/.ansible/tmp’ _ansible_diff=False _ansible_debug=False _ansible_shell_executable=/bin/sh _ansible_check_mode=False foo=baz → → → → → → → → → → → Martin Schütte | Ansible Modules | CLT’19 27/50
  • 31. Want JSON Option #!/bin/sh # WANT_JSON echo ’{”foo”:”bar”}’ exit 0 #!/usr/bin/python # WANT_JSON if __name__ == ’__main__’: print(’{”foo”:”bar”}’) exit(0) Martin Schütte | Ansible Modules | CLT’19 28/50
  • 32. Argument File Format With WANT_JSON flag: [vagrant@server ~]$ cat $TMPDIR/args {”_ansible_version”: ”2.7.5”, ”_ansible_selinux_special_fs”: [”fuse”, ”nfs”, ”vboxsf”, ”ramfs”, ”9p”], ”_ansible_no_log”: false, ”_ansible_module_name”: ”bash_sample_02”, ”_ansible_tmpdir”: ”/home/vagrant/.ansible/tmp/ansible-tmp-1548756933.19- 248002152304605/”, ”_ansible_verbosity”: 3, ”_ansible_keep_remote_files”: true, ”_ansible_syslog_facility”: ”LOG_USER”, ”_ansible_socket”: null, ”_ansible_remote_tmp”: ”~/.ansible/tmp”, ”_ansible_diff”: false, ”_ansible_debug”: false, ”_ansible_shell_executable”: ”/bin/sh”, ”_ansible_check_mode”: false, ”foo”: ”baz”} → → → → → → → → → → → Martin Schütte | Ansible Modules | CLT’19 29/50
  • 33. Remove Argument File: JSONARGS #!/bin/sh ARGS=’<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>’ echo ”arguments: $ARGS” | logger --tag $(basename ”$0”) echo ’{”foo”:”bar”}’ exit 0 #!/usr/bin/python import syslog args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>””” if __name__ == ’__main__’: syslog.openlog() syslog.syslog(args) print(’{”foo”:”bar”}’) exit(0) Martin Schütte | Ansible Modules | CLT’19 30/50
  • 34. JSONARGS – Verbose Output <192.168.56.202> ESTABLISH SSH CONNECTION FOR USER: vagrant <192.168.56.202> SSH: EXEC ssh $SSH_OPTS -tt 192.168.56.202 + ’/bin/sh -c ’”’”’/bin/sh $TMPDIR/AnsiballZ_bash_sample_03 + && sleep 0’”’”’’ <192.168.56.202> (0, ’{”foo”:”bar”}rn’, + ’Shared connection to 192.168.56.202 closed.rn’) ok: [server] => { ”changed”: false, ”foo”: ”bar” } Martin Schütte | Ansible Modules | CLT’19 31/50
  • 35. JSONARGS – Argument in Script File [vagrant@server ~]$ cat $TMPDIR/AnsiballZ_bash_sample_03 #!/bin/sh ARGS=’{”_ansible_version”: ”2.7.5”, ”_ansible_selinux_special_fs”: [”fuse”, ”nfs”, ”vboxsf”, ”ramfs”, ”9p”], ”_ansible_no_log”: false, ”_ansible_module_name”: ”bash_sample_03”, ”_ansible_tmpdir”: ”/home/vagrant/.ansible/tmp/ansible-tmp- 1548797622.11-222413844288764/”, ”_ansible_verbosity”: 3, ”_ansible_keep_remote_files”: true, ”_ansible_syslog_facility”: ”LOG_USER”, ”_ansible_socket”: null, ”_ansible_remote_tmp”: ”~/.ansible/tmp”, ”_ansible_diff”: false, ”_ansible_debug”: false, ”_ansible_shell_executable”: ”/bin/sh”, ”_ansible_check_mode”: false, ”foo”: ”baz”}’ → → → → → → → → → → → echo ”arguments: $ARGS” | logger --tag $(basename ”$0”) echo ’{”foo”:”bar”}’ exit 0 Martin Schütte | Ansible Modules | CLT’19 32/50
  • 36. Import AnsibleModule #!/usr/bin/python args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>””” from ansible.module_utils.basic import AnsibleModule if __name__ == ’__main__’: print(’{”foo”:”bar”}’) exit(0) Martin Schütte | Ansible Modules | CLT’19 33/50
  • 37. Import AnsibleModule – Verbose Output [vagrant@server ~]$ ls -hl $TMPDIR -rwx------. 1 vagrant vagrant 75K Jan 29 14:53 AnsiballZ_py_sample_04.py [vagrant@server ~]$ head $TMPDIR/AnsiballZ_py_sample_04.py #!/usr/bin/python # -*- coding: utf-8 -*- _ANSIBALLZ_WRAPPER = True # For test-module script to tell this is a ANSIBALLZ_WRAPPER # This code is part of Ansible, but is an independent component. # The code in this particular templatable string, and this templatable string # only, is BSD licensed. Modules which end up using this snippet, which is # dynamically combined together by Ansible still belong to the author of the # module, and they may assign their own license to the complete work. # # Copyright (c), James Cammarata, 2016 [vagrant@server ~]$ Martin Schütte | Ansible Modules | CLT’19 34/50
  • 38. AnsiballZ Wrapper Python template script for • helper functions (execute, explode) • zipped data of • module text • JSON arguments • all module_utils imports Martin Schütte | Ansible Modules | CLT’19 35/50
  • 39. Debugging Tools and Tips Dev environment: • Vagrant • keep_remote_files = True • ansible -vvv • AnsiballZ code expand • “print to output” • AnsibleModule.log() • q Martin Schütte | Ansible Modules | CLT’19 36/50
  • 40. Debugging – AnsiballZ Explode [vagrant@server ~]$ ls -hl $TMPDIR -rwx------. 1 vagrant vagrant 75K Jan 29 14:53 AnsiballZ_py_sample_04.py [vagrant@server ~]$ $TMPDIR/AnsiballZ_py_sample_04.py explode Module expanded into: $TMPDIR/debug_dir [vagrant@server ~]$ cd $TMPDIR/debug_dir; find . . ./ansible ./ansible/__init__.py ./ansible/module_utils ./ansible/module_utils/__init__.py ./ansible/module_utils/basic.py ./ansible/module_utils/parsing ./ansible/module_utils/parsing/convert_bool.py ./ansible/module_utils/parsing/__init__.py ./ansible/module_utils/common ./ansible/module_utils/common/_collections_compat.py ./ansible/module_utils/common/process.py ./ansible/module_utils/common/__init__.py ./ansible/module_utils/common/file.py ./ansible/module_utils/six ./ansible/module_utils/six/__init__.py ./ansible/module_utils/_text.py ./ansible/module_utils/pycompat24.py ./__main__.py ./args Martin Schütte | Ansible Modules | CLT’19 37/50
  • 41. Debugging – AnsiballZ Explode [vagrant@server debug_dir]$ cat __main__.py #!/usr/bin/python args=”””<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>””” from ansible.module_utils.basic import AnsibleModule if __name__ == ’__main__’: print(’{”foo”:”bar”}’) exit(0) [vagrant@server debug_dir]$ $TMPDIR/AnsiballZ_py_sample_04.py execute {”foo”:”bar”} Martin Schütte | Ansible Modules | CLT’19 38/50
  • 42. Debugging – printf • Ansible reads stdin and stdout, expects JSON ⇒ cannot use print() to debug • Use output values instead # ... debug_msg = ”some_func({}) returned {}”.format(bar, foo) # ... module.exit_json(result=foo, debug_msg=debug_msg) ok: [server] => { ”changed”: false, ”debug_msg”: ”some_func(bar) returned foo”, ... } Martin Schütte | Ansible Modules | CLT’19 39/50
  • 43. Debugging – AnsibleModule log() • AnsibleModule includes method log() with variants debug() and warn() • Writes to journald or Syslog module.log(”Hello World”) # tail /var/log/messages Feb 9 15:02:59 server ansible-my_module: Invoked with param=... Feb 9 15:02:59 server ansible-my_module: Hello World Martin Schütte | Ansible Modules | CLT’19 40/50
  • 44. Debugging – q • PyPI q or zestyping/q  • Always writes to /tmp/q • function decorators try: import q except ImportError: def q(x): return x @q def my_func(params): q(special_var) # ... $ tail /tmp/q 0.0s my_func(’VERSION’) 0.0s my_func: ’special_value’ 0.0s -> {’failed’: False, ’msg’: ’...’} Martin Schütte | Ansible Modules | CLT’19 41/50
  • 46. Ansible Modules in Other Languages • Python: the default choice, best tools and support • PowerShell: officially supported, but not covered here • Scripting Languages: can use JSON_ARGS • Binary Executables: can use WANT_JSON Martin Schütte | Ansible Modules | CLT’19 42/50
  • 47. Binary Executables, e. g. Go Tools possible, but not recommended: • binary in Ansible git repository • should have own build chain and versions • architecture dependent (all the world’s a x86_64?) better: • separate packaging and deployment (.deb and .rpm) • thin wrapper module to execute installed file or library • same for binary Python libraries (e. g. DB connectors) Martin Schütte | Ansible Modules | CLT’19 43/50
  • 48. Java • “executable” JAR file • not architecture dependent • otherwise the same as binaries Martin Schütte | Ansible Modules | CLT’19 44/50
  • 49. Groovy • JVM scripting language • compromise between Ansible scripts and Java apps • maybe best way to access/use Java libraries with Ansible • may need workarounds to use custom classpath Martin Schütte | Ansible Modules | CLT’19 45/50
  • 50. Groovy – Small Example #!/usr/bin/env groovy import groovy.json.* def jsonArgs=’’’<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>’’’ def args_object = new JsonSlurper().parseText(jsonArgs) def checkMode = (boolean) args_object[’_ansible_check_mode’] // do something useful def result = [ foo: ’bar’, code: 42, did_check_mode: checkMode ] print JsonOutput.toJson(result) Martin Schütte | Ansible Modules | CLT’19 46/50
  • 51. Scripting Languages • Perl, Ruby, etc. • need JSON library • works • question: why? Martin Schütte | Ansible Modules | CLT’19 47/50
  • 53. Useful Software Design Principles Not only for Ansible modules … • KISS • YAGNI • readability! Martin Schütte | Ansible Modules | CLT’19 48/50
  • 54. Links • Ansible Docs on Modules: Conventions, tips, and pitfalls • ansible/ansible  • Ansible: Up & Running, 2nd ed by Lorin Hochstein & René Moser Martin Schütte | Ansible Modules | CLT’19 49/50
  • 55. The End Thank You! — Questions? Martin Schütte @m_schuett  info@martin-schuette.de  slideshare.net/mschuett/  noti.st/mschuett/ Martin Schütte | Ansible Modules | CLT’19 50/50