Hi everyone!
This is MyAnsibleQuest!!!
In the previous post, I have discussed about Ansible playbooks for provisioning database and web servers. In this post, we will unleash the power of Ansible in Linux by writing our own module. We can write Ansible modules in many languages, but I would like to use Python here. The reasons for selecting Python are:
1. All the modules of Ansible are written in Python.
2. Easy and direct integration with Ansible is possible.
3. Python reduces the amount of code required, as we can use boiler plate code.
4. Handling JSON output is easy.
Most Importantly, I love to code in python.
Lets start with creating a setup for module development. Its very simple. We need a directory in which we place our playbook file and inside that a 'library' folder so that our playbook automatically look for the ansible module.
[root@server hands_on_ansible]# mkdir custom_module
[root@server hands_on_ansible]# cd custom_module
[root@server custom_module]# mkdir library
[root@server hands_on_ansible]# cd custom_module
[root@server custom_module]# mkdir library
[root@server custom_module]# touch custom.yml
[root@server custom_module]# touch library/custom2.py
Now we will define our playbook.
There must be a reason to define a custom module. It may be a case that you want a custom function/task to accomplish. There might not be sufficient modules available for your task or something like that.
For this post, I would like to create a trivial module for monitoring cpu usage of my Linux servers for 'n' peak processes. There are several commands in linux for monitoring the cpu usage with ram memory e.g. top, htop, ps, free, etc. Here I will be using 'ps' command with a set of arguments to be passed to display process id, parent process id, command, etc, in a meaningful sorted manner for every server in my inventory.
As we know about ansible-playbook writing with .yml extension, we will first write it.
1. My custom.yml file is defined to operate on 'hosts' as 'dbservers'.
2. I have included an entry of 'gather_facts' as 'no' because I don't want any kind of delay in output.
3. The name of my task is 'Get top cpu consuming process'
4. In my 'custom2' named module, I have passed 7 parameters viz. pid (process id), ppid (parent process id), cmd (command), mem(memory info), cpu (cpu info), sort (to define sorting basis), num (number of peak processes to show in output).
5. At last, I have used a 'result' variable with 'register' module to save the output and displayed the result with 'debug' module.
[root@server Desktop]# cat hands_on_ansible/custom_module/custom.yml
---
- hosts: dbservers
gather_facts: no
tasks:
- name: Get top cpu consuming process
custom2:
pid: pid
ppid: ppid
cmd: cmd
mem: mem
cpu: cpu
sort: mem
num: '17'
register: result
- debug:
var: result
Secondly, we will focus on the ansible module - 'custom2.py'. Ansible module must contain some basic information like metadata, documentation, examples, return values, etc. For more enhanced details of writing ansible module follow the documentation link. I have included only the documentation, for understanding the module.
#!/usr/bin/python
DOCUMENTATION = '''
---
module: my_monitoring_module
short_description: This is my server cpu-memory monitoring module.
version_added: "2.4"
description:
- "This is my cpu-memory monitoring module to show 'n' peak processes at the time of module call."
options:
pid:
description:
- This is the value same as pid denoting process id.
required: true
ppid:
description:
- This is the value same as ppid denoting parent process id.
required: true
cmd:
description:
- This is the value same as cmd denoting the command in process.
required: false
mem:
description:
- This is the value same as mem denoting the memory in percent for a process.
required: true
alias: memory
cpu:
description:
- This is the value same as cpu denoting the cpu usage in percent for a process.
required: true
sort:
description:
- This is the value as either cpu or mem to sort by the order of cpu usage or memory usage.
required: true
num:
description:
- This is the value to output the number of peak processes.
required: true
author:
- Nitin (@4hathacker)
'''
from ansible.module_utils.basic import *
import subprocess
def main():
# defining the available arguments/parameters
# the user must pass to module
module = AnsibleModule(
argument_spec = dict(
pid = dict(required=True, type='str'),
ppid = dict(required=True, type='str'),
cmd = dict(required=False, type='str'),
mem = dict(aliases=['memory'], required=True, type='str'),
cpu = dict(required=True, type='str'),
sort = dict(required=True, type='str'),
num = dict(required=True, type='str')
),
# module supports check_mode
# but value at exit remain unchanged
# as its for monitoring pusrpose only
supports_check_mode=True
)
if module.check_mode:
module.exit_json(changed=False)
params = module.params
# passing the params to a shell command
# command = 'ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -n num'
# passing the command in subprocess module
if params['cmd'] is None:
process = subprocess.Popen("ps -eo" + params['pid'] + "," + params['ppid'] + ",%" + params['mem'] + ",%" + params['cpu'] + " --sort=-%" + params['sort'] + " | head -n " + params['num'],shell=True, stdout=subprocess.PIPE, close_fds=True)
else:
process = subprocess.Popen("ps -eo" + params['pid'] + "," + params['cmd'] + "," + params['ppid'] + ",%" + params['mem'] + ",%" + params['cpu'] + " --sort=-%" + params['sort'] + " | head -n " + params['num'],shell=True, stdout=subprocess.PIPE, close_fds=True)
exists = process.communicate()[0]
# getting result if process is not None
if exists:
result = exists.split('\n')
module.exit_json(changed=True, meminfo=result)
else:
err_info = "Error Occured: Not able to get peak cpu info"
module.fail_json(msg=err_info)
if __name__ == '__main__':
main()
For this post, I would like to create a trivial module for monitoring cpu usage of my Linux servers for 'n' peak processes. There are several commands in linux for monitoring the cpu usage with ram memory e.g. top, htop, ps, free, etc. Here I will be using 'ps' command with a set of arguments to be passed to display process id, parent process id, command, etc, in a meaningful sorted manner for every server in my inventory.
As we know about ansible-playbook writing with .yml extension, we will first write it.
1. My custom.yml file is defined to operate on 'hosts' as 'dbservers'.
2. I have included an entry of 'gather_facts' as 'no' because I don't want any kind of delay in output.
3. The name of my task is 'Get top cpu consuming process'
4. In my 'custom2' named module, I have passed 7 parameters viz. pid (process id), ppid (parent process id), cmd (command), mem(memory info), cpu (cpu info), sort (to define sorting basis), num (number of peak processes to show in output).
5. At last, I have used a 'result' variable with 'register' module to save the output and displayed the result with 'debug' module.
[root@server Desktop]# cat hands_on_ansible/custom_module/custom.yml
---
- hosts: dbservers
gather_facts: no
tasks:
- name: Get top cpu consuming process
custom2:
pid: pid
ppid: ppid
cmd: cmd
mem: mem
cpu: cpu
sort: mem
num: '17'
register: result
- debug:
var: result
Secondly, we will focus on the ansible module - 'custom2.py'. Ansible module must contain some basic information like metadata, documentation, examples, return values, etc. For more enhanced details of writing ansible module follow the documentation link. I have included only the documentation, for understanding the module.
#!/usr/bin/python
DOCUMENTATION = '''
---
module: my_monitoring_module
short_description: This is my server cpu-memory monitoring module.
version_added: "2.4"
description:
- "This is my cpu-memory monitoring module to show 'n' peak processes at the time of module call."
options:
pid:
description:
- This is the value same as pid denoting process id.
required: true
ppid:
description:
- This is the value same as ppid denoting parent process id.
required: true
cmd:
description:
- This is the value same as cmd denoting the command in process.
required: false
mem:
description:
- This is the value same as mem denoting the memory in percent for a process.
required: true
alias: memory
cpu:
description:
- This is the value same as cpu denoting the cpu usage in percent for a process.
required: true
sort:
description:
- This is the value as either cpu or mem to sort by the order of cpu usage or memory usage.
required: true
num:
description:
- This is the value to output the number of peak processes.
required: true
author:
- Nitin (@4hathacker)
'''
from ansible.module_utils.basic import *
import subprocess
def main():
# defining the available arguments/parameters
# the user must pass to module
module = AnsibleModule(
argument_spec = dict(
pid = dict(required=True, type='str'),
ppid = dict(required=True, type='str'),
cmd = dict(required=False, type='str'),
mem = dict(aliases=['memory'], required=True, type='str'),
cpu = dict(required=True, type='str'),
sort = dict(required=True, type='str'),
num = dict(required=True, type='str')
),
# module supports check_mode
# but value at exit remain unchanged
# as its for monitoring pusrpose only
supports_check_mode=True
)
if module.check_mode:
module.exit_json(changed=False)
params = module.params
# passing the params to a shell command
# command = 'ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -n num'
# passing the command in subprocess module
if params['cmd'] is None:
process = subprocess.Popen("ps -eo" + params['pid'] + "," + params['ppid'] + ",%" + params['mem'] + ",%" + params['cpu'] + " --sort=-%" + params['sort'] + " | head -n " + params['num'],shell=True, stdout=subprocess.PIPE, close_fds=True)
else:
process = subprocess.Popen("ps -eo" + params['pid'] + "," + params['cmd'] + "," + params['ppid'] + ",%" + params['mem'] + ",%" + params['cpu'] + " --sort=-%" + params['sort'] + " | head -n " + params['num'],shell=True, stdout=subprocess.PIPE, close_fds=True)
exists = process.communicate()[0]
# getting result if process is not None
if exists:
result = exists.split('\n')
module.exit_json(changed=True, meminfo=result)
else:
err_info = "Error Occured: Not able to get peak cpu info"
module.fail_json(msg=err_info)
if __name__ == '__main__':
main()
With respect to the above mentioned module,
1. Its clear in the module, that I have used 'ps' command to accomplish the task.
2. User can use 'memory' as an alias for 'mem' in custom.yml file.
3. Python's subprocess module is used to run the ps command.
4. Result is displayed after splitting lines by '\n'.
Its confession time...
There is no need to write a module to find the top 'n' peak processes on the basis of cpu and memory usage. We can accomplish the same task by passing the ps command with same arguments in the shell module of Ansible. This check.yml file will look as given below.
[root@server Desktop]# cat check.yml
---
- hosts: dbservers
tasks:
- name: check memory and cpu usage in dbservers
shell: "ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -n 17"
register: result
- debug: var=result
Just have a look at result of this file. You can observe few more things like rc, stdout, stdout_lines, etc. and explore the ansible docs to add the following in your module.
0 comments: