您现在的位置是:首页 > 文章详情

分析ansible源码模块中test-module是如何实现自定义模块测试的

日期:2016-04-03点击:614

1. 为什么会有这篇文章的介绍呢?

在ansible文档中有一篇介绍用户自定义模块的文章 链接地址如下Developing Modules,但是在使用测试的时候总是有异常,无法继续进行下面的操作,于是就看了下是如何进行测试模块的.


2. 自定义模块分为两种情况

1> 不传参数的,如下 # ansible -i hosts hostgroup -m ping -k  2> 传递参数的, 如下 # ansible -i hsots hostgroup -m shell -a 'uptime' -k

ansible的文档上也给了两个对应的自定义模块的示例

1> 不传参数的     #!/usr/bin/python     import datetime     import json     date = str(datetime.datetime.now())     print json.dumps({         "time" : date     })

2> 传递参数的

#!/usr/bin/python # import some python modules that we'll use.  These are all # available in Python's core import datetime import sys import json import os import shlex # read the argument string from the arguments file args_file = sys.argv[1] args_data = file(args_file).read() # for this module, we're going to do key=value style arguments # this is up to each module to decide what it wants, but all # core modules besides 'command' and 'shell' take key=value # so this is highly recommended arguments = shlex.split(args_data) for arg in arguments:     # ignore any arguments without an equals in it     if "=" in arg:         (key, value) = arg.split("=")         # if setting the time, the key 'time'         # will contain the value we want to set the time to         if key == "time":             # now we'll affect the change.  Many modules             # will strive to be 'idempotent', meaning they             # will only make changes when the desired state             # expressed to the module does not match             # the current state.  Look at 'service'             # or 'yum' in the main git tree for an example             # of how that might look.             rc = os.system("date -s \"%s\"" % value)             # always handle all possible errors             #             # when returning a failure, include 'failed'             # in the return data, and explain the failure             # in 'msg'.  Both of these conventions are             # required however additional keys and values             # can be added.             if rc != 0:                 print json.dumps({                     "failed" : True,                     "msg"    : "failed setting the time"                 })                 sys.exit(1)             # when things do not fail, we do not             # have any restrictions on what kinds of             # data are returned, but it's always a             # good idea to include whether or not             # a change was made, as that will allow             # notifiers to be used in playbooks.             date = str(datetime.datetime.now())             print json.dumps({                 "time" : date,                 "changed" : True             })             sys.exit(0) # if no parameters are sent, the module may or # may not error out, this one will just # return the time date = str(datetime.datetime.now()) print json.dumps({     "time" : date })


不论是带参数的还是不带参数的,模块写完之后该如何测试你写的模块是否正确呢?

ansible的文档上给了一种检测模块的方式:


Testing Modules

There’s a useful test script in the source checkout for ansible


# 下载测试自定义模块的脚本 1. 克隆ansible源码到本地 # git clone git://github.com/ansible/ansible.git --recursive 2. source脚本中设定的环境变量到当前会话 # source ansible/hacking/env-setup 3. 赋予脚本执行权限 # chmod +x ansible/hacking/test-module 由于第一步在克隆的时候操作就失败了 索性直接将源码全部clone到本地 操作如下 # git clone https://github.com/ansible/ansible.git


3. 测试模块

1> 自定义模块不带参数传递 执行方式

比如你的脚本名字为timetest.py,那么执行命令如下所示

# ansible/hacking/test-module -m ./timetest.py

* including generated source, if any, saving to: /root/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"time": "2016-04-03 02:09:41.516592"} *********************************** PARSED OUTPUT {     "time": "2016-04-03 02:09:41.516592" }

2> 自定义模块带参数传递 执行方式

比如你的脚本名字为timetest.py,传递的参数为time="March 14 22:10",那么执行命令如下所示

# ansible/hacking/test-module -m ./timetest.py -a "time=\"March 14 12:23\""


带参数的这个地方执行失败 报错如下

[root@ManagerAnsible sourceCode_tmp]# ansible/hacking/test-module -m ../modules/timetest_params.py -a "time=\"March 14 12:23\"" * including generated source, if any, saving to: /root/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT Mon Mar 14 12:23:00 UTC 2016 {"changed": true, "time": "2016-03-14 12:23:00.000262"} *********************************** INVALID OUTPUT FORMAT Mon Mar 14 12:23:00 UTC 2016 {"changed": true, "time": "2016-03-14 12:23:00.000262"} Traceback (most recent call last):   File "ansible/hacking/test-module", line 167, in runtest     results = json.loads(out)   File "/usr/local/python27/lib/python2.7/json/__init__.py", line 339, in loads     return _default_decoder.decode(s)   File "/usr/local/python27/lib/python2.7/json/decoder.py", line 364, in decode     obj, end = self.raw_decode(s, idx=_w(s, 0).end())   File "/usr/local/python27/lib/python2.7/json/decoder.py", line 382, in raw_decode     raise ValueError("No JSON object could be decoded") ValueError: No JSON object could be decoded


