二、业务服务监控
1、文件内容差异对比方法
difflib模块实现文件内容差异对比,difflib作为python的标准库模块,无需安装,作用是对比文本之间的差异,且支持输出可读性比较强的HTML文档,与linux下的diff命令相似。我们可以使用difflib对比代码,配置文件的差别,在版本控制方面是非常有用。
(1)示例:两个字符串的差异对比
通过使用difflib模块实现两个字符串的差异对比,然后以版本控制风格进行输出
【/root/text1_lines.py】
#! /usr/bin/python
import difflib
text1 = """text1: #定义字符串1
This module provides classes and functions for comparint sequences.
including HTML and context and unified diffs.
difflib document v7.4
add string
"""
text1_lines = text1.splitlines() #以行进行分割,一遍进行对比
text2 = """text2:
This module provides classes and functions for Comparing sequences.
including HTML and context and unified diffs.
difflib document v7.5"""
text2_lines = text2.splitlines()
d = difflib.Differ() #创建Differ()对象
diff = d.compare(text1_lines, text2_lines) #采用compare方法对字符串进行比较
print ‘\n‘.join(list(diff))
# python text1_lines.py
- text1:
? ^
+ text2:
? ^
- This module provides classes and functions for comparint sequences.
? ^ ^
+ This module provides classes and functions for Comparing sequences.
? ^ ^
including HTML and context and unified diffs.
- difflib document v7.4
? ^
+ difflib document v7.5
? ^
- add string
符号说明:
"-": 包含在第一个序列行中,但不包含在第二个序列行
"+": 包含在第二个序列行中,但不包含在第一个序列行
" ": 两个序列行一致
"?": 标志两个徐磊行存在增量差异
"^": 标志出两个序列行存在的差异字符
(2)生成美观的对比HTML格式文档
采用HtmlDiff()类的make_file()方法就可以生成没关的HTML文档,对(1)的代码按一下进行修改
d = difflib.Differ() #创建Differ()对象
diff = d.compare(text1_lines, text2_lines) #采用compare方法对字符串进行比较
print ‘\n‘.join(list(diff))
替换成:
d = difflib.HtmlDiff()
print d.make_file(text1_lines, text2_lines)
# python text1_lines.py > diff.html
再使用浏览器打开diff.html文件
(3)对比nginx配置文件差异
[/root/simples.py]
#! /usr/bin/python
import difflib
import sys
try:
textfile1 = sys.argv[1] # 第一个配置文件路径参数
textfile2 = sys.argv[2] # 第二个配置文件路径参数
except Exception,e:
print "Error:"+str(e)
print "Usage: simple3.py filename1 filename2"
sys.exit()
def readfile(filename): # 文件读取分隔函数
try:
fileHandle = open(filename,‘rb‘)
text = fileHandle.read().splitlines() # 读取后以行进行分隔
fileHandle.close()
return text
except IOError as error:
print(‘Read file Error:‘+str(error))
sys.exit()
if textfile1 == "" or textfile2 == "":
print "Usage: simple3.py filename1 filename2"
sys.exit()
text1_lines = readfile(textfile1) # 调用readfile函数,获取分割后的字符串
text2_lines = readfile(textfile2)
d = difflib.HtmlDiff() #创建HtmlDiff()对象
print d.make_file(text1_lines, text2_lines) #通过make_file方法输出HTML格式的对比结果
来测试一下
# python simple3.py a b
Read file Error:[Errno 2] No such file or directory: ‘a‘
# python simple3.py
Error:list index out of range
Usage: simple3.py filename1 filename2
# python simple3.py /etc/nginx/conf.d/zabbix1.conf /etc/nginx/conf.d/zabbix2.conf > diff.html
用浏览器打开diff.html文件,就可以看到差异了
2、文件与目录差异对比方法
filecmp可以实现文件、目录、遍历子目录的差异对比功能。比如报告中输出目标目录比原始多出的文件或子目录,即使文件同名也会判断是否为同一个文件(内容级对比)等
(1)
filecmp 提供了三个操作方法,分别为cmp(单文件对比)、cmpfiles(多文件对比)、dircmp(目录对比)
a、单文件对比
采用filecmp.cmp(file1,file2[,shallow])方法,比较文件名为file1和file2的文件,相同返回True,不相同返回false,shallow默认为True,意思是只根据os.stat()方法返回的文件基本信息进行对比,比如最后访问时间,修改时间,状态改变时间等,会忽略文件内容的对比。当shallow为False是,则os.stat()与文件内容相同进行校验
>>> import filecmp
>>> filecmp.cmp("/root/a","/root/b")
True
>>> filecmp.cmp("/etc/nginx/conf.d/zabbix.conf","/etc/nginx/conf.d/zabbix2.conf")
False
b、多文件对比
采用filecmp.cmpfiles(dir1,dir2,common[,shallow]),对比dir1与dir2目录给定的文件清单。该方法返回文件名的三个列表,分别为匹配、不匹配、错误。
匹配为包含匹配的文件的列表,不匹配反之
错误列表包括了目录不存在文件、不具备读取权限或其他原因导致的不能比较的文件清单
示例:xiao与loyu目录中指定文件清单对比
令目录下的文件md5信息如下,其中f1,f2匹配,f3不匹配,f4,f5对应目录中不存在,无法比较
[[email protected] ~]# md5sum /xiao/*
60b725f10c9c85c70d97880dfe8191b3 /xiao/f1
3b5d5c3712955042212316173ccf37be /xiao/f2
e29311f6f1bf1af907f9ef9f44b8328b /xiao/f3
0602016d7a1bfa0a8281cf14b57812ee /xiao/f4
[[email protected] ~]# md5sum /loyu/*
60b725f10c9c85c70d97880dfe8191b3 /loyu/f1
3b5d5c3712955042212316173ccf37be /loyu/f2
2cd6ee2c70b0bde53fbe6cac3c8b8bb1 /loyu/f3
d41d8cd98f00b204e9800998ecf8427e /loyu/f5
>>> import filecmp
>>> filecmp.cmpfiles("/loyu","/xiao",[‘f1‘,‘f2‘,‘f3‘,‘f4‘,‘f5‘])
([‘f1‘, ‘f2‘], [‘f3‘], [‘f4‘, ‘f5‘])
c、目录对比
通过dircmp(a,b[,ignore[,hide]])类创建一个目录比较对象,其中a和b是参加比较的目录名。ignore代表文件名忽略的列表,并默认为[‘RCS‘,‘CVS‘,‘tags‘]; hide代表隐藏的列表
3、发送电子邮件模块smtplib
SMTP类定义:smtplib.SMTP([host[,port[.local_hostname[,timeout]]]]),作为smtp的构造函数,功能是与smtp服务器建立连接,在连接成功后,就可以像服务器发送相关请求,比如登陆,校验,发送,退出等。
(1)SMTP类具有如下方法:
a、SMTP.connect([host[,port])方法 ,连接远程smtp主机方法,host为远程主机地址,port为远程主机smtp端口,默认25,也可以直接使用host:port形式来表示,例如:SMTP.connect("smtp.163.com","25").
b、SMTP.login(user,password)方法,远程smtp主机的校验方法,参数为用户名与密码,如SMTP.login("[email protected]","123456").
c、SMTP.sendmail(from_addr,to_addrs,msg[,mail_options,rcpt_options])方法,实现邮件的发送功能,参数一次为师发件人,收件人,邮件内容,例如SMTP.sendmail("[email protected]","[email protected]",body) 其中body内容定义如下.
"""From: [email protected]
To: [email protected]
Subject: test mail
test mail body"""
d、SMTP.starttls([keyfile[,certfile]])方法 启用TLS(安全传输)模式,所有SMTP命令都将加密传输,例如使用gmail的smtp服务时需要启动此项才能正常发送邮件,如SMTP.starttls().
e、SMTP.quit()方法,断开smtp服务器的连接
示例:
#! /usr/bin/python
import smtplib
import string
SMTP_HOST = "smtp.ym.163.com" #定义smtp主机
SUBJECT = raw_input("please input your mail subject:\n") #定义邮件主题
TO = raw_input("Please enter the recipient:\n") #定义收件人
From = "[email protected]" #定义发件人
text = raw_input("please input your text:\n") #定义邮件内容
BODY = string.join(( #组装sendmail方法的邮件主题内容,各段以"\r\n"进行分隔
"From: %s" % From,
"TO: %s" % TO,
"Subject: %s" % SUBJECT,
"",
text
), "\r\n")
server = smtplib.SMTP() #创建一个SMTP()对象
server.connect(HOST,"25") #通过connect方法连接smtp主机
server.starttls() #启动安全传输模式
server.login("From","password") #邮箱账号登陆校验
server.sendmail(From,[TO],BODY) #邮件发送
server.quit() #断开smtp连接
(2)定制个性化的邮件格式方法
通过邮件传输简单的文本已经无法满足我们的需求,比如我们时常会定制业务质量报表,在邮件主体中会包含HTML、图像、声音以及附件格式等,MIME(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展)作为一种新的扩展邮件格式很好地补充了这一点。介绍常用的MIME实现类:
a、email.mime.multipart.MIMEMultipart([_subtype[,boundary[,_subparts[,params]]]])
作用是生成包含多个部分的邮件体的MIME对象,参数_subtype指定要添加到“Content-type:multipart/subtype"包头的可选的三种子类型,分别为mixed,related,alternative,默认值为mixed。定义mixed实现构建一个带附件的邮件体;定义related实现构建内嵌资源的邮件体;定义alternative则实现构建纯文本与超文本共存的邮件体"
b、email.mime.audio.MIMEAudio(_audiodata[,_subtype[,_encoder[,**_params]]]),创建包含音频数据的邮件体,_audiodata包含原始二进制音频数据的字节字符串
c、email.mime.image.MIMEImage(_imagedata[,_subtype[,_encoder[,**_params]]]),创建包含图片数据的邮件体,_imagedata包含原始图片数据的字节字符串
d、email.mime.text.MIMEText(_text[,_subtype[,_charset]]),创建包含文本数据的邮件体,_text是包含消息负载的字符串,_subtype指定文本类型,支持plain(默认值)或html类型的字符串
示例1:定制常用邮件格式示例
#coding: utf-8
import smtplib
from email.mime.text import MIMEText #导入MIMEText类
SMTP_HOST = "smtp.ym.163.com" #定义smtp主机
SUBJECT = u"官网流量数据报表" #定义邮件主题
TO = "[email protected]" #定义邮件收件人
From = "[email protected]" #定义邮件发送人
msg = MIMEText(""" #创建一个MIMEText对象,分别制定HTML内容,类型(文本或html),字符编码
<table width="800" border="0" cellspacing="0" cellpadding="4">
<tr>
<td bgcolor="#CECFAD" height="20" style="font-size:14px">*官网数据 <a href="www.baidu.com"> 更多>></a></td>
</tr>
<tr>
<td bgcolor="#EFEBDE" height="100" style="font-size:13px">
1)日访问量:<font color=red>1234</font> 访问次数:123 页面浏览量:1223 点击数:23323 数据流量:123Mb<br>
2)状态码信息<br>
500:105 404:3264 503:214<br>
3)访客浏览器信息<br>
IE:50% firefox:10% chrome:30%
</td>
</tr>
</table>""","html","utf-8"
)
msg[‘Subject‘] = SUBJECT #邮件主题
msg[‘From‘] = From #邮件发件人,邮件头部都可见
msg[‘To‘] = TO #邮件收件人,邮件头部可见
try:
server = smtplib.SMTP() #创建一个SMTP()对象
server.connect(SMTP_HOST,"25") #通过connect方法连接smtp主机
server.starttls() #启动安全传输模式
server.login("[email protected]","mypassword") #邮箱账号登陆校验
server.sendmail(From,TO,msg.as_string()) #邮件发送
server.quit()
print "邮件发送成功"
except Exception, e:
print "失败:"+str(e)
示例2:实现图文格式的服务器性能报表邮件
#coding: utf-8
import smtplib
from email.mime.multipart import MIMEMultipart # 邮件主题由多个MIME对象组成,则同事需要引用MIMEMultipart类来进行组装
from email.mime.text import MIMEText #导入MIMEText类
from email.mime.image import MIMEImage #导入MIMEImage类
HOST = "smtp.ym.163.com"
SUBJECT = u"业务性能数据报表"
TO = "[email protected]"
FROM = "loyu.l[email protected]"
def addimg(src,imgid): #添加图片函数,参数1:图片路径,参数2:图片 id
fp = open(src,‘rb‘) #打开文件
msgImage = MIMEImage(fp.read()) #创建MIMEImage对象,读取图片内容并作为参数
fp.close() #关闭文件
msgImage.add_header(‘Content-ID‘, imgid) #指定图片文件的Content-ID,<img>标签src用到
return msgImage
msg = MIMEMultipart(‘related‘) #创建MIMEMultipart对象,采用related定义内嵌资源的邮件体
msgtext = MIMEText("""
<table width="600" border="0" cellspacing="0" cellpadding="4">
<tr bgcolor="#CECFAD" height="20" style="font-size:14px">
<td colspan=2>*官网数据 <a href="http://10.0.8.132/zabbix">更多>> </a></td>
</tr>
<tr bgcolor="#EFEBDE" height="100" sytle="font-size:13px">
<td>
<img src="cid:io"> #<img>标签的src属性是通过Content-ID来引用的
</td>
<td>
<img src="cid:key_hit">
<td>
</tr>
<tr bgcolor="#EFEBDE" height="100" sytle="font-size:13px">
<td>
<img src="cid:men">
</td>
<td>
<img src="cid:swap">
<td>
</tr>
</table>""","html","utf-8")
msg.attach(msgtext) #MIMEMultipart对象附加MIMEText的内容
msg.attach(addimg("/root/disk.jpg","io")) #使用MIMEMultipart对象附加MIMEImage的内容
msg.attach(addimg("/root/disk.jpg","key_hit"))
msg.attach(addimg("/root/disk.jpg","men"))
msg.attach(addimg("/root/disk.jpg","swap"))
msg[‘Subject‘] = SUBJECT
msg[‘From‘] = FROM
msg[‘To‘] = TO
try:
server = smtplib.SMTP()
server.connect(HOST,"25")
server.starttls()
server.login("[email protected]","mysqlpassword")
server.sendmail(FROM,TO,msq.as_string())
server.quit()
print "邮件发送成功"
except Exception,e:
print "失败:" + str(e)
示例3:实现带附件格式的业务服务质量周报邮件
#coding: utf-8
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
HOST = "smtp.ym.163.com"
SUBJECT = u"业务性能数据报表"
TO = "[email protected]"
FROM = "[email protected]"
def addimg(src,imgid):
fp = open(src,‘rb‘)
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header(‘Content-ID‘, imgid)
return msgImage
msg = MIMEMultipart(‘related‘)
#创建一个MIMEText对象,HTML元素包括文字与图片<img>
msgtext = MIMEText("<font color=red>官网业务周平均延时图表:<br><img src=\"cid:weekly\" border=\"1\"><br>详细内容见附件。</front>","html","utf-8")
msg.attach(msgtext)
msg.attach(addimg("disk.png","weekly"))
#创建一个MIMEText对象,附加week_report.xlsx文档
attach = MIMEText(open("/root/disk.text","rb").read(),"base64","utf-8")
attach["Content-Type"] = "text/HTML"
attach["Content-Disposition"] = "attachment; filename = \"业务服务质量周报(12).text\"".decode("utf-8").encode("gb18030")
msg.attach(attach)
msg[‘Subject‘] = SUBJECT
msg[‘From‘] = FROM
msg[‘To‘] = TO
try:
server = smtplib.SMTP()
server.connect(HOST,"25")
server.login("[email protected]","mypassword")
server.sendmail(FROM,TO,msg.as_string())
server.quit()
print "邮件发送成功"
except Exception,e:
print "失败:"+str(e)
4、探索web服务质量方法
pycurl是一个用c语言写的libcurl Python实现,功能非常强大,支持的操作协议有FTP,HTTP,HTTPS,TELNET等,可以理解成Liux下curl命令功能的Python封装,简单易用
通过调用pycurl提供的方法,实现探测Web服务质量的情况,比如相应的HTTP状态吗,请求延时、HTTP头信息,下载速度等,利用这些信息可以定位服务相应慢的具体环节
pycurl模块安装方法如下:
easy_install pycurl #easy_install 安装方法
pip install pycurl #pip安装方法
(a) 模块常用方法
pycurl.Curl()类实现创建一个libcurl包的Curl句柄对象,无参数。
close() 方法,对应libcurl包中的curl_easy_cleanup方法,无参数,实现关闭,回收Curl对象
perform() 方法,对应libcurl包中的curl_easy_perform方法,无参数,实现Curl对象请求的提交
setopt(option,value) 方法,对应libcurl包中的curl_easy_setopt方法,参数option是通过libcurl的常量来指定的,参数value的值会依赖option,可以是一个字符串,整型,长整型、文件对象、列表或函数等
c = pycurl.Curl() # 创建一个curl对象
c.setopt(pycurl.CONNECTTIMEOUT,5) # 连接等待时间,设置为0则不等待
c.setopt(pycurl.TIMEOUT,5) # 请求超时时间
c.setopt(pycurl.NOPROGRESS,0) # 是否屏蔽下载进度条,非0则屏蔽
c.setopt(pycurl.MAXREDIRS,5) # 指定http重定向的最大数
c.setopt(pycurl.FORBID_REUSE,1) # 完成交互后强制断开连接。不重用
c.setopt(pycurl.FRESH_CONNECT,1) # 强制获取新的连接,即替代缓存中的连接
c.setopt(pycurl.DNS_CACHE_TIMEOUT,60) # 设置保存dns信息的时间,默认为120秒
c.setopt(pycurl.URL,"http://www.abc.com") # 指定请求的URL
c.setopt(pycurl.USERAGENT,"Mozilla/5.2 (compatible;MISIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.14322; .NET CLR 2.0.50324)" ) # 配置请求HTTP头的User-Agent
c.setopt(pycurl.HEADERFUNCTION, getheader) # 将返回的HTTP HEADER定向到回调函数getheader
c.setopt(pycurl.WRITEFUNTION,getbody) # 将返回的内容定向到回调函数getbody
c.setopt(pycurl.WRITEHEADER,fileobj) # 将返回的HTTP HEADER定向到fileobj文件对象
c.setopt(pycurl.WRITEDATA,fileobj) # 将返回的html内容定向到fileobj文件对象
getinfo(option) 方法,对应libcurl包中的curl_easy_getinfo方法,参数option是通过libcurl的常量来指定的
c = pycurl.Curl() # 创建一个Curl对象
c.getinfo(pycurl.HTTP_CODE) # 返回HTTP状态码
c.getinfo(pycurl.TOTAL_TIME) # 传输结束所消耗的总时间
c.getinfo(pycurl.NAMELOOKUP_TIME) # DNS解析所消耗的时间
c.getinfo(pycurl.CONNECT_TIME)# 建立连接所消耗的时间
c.getinfo(pycurl.PRETRANSFER_TIME) # 从建立连接到准备传输所消耗的时间
c.getinfo(pycurl.STARTTRANSFER_TIME)# 从建立连接到开始传输所消耗的时间
c.getinfo(pycurl.REDIRECT_TIME)# 重定向所消耗的时间
c.getinfo(pycurl.SIZE_UPLOAD)# 上传数据包的大小
c.getinfo(pycurl.SIZE_DOWNLOAD)# 下载数据包的大小
c.getinfo(pycurl.SPEED_DOWNLOAD)# 平均下载速度
c.getinfo(pycurl.SPEED_UPLOAD)# 平均上传速度
c.getinfo(pycurl.HEADER_SIZE)# HTTP头部大小
示例:实现探测web服务质量
# -*- coding: utf-8 -*-
import os,sys
import time
import pycurl
URL="http://www.baidu.com" # 探测的目标URL
c = pycurl.Curl() # 创建一个Curl对象
c.setopt(pycurl.URL, URL) # 定义请求的URL常量
c.setopt(pycurl.CONNECTTIMEOUT, 5) # 定义请求连接等待时间
c.setopt(pycurl.TIMEOUT, 5) # 定义请求超时时间
c.setopt(pycurl.NOPROGRESS, 1) # 屏蔽下载进度条
c.setopt(pycurl.FORBID_REUSE, 1) # 完成交互后强制断开连接,不重用
c.setopt(pycurl.MAXREDIRS, 1) # 指定HTTP重定向的最大数为1
c.setopt(pycurl.DNS_CACHE_TIMEOUT, 30) # 设置保存DNS信息的时间为30秒
indexfile = open(os.path.dirname(os.path.realpath(__file__))+"/content.txt","web") # 创建一个文件对象,以“web”方式打开,用来存储返回的http头部及页面内容
c.setopt(pycurl.WRITEHEADER, indexfile) # 将返回的HTTP HEADER定向到indexfile文件
c.setopt(pycurl.WRITEDATA, indexfile) # 将返回的HTMl内容定向到indexfile文件对象
try:
c.perform() # 提交请求
except Exception,e:
print "connection error:"+str(e)
indexfile.close()
c.close()
sys.exit()
NAMELOOKUP_TIME = c.getinfo(c.NAMELOOKUP_TIME) # 获取DNS解析时间
CONNECT_TIME = c.getinfo(c.CONNECT_TIME) # 获取建立连接时间
PRETRANSFER_TIME = c.getinfo(c.PRETRANSFER_TIME) # 获取从建立连接到准备传输所消耗的时间
STARTTRANSFER_TIME = c.getinfo(c.STARTTRANSFER_TIME) # 获取从建立连接到传输开始所消耗的时间
TOTAL_TIME = c.getinfo(c.TOTAL_TIME) # 获取传输的总时间
HTTP_CODE = c.getinfo(c.HTTP_CODE) # 获取HTTP状态码
SIZE_DOWNLOAD = c.getinfo(c.SIZE_DOWNLOAD) # 获取下载数据包大小
HEADER_SIZE = c.getinfo(c.HEADER_SIZE) # 获取HTTP头部大小
SPEED_DOWNLOAD = c.getinfo(c.SPEED_DOWNLOAD) # 获取平均下载速度
print "HTTP状态码: %s" % (HTTP_CODE)
print "DNS解析时间: %.2f ms" % (NAMELOOKUP_TIME)
print "建立连接时间: %.2f ms" % (CONNECT_TIME)
print "准备传输时间: %.2f ms" % (PRETRANSFER_TIME)
print "传输开始时间: %.2f ms" % (STARTTRANSFER_TIME)
print "传输结束时间: %.2f ms" % (TOTAL_TIME)
print "下载数据包大小: %d bytes/s" % (SIZE_DOWNLOAD)
print "HTTP头部大小: %d bytes" % (HEADER_SIZE)
print "平均下载速度: %d bytes/s" % (SPEED_DOWNLOAD)
indexfile.close()
c.close()