ansible模块定制开发
ansible做为流行的运维自动化工具,异常强大与好用,自身带了诸多的通用模块,在日常工作中,基本能满足需求,但是需求是变化多端的,尤其是在结合业务的时候,ansible就不是很给力了,这就是需要我们定制开发自己的模块,在ansible运行框架下,更好的服务的我们的业务。
现以pids.py模块进行讲解,该文件位于ansible/modules/system/pids.py,一旦你理解模块的基本开发流程,就可以开发的模块,让自己的能力更上一层楼:
代码语言:javascript复制#!/usr/bin/python
# Copyright: (c) 2019, Saranya Sridharan
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: pids
version_added: 2.8
description: "Retrieves a list of PIDs of given process name in Ansible controller/controlled machines.Returns an empty list if no process in that name exists."
short_description: "Retrieves process IDs list if the process is running otherwise return empty list"
author:
- Saranya Sridharan (@saranyasridharan)
requirements:
- psutil(python module)
options:
name:
description: the name of the process you want to get PID for.
required: true
type: str
'''
EXAMPLES = '''
# Pass the process name
- name: Getting process IDs of the process
pids:
name: python
register: pids_of_python
- name: Printing the process IDs obtained
debug:
msg: "PIDS of python:{{pids_of_python.pids|join(',')}}"
'''
RETURN = '''
pids:
description: Process IDs of the given process
returned: list of none, one, or more process IDs
type: list
sample: [100,200]
'''
from ansible.module_utils.basic import AnsibleModule
try:
import psutil
HAS_PSUTIL = True
except ImportError:
HAS_PSUTIL = False
def get_pid(name):
return [p.info['pid'] for p in psutil.process_iter(attrs=['pid', 'name']) if p.info and p.info.get('name', None) and p.info['name'].lower() == name.lower()]
def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(required=True, type="str"),
),
supports_check_mode=True,
)
if not HAS_PSUTIL:
module.fail_json(msg="Missing required 'psutil' python module. Try installing it with: pip install psutil")
name = module.params["name"]
response = dict(pids=get_pid(name))
module.exit_json(**response)
if __name__ == '__main__':
main()
1. 文档书写
代码语言:javascript复制pids.py可以看到关于文档说明的四个主要变量,都是由yaml格式来进行具体的说明:
1. DOCUMENTATION: 表示模块的使用文档,可以直接使用官方的格式来进行定制化修改,变量简单易懂;
2. EXAMPLES: 表示模块具体的实例,来说明模块的在playbook的具体使用方法;
3. RETURN: 表示模块返回值的具体含义,可参考官方的格式进行修改
4. ANSIBLE_METADATA: 模块的元数据信息,包含metadata_version、status、supported_by等信息;
2. 参数定义
代码语言:javascript复制from ansible.module_utils.basic import AnsibleModule
在模块开发的过程中,主要依赖AnsibleModule模块,来与ansible整个逻辑进行交互。其中包含参数定义、模块异常处理、以及结果返回。这里要说明一点,我们的开发的模块是放在目标主机上运行的,这在整个模块开发的过程中,要时刻牢记。下面主要从参数定义、模块异常处理,以及结果返回来具体说明:
1. 参数定义:在pids.py中,该内置模块是获取指定进程名的所有pid,在使用时,我们需要配置进程名称,此处声明参数的逻辑就封装在AnsibleModule实例化的过程中,来看具体的代码:
def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(required=True, type="str"),
),
supports_check_mode=True,
)
由代码可知,我们需要定义的参数是由argument_spec参数来定义,其类型为dict,通过具体的key=value的形式来具体定义模块需要的参数,其中key指的是参数名称,value为dict, 同时是通过key=value的形式具体配置参数的属性,比如参数的数据类型、默认值、是否必须等等,具体的可以直接看官方文档,或者文末的总结,AnsibleModule还支持更多的参数,比如supports_check_mode,来说明模块是否支持check模式等等,更多参数可翻看源码。
3. 模块返回值
代码语言:javascript复制在使用ansible-playbook时,我们需要保存模块执行的返回值来进行后续的处理,那如何让模块正常返回内容,同样以pids.py的内容为例,我们可以看到这样的代码:
module.exit_json(**response)
就是这句关键代码,它可以通过接受一个字典对象,来将内容正常返回,我们就可以获取返回内容,进行后续的逻辑处理。当执行这句代码时,整个模块就会正常退出,继续后面的task。
4. 异常处理
代码语言:javascript复制是程序就会有异常,这是我们不可避免的,你永远不会想到别人如何使用你的模块。这就需要我们在出现异常时,以更友好的方式通知我们的用户,而不是直接将异常传递给ansible-playbook,同样的,从代码中探寻真知:
module.fail_json(msg="Missing required 'psutil' python module. Try installing it with: pip install psutil")
这就代码就会告诉ansible,模块有异常,并把异常的信息通过参数传递给用户,比如pids.py就通过msg参数说明了失败的原因,我们可以指定更多不同的参数,来具体表明异常的原因即可。
5. 逻辑封装
代码语言:javascript复制从大量的官方模块可以看出,模块的逻辑都有单独的封装,然后再main函数中进行调用。我们在开发模块时,同样需要遵守该约定。以pids.py为例,其主要逻辑都封装在get_pid函数中,具体的函数实现就不在此讨论,就是正常的python代码逻辑,有python的基础的同学可以很快看懂。逻辑封装不仅仅是函数,我们更推荐以类的方式进行编写,此处的get_pid由于逻辑非常简单,用类的话有些大材小用,所以官方直接用一个函数就可以了。
我们再来看看main函数:
def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(required=True, type="str"),
),
supports_check_mode=True,
)
if not HAS_PSUTIL:
module.fail_json(msg="Missing required 'psutil' python module. Try installing it with: pip install psutil")
name = module.params["name"]
response = dict(pids=get_pid(name))
module.exit_json(**response)
if __name__ == '__main__':
main()
其实从main函数的定义和执行,就是python脚本的运行模式,我们开发时直接照搬就行。
6. 模块的存放位置
代码语言:javascript复制模块开发完成,那ansible是如何找到我们自定义的模块的呢?
Ansible自动将在某些目录中找到的所有可执行文件作为模块加载,因此您可以在以下任何位置创建或添加本地模块:
1. 添加到ANSIBLE_LIBRARY环境变量的任何目录($ANSIBLE_LIBRARY为冒号分隔的路径列表,和$PATH类似)
2. ~/.ansible/plugins/modules/
3. /usr/share/ansible/plugins/modules/
将模块文件保存在以下位置之一后,Ansible将对其进行加载,您可以在任何本地task,playbook或role中使用它。
要确认my_custom_module可用:
输入ansible-doc -t module my_custom_module 这可以显示该模块的文档。
要仅在某些playbook中使用本地模块,请执行以下操作:
将其存储在包含playbook(s)的目录中的一个名为library的子目录中
要仅在单个role中使用本地模块:
将其存储在该role内名为library的子目录中
7. 总结
ansible的强大之处在于我们通用需求已经可以完全满足,但是要想成为真正的高级玩家,ansible的扩展必不可少,我们的宗旨是别人有的我都有,别人没有的我可以自己创造。
参考文档
1.https://docs.ansible.com/ansible/latest/dev_guide/index.html