Python 調用 Ansible API 實現自動化管理,為后續運維平臺自動化管理提供幫助,也是學習Jumpserver源碼做實戰演練。
代碼已經做過測試,效果如下:
playbook 測試
python-ansible-playbook.png
adhoc 測試
python-ansible-adhoc.png
demo代碼
#!/usr/bin/env python
# encoding: utf-8
# Author: ColinSpace.com
# Date:2021-09
# Desc: 利用Python實現ansible的Adhoc 和 playbook 兩種任務方式
#
import json
import shutil
from ansible import context
from ansible.module_utils.common.collections import ImmutableDict
# 注意: 使用namedtuple實現Options的方式,官網已經不推薦使用
# 網上很多例子都是按照Options的方式,會有報錯如下:
# error_msg: "msg": "the connection plugin '<class 'ansible.utils.sentinel.Sentinel'>' was not found",
# 網上提供解決方案: https://blog.csdn.net/FMT21/article/details/103468284
# 但是目前驗證報錯,options 最終的tuple類型,在初始化的時候用到了 vars 系統函數,但是該函數報錯
# TypeError: vars() argument must have __dict__ attribute
# from collections import namedtuple
# Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])
# options = Options(connection='ssh', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False)
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
from ansible.executor.playbook_executor import PlaybookExecutor
import ansible.constants as C
# 注意: 該配置是為了避免沒有第一次公鑰訪問寫入 know_hosts 文件問題,
# 關于如何避免該問題,詳見
C.HOST_KEY_CHECKING = False
class ResultCallback(CallbackBase):
def v2_runner_on_ok(self, result, **kwargs):
host = result._host
print(json.dumps({host.name: result._result}, indent=4))
def v2_runner_on_failed(self, result, **kwargs):
host = result._host
print(json.dumps({host.name: result._result}, indent=4))
def v2_runner_on_unreachable(self, result, **kwargs):
host = result._host
print(json.dumps({host.name: result._result}, indent=4))
class AnsibleApi:
"""
Description:
"""
context.CLIARGS = ImmutableDict(
connection='ssh', remote_user=None, listtags=None, listhosts=None, listtasks=None,
module_path=None, verbosity=5, ask_sudo_pass=False, private_key_file=None,
# become=None, become_method=None, become_user=None,
become=True, become_method='sudo', become_user='root',
forks=10, check=False, diff=False, syntax=None,start_at_task=None,
)
"""
Options = namedtuple('Options', ['connection', 'remote_user', 'listtags', 'listhosts', 'listtasks',
'module_path', 'verbosity', 'ask_sudo_pass', 'private_key_file',
'become', 'become_method', 'become_user',
'forks', 'check', 'diff', 'syntax', 'start_at_task'])
options = Options(
connection='ssh', remote_user=None, listtags=None, listhosts=None, listtasks=None,
module_path=None, verbosity=5, ask_sudo_pass=False, private_key_file=None,
# become=None, become_method=None, become_user=None,
become=True, become_method='sudo', become_user='root',
forks=10, check=False, diff=False, syntax=None,start_at_task=None,
)
# 報錯: TypeError: vars() argument must have __dict__ attribute
context._init_global_context(options)
"""
# 一般使用ansible的時候都會通過免密的方式,所以這里直接初始化passwords變為空
def __init__(self):
# self.name = name
# self.host_list = host_list
# self.task_list = task_list
self.loader = DataLoader()
self.result_callback = ResultCallback()
# self.passwords = dict(vault_pass=passwords)
self.passwords = dict()
self.inventory = InventoryManager(loader=self.loader, sources=['/etc/ansible/inventory/hosts', '/etc/ansible/hosts'])
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
def run_adhoc(self, name, hosts, tasks):
play_source = dict(
name=name,
hosts=hosts,
gather_facts='no',
tasks=tasks
)
play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)
tqm = None
try:
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
passwords=self.passwords,
stdout_callback=self.result_callback, # Use our custom callback instead of the ``default`` callback plugin, which prints to stdout
run_additional_callbacks=C.DEFAULT_LOAD_CALLBACK_PLUGINS,
run_tree=False,
)
result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methods
finally:
# we always need to cleanup child procs and the structres we use to communicate with them
if tqm is not None:
tqm.cleanup()
# Remove ansible tmpdir
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
def run_playhook(self, playbook):
# self.variable_manager.extra_vars = {
# 'customer': 'test',
# 'disabled': 'yes'
# }
playbook = PlaybookExecutor(
playbooks=playbook,
inventory=self.inventory,
variable_manager = self.variable_manager,
loader=self.loader,
passwords=self.passwords,
)
result = playbook.run()
return result
if __name__ == '__main__':
a = AnsibleApi()
host_list = ['192.168.3.4']
task_list = [
dict(action=dict(module='shell', args="ls -l "))
]
# a.run_adhoc(name="checkConnection", hosts=host_list, tasks=task_list)
a.run_playhook(playbook=["/home/james.liu/test.yml"])