No written authorization. Do not reprint Chapter 5 custom Module
Using a moduleNow that we have written our very first module for Ansible, we should give it ago in a playbook. Ansible looks at several places for its modules: first it looks at theplace specified in the library key in its config file ( /etc/ansible/ansible.cfg ),next it will look in the location specified using the --module-path argument in thecommand line, then it will look in the same directory as the playbook for a librarydirectory containing modules, and finally it will look in the library directories for anyroles that may be set.Let's create a playbook that uses our new module and place it in a library directoryin the same place so that we can see it in action. Here is a playbook that uses thehostname module:---- name: Test the hostname filehosts: testmachinetasks:- name: Set the hostnamehostname: hostname=testmachine.example.com
Use custom modules
Now that we have finished writing the first custom module, let's put it in the playbook and run it. Ansible searches for modules from several places: it first looks at/etc/ansible. find the position defined by CFG, the location specified by the-module-path parameter, the directory where the playbook file is located, and the directory where the role is located.
Let's create a playbook using the newly created module and put it under the library directory. The Code is as follows:
---
-Name: test the hostname File
Hosts: tew.achine
Tasks:
-Name: Set the hostname
Hostname: Hostname = tew.achine.example.com
Then create a directory named library in the same directory as the playbook file.Place the hostname module inside the library. Your directory layout should looklike this:
Create a library folder under the directory where the playbook file is located, and put the hostname module in. Your directory structure looks like this:
When we run playbook, it will find the hostname module from the libray directory, and the output is as follows:
Play [test the hostname file] ******************************* ******
Gathering facts ************************************** ***************
OK: [ansibletest]
Task: [set the hostname] ********************************* *********
Changed: [ansibletest]
Play recap ************************************** ********************
Ansibletest
: OK = 2
Changed = 1
Unreachable = 0
Failed = 0
Run the playbook again and the result changes from changed to OK. Congratulations! You have created and executed a module of your own. It is very simple, but you can extend it to get the hostname file, or use other methods to modify the hostname when the system starts.
Writing modules in PythonAll of the modules that are distributed with Ansible are written in Python. BecauseAnsible is also written in Python, these modules can directly integrate with Ansible.This increases the speed at which they can run. Here are a few other reasons whyyou might write modules in Python:? Modules written in Python can use boilerplate, which reduces the amount ofcode required? Python modules can provide documentation to be used by Ansible? Arguments to your module are handled automatically? Output is automatically converted to JSON for you? Ansible upstream only accepts plugins using Python with the boilerplatecode included
Use python to write modules
All modules provided by ansible are written in Python, because ansible is also written in Python. These modules can be integrated with ansible. When running, they can speed up. The following are some reasons for writing modules in Python:
- You can reference modules written in Python, which saves a lot of code.
- Module parameters can be automatically processed
- The output format is automatically converted to JSON
- Ansible's upstream plug-ins accept references using Python include
You can still build Python modules without this integration by parsing thearguments and outputting JSON yourself. However, with all the things you get forfree, it would be hard to make a case for it.Let's build a Python module that lets us change the currently running init level of thesystem. There is a Python module called pyutmp that will let us parse the utmp file.Unfortunately, since Ansible modules have to be contained in a single file, we can'tuse it unless we know it will be installed on the remote systems, so we will resortto using the runlevel command and parsing its output. Setting the runlevel can bedone with the init command.The first step is to figure out what arguments and features the module supports. Forthe sake of simplicity, let's have our module only accept one argument. We'll use theargument runlevel to get the runlevel the user wants to change to. To do this, weinstantiate the AnsibleModule class with our data.module = AnsibleModule(argument_spec = dict(runlevel=dict(default=None, type='str')))
You can also parse parameters and output JSON data by yourself without using integration. However, since all these are free, why?
Now we can use python to write a module to change the current init level of the system. There is a module called pyutmp that allows us to parse the utmp file. However, since the ansible module must be included in a file, unless we are sure that it is installed on the remote managed host, otherwise, we cannot use this module. So we have to use the runlevel command and then parse its output. You can use the init command to set runlevel.
Step 1: Specify which parameters and features are supported by this module. For the sake of simplicity, we only allow our modules to accept one parameter. We use the runlevel parameter to allow users to specify the runtime level to be changed. To this end, we use our data to instantiate our ansiblemodule class.
Module = ansiblemodule (
Argument_spec = dict (
Runlevel = dict (default = none, type = 'str ')
)
)
Now we need to implement the actual guts of the module. The module object thatwe created previously provides us with a few shortcuts. There are three that we willbe using in the next step. As there are way too many methods to document here, youcan see the whole AnsibleModule class and all the available helper functions in lib/ansible/module_common.py .? run_command : This method is used to launch external commands and retrievethe return code, the output from stdout , and also the output from stderr .? exit_json : This method is used to return data to Ansible when the modulehas completed successfully.? fail_json : This method is used to signal a failure to Ansible, with an errormessage and return code.
The second step is to implement the actual functions of the module. The classes we created earlier provide some shortcuts. Next we will use these three shortcuts. There are too many methods to use, so we will not introduce them one by one. For more information about the function help of all the ansiblemidule classes, see lib/ansible/module_common.py.
- Run_command: This method is used to run an external command and then return code, which can be output to stdout and stderr.
- Exit_json: This method is used to return data when the module is successfully executed.
- Fail_json: When the module fails to be executed, this method returns a failure signal to ansible
The following code actually manages the init level of the system. Comments havebeen included in the following code to explain what it does.def main(): module = AnsibleModule( argument_spec = dict(
The following code shows how the module manages the init level. The comments are already included in the Code:
The above Python code is very simple and I will not explain it. If you have any questions, you can leave a message.
There is one final thing to add to the boilerplate to let Ansible know that it needsto dynamically add the integration code into our module. This is the magic thatlets us use the AnsibleModule class and enables our tight integration with Ansible.The boilerplate code needs to be placed right at the bottom of the file, with no codeafterwards. The code to do this looks as follows:# include magic from lib/ansible/module_common.py#<<INCLUDE_ANSIBLE_MODULE_COMMON>>main()So, finally, we have the code for our module built. Putting it all together, it shouldlook like the following code:
The last thing is to add a template to let ansible know that it needs to dynamically integrate code into our module. This is why we can use the ansiblemodule class and ansible to closely combine them. The sample code needs to be correctly placed at the bottom of the file, and there should be no code behind it. The Code is as follows:
# Include magic from LIB/ansible/module_common.py
# <Include_ansible_module_common>
Main ()
To add all the Code together, it should be like this:
You can test this module the same way you tested the Bash module with the test-module script. However, you need to be careful because if you run it with sudo , youmight reboot your machine or alter the init level to something you don't want. Thismodule is probably better tested by using Ansible itself on a remote test machine.We follow the same process as described in the Using a module section earlier in thischapter. We create a playbook that uses the module, and then place the module in alibrary directory that has been made in the same directory as the playbook. Here isthe playbook we should use:---- name: Test the new init modulehosts: testmachineuser: roottasks:- name: Set the init level to 5init: runlevel=5
You can test a python-written module like a bash-written module, but be careful when using sudo, because you may restart your machine, the best way to change your init level, or anything else unexpected, is to run on a remote managed host. The method for using the module is the same as that for using Bash. The Playbook code is as follows:
---
-Name: test the new init Module
Hosts: tew.achine
User: Root
Tasks:
-Name: Set the init level to 5
Init: runlevel = 5
Now you should be able to try and run this on a remote machine. The first timeyou run it, if the machine is not already in runlevel 5, you should see it change therunlevel. Then you should be able to run it for a second time to see that nothing haschanged. You might also want to check to make sure the module fails correctly whennot run as root.
Now you can test your module on a remote managed host. During the first running, if the host is not running at level 5, you will see that the running level has been changed, and running again will not change anything, you can also run the command correctly when testing the module using root.