ansible自定義模組

馬昌偉發表於2021-12-20

 

參考官網:http://www.ansible.com.cn/docs/developing_modules.html#tutorial

 

閱讀 ansible 附帶的模組(上面連結)是學習如何編寫模組的好方法。但是請記住,ansible 原始碼樹中的某些模組是內在的,因此請檢視serviceyum,不要太靠近async_wrapper 之類的東西,否則您會變成石頭。沒有人直接執行 async_wrapper。

好的,讓我們開始舉例。我們將使用 Python。首先,將其儲存為名為timetest.py的檔案

ansible自定義模組
#!/usr/bin/python

import datetime
import json

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

 

 

自定義模組

建立模組目錄

[root@mcw1 ~]$ mkdir /usr/share/my_modules   #這個目錄並不存在,ansible配置中存在這個註釋掉的路徑

編寫模組返回內容

[root@mcw1 ~]$ vim uptime

#!/usr/bin/python

import json
import os

up = os.popen('uptime').read()
dic = {"result":up}
print json.dumps(dic)

執行結果:

 

啟動模組目錄

[root@mcw1 ~]$ grep library /etc/ansible/ansible.cfg   #將配置中的這行內容註釋,取消掉,不需要重啟任何服務
library = /usr/share/my_modules/

測試模組使用情況

這裡顯示它使用的直譯器路徑了,這個直譯器是python2的直譯器,如果我寫的是python3的指令碼,並且不支援python2執行,我可能需要修改ansible預設使用的python直譯器。有點問題,我指令碼里寫的是python2的直譯器,我寫成python3應該就是python3了吧

 

按照上面想法試了下,果然是的,我另一個主機是沒有安裝python3的,所以報錯了。使用python3,貌似不會顯示python的路徑,跟之前python2有點區別

 

編寫指令碼:

 

 

程式如下:

[root@mcw1 ~]$ cat /usr/share/my_modules/mem
#!/usr/bin/python3
import subprocess,json
cmd="free -m |awk 'NR==2{print $2}'"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
cmdres, err = p.communicate()
mcw=str(cmdres).lstrip("b'").rstrip("\\n'")
print(mcw)

dic = {
    "result":{
             "mem_total": mcw
          } ,
    "err": str(err)
}
print(json.dumps(dic))
#print(dic)

上面程式中,python2和python3去掉\n換行符存在區別。python2:rstrip("\n"),python3:rstrip("\\n")

無論是否dumps,列印結果都是一行。當將字典dumps後,結果好看很多

 

 當不使用dumps時,都是報錯,但是有標準輸出的還是會一行列印出來

 

 當我程式裡將字典換成一行時,ansible輸出內容還是有層次的顯示出來。並且它把我們輸出的字典所有鍵值對,相當於批量追加進ansible自己的輸出字典中。同時它還會有自己的相關鍵值對,

 

 

總結:我們可以寫python(shell應該也可以)指令碼,將需要的資訊構建字典,列印出來。然後將指令碼放到自定義模組目錄下,無需新增執行許可權,就可以使用ansible呼叫自定義模組,將輸出結果顯示在ansible的列印結果字典中

 思考:上面總結列印的結果我怎麼用?python中如何使用這個結果,ansible劇本中是否能使用這個模組,如何使用,這麼用到它的列印結果?

 使用shell存在的問題

使用shell的方法還存在問題,有時間再看有辦法解決這個問題

 

 

set_fact模組作用

設定變數,其它地方使用變數

- hosts: testB
  remote_user: root
  tasks:
  - set_fact:
      testvar: "testtest"
  - debug:
      msg: "{{testvar}}"

設定變數,其它地方使用變數

我們通過set_fact模組定義了一個名為testvar的變數,變數值為testtest,然後使用debug模組輸出了這個變數:

設定變數接收其它變數的值,變數之間值的傳遞

如下:

ansible自定義模組
- hosts: localhost
  remote_user: root
  vars:
    testvar1: test1_string
  tasks:
  - shell: "echo test2_string"
    register: shellreturn
  - set_fact:
      testsf1: "{{testvar1}}"
      testsf2: "{{shellreturn.stdout}}"
  - debug:
      msg: "{{testsf1}} {{testsf2}}"
      #var: shellreturn
