AnsibleAPI 開(kāi)發(fā)

http://www.reibang.com/p/ec1e4d8438e9

一统刮、安裝方式

安裝方式有兩種:

  1. YUM 工具
  2. PIP 工具

1 YUM 工具安裝(適用于給系統(tǒng)自帶的 python 版本安裝 ansilbe 模塊)

  1. 安裝 epel 源
yum install epel-release  -y
  1. 安裝 Ansible
yum  install  ansible  -y
  1. 導(dǎo)入模塊
[root@5e4b448b73e5 ~]# python
Python 2.7.5 (default, Aug  7 2019, 00:51:29)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import ansible
>>> ansible.__file__
'/usr/lib/python2.7/site-packages/ansible/__init__.pyc'
>>>

2 PIP3 工具安裝(使用于給指定版本的 Python 安裝 ansible 模塊)

  1. 創(chuàng)建虛擬環(huán)境
[root@5e4b448b73e5 ~]# pip3 install virtualenvwrapper
export VIRTUALENVWRAPPER_PYTHON=$(which python3)
export WORKON_HOME=$HOME/.virtualenv
source /usr/local/bin/virtualenvwrapper.sh
[root@5e4b448b73e5 ~]# ~/.virtualenv
[root@5e4b448b73e5 ~]# source .bashrc

創(chuàng)建虛擬環(huán)境

[root@5e4b448b73e5 ~]# mkvirtualenv ansibleapi

  1. 安裝 ansible
(ansibleapi) [root@5e4b448b73e5 ~]# pip3 install ansible
  1. 導(dǎo)入模塊
(ansibleapi) [root@5e4b448b73e5 ~]# python3
Python 3.7.6 (default, Aug 11 2020, 10:30:02)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ansible
>>> ansible.__file__
'/root/.virtualenv/ansibleapi/lib/python3.7/site-packages/ansible/__init__.py'
>>>

3 配置文件位置說(shuō)明

這種安裝方式,配置文件和資產(chǎn)配置文件不會(huì)在 /etc 下產(chǎn)生澜术。

將會(huì)在下面的路徑下:

/root/.virtualenv/ansibleapi/lib/python3.7/site-packages/ansible/galaxy/data/container/tests/

用到這些文件的時(shí)候,需要在 /etc 目錄想創(chuàng)建目錄 ansible正勒, 之后把需要的配置文件和資產(chǎn)配置文件放到 /etc/ansible/ 目錄下募谎。

二梭灿、 先從官方示例入手

點(diǎn)擊 官方示例源碼 v2.8

注意:下面的所有開(kāi)發(fā),都是以 2.8 版本為例的( 2.9.9 已通過(guò)測(cè)試 )备埃。2.72.8 版本有一些差異:
2.7 使用了 Python 標(biāo)準(zhǔn)庫(kù)里的 命名元組來(lái)初始化選項(xiàng)姓惑,而 2.8 是 Ansible 自己封裝了一個(gè) ImmutableDict ,之后需要和 context 結(jié)合使用的按脚。兩者不能互相兼容于毙。 點(diǎn)擊 2.7 官方示例

#!/usr/bin/env python3

import json
import shutil
from ansible.module_utils.common.collections import ImmutableDict
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 import context
import ansible.constants as C

class ResultCallback(CallbackBase):
    """A sample callback plugin used for performing an action as results come in

    If you want to collect all results into a single object for processing at
    the end of the execution, look into utilizing the ``json`` callback plugin
    or writing your own custom callback plugin
    """
    def v2_runner_on_ok(self, result, **kwargs):
        """Print a json representation of the result

        This method could store the result in an instance attribute for retrieval later
        """
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

# since the API is constructed for CLI it expects certain options to always be set in the context object
context.CLIARGS = ImmutableDict(connection='local', module_path=['/to/mymodules'], forks=10, become=None,
                                become_method=None, become_user=None, check=False, diff=False)

# initialize needed objects
loader = DataLoader() # Takes care of finding and reading yaml, json and ini files
# 注意這里的  vault_pass 是錯(cuò)誤的,正確的應(yīng)該是 conn_pass
passwords = dict(vault_pass='secret')

# Instantiate our ResultCallback for handling results as they come in. Ansible expects this to be one of its main display outlets
results_callback = ResultCallback()

# create inventory, use path to host config file as source or hosts in a comma separated string
inventory = InventoryManager(loader=loader, sources='localhost,')

# variable manager takes care of merging all the different sources to give you a unified view of variables available in each context
variable_manager = VariableManager(loader=loader, inventory=inventory)

