AnsibleModule
撰寫支援冪等與 check mode 的模組因為實務上流程百百種,市面上的現成模組也基本上是基於開發者遇到的問題提出來的解決方案,所以當我們遇到一些特殊問題時,這個時候就會需要自己寫模組來解決。
我們來實作一個簡單的模組 file_write
,這個模組會將文字內容寫入檔案,並且當內容相同時維持冪等,並支援 check mode。
# library/file_write.py
#!/usr/bin/python
from __future__ import annotations
import hashlib
import os
from ansible.module_utils.basic import AnsibleModule
def read_text(path: str) -> str | None:
"""讀取檔案內容,若檔案不存在則回傳 None"""
if not os.path.exists(path):
return None
with open(path, "r", encoding="utf-8") as handle:
return handle.read()
def sha1_text(text: str | None) -> str:
"""計算文字內容的 SHA1 雜湊值,用於比較檔案變更"""
if text is None:
return ""
return hashlib.sha1(text.encode("utf-8")).hexdigest()
def ensure_parent(path: str) -> None:
"""確保目標檔案的父目錄存在,若不存在則自動建立"""
parent = os.path.dirname(path)
if parent and not os.path.isdir(parent):
os.makedirs(parent, exist_ok=True)
def write_text(path: str, content: str, mode: str | None) -> None:
"""寫入檔案內容並設定權限"""
with open(path, "w", encoding="utf-8") as handle:
handle.write(content)
if mode:
os.chmod(path, int(mode, 8)) # 將八進位字串轉換為整數
def main() -> None:
"""模組主要邏輯:處理參數、執行檔案寫入、回傳結果"""
# 定義模組參數規格
module = AnsibleModule(
argument_spec={
"path": {"type": "path", "required": True}, # 目標檔案路徑
"content": {"type": "str", "required": True}, # 要寫入的內容
"mode": {"type": "str"}, # 檔案權限 (選填)
},
supports_check_mode=True, # 支援 --check 模式
)
# 取得模組參數
path: str = module.params["path"]
content: str = module.params["content"]
mode: str | None = module.params.get("mode")
# 讀取目前檔案內容並判斷是否需要變更
current = read_text(path)
will_change = current != content
# 如果是 check mode,只回報結果不實際執行
if module.check_mode:
module.exit_json(
changed=will_change,
path=path,
before=sha1_text(current),
after=sha1_text(content),
check_mode=True,
)
# 實際執行檔案寫入
try:
if will_change:
ensure_parent(path) # 確保父目錄存在
write_text(path, content, mode) # 寫入檔案
except Exception as error:
# 發生錯誤時回傳失敗訊息
module.fail_json(msg=str(error), path=path)
# 成功完成,回傳執行結果
module.exit_json(
changed=will_change,
path=path,
before=sha1_text(current),
after=sha1_text(read_text(path)),
)
if __name__ == "__main__":
main()
# 實際寫入
ansible -i localhost, -c local localhost \
-M library -m file_write \
-a "path=/tmp/day20.txt content='Hello from module' mode=0644"
# check mode 模擬(只檢查不實際執行)
ansible -i localhost, -c local localhost \
-M library -m file_write \
-a "path=/tmp/day20.txt content='Hello from module'" --check
# 測試冪等性(重複執行應該顯示 changed=false)
ansible -i localhost, -c local localhost \
-M library -m file_write \
-a "path=/tmp/day20.txt content='Hello from module' mode=0644"
---
# custom-module-demo.yml
- name: Day20 custom module demo
hosts: all
gather_facts: false
tasks:
- name: Write text file via custom module
file_write:
path: /tmp/day20.txt
content: "Hello from custom module"
mode: "0644"
ANSIBLE_LIBRARY=library \
ansible-playbook -i inventory.ini custom-module-demo.yml --syntax-check
argument_spec
明確定義參數型別與必填狀態no_log=True
changed
狀態準確反映真實變更module.check_mode
為 true 時,只需要回報預期結果before
和 after
狀態資訊fail_json
回傳詳細的錯誤訊息library/file_write.py
模組嘗試為 file_write
模組添加以下功能:
backup
參數,當設為 true
時在覆寫前備份原檔案owner
和 group
參數,支援檔案擁有者設定mode
格式不正確時回傳錯誤訊息設計一個新的自訂模組 system_info
,功能需求:
💡Tips:可參考
ansible.module_utils.facts
相關工具函數。
問題現象:
ERROR! couldn't resolve module/action 'file_write'
解決方案:
library/
目錄或使用 -M
參數)chmod +x library/file_write.py
#!/usr/bin/python
問題現象:
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "..."}
解決方案:
-vvv
參數獲得詳細除錯資訊ansible-playbook --syntax-check
檢查語法問題現象:
重複執行同樣的任務總是顯示 changed=true
解決方案:
明天我們來使用 Molecule 來驗證 Ansible 專案在實際運行中的正確性