Python3监控Mysql基于GTID的主从状态
- 本文使用到的Python模块
- 使用telnetlib校验服务器是否可被访问
- 使用SMTP向管理员发送通知邮件
- 使用MySQL官方的驱动对数据库进行访问
- 使用optparse实现命令行参数的提取
- 实现原理
- 使用optparse模块获取命令行参数。读取defaults-file设置文件内容(如果存在),使用参数覆盖defaults-file的值(如果传递参数,如:–host, –user, –to之类)。
- 直接去连MySQL等待是否能进行访问的返回结果太慢了,所以使用telnet对服务器的连通性进行验证。可以设置等待时间,可控性高一些。
- 当服务器工作正常,使用MySQL与服务器进行连接,获取主从同步的状态。
- 将获取服务器的异常状态信息(服务器无法访问,主从同步的状态中断),使用SMTP发送给管理员,并把造成中断同步的异常信息一同发送到管理员的邮箱中。
'''
Created on 2016年9月7日
@author: Kael
'''
#coding=utf-8
import pymysql
import telnetlib
import smtplib
from email.mime.text import MIMEText
import optparse
import configparser
import os,time,sys
from pip._vendor.distlib._backport.tarfile import TUREAD
class SlaveStatu:
__instance__ = None
__error__ = []
def __init__(self,*args,**kwargs):
self.__config__ = {
"host":"192.168.1.179",
"user":"root",
"password":"xxx",
"port":3306,
"smtp_host":"smtp.163.com",
"smtp_user":"xxx@163.com",
"smtp_password":"xxx",
"from":"Server<xxx@163.com>",
"to":"DBA<xxx@163.com>"
}
#优先读取设置文件中的值
if not kwargs["defaults_file"] is None:
defaults_file = self.__read_defaults_file__( kwargs["defaults_file"] )
del kwargs["defaults_file"]
#使用参数的设置去覆盖设置文件的值
for key,val in kwargs.items():
if not val is None and len(val) > 0:
self.__config__[key] = val
def __configParseMySQL__(self):
"""
提取数据库的设置
:return: dict
"""
return {
"host" : self.__config__["host"],
"port" : self.__config__["port"],
"user" : self.__config__["user"],
"password" : self.__config__["password"]
}
def __configParseSMTP__(self):
"""
提取SMTP邮件设置
:return: dict
"""
return {
"smtp_host": self.__config__["smtp_host"],
"smtp_user": self.__config__["smtp_user"],
"smtp_password": self.__config__["smtp_password"],
"from": self.__config__["from"],
"to": self.__config__["to"]
}
def __read_defaults_file__( self, filePath ):
"""
加载设置文件设置的值
:param filePath: 设置文件路径
:return:
"""
section = "config"
if os.path.exists( filePath ):
cnf = configparser.ConfigParser()
cnf.read( filePath )
options = cnf.options( section )
for key in options:
self.__config__[key] = cnf.get( section, key )
def telnet( self, host, port, timeout=5 ):
"""
测试服务器地址和端口是否畅通
:param host: 服务器地址
:param port: 服务器端口
:param timeout: 测试超时时间
:return: Boolean
"""
try:
tel = telnetlib.Telnet( host, port, timeout )
tel.close()
return True
except:
return False
def connect(self):
"""
创建数据库链接
"""
try:
config = self.__configParseMySQL__()
if self.telnet( config["host"],config["port"]):
self.__instance__ = pymysql.connect( **config )
return True
else:
raise Exception("unable connect")
except:
self.__error__.append( "无法连接服务器主机: {host}:{port}".format( host=config[
"host"], port=config["port"]) )
return False
def isSlave(self):
"""
数据库同步是否正常
:return: None同步未开启,False同步中断,True同步正常
"""
cur = self.__instance__.cursor()
sql = "show slave status"
cur.execute(sql)
result = cur.fetchone()
print(result)
cur.close()
if result:
print(result[10])
print(result[11])
if result[10] == 'Yes' and result[11] == 'Yes':
return True
else:
if result[10] == 'No':
self.__error__.append( result[35] )
else:
self.__error__.append( result[37] )
return False
def get_last_error(self):
"""
获取第一个错误信息
:return: String
"""
if self.__error__:
return self.__error__.pop(0)
def notify(self,title,message):
"""
发送消息提醒
:param title: 消息的标题
:param message: 消息的内容
:return:
"""
msg = [title,message]
pool = []
notify = notify_email( self.__configParseSMTP__() )
pool.append( notify )
for item in pool:
item.ring( msg )
def close(self):
"""
关闭数据库链接
"""
if self.__instance__:
self.__instance__.close()
class notify_email(object):
def __init__(self,config):
self.config = config
def ring(self, message=[]):
subject = message.pop(0)
messageBody = "".join( message )
mailList = self.config["to"].split(";")
datetime = time.strftime("%Y-%m-%d %H:%M:%S")
for to in mailList:
body = """
<p>管理员<strong>{admin}</strong>,你好:</p>
<p style="text-indent:2em;">收到这封邮件说明你的数据库同步出现异常,请您及时进行处理。</p>
<p>异常信息:<br />{body}</p>
<p style="text-align:right;">{date}</p>
""".format( admin=to, body=messageBody, date=datetime )
msg = MIMEText( body, "html", "utf-8" )
msg["From"] = self.config["from"]
msg["To"] = to
msg["Subject"] = subject
smtp = smtplib.SMTP()
smtp.connect( self.config["smtp_host"],"25" )
if "smtp_host" in self.config:
smtp.login( self.config["smtp_user"], self.config["smtp_password"] )
smtp.sendmail( self.config["from"], to, msg.as_string() )
smtp.quit()
if __name__ == "__main__":
#命令行参数列表
usage = """usage: MySQLStat [options]"""
opt = optparse.OptionParser(usage=usage)
opt.add_option("-H","--host",dest="host",help="MySQL host (default: localhost)")
opt.add_option("-u","--user",dest="user",help="MySQL user")
opt.add_option("-p","--password",dest="password",help="MySQL password")
opt.add_option("-P","--port",dest="port",help="MySQL port (default: 3306)")
opt.add_option("","--smtp_host",dest="smtp_host",help="SMTP host (default: localhost)")
opt.add_option("","--smtp_user",dest="smtp_user",help="SMTP user")
opt.add_option("","--smtp_password",dest="smtp_password",help="SMTP password")
opt.add_option("","--from",dest="from",help="Email from")
opt.add_option("","--to",dest="to",help="Email to")
opt.add_option("","--defaults-file",dest="defaults_file",help="config file path")
(options,args) = opt.parse_args()
options = options.__dict__
Statu = SlaveStatu( **options )
subject = "您好,您的主从服务出现故障,请及时查看"
if Statu.connect() is False or Statu.isSlave() is False:
Statu.notify( subject, Statu.get_last_error() )
Statu.close()
创建default.cnf
[config] smtp_host=smtp.163.com smtp_user=xxx@163.com smtp_password=xxx from=Server<xxx@163.com> to=DBA<xxx@163.com> host=192.168.1.179 user=root password=xxx
开启定时任务监控主从
crontab */2 * * * * python slavecheckpoint.py --defaults-file=default.cnf --to=xxx@163.com
0 条评论