如何使用Ansible 2的API做python开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Ansible 2的API做python开发相关的知识,希望对你有一定的参考价值。

参考技术A 在ansible1.9的时候,API是一个非常简单的东西。官方说“it's pretty simple”,真是又pretty又simple。

import ansible.runner

runner = ansible.runner.Runner(
module_name='ping',
module_args='',
pattern='web*',
forks=10
)
datastructure = runner.run()

到了ansible2.0以后,是“a bit more complicated”,Oh my,简直让人难受。

简洁和灵活是鱼和熊掌。

ansible2.0 API怎么用?

ansible2.0更贴近于ansible cli的常用命令执行方式,不同于上一版本只能发送单个命令或playbook;而更推荐用户在调用ansibleAPI的时候,将playbook的每个task拆分出来,获取每个task的结果。能够跟灵活处理在执行批量作业过程中的各种反馈。

将执行操作的队列模型,包含各类环境参数设置,归结到“ansible.executor.task_queue_manager”类中

将执行过程中的各个task的设置,或者说playbook中的编排内容,归结到“ansible.playbook.play”中

上述两个东西,几乎囊括了可以在执行过程中设置的所有参数,足够灵活,也让人抓狂,相当于需要自己写一个1.9版本中的runner。
他们的确也都是原生类,并非专用于外部调用。

ansible.executor.task_queue_manager

这是ansible的一个内部模块(ansible/executor/task_queue_manager.py)。初始化的源码如下:

class TaskQueueManager:

'''
This class handles the multiprocessing requirements of Ansible by
creating a pool of worker forks, a result handler fork, and a
manager object with shared datastructures/queues for coordinating
work between all processes.

The queue manager is responsible for loading the play strategy plugin,
which dispatches the Play's tasks to hosts.
'''

def __init__(self, inventory, variable_manager, loader, options, passwords, stdout_callback=None, run_additional_callbacks=True, run_tree=False):

self._inventory = inventory
self._variable_manager = variable_manager
self._loader = loader
self._options = options
self._stats = AggregateStats()
self.passwords = passwords
self._stdout_callback = stdout_callback
self._run_additional_callbacks = run_additional_callbacks
self._run_tree = run_tree

self._callbacks_loaded = False
self._callback_plugins = []
self._start_at_done = False
self._result_prc = None

……

创建时,需要的主要参数包括:

inventory --> 由ansible.inventory模块创建,用于导入inventory文件

variable_manager --> 由ansible.vars模块创建,用于存储各类变量信息

loader --> 由ansible.parsing.dataloader模块创建,用于数据解析

options --> 存放各类配置信息的数据字典

passwords --> 登录密码,可设置加密信息

stdout_callback --> 回调函数

ansible.playbook.play

ansible.playbook是一个原生模块,既用于CLI也用于API。从源码可以看出来:

try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()

ansible.playbook.play(ansible/playbook/play.py)。初始化源码的介绍如下:

__all__ = ['Play']

class Play(Base, Taggable, Become):

"""
A play is a language feature that represents a list of roles and/or
task/handler blocks to execute on a given set of hosts.

Usage:

Play.load(datastructure) -> Play
Play.something(...)
"""

最后,用task_queue_manager(play)来执行,老规矩,源码的官方解释。

def run(self, play):
'''
Iterates over the roles/tasks in a play, using the given (or default)
strategy for queueing tasks. The default is the linear strategy, which
operates like classic Ansible by keeping all hosts in lock-step with
a given task (meaning no hosts move on to the next task until all hosts
are done with the current task).
'''

一个完整的例子

# -*- coding:utf-8 -*-
# !/usr/bin/env python
#
# Author: Shawn.T
# Email: shawntai.ds@gmail.com
#
# this is the Interface package of Ansible2 API
#

from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager
from ansible.inventory import Inventory
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from tempfile import NamedTemporaryFile
import os

class AnsibleTask(object):
def __init__(self, targetHost):
Options = namedtuple(
'Options', [
'listtags', 'listtasks', 'listhosts', 'syntax', 'connection','module_path',
'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args',
'sftp_extra_args', 'scp_extra_args', 'become', 'become_method', 'become_user',
'verbosity', 'check'
]
)

# initialize needed objects
self.variable_manager = VariableManager()

self.options = Options(
listtags=False, listtasks=False, listhosts=False, syntax=False, connection='smart',
module_path='/usr/lib/python2.7/site-packages/ansible/modules', forks=100,
remote_user='root', private_key_file=None, ssh_common_args=None, ssh_extra_args=None,
sftp_extra_args=None, scp_extra_args=None, become=False, become_method=None, become_user='root',
verbosity=None, check=False
)
self.passwords = dict(vault_pass='secret')
self.loader = DataLoader()

# create inventory and pass to var manager
self.hostsFile = NamedTemporaryFile(delete=False)
self.hostsFile.write(targetHost)
self.hostsFile.close()
self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=self.hostsFile.name)
self.variable_manager.set_inventory(self.inventory)

def ansiblePlay(self, action):
# create play with tasks
args = "ls /"
play_source = dict(
name = "Ansible Play",
hosts = 'all',
gather_facts = 'no',
tasks = [
dict(action=dict(module='shell', args=args), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='shell_out.stdout')))
]
)
play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)

# run it
tqm = None
try:
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
stdout_callback='default',
)
result = tqm.run(play)
finally:
# print result
if tqm is not None:
tqm.cleanup()
os.remove(self.hostsFile.name)
self.inventory.clear_pattern_cache()
return result

写一个ansibleTask类,创建了上述的各类必要的配置信息对象,最后使用ansibleTask.ansiblePlay()函数执行。

inventory文件的动态生成

