Ansible @ an efficient configuration management tool-Ansible configure management-translation (10), ansible-ansible
No written permission. Do not reprint it
Custom ModulesUntil now we have been working solely with the tools provided to us by Ansible.This does afford us a lot of power, and make many things possible. However, if youhave something particularly complex or if you find yourself using the script modulea lot, you will probably want to learn how to extend Ansible.In this chapter you will learn the following topics:• How to write modules in Bash scripting or Python• Using custom modules that you have developed• Writing a script to use an external data source as an inventoryOften when you approach something complex in Ansible, you write a script module.The issue with script modules is that you can't process their output, or triggerhandlers based on their output easily. So, although the script module works in somecases, using a module can be better.Use a module instead of writing a script when:• You don't want to run the script every single time• You need to process the output• Your script needs to make facts• You need to send complex variables as arguments
Chapter 5 custom modules
We have been using the built-in Ansible module, which has provided us with many strong 'batter', but if you have some special and complex tasks, you may want to learn how to expand Anisble. In this chapter, you will learn the following topics:
- Compile a module in python or bash
- Use the custom module you developed
- Write a script to use the external data source as the device inventory library inventory
When the Anisble method is very complicated, you may write a script module. The disadvantage of the script module is that you cannot output their execution process, or you can start the Handler program based on their output. So sometimes you can use the script module, and sometimes it will be better to use the built-in module!
The following scenarios are not suitable for script modules:
- Do not use scripts every time you run them.
- When output results are required
- When your script needs fact
- When too complex variables need to be passed as parameters
If you want to start writing modules, you should check ( )out the Ansible repository.If you want your module to work with a particular version, you should also switchto that version to ensure compatibility. The following commands will set you up todevelop modules for Ansible 1.3.0. Checking out the Ansible code gives you accessto a handy script that we will use later to test our modules. We will also make thisscript executable in anticipation of its use later in the chapter.$ git clone (https://github.com/ansible/ansible.git)$ cd ansible$ git checkout v1.3.0$ chmod +x hacking/test-module
Before writing a module, you 'd better check the Ansble version library. If you want your module to run in a special version, you need to switch to the corresponding version for development. The following command allows you to upgrade to the Ansible1.3.0 development module. Through Anisble code, we can find a simple script to test our module. Grant this script executable permissions for later sections
$ Git clone (https://github.com/ansible/ansible.git)
$ Cd ansible
$ Git checkout v1.3.0
$ Chmod + x hacking/test-module
Writing a module in BashAnsible allows you to write modules in any language that you prefer. Althoughmost modules in Ansible work with JSON, you are allowed to use shortcuts if youdon't have any JSON parsing facilities available. Ansible will hand you argumentsin their original key value forms, if they were provided in that format. If complexarguments are provided, you will receive JSON-encoded data. You could parse thisusing something like jsawk ( https://github.com/micha/jsawk ) or jq ( http://stedolan.github.io/jq/ ), but only if they are installed on your remote machine.Ansible doesn't yet have a module that lets you change the hostname of a systemwith the hostname command. So let's write one. We will start just printing thecurrent hostname and then expand the script from there. Here is what that simplemodule looks like:#!/bin/bashHOSTNAME="$(hostname)"echo "hostname=${HOSTNAME}"
Compiling modules in bash
Ansible allows you to write modules in any language you like. Although most modules use JSON, you can still use the short format if you do not have any JSON parser. If your parameter format is key valus, Ansible can process them. If it is a more complex parameter, you will be subject to JSON-encoded data. You can use JSAWK or JQ for parsing, but make sure your remote managed host has installed them.
Anisble does not have a module that can change the Host Name of the system. Let's start from here! First, write a simple script to display the host, and then expand it. The Code is as follows:
#! /Bin/bash
HOSTNAME = "$ (hostname )"
Echo "hostname =$ {HOSTNAME }"
If you have written Bash scripts before, this should seem extremely basic. Essentiallywhat we are doing is grabbing the hostname and printing it out in a key value form.Now that we have written the first cut of the module, we should test it out.To test the Ansible modules, we use the script that we ran the chmod command onearlier. This command simply runs your module, records the output, and returnsit to you. It also shows how Ansible interpreted the output of the module. Thecommand that we will use looks like the following:ansible/hacking/test-module -m ./hostname
If you have written a bash script before, you will find that this is very basic. We just get the host name and print it out in key valus format. Now we have completed the first part of the module. Let's test it.
To test the module, we only need to use the previously authorized detection script. This command runs your module, records the output, and returns it to you. It also shows how Anisble interprets the module output. The command is as follows:
Ansible/hacking/test-module-m./hostname
The output of the previous command should look like this:* module boilerplate substitution not requested in module, linenumbers will be unaltered***********************************RAW OUTPUThostname=admin01.int.example.com***********************************PARSED OUTPUT{"hostname": "admin01.int.example.com"}Ignore the notice at the top, it does not apply to modules built with bash. You can seethe raw output that our script sent, which looks exactly the way we expected. Thetest script also gives you the parsed output. In our example, we are using the shortoutput format and we can see here that Ansible is correctly interpreting it into theJSON that it normally accepts from modules.
The output is similar to the following:
* Module boilerplate substitution not requested in module, line
Numbers will be unaltered
***********************************
RAW OUTPUT
Hostname = admin01.int.example.com
***********************************
PARSED OUTPUT
{
"Hostname": "admin01.int.example.com"
}
Ignore the tip at the top and you can see the raw output of the script we wrote, as we expected. The test script also parses our output. In our example, we use the short format output, but Ansible parses it into the same JSON format output as other modules.
Let's expand out the module to allow setting the hostname . We should write it sothat it doesn't make any changes unless it is required, and lets Ansible know whetherchanges were made or not. This is actually pretty simple for the small command thatwe are writing. The new script should look something like this:#!/bin/bashset -e# This is potentially dangeroussource ${1}OLDHOSTNAME="$(hostname)"CHANGED="False"if [ ! -z "$hostname" -a "${hostname}x" != "${OLDHOSTNAME}x" ];thenhostname $hostnameOLDHOSTNAME="$hostname"CHANGED="True"fiecho "hostname=${OLDHOSTNAME} changed=${CHANGED}"exit 0
Now, let's extend our module to set the host name. The Code is as follows:
#! /Bin/bash
Set-e
# This is potentially dangerous
Source $ {1}
OLDHOSTNAME = "$ (hostname )"
CHANGED = "False"
If [! -Z "$ hostname"-a "$ {hostname} x "! = "$ {OLDHOSTNAME} x"];
Then
Hostname $ hostname
OLDHOSTNAME = "$ hostname"
CHANGED = "True"
Fi
Echo "hostname =$ {OLDHOSTNAME} changed =$ {CHANGED }"
Exit 0
The previous script works like this:1. We set Bash's exit on error mode, so that we don't have to deal with errorsfrom hostname. Bash will automatically exit on failure with its exit code. Thiswill signal Ansible that something went wrong.2. We source the argument file. This file is passed from Ansible as the firstargument to the script. It contains the arguments that were sent to ourmodule. Because we are sourcing the file, this could be used to run arbitrarycommands; however, Ansible can already do this, so it's not that much of asecurity issue.3. We collect the old hostname and default CHANGED to False . This allows us tosee if our module needs to perform any changes.4. We check if we were sent a new hostname to set, and check if that hostnameis different from the one that is currently set.5. If both those tests are true, we try to change the hostname, and set CHANGEDto True .6. Finally, we output the results and exit. This includes the current hostnameand whether we made changes or not.
The above script executes the following operations:
Changing the hostname on a Unix machine requires root privileges. So while testingthis script, you need to make sure to run it as the root user. Let's test this script usingsudo to see if it works. This is the command you will use:sudo ansible/hacking/test-module -m ./hostname -a'hostname=test.example.com'If test.example.com is not the current hostname of the machine, you should get thefollowing as the output:* module boilerplate substitution not requested in module, linenumbers will be unaltered***********************************RAW OUTPUThostname=test.example.com changed=True***********************************PARSED OUTPUT{"changed": true,"hostname": "test.example.com"}
The root permission is required to modify the hostname on unix machines. Therefore, when testing the script, we need to run it with the root user. The test command is as follows:
Sudo ansible/hacking/test-module-m./hostname-a 'hostname = test.example.com'
If the current host name is not test.example.com, you will get the following output:
* Module boilerplate substitution not requested in module, line
Numbers will be unaltered
***********************************
RAW OUTPUT
Hostname = test.example.com changed = True
***********************************
PARSED OUTPUT
{
"Changed": true,
"Hostname": "test.example.com"
}
As you can see, our output is being parsed correctly, and the module claims thatchanges have been made to the system. You can check this yourself with thehostname command. Now, run the module for the second time with the samehostname. You should see an output that looks like this:* module boilerplate substitution not requested in module, linenumbers will be unaltered***********************************RAW OUTPUThostname=test.example.com changed=False***********************************PARSED OUTPUT{"changed": false,"hostname": "test.example.com"}Again, we see that the output was parsed correctly. This time, however, the moduleclaims to not have made any changes, which is what we expect. You can also checkthis with the hostname command.
Our output is well parsed, and the module also applies changes to the system. You can use hostname to check. Run the script again with the same hostname. The output is as follows:
* Module boilerplate substitution not requested in module, line
Numbers will be unaltered
***********************************
RAW OUTPUT
Hostname = test.example.com changed = False
***********************************
PARSED OUTPUT
{
"Changed": false,
"Hostname": "test.example.com"
}
The output is still parsed, but the module has not changed. You can use the hostname to check it again.