美文网首页
08 Ansible 插件开发

08 Ansible 插件开发

作者: 运维开发_西瓜甜 | 来源:发表于2020-04-24 10:36 被阅读0次

    本文链接:https://www.jianshu.com/p/d8309f7ca243
    作者:西瓜甜

    第4天 自动化运维利器Ansible-扩展ansible

    一、使用插件

    1 回调插件介绍

    1.1 修改默认的回调插件

    同时只能有一个回调插件作为主要的管理者,用于输出到屏幕。

    如果想替换,应该在这个插件中修改 CALLBACK_TYPE = stdout

    之后在 ansible.cfg 中配置 stdout 插件。

    [defaults]
    stdout_callback = json  # 以 JSON 的格式输出结果
    

    或使用自定义的回调:

    [defaults]
    stdout_callback = mycallback
    

    默认 情况下这仅对 playbook 生效,如果想让 ad-hoc 方式生效应该在 ansible.cfg 文件中做如下设置:

    [defaults]
    bin_ansible_callbacks = True
    

    示例演示

    [root@qfedu.com ~]# ansible-playbook -i hosts checkhosts.yml --limit dbservers
    {
        "custom_stats": {},
        "global_custom_stats": {},
        "plays": [
            {
                "play": {
                    "duration": {
                        "end": "2020-04-23T02:32:44.163630Z",
                        "start": "2020-04-23T02:32:44.131390Z"
                    },
                    "id": "0242ac12-0002-b0c7-074b-00000000000d",
                    "name": "all"
                },
                "tasks": [
                    {
                        "hosts": {
                            "172.18.0.3": {
                                "_ansible_no_log": false,
                                "_ansible_verbose_always": true,
                                "action": "debug",
                                "ansible_distribution": "VARIABLE IS NOT DEFINED!",
                                "changed": false
                            }
                        },
                        "task": {
                            "duration": {
                                "end": "2020-04-23T02:32:44.163630Z",
                                "start": "2020-04-23T02:32:44.137440Z"
                            },
                            "id": "0242ac12-0002-b0c7-074b-00000000000f",
                            "name": "debug"
                        }
                    }
                ]
            }
        ],
        "stats": {
            "172.18.0.3": {
                "changed": 0,
                "failures": 0,
                "ignored": 0,
                "ok": 1,
                "rescued": 0,
                "skipped": 0,
                "unreachable": 0
            }
        }
    }
    
    

    1.2 启用其他内置的回调插件

    大部分情况下,无论是内置的回调插件还是自定义的回调插件,都需要在 ansible.cfg 中添加到白名单中,从而才能启用。

    callback_whitelist = timer, mail, profile_roles, custom_callback
    
    • timer 这个回调插件可以计算整个 playbook 的运行时间
    • mail 这个回调插件可以实现发送邮件的功能
    • profile_roles 这个插件是在执行某个 task 开始时,添加的开始时间
    • custom_callback 是自定义的插件,稍后会讲

    1.3 获取帮助

    ansible-doc -t callback -l 可以查看当前可用的回调插件列表

    ansible-doc -t callback <callback plugins name> 可查看具体回调插件的帮助文档

    比如:

    [root@qfedu ~]# ansible-doc -t callback timer
    > TIMER    (/usr/lib/python2.7/site-packages/ansible/plugins/callback/timer.py)
    
            This callback just adds total play duration to the play stats.
    
      * This module is maintained by The Ansible Community
    REQUIREMENTS:  whitelist in configuration
    
    CALLBACK_TYPE: aggregate
            METADATA:
              status:
              - preview
              supported_by: community
    
    
    

    2 回调插件类型

    回调插件类型在回调插件类中定义:

    class CallbackModule(CallbackBase):
        CALLBACK_TYPE = 'notification'
    

    不同的回调类型对于 playbook 的输出有不一样的效果

    • stdout 标准输出类型,用在回调的主管理者

    • aggregate 聚合类型, 把此类型插件处理的结果和 stdout 类型插件合并一起输出到标准输出。比如 :timerprofile_tasks 等。

    • notification 通知类型,不参与标准输出,也不影响标准输出插件的正常输出,只是会把执行 playbook 的返回值写的指定的媒介中。

      比如: log_playsmail。假如自定义把执行playbook 的结果输出到数据库中就可以使用此类型。

    查看所有默认的查看类型

    [root@qfedu ~]# grep 'CALLBACK_TYPE =.*'   /usr/lib/python2.7/site-packages/ansible/plugins/callback/*.py  |cut -d: -f 2 |sort -u
        CALLBACK_TYPE = 'aggregate'
        CALLBACK_TYPE = 'notification'
        CALLBACK_TYPE = 'stdout
    

    3 把返回结果输出到日志中

    内置的回调插件 log_plays 会将 playbook 的返回信息输出到 /var/log/ansible/hosts 目录中。

    可以在 ansible.cfg 中配置指定的目录,使用 log_folder

    比如,把日志存到 /tmp/ansible/hosts/ 目录下

    ansible.cfg 文件的最后添加如下配置

    配置日志存放的目录

    [callback_log_plays]
    log_folder=/tmp/ansible/hosts/
    

    配置到白名单

    ansible.cfg

    callback_whitelist = log_plays
    

    Inventory

    [root@qfedu.com ~]# cat hosts
    [dbservers]
    172.18.0.3
    
    [webservers]
    172.18.0.4
    172.18.0.5
    
    [allservers:children]
    dbservers
    webservers
    

    playbook

    remoteDate.yml

    - hosts: all
      gather_facts: no
      tasks:
      - name: test
        shell: date +"%F %T"
    

    **执行 playbook **

    [root@qfedu.com ~]# ansible-playbook -i hosts remoteDate.yml
    
    PLAY [all] *********************************************************************
    
    TASK [test] ********************************************************************
    fatal: [172.18.0.5]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 172.18.0.5 port 22: Connection refused", "unreachable": true}
    changed: [172.18.0.3]
    changed: [172.18.0.4]
    
    PLAY RECAP *********************************************************************
    172.18.0.3                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    172.18.0.4                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    172.18.0.5                 : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0
    
    Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
    
    

    查看输出结果

    [root@qfedu.com ~]# ls /tmp/ansible/hosts/
    172.18.0.3  172.18.0.4  172.18.0.5
    [root@qfedu.com ~]# cat /tmp/ansible/hosts/172.18.0.3
    Apr 24 2020 06:43:57 - OK - {"module_args": {"data": "pong"}} => {"changed": false, "ping": "pong", "_ansible_no_log": false, "ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}}
    
    Apr 24 2020 06:45:11 - OK - {"module_args": {"warn": true, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "date +\"%F %T\"", "removes": null, "argv": null, "creates": null, "chdir": null, "stdin_add_newline": true, "stdin": null}} => {"stderr_lines": [], "cmd": "date +\"%F %T\"", "end": "2020-04-24 06:45:11.110025", "_ansible_no_log": false, "stdout": "2020-04-24 06:45:11", "changed": true, "rc": 0, "start": "2020-04-24 06:45:10.878037", "stderr": "", "delta": "0:00:00.231988", "stdout_lines": ["2020-04-24 06:45:11"], "ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}}
    
    

    二、开发自定义插件

    1 log_plays 插件源码分析

    # (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
    # (c) 2017 Ansible Project
    # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
    
    from __future__ import (absolute_import, division, print_function)
    __metaclass__ = type
    
    DOCUMENTATION = '''
        callback: log_plays
        type: notification
        short_description: write playbook output to log file
        version_added: historical
        description:
          - This callback writes playbook output to a file per host in the `/var/log/ansible/hosts` directory
        requirements:
         - Whitelist in configuration
         - A writeable /var/log/ansible/hosts directory by the user executing Ansible on the controller
        options:
          log_folder:
            version_added: '2.9'
            default: /var/log/ansible/hosts
            description: The folder where log files will be created.
            env:
              - name: ANSIBLE_LOG_FOLDER
            ini:
              - section: callback_log_plays
                key: log_folder
    '''
    
    import os
    import time
    import json
    
    from ansible.utils.path import makedirs_safe
    from ansible.module_utils._text import to_bytes
    from ansible.module_utils.common._collections_compat import MutableMapping
    from ansible.parsing.ajson import AnsibleJSONEncoder
    from ansible.plugins.callback import CallbackBase
    
    
    # NOTE: in Ansible 1.2 or later general logging is available without
    # this plugin, just set ANSIBLE_LOG_PATH as an environment variable
    # or log_path in the DEFAULTS section of your ansible configuration
    # file.  This callback is an example of per hosts logging for those
    # that want it.
    
    
    class CallbackModule(CallbackBase):
        """
        logs playbook results, per host, in /var/log/ansible/hosts
        """
        CALLBACK_VERSION = 2.0
        CALLBACK_TYPE = 'notification'
        CALLBACK_NAME = 'log_plays'
        CALLBACK_NEEDS_WHITELIST = True
    
        TIME_FORMAT = "%b %d %Y %H:%M:%S"
        MSG_FORMAT = "%(now)s - %(category)s - %(data)s\n\n"
    
        def __init__(self):
    
            super(CallbackModule, self).__init__()
    
        def set_options(self, task_keys=None, var_options=None, direct=None):
            super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
    
            self.log_folder = self.get_option("log_folder")
    
            if not os.path.exists(self.log_folder):
                makedirs_safe(self.log_folder)
    
        def log(self, host, category, data):
            if isinstance(data, MutableMapping):
                if '_ansible_verbose_override' in data:
                    # avoid logging extraneous data
                    data = 'omitted'
                else:
                    data = data.copy()
                    invocation = data.pop('invocation', None)
                    data = json.dumps(data, cls=AnsibleJSONEncoder)
                    if invocation is not None:
                        data = json.dumps(invocation) + " => %s " % data
    
            path = os.path.join(self.log_folder, host)
            now = time.strftime(self.TIME_FORMAT, time.localtime())
    
            msg = to_bytes(self.MSG_FORMAT % dict(now=now, category=category, data=data))
            with open(path, "ab") as fd:
                fd.write(msg)
    
        def runner_on_failed(self, host, res, ignore_errors=False):
            self.log(host, 'FAILED', res)
    
        def runner_on_ok(self, host, res):
            self.log(host, 'OK', res)
    
        def runner_on_skipped(self, host, item=None):
            self.log(host, 'SKIPPED', '...')
    
        def runner_on_unreachable(self, host, res):
            self.log(host, 'UNREACHABLE', res)
    
        def runner_on_async_failed(self, host, res, jid):
            self.log(host, 'ASYNC_FAILED', res)
    
        def playbook_on_import_for_host(self, host, imported_file):
            self.log(host, 'IMPORTED', imported_file)
    
        def playbook_on_not_import_for_host(self, host, missing_file):
            self.log(host, 'NOTIMPORTED', missing_file)
    

    2 开发插件规则

    • 用Python编写
    • 引发错误,就是遇到问题后,主动抛出异常
    • 返回以unicode编码的字符串,主要是兼容 Jinja2
    • 符合Ansible的配置和文档标准,就是可以通过 ansible.cfg 进行配置

    2.1 使用兼容的 Python 版本编写

    由于开发出来的插件将在控制器上执行,因此您必须使用兼容版本的Python(Python 2(2.7版)或 Python 3(3.5版及更高版本)的)进行编写。

    2.2 抛出异常错误信息

    应该通过引发AnsibleError()或类似的类并返回描述错误的消息来返回插件执行过程中遇到的错误。

    将其他异常包装到错误消息中时,应始终使用Ansible 的函数to_native来确保跨Python版本的字符串兼容性:

    from ansible.errors import AnsibleError
    from ansible.module_utils._text import to_native
    
    try:
        cause_an_exception()
    except Exception as e:
        raise AnsibleError('Something happened, this was original exception: %s' % to_native(e))
    

    检查不同的AnsibleError对象,然后查看哪种对象最适合您的情况。

    2.3 妥当处理字符串

    您必须将插件返回的所有字符串转换为Python的unicode类型。转换为unicode可确保这些字符串可以通过Jinja2运行。

    转换字符串:

    from ansible.module_utils._text import to_text
    result_string = to_text(result_string)
    

    2.4 插件配置和文档标准

    Ansible的在线帮助文档是根据每个模块的源代码中的DOCUMENTATION模块生成的。该DOCUMENTATION块必须是有效的YAML。

    需要为您的插件定义可配置选项,在python文件的部分 DOCUMENTATION 中对其进行描述。

    自Ansible 2.4版以来,回调和连接插件已经开始以这种方式声明配置要求了。现在大多数插件类型都执行相同的操作。这种方法可确保插件选项的文档始终是正确的和最新的。

    DOCUMENTATION块中的所有字段均为小写。除非另有说明,否则所有字段都是必填字段:

    DOCUMENTATION = '''
        callback: log_plays
        type: notification
        short_description: write playbook output to log file
        version_added: historical
        description:
          - 此插件的详细描述信息。
          - 使用多条目,不要使用一个较长的语句。
          - 不应该提及模块名称。
        requirements:
         - 必须要求清单
         - 包括最低版本的限制
        options:
          log_folder:
            version_added: '2.9' 此插件添加到 Ansible 时候的当时 Ansible 的版本。
            default: 选项的默认值,如果 required 是 False, 则 default 可以设置
            description: 此选项的作用的详细说明。应该用完整的句子写成。
            env:
              - name: 环境变量的名字
            ini:
              - section: 在 asible.cfg 中的配置块名称
                key:  log_folder在对应配置块下面的变量名称
            required: True/False  必需时为 True,如果不设置,就认为
                      不是必须的。
            type: int/str/list 不是必须的
    '''
    

    要访问插件中的配置设置,请使用self.get_option("log_folder")

    如果需要显式个配置选项设置值,请使用self.set_options()

    3 开发回调插件 mysql_plays

    回调插件会在响应事件时,向 Ansible添加新行为。

    要创建回调插件,请使用 CallbacksBase 类作为父类创建一个新类:

    mysql_plays.py

    from ansible.plugins.callback import CallbackBase
    
    class CallbackModule(CallbackBase):
        pass
    

    在 CallbackModule 覆盖 CallbackBase 中的特定方法。

    对于打算与Ansible 2.0及更高版本一起使用的插件,您应该仅覆盖以v2开头的方法。

    对于可以重写哪些方法呢,可以参阅 lib/ansible/plugins/callback目录下的 __init__.py 文件的内容。

    我们下面就参考 log_plays 插件编写一个可以将 playbook 的执行结果写如到 MySQL 中的插件。

    3.1 准备数据库

    首先要设计一个库和表用于存储结果

    mysql> create database if not exists ansible default charset utf8mb4 collate utf8mb4_general_ci;
    Query OK, 1 row affected (0.00 sec)
    mysql> grant all on ansible.* to ansible@'%' identified by 'QFedu123!';
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    

    3.2 准备表

    image.png
    mysql> create table playsresult(
           id int auto_increment primary key,
           user varchar(16) not null,
           host varchar(32) not null,
           category varchar(11) not null,
           result text,
           create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
          );
    

    3.3 编写插件

    #coding:utf-8
    # (C) 2020,闫顺军, <sharkyun@aliyun.com><WeChat:y86000153>
    # (c) 2020 Ansible Custom Plugin Project
    # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
    
    from __future__ import (absolute_import, division, print_function)
    __metaclass__ = type
    
    DOCUMENTATION = '''
        callback: mysql_plays
        type: notification
        short_description: 将 playbook 的执行结果输出到 MySQL 中。
        version_added: historical
        description:
          - 这个回调插件将会把输出存入 MySQL 服务器中。
        requirements:
         - 需要配置到 ansible.cfg 中 Whitelist
         - 可以被访问的 MySQL 服务器实例
         - Python 版本对应的 pymysql 或者 mysqlclient 模块
         - 创表语句(注意:这里的表名需要根据选项中 mysql_table 的值一致)
           create table playsresult(
             id int auto_increment primary key,
             user varchar(16) not null,
             host varchar(32) not null,
             category varchar(11) not null,
             result text,
             create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
            );
        options:
          mysql_host:
            version_added: '2.9'
            default: locallhost
            description: MySQL 服务器 IP或者主机名.
            env:
              - name: ANSIBLE_MYSQL_HOST
            ini:
              - section: callback_mysql_plays
                key: mysql_host
          mysql_port:
            version_added: '2.9'
            default: 3306
            description: MySQL 服务器监听端口.
            env:
              - name: ANSIBLE_MYSQL_PORT
            ini:
              - section: callback_mysql_plays
                key: mysql_port
            type: int
          mysql_user:
            version_added: '2.9'
            default: ansible
            description: MySQL 服务器登录用户.
            env:
              - name: ANSIBLE_MYSQL_USER
            ini:
              - section: callback_mysql_plays
                key: mysql_user
          mysql_password:
            version_added: '2.9'
            default: 'QFedu123!'
            description: MySQL 服务器登录用户.
            env:
              - name: ANSIBLE_MYSQL_PASSWORD
            ini:
              - section: callback_mysql_plays
                key: mysql_password
          mysql_db:
            version_added: '2.9'
            default: ansible
            description: 存放数据的库名称.
            env:
              - name: ANSIBLE_MYSQL_DB
            ini:
              - section: callback_mysql_plays
                key: db
          mysql_table:
            version_added: '2.9'
            default: playsresult
            description: 存放数据的表名称.
            env:
              - name: ANSIBLE_MYSQL_TABLE
            ini:
              - section: callback_mysql_plays
                key: mysql_table
    '''
    
    import json
    import getpass
    
    from ansible.module_utils.common._collections_compat import MutableMapping
    from ansible.parsing.ajson import AnsibleJSONEncoder
    from ansible.plugins.callback import CallbackBase
    from ansible.errors import AnsibleError
    from ansible.module_utils._text import to_native
    
    
    try:
        import pymysql as mysqldb
        pwd = "password"
        database = "db"
    except ImportError:
        try:
            import MySQLdb as mysqldb
            pwd = "passwd"
            database = "database"
        except ImportError:
            raise AnsibleError("找不到 pymysql 或 mysqlclient 模块。")
    
    
    class CallbackModule(CallbackBase):
        """
        把 playbook 的结果保存到 MySQL 数据库中,默认的库.表是 ansible.playsresult
        """
        CALLBACK_VERSION = 2.0
        CALLBACK_TYPE = 'notification'
        CALLBACK_NAME = 'mysql_plays'
        CALLBACK_NEEDS_WHITELIST = True
    
        TIME_FORMAT = "%b %d %Y %H:%M:%S"
        MSG_FORMAT = "%(now)s - %(category)s - %(data)s\n\n"
    
        def __init__(self):
            super(CallbackModule, self).__init__()
    
        def set_options(self, task_keys=None, var_options=None, direct=None):
            """
            用于设置选项和获取选项, 选项包含了自定义的选项
            """
            super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
    
            self.mysql_host = self.get_option("mysql_host")
            self.mysql_port = self.get_option("mysql_port")
            self.mysql_user = self.get_option("mysql_user")
            self.mysql_password = self.get_option("mysql_password")
            self.mysql_db = self.get_option("mysql_db")
            self.mysql_table = self.get_option("mysql_table")
    
            self.user = getpass.getuser()
    
        def _mysql(self):
            """
            连接数据库,返回数据库对象和游标对象
            """
            db_conn={"host": self.mysql_host,
                     "port": self.mysql_port,
                     "user": self.mysql_user,
                     pwd: self.mysql_password,
                     database: self.mysql_db}
    
            try:
                db = mysqldb.connect(**db_conn)
            except Exception as e:
                raise AnsibleError("%s" % to_native(e))
    
            cursor= db.cursor()
    
            return db, cursor
    
    
        def _execute_sql(self, host, category, data):
            if isinstance(data, MutableMapping):
                if '_ansible_verbose_override' in data:
                    # avoid save extraneous data
                    data = 'omitted'
                else:
                    data = data.copy()
                    invocation = data.pop('invocation', None)
                    data = json.dumps(data, cls=AnsibleJSONEncoder)
                    if invocation is not None:
                        data = json.dumps(invocation) + " => %s " % data
    
            sql = """
                  insert into {}(host,user,category,result)
                  values(%s,%s,%s,%s)
                  """.format(self.mysql_table)
    
            db, cursor = self._mysql()
                                   
            try:
                # 执行 sql,记录事件类型和事件结果
                cursor.execute(sql, (host, self.user, category, data))
                db.commit()
            except Exception as e:
                raise AnsibleError("%s" % to_native(e))
            finally:
                cursor.close()
                db.close()
    
        def runner_on_failed(self, host, res, ignore_errors=False):
            self._execute_sql(host, 'FAILED', res)
    
        def runner_on_ok(self, host, res):
            self._execute_sql(host, 'OK', res)
    
        def runner_on_skipped(self, host, item=None):
            self._execute_sql(host, 'SKIPPED', '...')
    
        def runner_on_unreachable(self, host, res):
            self._execute_sql(host, 'UNREACHABLE', res)
    
        def runner_on_async_failed(self, host, res, jid):
            self._execute_sql(host, 'ASYNC_FAILED', res)
    
        def playbook_on_import_for_host(self, host, imported_file):
            self._execute_sql(host, 'IMPORTED', imported_file)
    
        def playbook_on_not_import_for_host(self, host, missing_file):
            self._execute_sql(host, 'NOTIMPORTED', missing_file)
    
    

    请注意,CALLBACK_VERSIONCALLBACK_NAME定义是Ansible 2.0版及更高版本正确运行的插件所必需的。

    3.4 保存插件到有效的目录下

    把插件保存为 mysql_plays.py 文件,并存到ansible 控制节点的如下目录下: ~/.ansible/plugins/callback/

    [root@qfedu.com ~]# pwd
    /root
    [root@qfedu.com ~]# ls .ansible/plugins/callback/mysql_plays.py
    .ansible/plugins/callback/mysql_plays.py
    

    或者 /usr/share/ansible/plugins/callback

    3.5开启使用插件

    在 ansible.cfg 中编辑如下配置

    callback_whitelist = mysql_plays
    

    如果还使用了其他插件,请用英文的逗号分开。

    比如

    callback_whitelist = timer, mysql_plays
    

    默认此插件仅对 playbook 生效,假如希望在 ad-hoc (快捷命令)中生效,继续打开如下配置,并职位 True

    bin_ansible_callbacks = True
    

    3.6 关于此插件的使用先决条件等信息

    在做好以上步骤后,使用如下方式获取帮助

    验证配置的正确性

    [root@qfedu.com ~]# ansible-doc -t callback -l |grep mysql_plays
    mysql_plays          将 playbook 的执行结果输出到 MySQL 中。
    

    查看帮助文档

    [root@qfedu.com ~]# ansible-doc -t callback  mysql_plays
    > MYSQL_PLAYS    (/root/.ansible/plugins/callback/mysql_plays.py)
    
            这个回调插件将会把输出存入 MySQL 服务器中。
    
      * This module is maintained by The Ansible Community
    OPTIONS (= is mandatory):
    
    - mysql_db
            存放数据的库名称.
            [Default: ansible]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_DB
              ini:
              - key: db
                section: callback_mysql_plays
    
            version_added: 2.9
    
    - mysql_host
            MySQL 服务器 IP或者主机名.
            [Default: locallhost]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_HOST
              ini:
              - key: mysql_host
                section: callback_mysql_plays
    
            version_added: 2.9
    
    - mysql_password
            MySQL 服务器登录用户.
            [Default: QFedu123!]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_PASSWORD
              ini:
              - key: mysql_password
                section: callback_mysql_plays
    
            version_added: 2.9
    
    - mysql_port
            MySQL 服务器监听端口.
            [Default: 3306]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_PORT
              ini:
              - key: mysql_port
                section: callback_mysql_plays
    
            type: int
            version_added: 2.9
    
    - mysql_table
            存放数据的表名称.
            [Default: playsresult]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_TABLE
              ini:
              - key: mysql_table
                section: callback_mysql_plays
    
            version_added: 2.9
    
    - mysql_user
            MySQL 服务器登录用户.
            [Default: ansible]
            set_via:
              env:
              - name: ANSIBLE_MYSQL_USER
              ini:
              - key: mysql_user
                section: callback_mysql_plays
    
            version_added: 2.9
    
    
    REQUIREMENTS:  需要配置到 ansible.cfg 中 Whitelist, 可以被访问的 MySQL 服务器实例, Python 版本对应的 pymysql 或者
            mysqlclient 模块, 创表语句(注意:这里的表名需要根据选项中 mysql_table 的值一致) create table
            playsresult( id int auto_increment primary key, user varchar(16) not
            null, host varchar(32) not null, category varchar(11) not null, result
            text, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP );
    
            METADATA:
              status:
              - preview
              supported_by: community
    
    TYPE: notification
    

    3.6 配置插件使用的选项

    关于限制条件

    此插件已经有默认值,如果想修改需在 ansible.cfg 文件的最后添加如下配置

    [callback_mysql_plays]
    mysql_host = MySQL IP
    mysql_port = MySQL 监听端口
    mysql_user = MySQL 用户
    mysql_password = MySQL 密码
    mysql_db = MySQL 库名
    mysql_table = MySQL 表名
    

    3.7 执行 playbook

    playbook

    - hosts: all
      gather_facts: no
      tasks:
      - name: test
        shell: date +"%F %T"
    

    Inventory

    [root@qfedu.com ~]#[dbservers]
    172.18.0.3
    
    [webservers]
    172.18.0.4
    172.18.0.5
    
    [allservers:children]
    dbservers
    webservers
    

    执行playbook

    [root@qfedu.com ~]# ansible-playbook -i hosts remoteDate.yml
    
    PLAY [all] *************************************************
    
    TASK [test] *************************************************
    fatal: [172.18.0.5]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 172.18.0.5 port 22: Connection refused", "unreachable": true}
    changed: [172.18.0.3]
    changed: [172.18.0.4]
    
    PLAY RECAP ************************************************************************************************************
    172.18.0.3                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    172.18.0.4                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    172.18.0.5                 : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0
    

    查询数据库

    为了输出效果,已对输出信息做成修改

    mysql> select * from playsresult\G
    ************ 11. row ************
             id: 21
           user: root
           host: 172.18.0.5
       category: UNREACHABLE
         result: {
         "msg": "Failed to connect to the host via ssh: ssh: connect to host 172.18.0.5 port 22: Connection refused", 
         "unreachable": true, 
         "changed": false}
    create_time: 2020-04-24 01:34:46
    ************ 12. row ************
             id: 22
           user: root
           host: 172.18.0.3
       category: OK
         result: {
         "module_args": {"warn": true, 
                         "executable": null, 
                         "_uses_shell": true, 
                         "strip_empty_ends": true,
                         "_raw_params": "date +\"%F %T\"", 
                         "removes": null, 
                         "argv": null, 
                         "creates": null, 
                         "chdir": null, 
                         "stdin_add_newline": true, 
                         "stdin": null
                         }
         } => {"stderr_lines": [],
               "cmd": "date +\"%F %T\"", 
               "end": "2020-04-24 01:34:46.762027", 
               "_ansible_no_log": false,
               "stdout": "2020-04-24 01:34:46",
               "changed": true,
               "rc": 0,
               "start": "2020-04-24 01:34:46.518139",
               "stderr": "",
               "delta": "0:00:00.243888",
               "stdout_lines": ["2020-04-24 01:34:46"],
               "ansible_facts": {
               "discovered_interpreter_python": "/usr/bin/python"
               }
        }
    create_time: 2020-04-24 01:34:46
    ************ 13. row ************
             id: 23
           user: root
           host: 172.18.0.4
       category: OK
         result: {
         "module_args": {"warn": true, 
                         "executable": null, 
                         "_uses_shell": true,                                      "strip_empty_ends": true,
                         "_raw_params": "date +\"%F %T\"",
                         "removes": null,
                         "argv": null,
                         "creates": null,
                         "chdir": null,
                         "stdin_add_newline": true,
                         "stdin": null
                         }
           } => {"stderr_lines": [], 
                 "cmd": "date +\"%F %T\"",
                 "end": "2020-04-24 01:34:46.767316", 
                 "_ansible_no_log": false,
                 "stdout": "2020-04-24 01:34:46", 
                 "changed": true, 
                 "rc": 0, 
                 "start": "2020-04-24 01:34:46.528226",
                 "stderr": "",
                 "delta": "0:00:00.239090",
                 "stdout_lines": ["2020-04-24 01:34:46"],
                 "ansible_facts": {
                 "discovered_interpreter_python": "/usr/bin/python"}
                }
    create_time: 2020-04-24 01:34:46
    
    

    相关文章

      网友评论

          本文标题:08 Ansible 插件开发

          本文链接:https://www.haomeiwen.com/subject/xxpxwhtx.html