0
点赞
收藏
分享

微信扫一扫

自动化运维工具Ansible(24)开发回调插件 mysql_plays

注意事项

  • 回调插件会在响应事件时,向 Ansible添加新行为。
  • 要创建回调插件,使用 CallbacksBase 类作为父类创建一个新类:

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


环境

1 准备数据库

mysql> create database if not exists ansible default charset utf8mb4 collate utf8mb4_general_ci;
//字符集调整

mysql>grant all on ansbile.* to 'ansible'@'%' identified by '666666';
//给予ansible用户所有权限

2 准备表

mysql> create table ansible.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
-> );
mysql> desc ansible.playsresult

自动化运维工具Ansible(24)开发回调插件 mysql_plays_linux


编写插件

在log_plays.py基础上修改

[root@localhost /]# cd /usr/lib/python2.7/site-packages/ansible/plugins/callback/
[root@localhost callback]# cp log_plays.py mysql_plays.py
[root@localhost callback]# vim mysql_plays.py

文档部分

# (C) 2012, Michael DeHaan, 
# (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: mysql_plays
type: notification
#修改描述信息
short_description: write playbook output to mysql
version_added: historical
#修改描述信息
description:
- 这个回调插件将会把输出存入 MySQL 服务器中
requirements:
- 需要配置到 ansible.cfg Whitelist
- 可以被访问的 MySQL 服务器实例
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: '666666'
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 os
import json
import time
#截取当前用户模块
import getpass

#导入AnsibleError
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
from ansible.errors import AnsibleError
from ansible.module.utils.text import to_native

#导入pymysl 或者 MySQLdb
#pip install pymysql
#pip install mysqlclient
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 模块。")

#导入成功都统一名称为mysqldb
#导入失败输出AnsibleError

ipython测试1 try

#try这个关键字来捕获异常
#try:尝试执行的代码
#except:出现错误的处理
#raise 手动抛出异常
In [12]: try:
...: import ddd
...: except ImportError as e:
...: try:
...: import ccc
...: except ImportError:
...: raise AnsibleError("找不到pymsql 或者 MySQLdb 模块")

自动化运维工具Ansible(24)开发回调插件 mysql_plays_linux_02

ipython测试2 pymysql

In [13]: import pymysql

In [14]: from pymysql.connections import Connection

In [15]: ??Connection

#通过password连接到pymysql

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_03

ipython测试3 MySQLdb

In [1]: import MySQLdb

In [2]: from MySQLdb.connections import Connection

In [3]: ??Connection

#通过passwd连接到MySQLdb

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_04


选项部分

class CallbackModule(CallbackBase):
"""
logs playbook results, per host, in /var/log/ansible/hosts
"""
#`CALLBACK_VERSION`和`CALLBACK_NAME`定义是Ansible 2.0版及更高版本正确运行的插件所必需的
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
#名称与描述信息一致
CALLBACK_NAME = 'mysqls_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.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")
#getpass当前用户的值
self.user = getpass.getuser()

#日志调用类不需要
#if not os.path.exists(self.log_folder):
# makedirs_safe(self.log_folder)

def _mysql(self):
"""
连接 MySQL 数据库,返回 游标对象 数据库对象
"""
#词典方式解释 pwd 和 db 变量(模块导入部分定义pymysql或MySQLdb)
db_conn = {"host": self.mysql_host,y
"port": self.mysql_port,y
"user": self.mysql_user,
pwd: self.mysql_password,
db: self.mysql_db}

#db_conn作为连接对象
db = mysqldb.connect(**db_conn)
#返回游标对象
cursor = db.cursor()
return db,coursor

