博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ansible_runner使用总结

Posted on 2022-06-08 09:44  Sxcan  阅读(2369)  评论(0编辑  收藏  举报

ansible_runner介绍请看https://ansible.leops.cn/dev/api/api-ansible-runner/
之前查看ansible的api,感觉好复杂,使用过程中也老是出各种问题,后来发现了ansible_runner这个工具,官方的介绍是不管ansible版本怎么迭代,它都提供一个稳定且一致的接口抽象。听不懂说啥玩意,但是不影响俺使用。我使用它的原因是:

  1. ansible提供了community.general.sudosu方法实现普通用户提权。我们用的是统一的运维账户,有时候需要切换至其他用户进行操作,这个方法可以实现sudo su - user可以免密切换至你指定的用户,符合我们的需求,其他工具的话暂时没有了解过。后期计划加入saltstack。
  2. 目前我们使用场景大多是远程执行命令,playbook用的不多,ansible api上手难度较高,ansible_runner只需按照配置环境,即可直接调用方法执行命令,并且返回的json格式内容比较一致。

使用过程的缺点:

  1. 执行速度较慢
  2. 相关文档较少
  3. 只能使用jsonfile方式缓存fact_cache
环境配置

ansible_runner使用的时候需要配置private_data_dir目录,不配置会默认生成一个目录,所有相关数据都会存在这个目录里,建议提前新建个目录,方便管理。
private_data_dir目录的构成:
注:以下目录不需要你刻意去建,你只需要在使用ansible_runner时指定下private_data_dir,之后里面的大部分目录和文件会自动生成,你再根据需求去修改和创建即可。

.
├── ansible.cfg  # type: file. 自己建的ansible配置文件,专门给ansible_runner使用,也可以使用ansible的配置文件
├── artifacts  # type: directory. ansible_runner每次执行产生的所有信息,类似把json格式的输出改成以文件格式存储,后期量会很大
├── env # type:directory. 环境文件夹,cmdline、envvars、extravars、settings你可以新建这些文件也可以直接在run方法里通过配置参数使用
│   ├── cmdline # type: file. 等价ansible --参数,例:--become,
│   ├── envvars # type: file. ansible_runner执行时的环境变量,我这里只配置了ANSIBLE_CONFIG,用来指定ansible_runner使用的配置文件
│   ├── extravars # type: file. 等价ansible -e,ansible_ssh_pass之类的参数可以写在这里
│   └── settings # type: file. ansible.cfg里的参数可以放在这里
├── fact_cache # type:directory. fact缓存文件夹
│   └── host # type: file. 以主机ip命名,里面是主机的缓存信息
├── inventory
│   └── hosts # ansible_runner使用的hosts
├── playbook # 自己建的
├── runner.log # ansible.cfg里指定的日志输出文件
└── templates # 自己建的
使用

文章开头放的链接里有简单的使用方法,这里只说下相关的参数

# private_data_dir 指定ansible_runner的数据目录
# host_pattern 指定使用哪个pattern的主机
# module 使用ansible的什么模块,shell、file、user...
# module_args ansible模块要带的参数,等价ansible -m shell -a里的-a
# limit 限定哪些主机执行,单主机limit='XX.XX.XX.XX',多主机limit='XX.XX.XX.XX,YY.YY.YY.YY'
# extravars 等价于ansible -e,这里我用来设置ansible_ssh_user、ansible_become_user等参数,可以实现同一方法中不同步骤使用不同用户,ansible_ssh_pass我是直接写死在文件private_data_dir/env/extravars里了
# quiet 是否打印ansible_runner执行过程的输出,True代表不输出

以上参数对于我目前的情况是够用了,具体其他的用到时再研究
附个我封装的类,水平不足,有可以改进的地方欢迎指出

import ansible_runner
from configparser import ConfigParser
# from conf.logger import ansible_logger


class AnsibleRunner():

    def __init__(self, host_pattern='', limit=None):
        self.data_dir = '/home/user/.ansible/runner_data'
        self.inventory_path = '/home/user/.ansible/runner_data/inventory/hosts'
        self.host_pattern = host_pattern
        self.limit = limit

    def init_hosts(self):
        config = ConfigParser(allow_no_value=True)
        config.read(self.inventory_path)
        if not config.has_section(self.host_pattern):
            config.add_section(self.host_pattern)
        for host in self.limit.rstrip(',').split(','):
            if not config.has_option(self.host_pattern, host):
                config.set(self.host_pattern, host)

    def run_module(self, module, module_args, extravars=None):
        self.init_hosts()
        r = ansible_runner.run(private_data_dir=self.data_dir,
                               host_pattern=self.host_pattern, module=module,
                               module_args=module_args, limit=self.limit, extravars=extravars, quiet=True)

        # ansible_logger.info(
        #     f"执行完成!{self.host_pattern}, {module}, {module_args}, {self.limit}")
        result = self.parse_result(r)
        return result

    def parse_result(self, r):
        result = {}
        event_type_list = ['runner_on_ok', 'runner_on_failed',
                           'runner_on_unreachable', 'runner_on_skipped']
        for host in self.limit.rstrip(',').split(','):
            host_event = list(filter(
                lambda x: x['event'] in event_type_list, r.host_events(host)))
            event_res = host_event[0].get('event_data').get('res')
            single_host_res = {}
            if host_event[0].get('event') == 'runner_on_ok':
                single_host_res.update({'status': 'ok', 'data': event_res})
            if host_event[0].get('event') == 'runner_on_failed':
                single_host_res.update({'status': 'failed', 'data': event_res})
            if host_event[0].get('event') == 'runner_on_unreachable':
                single_host_res.update(
                    {'status': 'unreachable', 'msg': '主机不可达'})
            if host_event[0].get('event') == 'runner_on_skipped':
                single_host_res.update({'status': 'skipped', 'msg': '任务被忽略'})
            result.update({host: single_host_res})
        return result


runner = AnsibleRunner('expdp', limit='')
res = runner.run_module('shell', 'pwd', extravars={
                        'ansible_become_user': 'root'})