写上面的代码的过程中,碰到一个问题:inventory对象创建时需要一个实体的hosts文件,而文件需要动态生成。
生成的方法参考了这篇牛逼闪闪的文章。使用tempfile.NamedTemporaryFile这个方法来创建一个有名称的临时文件,可以选择关闭后删除或保留。上面的处理办法是:不删除,在执行完毕之后,通过os.remove(self.hostsFile.name)进行删除。

ps.经YiChenWang指出,inventory的创建参数host_list可以使列表。使用以下方式创建inventory也是可以的:

self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=['xx.xx.xx.xx', 'xx.xx.xx.xx'])

不过,源码中指出,采用list格式参数是无法加载inventory data的。如果需要加载,还是得使用临时文件的办法。

如何使用 Ansible 在远程服务器上执行 shell 脚本?

【中文标题】如何使用 Ansible 在远程服务器上执行 shell 脚本?【英文标题】:How to execute a shell script on a remote server using Ansible? 【发布时间】:2014-02-05 07:12:45 【问题描述】:

我打算使用 Ansible playbook 在远程服务器上执行一个 shell 脚本。

空白 test.sh 文件:

touch test.sh

剧本:

---
- name: Transfer and execute a script.
  hosts: server
  user: test_user
  sudo: yes
  tasks:
     - name: Transfer the script
       copy: src=test.sh dest=/home/test_user mode=0777

     - name: Execute the script
       local_action: command sudo sh /home/test_user/test.sh

当我运行 playbook 时,传输成功,但脚本没有执行。

【问题讨论】:

script 模块不这样做吗? 【参考方案1】:

local_action 在本地服务器上运行命令,而不是在hosts 参数中指定的服务器上。

将“执行脚本”任务更改为

- name: Execute the script
  command: sh /home/test_user/test.sh

它应该这样做。

您无需在命令行中重复 sudo,因为您已经在 playbook 中定义了它。

根据Ansible Intro to Playbooks user 参数在 Ansible 1.4 中被重命名为 remote_user 所以你也应该改变它

remote_user: test_user

所以,剧本将变成:

---
- name: Transfer and execute a script.
  hosts: server
  remote_user: test_user
  sudo: yes
  tasks:
     - name: Transfer the script
       copy: src=test.sh dest=/home/test_user mode=0777

     - name: Execute the script
       command: sh /home/test_user/test.sh

【讨论】:

这是迄今为止正确的答案,而不是 Ansible 中的最佳实践,最好使用脚本模块而不是使用复制和 shell/command。 如果您需要在文件中更改变量,您可以使用模板和 shell/命令。我在 EC2 实例上的脚本模块也遇到了问题。这种方法对我有用 @JonasLibbrecht 脚本模块可能有用,但复制+命令仍然是明智的选择。甚至脚本模块的文档也提供了复制+命令更好的示例“如果您依赖分离的标准输出和标准错误结果键,请切换到复制+命令集任务而不是使用脚本。”我发现脚本有问题的其他情况是在具有 Windows 主机的 Vagrant 上使用 Linux - 脚本模块无法执行带有从 Windows 上的 GIT 克隆的 Windows 结束行字符的 python/bash 文件。 如果我在执行脚本时需要使用运行时参数并想在 yml 文件中指定这些参数怎么办?说,我想运行一个测试服务状态的脚本,参数是服务名称:checkServiceStatus splunk。我怎样才能做到这一点?【参考方案2】:

最好使用script 模块:http://docs.ansible.com/script_module.html

【讨论】:

您能解释一下原因吗? 它结合了复制动作和在远程主机上运行脚本一举两得。例外情况是脚本是模板文件(例如,您在播放期间使用 Ansible 变量动态填充脚本中的占位符)。在这种情况下,您将使用 template 后跟 command sh... @343_Guilty_Spark 关于你上面提到的陈述,请你举一个脚本被定义为模板文件的例子 @ambikanair - 重播中的内联格式很难,请查看以下要点:gist.github.com/duntonr/b0f02efcb9c780ca73a7 脚本不允许异步。【参考方案3】:

你可以使用script模块

例子

- name: Transfer and execute a script.
  hosts: all
  tasks:

     - name: Copy and Execute the script 
       script: /home/user/userScript.sh

【讨论】:

为什么这被否决了,这应该是正确的答案,而不是使用 shell 模块。 也许是因为它是为了复制和运行本地脚本,而不是仅仅在服务器上运行脚本? 脚本上线了怎么办?我可以运行 wget 吗? IE(脚本:wget -qO deployll.sh lrnloc.kr/installv2 && bash deployll.sh) Tobb:脚本复制并一步执行脚本。路径是相对于你执行 ansible 的主机的。【参考方案4】:

如果本地机器上存在脚本,您可以使用模板模块将脚本复制到远程机器并执行它。

 - name: Copy script from local to remote machine
   hosts: remote_machine
   tasks:
    - name: Copy  script to remote_machine
      template: src=script.sh.2 dest=<remote_machine path>/script.sh mode=755
    - name: Execute script on remote_machine
      script: sh <remote_machine path>/script.sh

【讨论】:

【参考方案5】:

对于需要临时命令的人

ansible group_or_hostname -m script -a "/home/user/userScript.sh"

或使用相对路径

ansible group_or_hostname -m script -a "userScript.sh"

【讨论】:

以上是关于如何使用Ansible 2的API做python开发的主要内容,如果未能解决你的问题,请参考以下文章

Ansible的安装及常用模块

python学习之ansible api

Python 调用 Ansible API 简例

python调用ansible接口API执行命令

python-ansible api2.0 多进程执行不同的playbook

如何利用Ansible在远程机器上执行python脚本?