模块自定义开发

ansible的模块非常多,官方/社区提供的模块数量非常多,大多数都可以满足需求,但有时候需要满足公司业务的一些特殊需求,则需要自己对模块的开发。

ansible module的原理

  1. 根据ansible 搜索路径找到inventory 以及对应的模块路径
  2. 加载模块文件
  3. ansible 将模块加载到内存中,并同步生成对应的临时py文件(python脚本), 并将文件传到目标服务器(基于ssh),期间会打开3次ssh 链接,分别是传脚本-传文件-赋权限
  4. 执行并返回结果
  5. 删除临时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项后,用其他语言都可以进行模块的开发。

个人公众号, 分享一些日常开发,运维工作中的日常以及一些学习感悟,欢迎大家互相学习,交流

ansible开发(2)_Python教程