# create data structure that represents our play, including tasks, this is basically what our YAML loader does internally.
play_source =  dict(
        name = "Ansible Play",
        hosts = 'localhost',
        gather_facts = 'no',
        tasks = [
            dict(action=dict(module='shell', args='ls'), register='shell_out'),
            dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
         ]
    )

# Create play object, playbook objects use .load instead of init or new methods,
# this will also automatically create the task objects from the info provided in play_source
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

# Run it - instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks
tqm = None
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              passwords=passwords,
              stdout_callback=results_callback,  # Use our custom callback instead of the ``default`` callback plugin, which prints to stdout
          )
    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 structures we use to communicate with them
    if tqm is not None:
        tqm.cleanup()

    # Remove ansible tmpdir
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

下面是把官方的示例分解開(kāi)了

1. 首先是需要導(dǎo)入的模塊

import json
import shutil
from ansible.module_utils.common.collections import ImmutableDict
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 import context
import ansible.constants as C

核心類介紹

導(dǎo)入類完整路徑 功能用途
from ansible.module_utils.common.collections import ImmutableDict 用于添加選項(xiàng)辅搬。比如: 指定遠(yuǎn)程用戶remote_user=None
from ansible.parsing.dataloader import DataLoader 讀取 json/ymal/ini 格式的文件的數(shù)據(jù)解析器
from ansible.vars.manager import VariableManager 管理主機(jī)和主機(jī)組的變量管理器
from ansible.inventory.manager import InventoryManager 管理資源庫(kù)的唯沮,可以指定一個(gè) inventory 文件等
from ansible.playbook.play import Play 用于執(zhí)行 Ad-hoc 的類 ,需要傳入相應(yīng)的參數(shù)
from ansible.executor.task_queue_manager import TaskQueueManager ansible 底層用到的任務(wù)隊(duì)列管理器
ansible.plugins.callback.CallbackBase 處理任務(wù)執(zhí)行后返回的狀態(tài)
from ansible import context 上下文管理器,他就是用來(lái)接收 ImmutableDict 的示例對(duì)象
import ansible.constants as C 用于獲取 ansible 產(chǎn)生的臨時(shí)文檔伞辛。
from ansible.executor.playbook_executor import PlaybookExecutor 執(zhí)行 playbook 的核心類
from ansible.inventory.host import Group 對(duì) 主機(jī)組 執(zhí)行操作 烂翰,可以給組添加變量等操作夯缺,擴(kuò)展
from ansible.inventory.host import Host 對(duì) 主機(jī) 執(zhí)行操作 蚤氏,可以給主機(jī)添加變量等操作,擴(kuò)展

2. 回調(diào)插件

回調(diào)插件就是一個(gè)類踊兜。
用于處理執(zhí)行結(jié)果的竿滨。
后面我們可以改寫(xiě)這個(gè)類,以便滿足我們的需求捏境。

class ResultCallback(CallbackBase):
    """回調(diào)插件于游,用于對(duì)執(zhí)行結(jié)果的回調(diào),
    如果要將執(zhí)行的命令的所有結(jié)果都放到一個(gè)對(duì)象中垫言,應(yīng)該看看如何使用 JSON 回調(diào)插件或者
    編寫(xiě)自定義的回調(diào)插件
    """
    def v2_runner_on_ok(self, result, **kwargs):
        """
        將結(jié)果以 json 的格式打印出來(lái)
        此方法可以將結(jié)果存儲(chǔ)在實(shí)例屬性中以便稍后檢索
        """
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

3. 選項(xiàng)

這是最新 2.8 中的方式贰剥。老版本,請(qǐng)自行谷歌筷频。


# 需要始終設(shè)置這些選項(xiàng)蚌成,值可以變
context.CLIARGS = ImmutableDict(
  connection='local', module_path=['module_path'],
  forks=10, become=None, become_method=None, 
  become_user=None, check=False, diff=False)

4. 數(shù)據(jù)解析器前痘、密碼和回調(diào)插件對(duì)象

數(shù)據(jù)解析器,用于解析 存放主機(jī)列表的資源庫(kù)文件 (比如: /etc/ansible/hosts) 中的數(shù)據(jù)和變量數(shù)據(jù)的担忧。
密碼這里是必須使用的一個(gè)參數(shù)芹缔,假如通過(guò)了公鑰信任,也可以給一個(gè)空字典

# 需要實(shí)例化一下
loader = DataLoader()

# ssh 用戶的密碼, 2.9.9 版本中測(cè)試通過(guò)瓶盛,需要安裝 sshpass 程序
passwords = dict(conn_pass='secret')

