设为首页 收藏本站
查看: 1241|回复: 0

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

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-4-5 10:50:55 | 显示全部楼层 |阅读模式
1. 为什么会有这篇文章的介绍呢?

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

2. 自定义模块分为两种情况
1
2
3
4
5
1> 不传参数的,如下
# ansible -i hosts hostgroup -m ping -k

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



ansible的文档上也给了两个对应的自定义模块的示例
1
2
3
4
5
6
7
8
9
10
1> 不传参数的
    #!/usr/bin/python

    import datetime
    import json

    date = str(datetime.datetime.now())
    print json.dumps({
        "time" : date
    })



2> 传递参数的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/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
2
3
4
5
6
7
8
9
10
11
12
13
# 下载测试自定义模块的脚本

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
1
2
3
4
5
6
7
8
9
10
11
12
* 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\""

带参数的这个地方执行失败 报错如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[iyunv@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对象的时候失败.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    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()
1
2
3
4
5
# 接受一个参数
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代码
1
2
3
4
5
6
修改前
rc = os.system("date -s \"%s\"" % value)

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



其实有两处才让我想到是这里的原因:
原因1:
首先看timetest.py代码中 摘取一段
1
2
rc = os.system("date -s \"%s\"" % value)
if rc != 0:



这个rc到底是获取os.system的命令执行结果还是获取os.system的返回值呢?
想必第二行的if语句你就弄明白.

原因2:
ansible/hacking/test-module文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
那么我就举个列子来说明下吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 对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

1
2
3
4
  -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)我使用了最后一种比较笨的方式:
1
2
3
4
5
6
7
8
9
10
当前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


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-199935-1-1.html 上篇帖子: Ansible dockerimage docker模块使用记录 下篇帖子: ansible模块介绍 皇冠 如何
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表