自动化运维工具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
编写插件
在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 模块")
ipython测试2 pymysql
In [13]: import pymysql
In [14]: from pymysql.connections import Connection
In [15]: ??Connection
#通过password连接到pymysql
ipython测试3 MySQLdb
In [1]: import MySQLdb
In [2]: from MySQLdb.connections import Connection
In [3]: ??Connection
#通过passwd连接到MySQLdb
选项部分
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任务
[root@localhost home]# ansible all -i hosts -m ping
报错1
不支持中文字符
[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py
#coding:utf-8
文件第一行‘#coding:utf-8’
报错2
[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py
搜索CallbackModule
字符错误
报错3
[root@localhost home]# vim ~/.ansible/plugins/callback/mysql_plays.py
搜索cursor
若表达式的值出现异常将无法赋予后面的变量
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
变量重复,重设变量名
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权限和密码
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
[root@localhost home]# systemctl start redis
[root@localhost home]# systemctl status redis
验证
数据库
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的主要内容,如果未能解决你的问题,请参考以下文章