二、业务服务监控

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对应目录中不存在,无法比较

[root@SG12 ~]# md5sum /xiao/*

60b725f10c9c85c70d97880dfe8191b3  /xiao/f1

3b5d5c3712955042212316173ccf37be  /xiao/f2

e29311f6f1bf1af907f9ef9f44b8328b  /xiao/f3

0602016d7a1bfa0a8281cf14b57812ee  /xiao/f4

[root@SG12 ~]# 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("test@163.com","123456").

c、SMTP.sendmail(from_addr,to_addrs,msg[,mail_options,rcpt_options])方法,实现邮件的发送功能,参数一次为师发件人,收件人,邮件内容,例如SMTP.sendmail("test@163.com","123@123.com",body) 其中body内容定义如下.

"""From: test@163.com

To: 123@123.com

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 = "loyu@smartsoft-tech.com"   #定义发件人

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.p_w_picpath.MIMEImage(_p_w_picpathdata[,_subtype[,_encoder[,**_params]]]),创建包含图片数据的邮件体,_p_w_picpathdata包含原始图片数据的字节字符串

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 = "877659846@qq.com"           #定义邮件收件人

From = "loyu.liu@smartsoft-tech.com"  #定义邮件发送人

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>

&nbsp;&nbsp;500:105 404:3264 503:214<br>

3)访客浏览器信息<br>

&nbsp;&nbsp;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("loyu.liu@smartsoft-tech.com","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.p_w_picpath import MIMEImage #导入MIMEImage类


HOST = "smtp.ym.163.com"

SUBJECT = u"业务性能数据报表"

TO = "877659846.@qq.com"

FROM = "loyu.liu@smartsoft-tech.com"


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("loyu.liu@smartsoft-tech.com","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.p_w_picpath import MIMEImage

    

HOST = "smtp.ym.163.com"

SUBJECT = u"业务性能数据报表"

TO = "877659846.@qq.com"

FROM = "loyu.liu@smartsoft-tech.com"

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"] = "p_w_upload; 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("loyu.liu@smartsoft-tech","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()