从上面的报错可以追踪到ansible/hacking/test-module脚本的167行在json.loads对象的时候失败.

    try:         print("***********************************")         print("RAW OUTPUT")         print(out)         print(err)         results = json.loads(out)    # 第167行     except:         print("***********************************")         print("INVALID OUTPUT FORMAT")         print(out)         traceback.print_exc()         sys.exit(1)     print("***********************************")     print("PARSED OUTPUT")     print(jsonify(results,format=True))

至于为什么会出现这个问题,在文章的后面会有解决办法......



首先看timetest.py文件(注释比较多 代码量其实就几行)

正文 前两行没怎么看懂

args_file = sys.argv[1]

args_data = file(args_file).read()

# 接受一个参数 args_file = sys.argv[1] # 打开这个参数 file <<>> open args_data = file(args_file).read()  //开始纳闷了开始纳闷了开始纳闷了

于是又对这个文件ansible/hacking/test-module进行追踪

我对test-module添加了中文注释 有兴趣的朋友可以参考下 已经上传文章末尾到附件中.



解决了两个问题:

问题1:

ansible/hacking/test-module

有以下几个函数

parse # 接受命令行参数.

write_argsfile # 将命令行传递的参数写入到指定的文件中.

boilerplate_module # 将./timetest.py文件的内容全部写入到命令行-o默认指定的模块文件

runtest # 执行脚并打开参数本文件

总结下

boilerplate_module这个函数:将用户自定义的模块写入到~/.ansible_module_generated这个文件中

write_argsfile这个函数:将用户传递的参数写入到~/.ansible_test_module_arguments这个文件中

runtest这个函数:执行脚本和传递的参数~/.ansible_module_generated ~/.ansible_test_module_arguments


问题2:

修改文档中timetest.py代码

修改前 rc = os.system("date -s \"%s\"" % value) 修改后 import commands rc, output = commands.getstatusoutput('date -s \"%s\"' % value)

其实有两处才让我想到是这里的原因:

原因1:

首先看timetest.py代码中 摘取一段

rc = os.system("date -s \"%s\"" % value) if rc != 0:

这个rc到底是获取os.system的命令执行结果还是获取os.system的返回值呢?

想必第二行的if语句你就弄明白.


原因2:

ansible/hacking/test-module文件

def runtest( modfile, argspath):     """Test run a module, piping it's output for reporting."""     os.system("chmod +x %s" % modfile)     invoke = "%s" % (modfile)     if argspath is not None:         invoke = "%s %s" % (modfile, argspath)     cmd = subprocess.Popen(invoke, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)     (out, err) = cmd.communicate()     try:         print("***********************************")         print("RAW OUTPUT")         print(out)         print(err)         results = json.loads(out)     except:         print("***********************************")         print("INVALID OUTPUT FORMAT")         print(out)         traceback.print_exc()         sys.exit(1)     print("***********************************")     print("PARSED OUTPUT")     print(jsonify(results,format=True))

这个函数的正确返回结果肯定要是json格式的,而timetest.py文件有两处打印;第一处打印便是os.system的执行结果;第二处便是print json.dumps的结果,显然这是两行打印 无法进行json

那么我就举个列子来说明下吧

# 对timetest.py简写成如下格式 import os, datetime, json os.system('date -s %s' % 3) date = str(datetime.datetime.now()) print json.dumps({                 "time" : date,                 "changed" : True             })              # 那么test-module中的out就相当于上面执行结果的相加 "Mon Mar 14 03:00:00 UTC 2016" + "{"changed": true, "time": "2016-03-14 03:00:00.013835"}" 以上这种格式你是无法进行json.loads成json对象的 所以也就是报错的原因.


在文章的末尾就是如何使用用户自定义的模块呢,前面介绍了那么多都是如何测试模块,接下来就是用户如何正确的使用自定义完成的模块.

(1)通过ansible --help |grep module

  -m MODULE_NAME, --module-name=MODULE_NAME                         module name to execute (default=command)   -M MODULE_PATH, --module-path=MODULE_PATH                         specify path(s) to module library (default=None)

通过-M的方式来指定自定义模块的路径.


(2)通过ANSIBLE_LIBRARY 变量来指定


<<不够庆幸的是 前两种方式我Google好多文章也都没有解决,如果哪位朋友有解决的版本 也请告知>>


(3)我使用了最后一种比较笨的方式:

当前python版本为源码安装方式 2.7版本 python的安装路径为/usr/local/python27 python模块包路径为/usr/local/python27/lib/python2.7/site-packages/ansible/modules 其中有core(核心包)和extras(扩展包)两个目录 那么自定义模块 应该属于扩展包吧 于是做如下操作 # cd /usr/local/python27/lib/python2.7/site-packages/ansible/modules/extras # mkdir zhengys # cp ~/demo/ansible/modules/* ./ 也就是把自定义的模块都放置与extras/zhengys目录下 就可以使用了


附件下载地址:http://pan.baidu.com/s/1kVQwgej


原文链接:https://blog.51cto.com/467754239/1759873
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章