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

Posted 资本家的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自动化运维工具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)开发回调插件


编写插件

在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)开发回调插件

ipython测试2 pymysql

In [13]: import pymysql

In [14]: from pymysql.connections import Connection

In [15]: ??Connection

#通过password连接到pymysql

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

ipython测试3 MySQLdb

In [1]: import MySQLdb

In [2]: from MySQLdb.connections import Connection

In [3]: ??Connection

#通过passwd连接到MySQLdb

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


选项部分

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)开发回调插件


执行ansible任务

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

报错1

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

不支持中文字符

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

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


报错2

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

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

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

字符错误


报错3

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

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

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


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)开发回调插件

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

变量重复,重设变量名

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)开发回调插件

检查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)开发回调插件

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


验证

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

数据库

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

以上是关于自动化运维工具Ansible(24)开发回调插件 mysql_plays的主要内容,如果未能解决你的问题,请参考以下文章

Linux中ansible自动化运维工具

自动化运维工具Ansible实战简介和部署

自动化运维工具ansible——安装及模块管理

自动化运维工具Ansible实战安装部署

Ansible运维的必备工具

Ansible自动化运维工具-上