# 實(shí)例化一下
results_callback = ResultCallback()

5. 創(chuàng)建資源庫(kù)對(duì)象

這里需要使用數(shù)據(jù)解析器
sources 的值可以是一個(gè)配置好的 inventory 資源庫(kù)文件最欠;也可以是一個(gè)含有以 逗號(hào) , 為分割符的字符串,
注意不是元組哦,正確示例:sources='localhost,'
這里寫(xiě)的是自己主機(jī)上實(shí)際的資源庫(kù)文件

inventory = InventoryManager(loader=loader, sources='/etc/ansible/hosts')

6. 變量管理器

假如有變量惩猫,所有的變量應(yīng)該交給他管理芝硬。
這里他會(huì)從 inventory 對(duì)象中獲取到所有已定義好的變量。
這里也需要數(shù)據(jù)解析器帆锋。

variable_manager = VariableManager(loader=loader, inventory=inventory)

7. 創(chuàng)建一個(gè) Ad-hoc

這里創(chuàng)建一個(gè)命令行執(zhí)行的 ansible 命令是可以是多個(gè)的吵取。以 task 的方式體現(xiàn)。
既然都是 python 寫(xiě)的锯厢,那么這些變量的值都可以是從數(shù)據(jù)庫(kù)中取到的數(shù)據(jù)或者從前端接收到的參數(shù)皮官。
開(kāi)發(fā)的雛形若隱若現(xiàn)了...

play_source =  dict(
        name = "Ansible Play",
        hosts = 'nginx',
        gather_facts = 'no',
        tasks = [
            dict(action=dict(module='shell', args='ls'), register='shell_out'),
            dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
         ]
    )

9. 從 Play 的靜態(tài)方法 load 中創(chuàng)建一個(gè) play 對(duì)象。

play = Play().load(play_source, variable_manager=variable_manager, loader=loader)


8. 任務(wù)隊(duì)列管理器

要想執(zhí)行 Ad-hoc 实辑,需要把上面的 play 對(duì)象交個(gè)任務(wù)隊(duì)列管理器的 run 方法去運(yùn)行捺氢。

# 先定義一個(gè)值,防止代碼出錯(cuò)后剪撬,   `finally` 語(yǔ)句中的 `tqm` 未定義摄乒。
tqm = None
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              passwords=passwords,
              stdout_callback=results_callback,  # 這里是使用了之前,自定義的回調(diào)插件残黑,而不是默認(rèn)的回調(diào)插件 `default`
          )
    result = tqm.run(play) # 執(zhí)行的結(jié)果返回碼馍佑,成功是 0
finally:
    # `finally` 中的代碼,無(wú)論是否發(fā)生異常梨水,都會(huì)被執(zhí)行拭荤。
    # 如果 `tqm` 不是 `None`, 需要清理子進(jìn)程和我們用來(lái)與它們通信的結(jié)構(gòu)。
    if tqm is not  None:
        tqm.cleanup()

    # 最后刪除 ansible 產(chǎn)生的臨時(shí)目錄
    # 這個(gè)臨時(shí)目錄會(huì)在 ~/.ansible/tmp/ 目錄下
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

總結(jié)一下

Ad-hoc 模式到 API 的映射

image.png

三疫诽、二次開(kāi)發(fā)

說(shuō)完官方示例舅世,那么我們究竟可以使用 ansible API 干些不一樣的事情呢?

1. 重寫(xiě)回調(diào)插件

首先奇徒,官方示例值的回調(diào)函數(shù)沒(méi)有進(jìn)行進(jìn)一個(gè)的格式化雏亚,這個(gè)我們可以改寫(xiě)一下這個(gè)類里的回調(diào)函數(shù)。
還有摩钙,可以增加上失敗的信息展示以及主機(jī)不可達(dá)的信息展示罢低。

