分析ansible源碼模塊中test-module是如何實現自定義模塊測試的

1. 爲何會有這篇文章的介紹呢?
html

在ansible文檔中有一篇介紹用戶自定義模塊的文章 連接地址以下Developing Modules,可是在使用測試的時候老是有異常,沒法繼續進行下面的操做,因而就看了下是如何進行測試模塊的.python


2. 自定義模塊分爲兩種狀況git

1> 不傳參數的,以下
# ansible -i hosts hostgroup -m ping -k 

2> 傳遞參數的, 以下
# ansible -i hsots hostgroup -m shell -a 'uptime' -k

ansible的文檔上也給了兩個對應的自定義模塊的示例github

1> 不傳參數的
    #!/usr/bin/python

    import datetime
    import json

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

2> 傳遞參數的shell

#!/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
})


不管是帶參數的仍是不帶參數的,模塊寫完以後該如何測試你寫的模塊是否正確呢?express

ansible的文檔上給了一種檢測模塊的方式:json


Testing Modulespython2.7

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


# 下載測試自定義模塊的腳本

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

相關文章
相關標籤/搜索