How to Build Custom Ansible Module
Ansible Custom Module
I am going very short about Ansible module description, here is from Ansible website:
Ansible ships with a number of modules (called the ‘module library’) that can be executed directly on remote hosts or through Playbooks. Users can also write their own modules. These modules can control system resources, like services, packages, or files (anything really), or handle executing system commands.
Ansible built-in modules are practical and handy. Indeed, their modules cover your needs. But, in several cases, you need more. So let say their built-in modules sometimes is not enough. Hence Ansible custom module comes to play.
Use Case
Let’s build a simple custom module. It is a dice game. The game is about guessing the result. And you have maximum three chances for each guess.
Step-by-step
First of all, we need to create project structure as follow:
1 2 3 |
play.yml [library] |_ dice.py |
library/dice.py
There are several steps for implementing our dice game in this file.
Import required library
Since this has correlation with ansible module, then we need import ansible module utils. And one more thing, because dice means probability, so we need to import python random class as well.
1 2 3 4 |
#!/usr/bin/python from ansible.module_utils.basic import * import random |
Note: the hash bang (#!) is required for creating custom module
Main method
Next step is creating an entry point, which is the main() method. In this main method, we determine each field that are going to be define in playbook, then pass those fields into AnsibleModule
constructor. Subsequently, pass the module.params object into custom function, then exit the program gracefully using exit_json
, if success or fail_json
whenever it fails.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
def main(): fields = { "guess_number": { "required": True, "type": "int", "choices": [1, 2, 3, 4, 5, 6] }, "number_of_roll": { "required": False, "default" : 1, "type": "int", "choices": [1, 2, 3] }, } module = AnsibleModule(argument_spec=fields) is_error, has_changed, result = roll_dice(module.params) if not is_error: module.exit_json(changed=has_changed, meta=result) else: module.fail_json(msg="Error playing dice", meta=result) |
Note:
- roll_dice function will be implemented in next paragraph.
- each field contains choices key, to limit the input between those number. This key is optional.
roll_dice Function
This function is basically
- Produce random number between 1 – 6 (of course, this is dice).
- Check the outcome between the guess number.
- Change Ansible state and you win. Otherwise you lose and no state will be changed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
def roll_dice(data): is_error = False has_changed = False gn = data['guess_number'] nor = data['number_of_roll'] flag = "You lose" res = [] counter = 1 while counter <= nor: r = random.randrange(1,7) result = { "round": counter, "dice_output" : r } res.append(result) if r == gn: counter = nor + 1 flag = "You win" has_changed = True counter += 1 resp = { "your_guess": gn, "result": flag, "dice_fact": res } meta = {"status" : "OK", "response" : resp} return is_error, has_changed, meta |
Note: Getting value from yml input can be done by putting field name as a dictionary key.
play.yml
It is time to put our module in playbook file. Please take a note on two things. First, module name refers with filename (without .py extension) under library folder. Second, module parameters refer with key dictionary in fields property (see main method section).
1 2 3 4 5 6 7 8 9 |
- hosts: localhost tasks: - name: Rolling dice dice: guess_number: 3 number_of_roll: 3 register: result - debug: var=result |
Run Playbook
Now it is time to run the playbook by executing this command:
1 |
ansible-playbook play.yml |
Then, you should get output similar with this. Hopefully you have some lucky guesses 🙂
Conclusion
And that’s all folks. Viola! Now we can continue to create more useful custom module, such as creating server group in wildfly domain mode or deploying war files into application server. Hopefully, I will take those topics up in the next post (*finger crossed*).
PS: you can checkout this full example on my github, in the subfolder dice_module.