class ResultCallback(CallbackBase):
    """
    重寫(xiě)callbackBase類的部分方法
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.host_ok = {}
        self.host_unreachable = {}
        self.host_failed = {}
    def v2_runner_on_unreachable(self, result):
        self.host_unreachable[result._host.get_name()] = result

    def v2_runner_on_ok(self, result, **kwargs):
        self.host_ok[result._host.get_name()] = result

    def v2_runner_on_failed(self, result, **kwargs):
        self.host_failed[result._host.get_name()] = result

如何使用

for host, result in results_callback.host_ok.items():
    print("主機(jī){}, 執(zhí)行結(jié)果{}".format(host, result._result))

四、如何執(zhí)行 playbook

shark

1. 先看 playbook_executor.PlaybookExecutor 源碼


class PlaybookExecutor:

    '''
    This is the primary class for executing playbooks, and thus the
    basis for bin/ansible-playbook operation.
    '''

    def __init__(self, playbooks, inventory, variable_manager, loader, passwords):
        self._playbooks = playbooks
        self._inventory = inventory
        self._variable_manager = variable_manager
        self._loader = loader
        self.passwords = passwords
        self._unreachable_hosts = dict()
...略...

2. 執(zhí)行 playbook

其他用到的部分和 Ad-hoc 方式時(shí)的一樣

from ansible.executor.playbook_executor import PlaybookExecutor

playbook = PlaybookExecutor(playbooks=['/root/test.yml'],  # 注意這里是一個(gè)列表
                 inventory=inventory,
                 variable_manager=variable_manager,
                 loader=loader,
                 passwords=passwords)

# 使用回調(diào)函數(shù)
playbook._tqm._stdout_callback = results_callback

result = playbook.run()

for host, result in results_callback.host_ok.items():
    print("主機(jī){}, 執(zhí)行結(jié)果{}".format(host, result._result['result']['stdout'])


五胖笛、動(dòng)態(tài)添加主機(jī)和主機(jī)組

使用 ansible API 可以從其他來(lái)源中動(dòng)態(tài)的創(chuàng)建資源倉(cāng)庫(kù)网持。比如從 CMDB 的數(shù)據(jù)庫(kù)中宜肉。

InventoryManager 類

方法

inv = InventoryManager(loader=loader, sources='localhost,')

# 創(chuàng)建一個(gè)組  nginx
inv.add_group('nginx')

# 向 nginx 組中添加主機(jī)
inv.add_host(host='node1',group='nginx')
inv.add_host(host='node2',group='nginx')

# 獲取目前所有的組和主機(jī)的信息
In [38]: inv.get_groups_dict()
Out[38]: {'all': ['localhost'], 'ungrouped': ['localhost'], 'nginx': ['node1', 'node2']}

這樣就會(huì)動(dòng)態(tài)創(chuàng)建一個(gè) nginx 的組,并且向組內(nèi)添加了兩個(gè)主機(jī)

寫(xiě)到一個(gè)類里

import json
import shutil
from ansible.module_utils.common.collections import ImmutableDict
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 import context
import ansible.constants as C


class ResultCallback(CallbackBase):
    """
    重寫(xiě)callbackBase類的部分方法
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.host_ok = {}
        self.host_unreachable = {}
        self.host_failed = {}
        self.task_ok = {}
    def v2_runner_on_unreachable(self, result):
        self.host_unreachable[result._host.get_name()] = result

    def v2_runner_on_ok(self, result, **kwargs):
        self.host_ok[result._host.get_name()] = result

    def v2_runner_on_failed(self, result, **kwargs):
        self.host_failed[result._host.get_name()] = result