# "_"内部调用
def _execute_mysql(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的值
data = json.dumps(invocation) + " => %s " % data

#根据table字段进行填充,format 函数可以接受不限个参数位置可以不按顺序
sql = """
insert into {}(host,user,category,result)
values (%s,%s,%s,%s)
""".format(self.mysql_table)

#检测连接对象异常
try:
db,cursor = self._mysql()
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()

#需要mysql自定义逻辑
#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._execute_mysql(host, 'FAILED', res)

def runner_on_ok(self, host, res):
self._execute_mysql(host, 'OK', res)

def runner_on_skipped(self, host, item=None):
self._execute_mysql(host, 'SKIPPED', '...')

def runner_on_unreachable(self, host, res):
self._execute_mysql(host, 'UNREACHABLE', res)

def runner_on_async_failed(self, host, res, jid):
self._execute_mysql(host, 'ASYNC_FAILED', res)

def playbook_on_import_for_host(self, host, imported_file):
self._execute_mysql(host, 'IMPORTED', imported_file)

def playbook_on_not_import_for_host(self, host, missing_file):
self._execute_mysql(host, 'NOTIMPORTED', missing_file)

ipython测试1 getpass

In [1]: import getpass

In [2]: getpass.getuser()
Out[3]: 'root'
#getpass模块取得当前用户


测试插件

保存插件到有效的目录下

/usr/lib/python2.7/site-packages/ansible/plugins/callback/

~/.ansible/plugins/callback/

在 ansible.cfg 中编辑配置

[root@localhost ~]# vim /etc/ansible/ansible.cfg
#添加白名单
callback_whitelist = mysql_plays

#默认此插件仅对 playbook 生效,假如希望在 ad-hoc (快捷命令)中生效
bin_ansible_callbacks = True

#添加服务地址
[callback_mysql_plays]
mysql_host = 192.168.19.100

验证配置的正确性

[root@localhost ~]# ansible-doc -t callback  -l|grep mysql_plays
mysql_plays write playbook output to mysql

查看帮助文档(文档部分)

[root@localhost ~]# ansible-doc -t callback  mysql_plays

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_05


执行ansible任务

[root@localhost home]# ansible all -i hosts -m ping

报错1

自动化运维工具Ansible(24)开发回调插件 mysql_plays_linux_06

不支持中文字符

[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py 
#coding:utf-8

文件第一行‘#coding:utf-8’


报错2

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_07

[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py 
搜索CallbackModule

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_08

字符错误


报错3

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_09

[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py 
搜索cursor

自动化运维工具Ansible(24)开发回调插件 mysql_plays_linux_10表达式的值出现异常将无法赋予后面的变量


db,cursor = self._mysql()
try:
cursor.execute(sql,(host,self.user,category,data))
#赋值运算不参与检测语句,防止使用值前丢失

def _mysql(self):
"""
连接 MySQL 数据库,返回 游标对象 数据库对象
"""
#词典方式解释 pwd 和 db 变量(模块导入部分定义pymysql或MySQLdb)
db_conn = {"host": self.mysql_host,
"port": self.mysql_port,
"user": self.mysql_user,
pwd: self.mysql_password,
db: self.mysql_db}

#在函数中就检测self._mysql的值,并抛出异常
try:
#db_conn作为连接对象
db = mysqldb.connect(**db_conn)
except Exception as e:
raise AnsibleError("%s" % to_native(e))


报错4

自动化运维工具Ansible(24)开发回调插件 mysql_plays_centos7_11

自动化运维工具Ansible(24)开发回调插件 mysql_plays_centos7_12

变量重复,重设变量名

try:
import pymysql as mysqldb
pwd = "password"
database = "database"
except ImportError as e:
try:
import MySQLdb as mysqldb
pwd = "passwd"
database = "db"
except ImportError:
raise AnsibleError("找不到pymsql 或者 MySQLdb 模块")

。。。。。

db_conn = {"host": self.mysql_host,
"port": self.mysql_port,
"user": self.mysql_user,
pwd: self.mysql_password,
database: self.mysql_db}


报错5

自动化运维工具Ansible(24)开发回调插件 mysql_plays_Ansible_13

检查ansible权限和密码

mysql> show grants for ansible;

---mysql_plays---
mysql_password:
version_added: '2.9'
default: '666666'
description: MySQL 服务器登录用户.
env:
- name: ANSIBLE_MYSQL_PASSWORD
ini:
- section: callback_mysql_plays
key: mysql_password


报错6

自动化运维工具Ansible(24)开发回调插件 mysql_plays_centos7_14

[root@localhost home]# systemctl start redis
[root@localhost home]# systemctl status redis


验证

自动化运维工具Ansible(24)开发回调插件 mysql_plays_centos7_15

数据库

mysql> select * from ansible.playsresult \G
*************************** 1. row ***************************
id: 1
user: root
host: 192.168.19.102
category: OK
result: {"module_args": {"data": "pong"}} => {"changed": false, "ping": "pong", "_ansible_no_log": false}
create_time: 2022-05-28 02:17:36
*************************** 2. row ***************************
id: 2
user: root
host: 192.168.19.103
category: OK
result: {"module_args": {"data": "pong"}} => {"changed": false, "ping": "pong", "_ansible_no_log": false}
create_time: 2022-05-28 02:17:36
2 rows in set (0.00 sec)

日志文件

[root@localhost hosts]# cat /tmp/ansible/hosts/192.168.19.102
。。。。。。
May 28 2022 22:17:35 - OK - {"module_args": {"data": "pong"}} => {"changed": false, "ping": "pong", "_ansible_no_log": false}

举报

相关推荐

0 条评论