劇本2

在劇本中定義變數testvar1,在劇本中可以引用這個變數。執行shell命令,將命令返回值註冊為一個變數。set_fact模組設定兩個變數,變數1讓它等於前面設定的變數testvar1,變數2給它賦值shell命令的返回結果變數的標準輸出。後面對這兩個變數列印檢視

劇本中想要檢視資訊,需要使用debug 模組(debug msg 列印)

set_fact:可以自定義變數,可以進行變數值的傳遞。可以用這個模組重新定義一個變數去接收其它變數的值,包括接收其它註冊的變數值 

 

上例中,我們先定義了一個變數testvar1,又使用register將shell模組的返回值註冊到了變數shellreturn中,

之後,使用set_fact模組將testvar1變數的值賦予了變數testsf1,將shellreturn變數中的stdout資訊賦值給了testsf2變數,(可以將註釋去掉檢視變數shellreturn的值)

最後,使用debug模組輸出了testsf1與testsf2的值:

vars關鍵字變數,set_fact設定變數和註冊變數。vars的只在當前play生效,另外兩個可以跨play

如上述示例所示,set_fact模組可以讓我們在tasks中建立變數,也可以將一個變數的值賦值給另一個變數。

其實,通過set_fact模組建立的變數還有一個特殊性,通過set_fact建立的變數就像主機上的facts資訊一樣,可以在之後的play中被引用。

預設情況下,每個play執行之前都會執行一個名為”[Gathering Facts]”的預設任務,這個任務會收集對應主機的相關資訊,我們可以稱這些資訊為facts資訊,我們已經總結過怎樣通過變數引用這些facts資訊,此處不再贅述,而通過set_fact模組建立的變數可以在之後play中被引用,就好像主機的facts資訊可以在play中引用一樣,這樣說可能還是不是特別容易理解,不如來看一個小例子,如下

ansible自定義模組
- hosts: localhost
  remote_user: root
  vars:
    testvar1: tv1
  tasks:
  - set_fact:
      testvar2: tv2
  - debug:
      msg: "{{testvar1}} ----- {{testvar2}}"
- hosts: localhost
  remote_user: root
  tasks:
  - name: other play get testvar2
    debug:
      msg: "{{testvar2}}"
  - name: other play get testvar1
    debug:
      msg: "{{testvar1}}"
劇本

 

 變數1是vars關鍵字設定變數,在當前play生效,不能跨越play使用變數,但是變數2卻可以跨越play使用變數,變數2是set_facts模組設定變數

 

 

可以發現,這兩個變數在第一個play中都可以正常的輸出。但是在第二個play中,testvar2可以被正常輸出了,testvar1卻不能被正常輸出,會出現未定義testvar1的錯誤,因為在第一個play中針對testB主機進行操作時,testvar1是通過vars關鍵字建立的,而testvar2是通過set_fact建立的,所以testvar2就好像testB的facts資訊一樣,可以在第二個play中引用到,而建立testvar1變數的方式則不能達到這種效果,雖然testvar2就像facts資訊一樣能被之後的play引用,但是在facts資訊中並不能找到testvar2,只是”效果上”與facts資訊相同罷了。

通過註冊變數實現跨play呼叫變數

ansible自定義模組
- hosts: localhost
  remote_user: root
  vars:
    testvar3: tv3
  tasks:
  - shell: "echo tv4"
    register: testvar4
  - debug:
      msg: "{{testvar3}} -- {{testvar4.stdout}}"
- hosts: localhost
  remote_user: root
  tasks:
  - name: other play get testvar4
    debug:
      msg: "{{testvar4.stdout}}"
  - name: other play get testvar3
    debug:
      msg: "{{testvar3}}"
劇本

 

3是vars定義變數,4是註冊變數,4是可以跨play的,3卻不行 。是需要4還是3看情況

 

在第二個play中獲取”testvar3″時會報錯,而在第二個play中獲取註冊變數”testvar4″時則正常,但是,註冊變數中的資訊是模組的返回值,這並不是我們自定義的資訊,所以,如果想要在tasks中給變數自定義資訊,並且在之後的play操作同一個主機時能夠使用到之前在tasks中定義的變數時,則可以使用set_facts定義對應的變數。