class MyAnsiable2():
    def __init__(self,
        connection='local',  # 連接方式 local 本地方式翎碑,smart ssh方式
        remote_user=None,    # ssh 用戶
        remote_password=None,  # ssh 用戶的密碼谬返,應(yīng)該是一個(gè)字典, key 必須是 conn_pass
        private_key_file=None,  # 指定自定義的私鑰地址
        sudo=None, sudo_user=None, ask_sudo_pass=None,
        module_path=None,    # 模塊路徑,可以指定一個(gè)自定義模塊的路徑
        become=None,         # 是否提權(quán)
        become_method=None,  # 提權(quán)方式 默認(rèn) sudo 可以是 su
        become_user=None,  # 提權(quán)后日杈,要成為的用戶遣铝,并非登錄用戶
        check=False, diff=False,
        listhosts=None, listtasks=None,listtags=None,
        verbosity=3,
        syntax=None,
        start_at_task=None,
        inventory=None):

        # 函數(shù)文檔注釋
        """
        初始化函數(shù),定義的默認(rèn)的選項(xiàng)值莉擒,
        在初始化的時(shí)候可以傳參酿炸,以便覆蓋默認(rèn)選項(xiàng)的值
        """
        context.CLIARGS = ImmutableDict(
            connection=connection,
            remote_user=remote_user,
            private_key_file=private_key_file,
            sudo=sudo,
            sudo_user=sudo_user,
            ask_sudo_pass=ask_sudo_pass,
            module_path=module_path,
            become=become,
            become_method=become_method,
            become_user=become_user,
            verbosity=verbosity,
            listhosts=listhosts,
            listtasks=listtasks,
            listtags=listtags,
            syntax=syntax,
            start_at_task=start_at_task,
        )

        # 三元表達(dá)式,假如沒(méi)有傳遞 inventory, 就使用 "localhost,"
        # 指定 inventory 文件
        # inventory 的值可以是一個(gè) 資產(chǎn)清單文件
        # 也可以是一個(gè)包含主機(jī)的元組涨冀,這個(gè)僅僅適用于測(cè)試
        #  比如 : 1.1.1.1,    # 如果只有一個(gè) IP 最后必須有英文的逗號(hào)
        #  或者: 1.1.1.1, 2.2.2.2

        self.inventory = inventory if inventory else "localhost,"

        # 實(shí)例化數(shù)據(jù)解析器
        self.loader = DataLoader()

        # 實(shí)例化 資產(chǎn)配置對(duì)象
        self.inv_obj = InventoryManager(loader=self.loader, sources=self.inventory)

        # 設(shè)置密碼
        self.passwords = remote_password

        # 實(shí)例化回調(diào)插件對(duì)象
        self.results_callback = ResultCallback()

        # 變量管理器
        self.variable_manager = VariableManager(self.loader, self.inv_obj)

    def run(self, hosts='localhost', gether_facts="no", module="ping", args='', task_time=0):
        """
        參數(shù)說(shuō)明:
        task_time -- 執(zhí)行異步任務(wù)時(shí)等待的秒數(shù)填硕,這個(gè)需要大于 0 ,等于 0 的時(shí)候不支持異步(默認(rèn)值)鹿鳖。這個(gè)值應(yīng)該等于執(zhí)行任務(wù)實(shí)際耗時(shí)時(shí)間為好
        """
        play_source =  dict(
            name = "Ad-hoc",
            hosts = hosts,
            gather_facts = gether_facts,
            tasks = [
                # 這里每個(gè) task 就是這個(gè)列表中的一個(gè)元素扁眯,格式是嵌套的字典
                # 也可以作為參數(shù)傳遞過(guò)來(lái),這里就簡(jiǎn)單化了翅帜。
               {"action":{"module": module, "args": args}, "async": task_time, "poll": 0}])

        play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)

        tqm = None
        try:
            tqm = TaskQueueManager(
                      inventory=self.inv_obj ,
                      variable_manager=self.variable_manager,
                      loader=self.loader,
                      passwords=self.passwords,
                      stdout_callback=self.results_callback)

            result = tqm.run(play)
        finally:
            if tqm is not None:
                tqm.cleanup()
            shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

    def playbook(self,playbooks):
        """
        Keyword arguments:
        playbooks --  需要是一個(gè)列表類型
        """
        from ansible.executor.playbook_executor import PlaybookExecutor

        playbook = PlaybookExecutor(playbooks=playbooks,
                        inventory=self.inv_obj,
                        variable_manager=self.variable_manager,
                        loader=self.loader,
                        passwords=self.passwords)

        # 使用回調(diào)函數(shù)
        playbook._tqm._stdout_callback = self.results_callback

        result = playbook.run()


    def get_result(self):
      result_raw = {'success':{},'failed':{},'unreachable':{}}

      # print(self.results_callback.host_ok)
      for host,result in self.results_callback.host_ok.items():
          result_raw['success'][host] = result._result
      for host,result in self.results_callback.host_failed.items():
          result_raw['failed'][host] = result._result
      for host,result in self.results_callback.host_unreachable.items():
          result_raw['unreachable'][host] = result._result

      # 最終打印結(jié)果姻檀,并且使用 JSON 繼續(xù)格式化
      print(json.dumps(result_raw, indent=4))

這個(gè)類可以執(zhí)行 Ad-hoc 也可以執(zhí)行 playbook
為了測(cè)試方便,這里設(shè)置了一些默認(rèn)值:
連接模式采用 local ,也就是本地方式
資產(chǎn)使用 "localhost,"
執(zhí)行 Ad-hoc 時(shí)涝滴,默認(rèn)的主機(jī)是 localhost

執(zhí)行 Playbook 時(shí)绣版,必須傳入 playbooks 位置參數(shù),其值是一個(gè)包含 playbook.yml 文件路徑的列表歼疮,如:
絕對(duì)路徑寫(xiě)法 ["/root/test.yml"]
當(dāng)前路徑寫(xiě)法 ["test.yml"]

使用范例一:

記得要先建立免密登錄

執(zhí)行 默認(rèn)的 Ad-hoc

實(shí)例化
ansible2 = MyAnsiable2()

執(zhí)行 ad-hoc
ansible2.run()

打印結(jié)果
ansible2.get_result()

輸出內(nèi)容

