模块自定义开发
ansible的模块非常多,官方/社区提供的模块数量非常多,大多数都可以满足需求,但有时候需要满足公司业务的一些特殊需求,则需要自己对模块的开发。
ansible module的原理
- 根据ansible 搜索路径找到inventory 以及对应的模块路径
- 加载模块文件
- ansible 将模块加载到内存中,并同步生成对应的临时py文件(python脚本), 并将文件传到目标服务器(基于ssh),期间会打开3次ssh 链接,分别是传脚本-传文件-赋权限
- 执行并返回结果
- 删除临时py文件
根据以上步骤,可以发现几个问题
- module 文件是被传送到 远端主机 执行的。
- module是文件, 而非脚本,所以可以使用任何语言编写,哪怕最终是个编译后的二进制文件也可以。
- 模块的返回值必须是json dumps的字符串。
调试
module 是远端主机执行的,所以调试会比较麻烦,一般有2个方法进行调试
- 保留传过去的python 脚本以及文件:
# 设置环境变量,让传过去的文件不会自动被清理,这样就可以在远端进行调试
ANSIBLE_KEEP_REMOTE_FILES=1
- 本地调试
# 下载官方的本地调试脚本
wget https://raw.githubusercontent.com/ansible/ansible/devel/hacking/test-module.py
# 执行脚本
python test-module.py -m mymod
{"changed": true, "end": "2020-01-01 23:00:00.125895", "stdout": "", "cmd": ["/bin/sleep", "3"], "rc": 0, "start": "2020-01-01 23:10:00.125895", "stderr": "", "delta": "0:00:03.077342", "invocation": {"module_args": {"creates": null, "executable": null, "_uses_shell": false, "strip_empty_ends": true, "_raw_params": "/bin/sleep 3", "removes": null, "argv": null, "warn": true, "chdir": null, "stdin_add_newline": true, "stdin": null}}}
模块开发
需要修改ansible.cfg的配置文件,将library 里面的路径改成自己定义的模块路径
以下是一个简单的检查文件md5的demo:
#!/usr/bin/env python
# -*- codingutf-8-*-
from ansible.module_utils.basic import *
import hashlib
import os.path
# 初始化一个module实例,需要传入文件的m5d 以及 路径,用于对比文件是否经过修改。
module = AnsibleModule(
argument_spec = dict(
path=dict(type='str', required=True),
checksum=dict(type='str', required=True),
),
)
# 获取参数变量
path = module.params['path']
checksum = module.params['checksum']
if os.path.isfile(path):
fp = open(path,'rb')
contents = fp.read()
fp.close()
md5sum = hashlib.md5(contents).hexdigest()
if md5sum == checksum:
result = dict(stdout = "file checksum is ok", changed = False, rc = 0)
module.exit_json(**result)
else:
result = dict(msg = 'file checksum is wrong',rc = 1)
module.fail_json(**result)
else:
result = dict(msg='file not exists',rc = 1)
module.fail_json(**result)
其他语言编写module
args_file=$1
[ ! -f "$args_file" ] && echo -n '{"failed": true, "msg": "missing required arguments: file"}' && exit 1
args_result=$(cat $args_file | gawk -F'file=' '{print $2}' | gawk -F' ' '{print $1}')
[ ! -n "$args_result" ] && echo -n "{\"failed\": true, \"msg\": \"file () is absent, cannot continue\", \"file\": \"$args_result\"}" && exit 1
touch $args_result && echo -n "{\"changed\": true, \"rc\": $?,\"file\": \"$args_result\"}" || echo -n "{\"failed\": true, \"rc\": $?, \"file\": \"$args_result\"}"
exit $?
参数是通过文件形式进行传递的,而不是通过参数形式进行传输的,所以参数的解析需要注意,且返回值必须是json格式的字符串,满足这2项后,用其他语言都可以进行模块的开发。