上述示例中,即使是跨play獲取變數,也都是針對同一臺主機。

自定義過濾外掛

 

找到過濾外掛所在的目錄,當前沒有任何過濾外掛,新增一個外掛deal_list_num.py

[root@mcw1 ~]$ ls /usr/share/ansible/plugins/filter
deal_list_num.py

 

 外掛的好處在於編寫YML檔案時可以減少我們的工作量,而且結果易於展示,只要學習一些比較重要的比如Filter、Callbacks等即可。

    在普通情況下,我們主要是以{{somevars|filter}對somevars使用filter方法過濾,Ansible已經為我們提供了很多的過濾方法,比如找到列表中最大、最小數的max、min,把資料轉換成JSON格式的fromjson等,但這還遠遠不夠,我們還需要自定義一些過濾的方法來滿足一些特殊的需求,比如查詢列表中大於某個常數的所有數字等。以這個例子,展示如何編寫。

    首先,到ansible.cfg中去掉#,開啟filter_plugins的存放目錄,filter_plugins=/usr/share/ansible/plugins/filter。

    編寫deal_list_num.py檔案,他主要提供過濾列表中的正數、負數和查詢列表大於某一個給定數值這3個方法。/usr/share/ansible/filter/deal_list_num.py。

# !/usr/bin/env python
# encoding=utf-8
class FilterModule(object):
    def filters(self):    # 把使用的方法寫在這個return中
        filter_map = {
            'positive': self.positive,
            'negative': self.negative,
            'no_less_than': self.no_less_than
        }
        return filter_map
    def positive(self, data):
        r_data = []
        for item in data:
            if item >= 0:
                r_data.append(item)
        return r_data
    def negative(self, data):
        r_data = []
        for item in data:
            if item <= 0:
                r_data.append(item)
        return r_data
    def no_less_than(self, data, num):    # 第1個變數為傳入的數值,第2個為條件1
        r_data = []
        for item in data:
            if item >= num:
                r_data.append(item)
        return r_data
ansible自定義模組
[root@mcw1 ~]$ cat deal_list_num.yml 
- hosts: localhost
  gather_facts: False
 
  tasks:
  - name: set fact
    set_fact:
      num_list: [-1,-2,5,3,1]
 
  - name: echo positive
    shell: echo {{num_list | positive}}
    register: print_positive
  - debug: msg="{{print_positive.stdout_lines[0]}}"
 
  - name: echo negative
    shell: echo {{num_list | negative}}
    register: print_negative
  - debug: msg="{{print_negative.stdout_lines[0]}}"
 
  - name: echo negative
    shell: echo {{num_list | no_less_than(4)}}
    register: print_negative
  - debug: msg="{{print_negative.stdout_lines[0]}}"
劇本

 

第一個是取列表中的正數,第二個是取列表中的負數。第三個是取列表中不小於4的數

 

 

 

變數引用和檢視獲取shell命令結果作為註冊變數,該如何取到命令結果

 

過濾方法這裡做個修改

 

 

 

 

 

 總結:

1、過濾外掛定義類,類中定義方法,方法返回內容

2、將過濾外掛放到ansible過濾外掛目錄下

3、劇本中{{變數}}的方式呼叫變數。然後變數後面|加過濾方法。這樣就可以將變數傳遞進外掛對應方法中,除了self之外的第一個位置引數就是這個劇本中的變數。

4、在過濾外掛方法中對這個變數做過濾,然後返回結果(這裡是定義空列表,然後將劇本變數列表遍歷一次,篩選出指定條件的元素追加到新的列表中,方法返回新的列表這樣劇本中就是使用過濾後的資料)

 

 

 

 參考地址:

劇本編寫和定義模組: https://blog.csdn.net/weixin_46108954/article/details/104990063

設定和註冊變數:https://blog.csdn.net/weixin_45029822/article/details/105280206

自定義外掛:過濾外掛:https://blog.csdn.net/JackLiu16/article/details/82121044

相關文章