[root@635bbe85037c ~]# /usr/local/bin/python3 /root/code/ansible2api2.8.py
{
    "success": {
        "localhost": {
            "invocation": {
                "module_args": {
                    "data": "pong"
                }
            },
            "ping": "pong",
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false,
            "changed": false
        }
    },
    "failed": {},
    "unreachable": {}
}


使用范例二:

執(zhí)行自定義的 ad-hoc

資產(chǎn)配置文件 /etc/ansible/hosts 內(nèi)容

[nginx]
172.19.0.2
172.19.0.3

代碼

使用自己的 資產(chǎn)配置文件杂抽,并使用 ssh 的遠(yuǎn)程連接方式
ansible2 = MyAnsiable2(inventory='/etc/ansible/hosts', connection='smart')

執(zhí)行自定義任務(wù),執(zhí)行對(duì)象是 nginx 組
ansible2.run(hosts= "nginx", module="shell", args='ip a |grep "inet"')

打印結(jié)果
ansible2.get_result()


輸出內(nèi)容

[root@635bbe85037c ~]# /usr/local/bin/python3 /root/code/ans
ible2api2.8.py
{
    "success": {
        "172.19.0.2": {
            "changed": true,
            "end": "2019-08-11 11:09:28.954365",
            "stdout": "    inet 127.0.0.1/8 scope host lo\n 
   inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0",
            "cmd": "ip a |grep \"inet\"",
            "rc": 0,
            "start": "2019-08-11 11:09:28.622177",
            "stderr": "",
            "delta": "0:00:00.332188",
            "invocation": {
                "module_args": {
                    "creates": null,
                    "executable": null,
                    "_uses_shell": true,
                    "strip_empty_ends": true,
                    "_raw_params": "ip a |grep \"inet\"",
                    "removes": null,
                    "argv": null,
                    "warn": true,
                    "chdir": null,
                    "stdin_add_newline": true,
                    "stdin": null
                }
            },
            "stdout_lines": [
                "    inet 127.0.0.1/8 scope host lo",
                "    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0"
            ],
            "stderr_lines": [],
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false
        },
        "172.19.0.3": {
            "changed": true,
            "end": "2019-08-11 11:09:28.952018",
            "stdout": "    inet 127.0.0.1/8 scope host lo\n    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0",
            "cmd": "ip a |grep \"inet\"",
            "rc": 0,
            "start": "2019-08-11 11:09:28.643490",
            "stderr": "",
            "delta": "0:00:00.308528",
            "invocation": {
                "module_args": {
                    "creates": null,
                    "executable": null,
                    "_uses_shell": true,
                    "strip_empty_ends": true,
                    "_raw_params": "ip a |grep \"inet\"",
                    "removes": null,
                    "argv": null,
                    "warn": true,
                    "chdir": null,
                    "stdin_add_newline": true,
                    "stdin": null
                }
            },
            "stdout_lines": [
                "    inet 127.0.0.1/8 scope host lo",
                "    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0"
            ],
            "stderr_lines": [],
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false
        }
    },
    "failed": {},
    "unreachable": {}
}

使用范例三 執(zhí)行 playbook:

沿用上例繼續(xù)執(zhí)行一個(gè) playbook

playbook /root/test.yml 內(nèi)容

---
- name: a test playbook
  hosts: [nginx]
  gather_facts: no
  tasks:
  - shell: ip a |grep inet
...

代碼

ansible2 = MyAnsiable2(inventory='/etc/ansible/hosts', connection='smart')

傳入playbooks 的參數(shù)韩脏,需要是一個(gè)列表的數(shù)據(jù)類型缩麸,這里是使用的相對(duì)路徑
相對(duì)路徑是相對(duì)于執(zhí)行腳本的當(dāng)前用戶的家目錄
ansible2.playbook(playbooks=['test.yml'])


ansible2.get_result()


輸出內(nèi)容

[root@635bbe85037c ~]# /usr/local/bin/python3 /root/code/ansible2api2.8.py
{
    "success": {
        "172.19.0.2": {
            "changed": true,
            "end": "2019-08-11 11:12:34.563817",
            "stdout": "    inet 127.0.0.1/8 scope host lo\n    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0",
            "cmd": "ip a |grep inet",
            "rc": 0,
            "start": "2019-08-11 11:12:34.269078",
            "stderr": "",
            "delta": "0:00:00.294739",
            "invocation": {
                "module_args": {
                    "creates": null,
                    "executable": null,
                    "_uses_shell": true,
                    "strip_empty_ends": true,
                    "_raw_params": "ip a |grep inet",
                    "removes": null,
                    "argv": null,
                    "warn": true,
                    "chdir": null,
                    "stdin_add_newline": true,
                    "stdin": null
                }
            },
            "stdout_lines": [
                "    inet 127.0.0.1/8 scope host lo",
                "    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0"
            ],
            "stderr_lines": [],
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false
        },
        "172.19.0.3": {
            "changed": true,
            "end": "2019-08-11 11:12:34.610433",
            "stdout": "    inet 127.0.0.1/8 scope host lo\n    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0",
            "cmd": "ip a |grep inet",
            "rc": 0,
            "start": "2019-08-11 11:12:34.290794",
            "stderr": "",
            "delta": "0:00:00.319639",
            "invocation": {
                "module_args": {
                    "creates": null,
                    "executable": null,
                    "_uses_shell": true,
                    "strip_empty_ends": true,
                    "_raw_params": "ip a |grep inet",
                    "removes": null,
                    "argv": null,
                    "warn": true,
                    "chdir": null,
                    "stdin_add_newline": true,
                    "stdin": null
                }
            },
            "stdout_lines": [
                "    inet 127.0.0.1/8 scope host lo",
                "    inet 172.19.0.3/16 brd 172.19.255.255 scope global eth0"
            ],
            "stderr_lines": [],
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false
        }
    },
    "failed": {},
    "unreachable": {}
}

使用范例四 playbook 中提權(quán)

---
- name: a test playbook
  hosts: [nginx]
  gather_facts: no
  remote_user: shark
  become: True
  become_method: sudo
  vars:
    ansible_become_password: upsa
  tasks:
  - shell: id
...

使用范例五 執(zhí)行異步任務(wù)

代碼

ansible2 = MyAnsiable2(connection='smart')

ansible2.run(module="shell", args="sleep 15;hostname -i", task_time=15)

ansible2.get_result()

執(zhí)行輸出結(jié)果

(ansible) [root@iZ2zecj761el8gvy7p9y2kZ ~]# python3 myansibleapi2.py
{
    "success": {
        "localhost": {
            "started": 1,
            "finished": 0,
            "results_file": "/root/.ansible_async/118567079981.4210",
            "ansible_job_id": "118567079981.4210",
            "changed": true,
            "ansible_facts": {
                "discovered_interpreter_python": "/usr/bin/python"
            },
            "_ansible_no_log": false
        }
    },
    "failed": {},
    "unreachable": {}
}

ansible_job_id 是這個(gè)任務(wù)返回的任務(wù) ID

獲取任務(wù)結(jié)果

(ansible) [root@iZ2zecj761el8gvy7p9y2kZ ~]# ansible 127.0.0.1 -i 127.0.0.1, -m async_status -a "jid=118567079981.4210"
127.0.0.1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "ansible_job_id": "118567079981.4210",
    "changed": true,
    "cmd": "sleep 15;hostname -i",
    "delta": "0:00:15.034476",
    "end": "2020-06-11 12:06:57.723919",
    "finished": 1,
    "rc": 0,
    "start": "2020-06-11 12:06:42.689443",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "127.0.0.1 172.17.125.171 ::1",
    "stdout_lines": [
        "127.0.0.1 172.17.125.171 ::1"
    ]
}

使用范例六 在 playbook 中執(zhí)行異步任務(wù)

playbook 中執(zhí)行異步任務(wù)可以說(shuō)和 api 的開(kāi)發(fā)幾乎沒(méi)有關(guān)系, 是在 playbook 中實(shí)現(xiàn)的。

---
- name: a test playbook
  hosts: [localhost]
  gather_facts: no
  tasks:
  - shell: sleep 10;hostname -i
    async: 10  # 異步
    poll: 0
    register: job
  - name: show  job id
    debug:
      msg: "Job id is {{ job }}"
...

執(zhí)行和獲取結(jié)果參考前面的范例三

參考

Inventory 部分參數(shù)說(shuō)明

ansible_ssh_host
      將要連接的遠(yuǎn)程主機(jī)名.與你想要設(shè)定的主機(jī)的別名不同的話,可通過(guò)此變量設(shè)置.
?
ansible_ssh_port
      ssh端口號(hào).如果不是默認(rèn)的端口號(hào),通過(guò)此變量設(shè)置.
?
ansible_ssh_user
      默認(rèn)的 ssh 用戶名
?
ansible_ssh_pass
      ssh 密碼(這種方式并不安全,我們強(qiáng)烈建議使用 --ask-pass 或 SSH 密鑰)
?
ansible_sudo_pass
      sudo 密碼(這種方式并不安全,我們強(qiáng)烈建議使用 --ask-sudo-pass)
?
ansible_sudo_exe (new in version 1.8)
      sudo 命令路徑(適用于1.8及以上版本)
?
ansible_connection
      與主機(jī)的連接類型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默認(rèn)使用 paramiko.1.2 以后默認(rèn)使用 'smart','smart' 方式會(huì)根據(jù)是否支持 ControlPersist, 來(lái)判斷'ssh' 方式是否可行.
?
ansible_ssh_private_key_file
      ssh 使用的私鑰文件.適用于有多個(gè)密鑰,而你不想使用 SSH 代理的情況.
?
ansible_shell_type
      目標(biāo)系統(tǒng)的shell類型.默認(rèn)情況下,命令的執(zhí)行使用 'sh' 語(yǔ)法,可設(shè)置為 'csh' 或 'zsh'.
?
ansible_python_interpreter
      目標(biāo)主機(jī)的 python 路徑.適用于的情況: 系統(tǒng)中有多個(gè) Python, 或者命令路徑不是"/usr/bin/python",比如  \*BSD, 或者 /usr/bin/python
      不是 2.X 版本的 Python.我們不使用 "/usr/bin/env" 機(jī)制,因?yàn)檫@要求遠(yuǎn)程用戶的路徑設(shè)置正確,且要求 "python" 可執(zhí)行程序名不可為 python以外的名字(實(shí)際有可能名為python26).
?
      與 ansible_python_interpreter 的工作方式相同,可設(shè)定如 ruby 或 perl 的路徑....
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載骤素,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者匙睹。
  • 序言:七十年代末愚屁,一起剝皮案震驚了整個(gè)濱河市济竹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霎槐,老刑警劉巖送浊,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異丘跌,居然都是意外死亡袭景,警方通過(guò)查閱死者的電腦和手機(jī)唁桩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耸棒,“玉大人荒澡,你說(shuō)我怎么就攤上這事∮胙辏” “怎么了单山?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)幅疼。 經(jīng)常有香客問(wèn)我米奸,道長(zhǎng),這世上最難降的妖魔是什么爽篷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任悴晰,我火速辦了婚禮,結(jié)果婚禮上逐工,老公的妹妹穿的比我還像新娘铡溪。我一直安慰自己,他們只是感情好泪喊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布佃却。 她就那樣靜靜地躺著,像睡著了一般窘俺。 火紅的嫁衣襯著肌膚如雪饲帅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天瘤泪,我揣著相機(jī)與錄音灶泵,去河邊找鬼。 笑死对途,一個(gè)胖子當(dāng)著我的面吹牛赦邻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播实檀,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惶洲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了膳犹?” 一聲冷哼從身側(cè)響起恬吕,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎须床,沒(méi)想到半個(gè)月后铐料,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年钠惩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柒凉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡篓跛,死狀恐怖膝捞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情愧沟,我是刑警寧澤绑警,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站央渣,受9級(jí)特大地震影響计盒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芽丹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一北启、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拔第,春花似錦咕村、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至泳猬,卻和暖如春批钠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背得封。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工埋心, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忙上。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓拷呆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疫粥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茬斧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Ansible Ansible version : 2.6.2 ad-hoc命令簡(jiǎn)介 什么是ad-hoc命令? a...
    洛神鬼道閱讀 2,954評(píng)論 0 1
  • 一梗逮、簡(jiǎn)介 在Linux自動(dòng)化運(yùn)維中项秉,常見(jiàn)的自動(dòng)化運(yùn)維工具可分為需要安裝終端的puppet、func和不需要安裝終端...
    小尛酒窩閱讀 2,721評(píng)論 0 6
  • 一库糠、初識(shí)ansible 1伙狐、ansible是新出現(xiàn)的自動(dòng)化運(yùn)維工具 ansible是一個(gè)配置管理和應(yīng)用部署工具,基...
    清風(fēng)徐來(lái)_簡(jiǎn)閱讀 2,496評(píng)論 0 15
  • ansible 系統(tǒng)架構(gòu) ansible簡(jiǎn)介ansible是新出現(xiàn)的自動(dòng)化運(yùn)維工具,ansible是一個(gè)配置管理和...
    運(yùn)維阿文閱讀 9,586評(píng)論 1 52
  • 1. 什么是Ansible瞬欧,它有什么用贷屎? Ansible它是個(gè)集配置管理和應(yīng)用部署于一體的自動(dòng)化運(yùn)維工具。 應(yīng)用情...
    午覺(jué)不眠Orz閱讀 1,518評(píng)論 0 0