自動化運維學習--python 頂 原

環境搭建

單機環境搭建

安裝python

apt-get install python python-dev python-setuptools

安裝pip

apt-get install python-pip

簡單主機信息

說明:主要參考網站psutil文檔

安裝psutil

pip install psutil

cpu

cpu times

import psutil
cputimes = psutil.cpu_times()
psutil.cpu_times().user

結果如下:

scputimes(user=1751.49, nice=57.5, system=1126.75, idle=61666.12, iowait=745.43, irq=0.0, softirq=1.58, steal=0.0, guest=0.0, guest_nice=0.0)

解釋: user:用戶cpu time; nice: ; system:系統cpu time; idle:cpu空閒狀態cpu time; iowait:io等待時間;

取出cpu_times()中的參數,例如用戶cpu時間

psutil.cpu_times().user

獲取物理cpu個數

psutil.cpu_count(lgicol=False)

內存和交換分區

內存信息

psutil.virtual_memory()

結果如下:

svmem(total=8288972800L, available=6737719296L, percent=18.7, used=3927216128L, free=4361756672L, active=2380361728, inactive=1244004352, buffers=150831104L, cached=2225131520)

解釋 total: 所有物理內存 available: 實際上可以立刻使用的內存(單位是byte) percent:內存使用的內存(total - available) / total * 100. used: 使用的內存 free: 空閒內存 Platform-specific fields:特定平臺的屬性

active: (UNIX): memory currently in use or very recently used, and so it is in RAM. inactive: (UNIX): 未被使用的內存 buffers: (Linux, BSD): 文件緩存 cached: (Linux, BSD): 緩存 wired: (BSD, OSX):常駐內存 在RAM中的 shared: (BSD): 共享內存

交換分區

psutil.virtual_memory()

結果: sswap(total=4999606272L, used=0L, free=4999606272L, percent=0.0, sin=0, sout=0)

硬盤

分區信息

psutil.disk_partitions()

結果: [sdiskpart(device='/dev/sda4', mountpoint='/', fstype='ext4', opts='rw,errors=remount-ro')]

使用信息

psutil.disk_usage('/')

結果: sdiskusage(total=202632327168, used=6518771712, free=185796829184, percent=3.2)

進程信息

pid=3923
p = psutil.Process(pid)

#print process name
p.name()

#print process execuable file path
p.exe()

#print process current work directory
p.cwd()

#print process children process
p.get_children()

net IO

psutil.net_io_counters()

結果: snetio(bytes_sent=4469434, bytes_recv=64367107, packets_sent=40469, packets_recv=53242, errin=0, errout=0, dropin=0, dropout=0)

Ip information

IPy模塊處理IP信息

  1. 安裝sudo pip install Ipy
  2. IPy包中的IP類 2.1 列出網段中的IP數目:
    ip = IP('8.8.8.8')
    for i in ip:
    	print (x)

2.2 IP轉換

	ip.int()#將IP轉爲整數型
    ip.strHex()#將IP轉爲十六進制
    ip.strBIn()#將IP轉爲二進制

2.3 將十六進制的IP地址轉換成爲常見的澱粉十進制

	print(IP(0x8080808))#輸出爲8.8.8.8

2.4 生成子網掩碼

	IP('8.8.8.8').make_net('255.255.255.0)
    IP('8.8.8.8/255.255.255.0',make_net=True)

dns information

  1. 安裝dnspython sudo pip install dnspython
  2. 域名解析知識清理:
    • A記錄:域名轉爲IP地址
    • MX記錄:郵件交換記錄,定義郵件服務器的域名信息
    • CNAME記錄:實現域名見的映射。別名記錄
    • NS記錄:標記區域的域名服務器授權服務器和授權子域
    • PTR記錄:反響解析,與A記錄相反
    • SOA記錄:標記一個其實授權去的定義

A記錄解析過程:

	import dns.resolver
    domain = "www.google.com"
    A = dns.resolver.query(domain,'A')
    for x in A.response.answer:
    	for i in x:
        	print (i.address)

MX記錄解析

	#! /usr/bin/python
	import dns.resolver

	domain="163.com"
	MX = dns.resolver.query(domain,'MX')
	for i in MX:
		print i.preference, i.exchange

NS記錄解析

	#! /usr/bin/python
	import dns.resolver

	domain= "baidu.com"
	NC = dns.resolver.query(domain,'Ns')
	for x in NC.response.answer:
		for i in x.items:
			print i.to_text()

CNAME記錄解析

	#!/usr/bin/python
	import dns.resolver

	domain = "www.baidu.com"
	cname = dns.resolver.query(domain,'CNAME')
	for i in cname.response.answer:
        for j in i.items:
        	print (j.to_text())

實例:DNS輪循服務業務監控

#! /usr/bin/python
import dns.resolver
import os
import httplib

iplist=[]
domain = "baidu.com"

def getIpList(domain=""):
        try:
                A = dns.resolver.query(domain,'A')
        except Exception,e:
                print "dns resolver error:"+str(e)
                return
        for x in A.response.answer:
                for i in x.items:
                        iplist.append(i.address)
        return True

def checkIp(ip):
        checkurl = ip + ":80"
        getcontent = ""
        httplib.socket.setdefaulttimeout(5)
        conn = httplib.HTTPConnection(checkurl)

        try:
                conn.request("GET","/",headers={"Host":domain})
                r = conn.getresponse()
                getcontent = r.read(15)
        finally:
                if getcontent == "<!doctype html>":
                        print ip+"[OK]"
                else:
                        print ip+"[Error]"
                        print getcontent+"\n"

if __name__ =="__main__":
        if(getIpList(domain) and len(iplist)>0):
                for ip in iplist:
                        checkIp(ip)
        else:
                print "dns resolver error."

業務服務質量監控

文本內容對比工具

#!/usr/bin/python
#coding:utf-8

import difflib
import sys

try:
        firstfile = sys.argv[1]#python 的參數是從一開始的
        secondfile = sys.argv[2]
except Exception,e:
        print ("Error:"+ str(e))
        print ("Usage: compare.py file1 file2")
        sys.exit()

def readfile(path):
        try:
                filehandle = open(path,'r')
                text = filehandle.read().splitlines()
                filehandle.close()
                return text
        except IOError as error:
                print ("Read file error:" + str(error))
                print ("Usage: compare.py file1 file2")
                sys.exit()

if(firstfile =="" or secondfile ==""):
        print ("Usage: compare.py file1 file2")
        sys.exit()

file1lines = readfile(firstfile)
file2lines = readfile(secondfile)

diff = difflib.HtmlDiff()
print diff.make_file(file1lines,file2lines)

文件目錄差異對比工具

python自帶的filecmp滿足需求。

filecmp模塊常見的方法的說明

filecmp常見的方法有:cmp(單文件對比),cmpfiles(多文件對比),dircmp(目錄對比) + filecmp.cmp(f1,f2[,shallow=True]):對比文件f1和f2是否相同,相同就返回True,不同爲False。shallow爲可選的參數,默認爲True。shallow爲True時表示根據文件的屬性值(最後修改時間,作者,狀態改變時間等)進行對比判斷兩文件是否相同;shallow爲false時,同時對比os.stat()和對比兩文件的內容判斷是否相同。 + filecmp.cmpfiles(dir1,dir2[,shallow=True]).對比dir1和dir2給定的文件清單。該方法返回三個列表:匹配,不匹配,錯誤(目錄中不存在,讀寫權限不夠,或其他原因導致的不能比較的清單)。 + filecmp.dircmp(a,b[,ignore[,hide]])創建一個目錄比較對象,a,b,是參加比較的目錄名,ignore是忽略的文件列表,並默認爲['RCS','CVS',tags'];hide表示隱藏的文件愛你列表,默認爲[os.curdir,os.pardir]。dircmp類可以獲得目錄比較的詳細信息,如只有a含有的文件。dircmp提供3個輸出報告的方式: * report():比較當前目錄的內容 * report_partial_closure():比較當前目錄和第一級子目錄的內容 * report_full_clourse():遞歸比較所有的文件

校驗源與備份目錄的差異

python shutil 模塊常見函數:

  • copyfile( src, dst) 從源src複製到dst中去。當然前提是目標地址是具備可寫權限。拋出的異常信息爲IOException. 如果當前的dst已存在的話就會被覆蓋掉
  • copymode( src, dst) 只是會複製其權限其他的東西是不會被複制的
  • copystat( src, dst) 複製權限、最後訪問時間、最後修改時間
  • copy( src, dst) 複製一個文件到一個文件或一個目錄
  • copy2( src, dst) 在copy上的基礎上再複製文件最後訪問時間與修改時間也複製過來了,類似於cp –p的東西
  • copy2( src, dst) 如果兩個位置的文件系統是一樣的話相當於是rename操作,只是改名;如果是不在相同的文件系統的話就是做move操作
  • copytree(olddir,newdir,True/Flase) 把olddir拷貝一份newdir,如果第3個參數是True,則複製目錄時將保持文件夾下的符號連接,如果第3個參數是False,則將在複製的目錄下生成物理副本來替代符號連接

使用python發送郵件

簡單郵件發送

1.smtplib.SMTP(host,port[,local_hostname[,timeout]])構造smtp類,每個參數的含義:

  • host:郵件服務器地址
  • port:郵件服務器端口
  • local_hostname:在本地主機的FQDN發送HELO/EHLO(標識身份)指令
  • timeout:超時時間,單位s(秒)
#! /usr/bin/python

# coding:utf-8

import smtplib
import string

HOST = "smtp.google.com"
SUBJECT = "TEST EMAIL"
TO = "[email protected]"
FROM = "[email protected]"
text = "this is the mail body main test!"
other = "other info"		#BODY中的other如果包含字符,則收到的郵件中沒有內容,爲什麼?這個字段是什麼意思?
BODY = string.join(
    (
        "From: %s" % FROM,
        "TO: %s" % TO,
        "Subject: %s" % SUBJECT,
        other,
        text
    ), "\r\n"
)
server = smtplib.SMTP()
server.connect(HOST, 25)
server.starttls()
server.login("[email protected]", "password")
server.sendmail(FROM, TO, BODY)
server.quit()

定製豐富內容的郵件

1.使用html編寫郵件內容

#! /usr/bin/python
# coding:utf-8

import smtplib
from email.mime.text import MIMEText

HOST = "smtp.qq.com"
SUBJECT = u"官方流量數據報表"
FROM = "[email protected]"
TO = "[email protected]"
msg = MIMEText("""
<table width="800" b cellspacing="0" order="0">
    <tr>
        <td bgcolor="gray" style="font-size:14px">
            *官網數據
        </td>
    </tr>
    <tr>
        <td>
            <ol>
                <li>日訪問量:<font color=red>152433</font> 訪問次數:23651 頁面瀏覽量:45123 點擊數:545122 數據流量:504Mb</li>
                <li>狀態碼信息:<br></li>&nbsp;&nbsp;&nbsp;500:105 404:3264 503:214
                <li>瀏覽器瀏覽信息:<br></li>&nbsp;&nbsp;&nbsp;IE:50% firefox:10% chrome:30% other:10%
                <li>頁面信息:<br></li>&nbsp;&nbsp;&nbsp;/index.php 42153<br>&nbsp;&nbsp;&nbsp;/view.php 21451<br>&nbsp;&nbsp;&nbsp;/login.php 5122<br>
            </ol>
        </td>
    </tr>
</table>
""", "html", "utf-8"
)

msg['Subject'] = SUBJECT
msg['From'] = FROM
msg['TO'] = TO

try:
    server = smtplib.SMTP()
    server.connect(HOST, "25")
    server.starttls()
    server.login("[email protected]", "password")
    server.sendmail(FROM, TO, msg.as_string())
    server.quit()
    print("郵件發送成功!")
except Exception, e:
    print("失敗!原因:"+str(e))

注意點 在使用email.mime.text.MIMEText對象的時候,需要將MIMEText對象的內容,編碼和內容使用的語言交代清楚。

2.在郵件中添加圖片

  • 定義一個email.mime.multipart.MIMEMultipart對象
  • 使用MIMEMultipart對象的attach函數,將html內容嵌入到郵件中,其中使用contentID來訪問嵌入到郵件中的圖片信息
  • 使用MIMEImage對象,將圖片文件從本地以二進制的形式讀入到內存,並用圖像文件的二進制序列初始化MIMEImage對象,使用MIMEImageadd_header()方法給MIMEImage對象設置contentID
  • 使用MIMEMultipart對象的attach函數,將MIMEImage對象嵌入到郵件中
#! /usr/bin/python
# coding:utf-8

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText

__author__ = 'hgf'

HOST = "smtp.qq.com"
SUBJECT = u"業務性能數據表"
FROM = "[email protected]"
TO = "[email protected]"

def addimg(src, imgid):
    fp = open(src, 'rb')
    msgImg = MIMEImage(fp.read())
    fp.close()
    msgImg.add_header('Content-ID', imgid)
    return msgImg

# 使用related定義內嵌資源
msg = MIMEMultipart('related')

msgtext = MIMEText("""
<table width="800" b cellspacing="0" order="0">
    <tr>
        <td bgcolor="gray" style="font-size:14px">
            *官網logo
        </td>
    </tr>
    <tr bgcolor="#EFEBDE" height="100" style="font-size:13px">
        <td>
           <img src="cid:p1">
        </td>
        <td>
            <img src="cid:p2">
        </td>
    </tr>
    <tr bgcolor="#EFEBDE" height="100" style="font-size:13px">
        <td>
           <img src="cid:p3">
        </td>
        <td>
            <img src="cid:p4">
        </td>
    </tr>
</table>
""", "html", "utf-8")
msg.attach(msgtext)
msg.attach(addimg("/home/hgf/Pictures/program/logo/1.PNG", "p1"))
msg.attach(addimg("/home/hgf/Pictures/program/logo/2.PNG", "p2"))
msg.attach(addimg("/home/hgf/Pictures/program/logo/3.PNG", "p3"))
msg.attach(addimg("/home/hgf/Pictures/program/logo/4.PNG", "p4"))

msg['Subject'] = SUBJECT
msg['From'] = FROM
msg['To'] = TO


try:
    server = smtplib.SMTP()
    server.connect(HOST, 25)
    server.starttls()
    server.login(FROM, "password")
    server.sendmail(FROM, TO, msg.as_string())
    server.quit()
    print("發送成功")
except Exception, e:
    print("失敗!原因:"+str(e))

3.在郵件中添加附件

  • 初始化MIMEText對象作爲附件:將本地的文件以讀取二進制的方式,將文件內容用於初始化MIMEText對象,設置MIMEText的格式爲base64編碼,文字編碼爲utf-8
  • 設置MIMEText實例的Content-Type屬性爲application/octet-stream(專用來說明不知道文件類型的二進制流),
  • 設置MIMEText實例的Content-Disposition屬性爲attachment;filename=文件名,使得附件可以用來下載
  • MIMEText實例attach到MIMEMultipart對象中
#! /usr/bin/python
# coding:utf-8

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText

__author__ = 'hgf'

HOST = "smtp.qq.com"
SUBJECT = u"業務性能數據表"
FROM = "[email protected]"
TO = "[email protected]"


def addimg(src, imgid):
    fp = open(src, 'rb')
    msgImg = MIMEImage(fp.read())
    fp.close()
    msgImg.add_header('Content-ID', imgid)
    return msgImg

msg = MIMEMultipart('related')

msgtext = MIMEText("""
<font>官網業務平均時延圖表:<br><img src=\"cid:p1\" border =\"1\"><br>詳細內附件圖表</font>
""", "html", "utf-8")

msg.attach(msgtext)
msg.attach(addimg("/home/hgf/Pictures/program/logo/1.PNG", "p1"))

attach = MIMEText(open("/home/hgf/test.xlsx", 'rb').read(), "base64", "utf-8")
attach["Content-type"] = "application/octet-stream"
# 指定文件格式類型

# 指定ContentDisposition 屬性值爲attachement 則會出現自愛在保存對話框
# qqmail使用gb18030編碼,爲保證中文不會亂碼,對文件名進行編碼轉換
attach["Content-Disposition"] = "attachment;filename=\"excel.xlsx\"".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.starttls()
    server.login(FROM, "passwprd")
    server.sendmail(FROM, TO, msg.as_string())
    server.quit()
    print("發送成功!")
except Exception, e:
    print("發送失敗:"+str(e))

web服務器性能

安裝pycurl

  1. 安裝curl:sudo apt-get install curl
  2. 安裝openssl:apt-get install openssl
  3. 安裝pycurl:sudo pip install pycurl

TIPS:

In [2]: pycurl.version
Out[2]: 'PycURL/7.19.5.1 libcurl/7.35.0 OpenSSL/1.0.1f zlib/1.2.8 libidn/1.28 librtmp/2.3'

安裝問題

  1. 錯誤__main__.ConfigurationError: Could not run curl-config: [Errno 2] No such file or directory
Downloading/unpacking pycurl
  Running setup.py egg_info for package pycurl
    Traceback (most recent call last):
      File "<string>", line 16, in <module>
      File "/tmp/pip-build-root/pycurl/setup.py", line 563, in <module>
        ext = get_extension()
      File "/tmp/pip-build-root/pycurl/setup.py", line 368, in get_extension
        ext_config = ExtensionConfiguration()
      File "/tmp/pip-build-root/pycurl/setup.py", line 65, in __init__
        self.configure()
      File "/tmp/pip-build-root/pycurl/setup.py", line 100, in configure_unix
        raise ConfigurationError(msg)
    __main__.ConfigurationError: Could not run curl-config: [Errno 2] No such file or directory
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 16, in <module>
  File "/tmp/pip-build-root/pycurl/setup.py", line 563, in <module>
    ext = get_extension()
  File "/tmp/pip-build-root/pycurl/setup.py", line 368, in get_extension
    ext_config = ExtensionConfiguration()
  File "/tmp/pip-build-root/pycurl/setup.py", line 65, in __init__
    self.configure()
  File "/tmp/pip-build-root/pycurl/setup.py", line 100, in configure_unix
    raise ConfigurationError(msg)
__main__.ConfigurationError: Could not run curl-config: [Errno 2] No such file or directory

解決方案

在debian系列操作系統中需要安裝openssl

sudo apt-get install libcurl4-openssl-dev

簡單使用curl

#! /usr/bin/python
# coding:utf-8

import pycurl


__author__ = 'hgf'


c = pycurl.Curl()
c.setopt(pycurl.URL, "http://www.baidu.com")
c.setopt(pycurl.CONNECTTIMEOUT, 5)
c.setopt(pycurl.NOPROGRESS, 0)
c.setopt(pycurl.FORBID_REUSE, 1)
f = open("a.txt",'wb')
c.setopt(pycurl.WRITEHEADER, f)
c.setopt(pycurl.WRITEDATA, f)
c.setopt(pycurl.DNS_CACHE_TIMEOUT, 30)

c.perform()
c.close()

** 注意: ** 使用pycurl必須定義pycurl.URL的值,必須調用perform函數使pycurl生效,並且一般需要定義處理pycurl返回的結果。

使用curl探測web服務質量

#! /usr/bin/python
# coding:utf-8

import pycurl
import os
import sys

__author__="hgf"

URL = "http://www.baidu.com"
c = pycurl.Curl()
c.setopt(pycurl.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.DNS_CACHE_TIMEOUT, 30)
c.setopt(pycurl.MAXREDIRS, 1)

indexfile = open(os.path.dirname(os.path.realpath(__file__))+"/content.txt",'wb')

c.setopt(pycurl.WRITEHEADER, indexfile)
c.setopt(pycurl.WRITEDATA, indexfile)

try:
	c.perform()
except Exception, e:
	print ("error:"+str(e))
	indexfile.close()
	c.close()
	sys.exit()

NAMELOOKUP_TIME = c.getinfo(c.NAMELOOKUP_TIME)
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)
SIZE_DOWNLOAD = c.getinfo(c.SIZE_DOWNLOAD)
HEADER_SIZE = c.getinfo(c.HEADER_SIZE)
SPEED_DOWNLOAD = c.getinfo(c.SPEED_DOWNLOAD)

print "HTTP狀態碼:%s" % HTTP_CODE
print "DNS解析時間:%.2f ms" % (NAMELOOKUP_TIME*1000)
print "建立連接時間:%.2f ms" % (CONNECT_TIME*1000)
print "準備傳輸時間: %.2f ms" % (PRETRANSFER_TIME*1000)
print "傳輸開始時間:%.2f ms" % (STARTTRANSFER_TIME*1000)
print "傳輸結束時間: %.2f ms" % ((TOTAL_TIME - STARTTRANSFER_TIME)*1000)
print "下載數據包大小: %.2f bytes/s" % SIZE_DOWNLOAD
print "HTTP 頭部大小: %.2f byte" % HEADER_SIZE
print "下載速度:%.2f bytes/s" % SPEED_DOWNLOAD
indexfile.close()
c.close()

定製報表

python處理excel

安裝slsxwriter

pip install XlsxWriter

XlsxWriter常見方法的使用

  1. Workbook類

    • Worlbook對象代表了Excel的整個文件,並且存儲在磁盤上,
    • 構造方法:Workbook(filename[, option])
    • 主要的方法:
      • add_Worksheet([sheetname]):添加一個新的工作表,不定義sheetname時,默認爲sheet[工作表序號]
      • add_format([proprties]):創建一個新的格式對象來格式化單元格,參數proprties爲dict類型, 指定一個單元格屬性的字典。
        • 例如:workbook.add_format({'bold':True}),設置加粗的單元格。
        • 上述設置等價方式:bold = workbook.add_format()bold.set_bold()
      • add_chart(options):在工作表中創建一個圖表對象,內部通過insert_chart()實現。option爲dict類型。
      • close():關閉工作表文件。
  2. WorkSheet類

    • write(row, col, *args):將普通數據寫道單元格中,其中(row, col)爲單元格在表格中的位置,起始位置爲(0,0);×args爲要寫入的數據內容, 可以爲數字,字符串,格式對象。
    • set_row(row, height, cell_format, option):設置行單元格的屬性,row指定行位置;height設置行高,單位爲像素;cell_formatformat類型,指定格式對象;參數option是dict類型,設置行hiden(隱藏),level(組合分級),collapsed(摺疊)。
    • set_column(first_col, last_col, wodth, cell_format, option):設置一列或多列單元格屬性。參數wodth(float類型)設置列寬,cell_formatoptions同上。
    • insert_image(row, col, image[, option]):插入圖片到指定單元格,支持PNG,JPEG,BMP等圖片格式。image(String類型)表示圖片的路徑。options(dist類型):制定圖片的位置、比例、鏈接URL等。
      • 例如:worksheet.insert_image('B5', 'img/python-logo.jpg', {'url':'http://python.org'})`
  3. Chart類

    • 支持的圖表類型包括面積,條形圖,柱形圖,餅狀圖,散點圖,shares,雷達。
    • 圖表通過worksheetadd_chart方法創建圖表。chart = worksheet.add_chart({type,'column'})創建一個柱形圖。

圖表類型說明: |類型關鍵字|圖表類型| |--------|-------| |area|面積樣式圖表| |bar|條形態| |column|柱形圖| |line|條形圖| |pie|餅狀圖| |scatter|散點圖| |stock|shares圖| |radar|雷達圖|

+ 通過`insert_chart()`方法將圖標插入到指定的地方。
+ 主要的方法:
	- `chat.add_series(options)`:添加一個數據系列到圖表。
		* 例子:
		```python
		chat.add_series({
 		   	'categories':	'=Sheet1!$a$1:$A$5',
 	       'values':		'=Sheet1!$B$1:$B$5',
 	       'line':			{'color':'red'},
	    })
>說明:
>`add_series`最長見的option是
>`categories`:表示圖表標籤的範圍[將表格中的對應位置的內容作爲圖表的橫軸];
>`value`:圖表的數據範圍[表格中的範圍內的數據作爲畫圖的數據]
>`line`:圖標的線條屬性,包括顏色,寬度等。
>`name`:圖例項,[一個圖的多種內容的區分]
		- `set_x_axis(options)`:設置X軸選項,
			* 例子
			```python
            char.set_x_axis({
            	'name':	'Earning_per_!Quater',#設置X軸標題名字
                'name_font':	{'size':14, 'bold':True},#設置X軸標題字體
                'num_foont':	{'italic':True},#設置X軸數字字體
            })
	- `set_size(options)`:設置圖表大小。例如`chart.set_size({'width':720, 'height':576})`
	- `set_title`:設置圖表的標題。`chart.set_title({'name':'Year End Result'})`
	- `set_styke(style_id))':`style_id`爲不同數字代表不同的樣式。
	- `set_table(options)`:設置X軸爲數字表格樣式

實例:定製自動化業務流量報表

python與rrdtool結合

安裝rrdtool

pip install python-rrdtool

可能的錯誤:

  1. I found a copy of pkgconfig, but there is no libxml-2.0.pc file around. You may want to set the PKG_CONFIG_PATH variable to point to its location. 原因:沒有安裝libxml 解決方案:sudo apt-get install libxml2 sudo apt-get install libxml2-dev
  2. 出現cannot find -lrrd 原因:庫文件沒有導入到ld檢索目錄中,或者是庫文件是在so後面加上了序號,導致找不到庫文件 解決方案:只需要使用ln命令,將帶號碼的so文件軟鏈接到不帶序號的庫文件,如sudo ln -sv librrd.so.4 librrd.so

python與系統安全

病毒掃描

nmap端口掃描

在使用python-nmap前,需要在操作系統上安裝namp:yum install namp

  1. 模塊說明
  • PortScanner類【nmap端口掃描的封裝】
    • scan(self, host='127.0.0.1', ports=None, arguments='-sV'):制定掃描的主機端口,nmap命令行掃描的參數。其中host的參數形式可以是域名('scanne.nmap.org'),網段('192.168.0-255.1-127'或'192.168.128.20/20'),IP地址;ports表示掃描的端口,可以用22,53,110,143-4564來表示;arguments爲字符串類型,是nmap命令行下的掃描參數。
    • comman_line(self):返回掃描方法對應的命令行下面的nmap命令。
    • scaninfo(self):掃描的信息,字典類型
    • all_hosts(self):返回nmap掃描的主機清單,格式爲list
    • hostname(self):返回掃描對西那個的名字
    • state(self):返回掃描對象的狀態(主要包括up,down,unknow,skipped)
    • all_protocals(self):返回掃描協議
    • all_tcp(self):返回TCP協議端口
    • tcp(self,port):返回TCP協議port端口的信息
  • PortScannerHostDict類【存儲與訪問主機的掃描結果】
  1. 示例

#! /usr/bin/python
# -*- coding:utf-8 -*-

import nmap
import sys

__author__ = 'hgf'

def detailinfo(hosts, ports):
    '''
    使用nmap test hosts中的所有的ports,並輸出詳細的信息
    :param hosts: 所有主機
    :param ports:所有端口號
    :return:
    '''
    try:
        nm.scan(hosts = hosts, arguments='-v -sS -p'+ports)
    except Exception,e:
        print("Scan error:"+ str(e))

    for host in nm.all_hosts():
        print('-------------------------------------------------------------------------------------------------------')
        print('Host: %s (%s)'% (host, nm[hosts].hostname()))
        print('State: %s' % nm[host].state())
        for proto in nm[host].all_protocols():
            print('------------------------------------------------')
            print('Protocal: %s'% proto)

            iport = nm[host][proto].keys()
            iport.sort()
            for port in iport:
                print('Port: %s \tstate: %s' %(port, nm[host][proto][port]['state']))

def printcsv(nm):
    '''
    將nmap的結果以excel表的格式輸出
    :param nm: namp實例
    :return:
    '''
    print('-----------------------------------------------------------------------------------------------------------')
    print('print result as csv')
    print(nm.csv())

def pingsweep(hosts,ports):
    '''
    根據ports端口,查詢網段內存活主機
    :param hosts:網段
    :param ports:端口
    :return:
    '''
    nm.scan(hosts=hosts, arguments='-n -sP -PE -PA'+ports)
    host_list=[(x,nm[x]['status']['state']) for x in nm.all_hosts()]
    for host, state in host_list:
        print('{0}:{1}'.format(host,state))

def call_back(host, scan_result):
    print("----------------------------------")
    print host, scan_result
def arsynNmap(hosts):
    '''
    異步Nmap
    :param hosts:
    :return:
    '''
    nma = nmap.PortScannerAsync()
    nma.scan(hosts=hosts, arguments='-sP', callback=call_back)
    while nma.still_scanning():
        print('wait')
        nma.wait(2)
        # do some other things

scan_row=[]
input_data = raw_input("Please input hosts and ports:")
scan_row = input_data.split(' ')
if len(scan_row)!=2:
    print("input error, example input \"192.168.1.0/24 80,433-560\"")
    sys.exit(0)
hosts = scan_row[0]
ports = scan_row[1]

try:
    nm = nmap.PortScanner()
except nmap.PortScannerError:
    print("Nmap not found ", sys.exc_info()[0])
    sys.exit(0)
except:
    print("Unexcepted error!")
    sys.exit(0)

arsynNmap(hosts)

pexpect

純python編寫的系統批量運維管理器。

安裝

sudo pip install pexpect

pexpect使用

pexpect主要有兩大功能:

run方法

run方法主要替代原來的'os.system()'

spawn 類

spawn主要實現了自動交互的功能。

注意: spawn;類不會解析shell命令中的元字符,包括重定向>,管道|,或者是通配符*,但是我們可以使用給/bin/bash傳遞參數的形式使用元字符。 例如:pexpect.spawn('/bin/bash -c "ls -al | grep LOG > logs.txt"')

  • pexpect日誌輸出到文件:``` child = pexpect.spawn('some_command') fout = file('mylog.txt','w') child.logfile(fout)

* pexpect日誌輸出到標準輸出流:```
child = pexpect.spawn('some_command')
child.logfile = sys.stout

使用pexpect的注意事項

  1. 注意使用scp時,傳送文件的權限,當使用pexpect的命令中,包含用scp傳送權限比較高,或者傳送文件的所有者不是運行pexpect程序的人的時候,需要考慮處理文件權限的問題。特別是傳送系統配置文件時,就算root運行程序也不能傳送文件成功。
  2. 一般的使用步驟:
  • 保證目錄下有那個文件
  • 保證執行py的用戶有那個傳送文件的權限

paramiko

基於python實現的SSH2安全連接,支持認證及密鑰方式,可以實現遠程命令執行,文件傳輸,中間ssh代理等。

安裝paramiko

sudo pip install paramiko

paramiko的簡單test

#/usr/bin/python
# coding:utf-8
import paramiko
import sys

__author__='hgf'

try:
    data =  raw_input("Please input hostname username and password:").split()
    if len(data)!=3:
        print("input error! make sure your input like \"192.168.1.1 root 123456\"")
        sys.exit(0)
except Exception,e:
    print("Exception occourred!")
    print (str(e))
hostname = data[0]
username = data[1]
password = data[2]

paramiko.util.log_to_file('sys.log')

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()

ssh.connect(hostname = hostname, username = username, password = password)
stdin, stdout, stderr = ssh.exec_command('free -m')
print stdout.read()
ssh.close()

paramiko的核心組件

  1. SSHClient
  • connect方法

    • connect(self, hostname, port=22, username=None, password=None, pkey=None, key_filename=None, timeout=None, allow=True, look_for_keys=True. compress=False)
    • 各個參數的含義:
      • hostname:(str類型)目的主機
      • port:(int類型)端口號
      • username:(str類型)目的主機的用戶名(默認與當前的本地主機用戶名相同)
      • password:(str類型)密碼用於身份校驗或解鎖私鑰
      • pkey:(PKey類型)私鑰方式進行驗證
      • key_filename:(list(str))或者是str類型)一個文件名或是文件列表,用於私鑰的身分驗證
      • timeout:(int類型)設定鏈接的超時信息
      • allow_agent:(bool類型)設置爲False時,禁止連接到SSH代理
      • look_for_keys:(bool類型)False時禁止在~/.ssh目錄中搜索私鑰文件
      • compress:(bool類型) 設置爲True打開壓縮
  • exec_command方法:遠程執行命令的方法,輸入輸出流爲標準的的輸入輸出流,錯誤流(stdin,stdout,stderr)

    • exec_command(self, command, bufsize=-1)
    • command:(str類型),執行的命令
  • load_system_host_keys方法,指定遠程主機的公鑰記錄文件(默認爲~/.ssh/known_hosts

    • load_system_host_keys(self, filename=None)
  • set_missing_host_key_policy方法:設置遠程主機沒有本地主機密鑰或Hostskye的情況時的策略,目前支持3種。

    • AutoAddPolicy,自動添加主機名及主機密鑰到本地hostsKeys對象(具體時那個文件????),並保存。不依賴於load_system_host_keys()的配置。
    • RejectPolicy,自動拒絕未知主機名和密鑰, 依賴load_system_host_keys的配置。
    • WarningPolicy,用於記錄一個位置的主機名和密鑰的python警告,並接受。功能上與AutoAddPolicy相似但是未知主機會有警告。

    使用方法:

    ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

說明: 可以通過SSHClient對象的invoke_shell方法獲取paramikoChannel,開啓命令調用.即使用Channelsend()方法,可以發送一系列的shell命令。例如:

ssh = paramiko.SSHClient() ssh.connect(hostname,username,password) channel = ssh.invoke_shell() channel.send('ls')

  1. SFTPClient
  • from_transport方法:創建愛哪一個已經連通的SFTP客戶端通道

    • from_transport(self, t)
    • 參數t表和isyige已經驗證過的傳輸對象(paramiko.Transport對象)
    • 例子:```python t = paramiko.Transport(('192.168.1.22',22)) t.connect(username='root', password = '123456') sftp = paramiko.SFTPClient.from_transport(t)
  • put方法:上傳本地文件到遠程SFTP服務端

    • put(self, localpath, remotepath, callback=None, confirm=True)
    • 參數說明:
      • localpath: (str類型)需要上傳的本地文件
      • remotepath:(str類型)遠程路徑
      • callback(function (int, int)):獲取已經接收的字節數及宗的傳輸字節數, 以便毀掉函數調用
      • confirm:文件上傳完畢後是否使用stat()方法,以便確認文件的大小

    注意: remotepath必須是文件路徑+文件名+文件擴展名的完整形式,不能只指定文件夾的名字

    • 例子:
    localpath = '/home/access.log'
    remotepath = '/data/logs/access.log'
    sftp.put(localpath, remotepath)
    
  • get方法:從遠程sftp服務端下載文件到本地 + get(self, remotepath, localpath, callback=None) + 參數說明: - remotepath:(str類型)遠程文件路徑 - localpath: (str類型)本地保存路徑 - callback(function (int, int)):獲取已經接收的字節數及宗的傳輸字節數, 以便毀掉函數調用 >注意: >localpath必須是文件路徑+文件名+文件擴展名的完整形式,不能只指定文件夾的名字 + 例子: python localpath = '/home/access.log' remotepath = '/data/logs/access.log' sftp.put(localpath, remotepath)

  • mkdir:在遠程服務器上創建文件夾

  • remove:刪除SFTP服務端指定的目錄

  • rename:重命名SFTP服務端的文件或目錄

  • stat:獲取SFTP服務端指定文件的信息

  • listdir:獲取SFTP服務端指定的目錄列表,以python list的形式返回。

      #! /usr/bin/python
      import paramiko
    
      __author__ = 'hgf'
    
      username = "root"
      password = "1qaz2wsx"
      hostname = "10.109.33.163"
      port = 22
      paramiko.util.log_to_file('log.txt')
      try:
          t = paramiko.Transport((hostname, port))
          t.connect(username=username, password=password)
          sftp = paramiko.SFTPClient.from_transport(t)
          sftp.put('/home/hgf/authorized_keys', '/root/aaaaa',)
          sftp.get('/root/install.log', './server.log')
          sftp.mkdir('/root/test', '700')
          print(sftp.stat('/root/install.log'))
          sftp.rmdir('/root/a')
          print(sftp.listdir('/home'))
          t.close()
      except Exception, e:
          print "Error!"
          print(str(e))
    

Fabric

Fabricparamiko的基礎上,做了更高一層的封裝

安裝

pip install fabric

全局屬性的設置

與全局屬性有關的是env對象。

常用API

例子1

查看本地與遠程主機信息

例子2

動態獲取遠程目錄列表

例子3

網關模式文件的上傳

編寫webserver

python處理命令行提示

python主要是使用optparse處理命令行的提示功能,並且響應命令行的輸入參數。 例子:

MSG_USAGE = "program [-v] [-h]"
parser = OptionParser(MSG_USAGE)
parser.add_option("-v", "--version", action="store_true", dest="verbose", help="描述命令參數的用途")
opts, args = parser.parse_args()

if opts.verbose:
  # 執行滿足參數-v後的運行程序
  print "輸出系統的版本"
  sys.exit()

python處理配置文件

python主要是使用configobj庫來處理配置文件。 詳細的使用請看《python配置文件configobj使用》

BaseHTTPServer.py源碼分析

BaseHTTPServer.py源碼分析主要是方便理解如何自己編寫webserver。

SocketServer.py分析中可以知道其設計思想是將socket編程的監聽循環和客戶端處理劃分成Server類和RequestHandler類,而BaseHTTPServer是基於SocketServer基礎之上的,因此可以知道BaseHTTPServer就是分別擴展Server類和RequestHandler類。BaseHTTPServer實現了一個簡單的HTTP Server,可以知道主要工作應該是擴展RequestHandler的功能,處理客戶端的HTTP請求,具體包括HTTP協議解析,給客戶端返回HTTP響應,日誌記錄等功能。

  1. HTTPServer

該類只是簡單包裝了SocketServer中的TCPServer

  1. BaseHTTPRequestHandler

類定義: class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):

擴展RequestHandler需要覆蓋一個接口handler(),定義如下:

def handle(self):
    """Handle multiple requests if necessary."""
    self.close_connection = 1
    self.handle_one_request()
    while not self.close_connection:
        self.handle_one_request()

主要處理的是HTTP是否保持連接的問題,如果保持連接就持續處理客戶請求,否則就結束了。

handle_one_request()比較重要語句:

self.raw_requestline = self.rfile.readline(65537)
self.parse_request()
mname = 'do_' + self.command
if not hasattr(self, mname)
    self.send_error(501, "Unsupported method (%r)" % self.command)
    return
method = getattr(self, mname)
method()
self.wfile.flush() #actually send the response if not already done.
  • 讀取第一行raw_requestline,一般格式應該是:COMMAND PATH VERSION\r\n
  • 解析請求,parse_request()主要的代碼都在處理raw_requestline,最終得到self.command, self.path, self.request_version幾個變量,然後就是利用mimetools.Message解析頭部。
  • 後面幾行代碼意思是根據HTTP方法獲取對應處理函數,其實就是根據GET或POST請求調用對應的do_GET()do_POST()方法,然後刷新輸出。

下面還有幾個的函數,功能都比較簡單:

def send_error(self, code, message=None):
def send_response(self, code, message=None):
def send_header(self, keyword, value):
def end_headers(self):
def log_request(self, code='-', size='-'):
def log_error(self, format, *args):
def log_message(self, format, *args):

總結:可以看到,BaseHTTPRequestHandler主要實現了客戶端HTTP請求解析,以及一些輔助功能,如日誌記錄、錯誤處理、發送響應代碼等。 但是還沒有實現如何執行HTTP請求,簡單來說就是還需要實現諸如do_GET(),do_POST()等函數,具體執行對應的HTTP命令。

HTTP協議返狀態碼含義

  1. 1xx 狀態碼
表示臨時響應並需要請求者繼續執行操作的狀態碼。

* `100(繼續)` 請求者應當繼續提出請求。服務器返回此代碼表示已收到請求的第一部分,正在等待其餘部分。
* `101(切換協議)` 請求者已要求服務器切換協議,服務器已確認並準備切換。
  1. 2xx 狀態碼
表示成功處理了請求的狀態碼。

* `200(成功)` 服務器已成功處理了請求。通常,這表示服務器提供了請求的網頁。如果針對您的 robots.txt 文件顯示此狀態碼,則表示 Googlebot 已成功檢索到該文件。
* `201(已創建)` 請求成功並且服務器創建了新的資源。
* `202(已接受)` 服務器已接受請求,但尚未處理。
* `203(非授權信息)` 服務器已成功處理了請求,但返回的信息可能來自另一來源。
* `204(無內容)` 服務器成功處理了請求,但沒有返回任何內容。
* `205(重置內容)` 服務器成功處理了請求,但沒有返回任何內容。與 204 響應不同,此響應要求請求者重置文檔視圖(例如,清除表單內容以輸入新內容)。
* `206(部分內容)` 服務器成功處理了部分 GET 請求。
  1. 3xx 狀態碼
要完成請求,需要進一步操作。通常,這些**狀態碼用來重定向**。建議您在每次請求中使用重定向不要超過 5 次。您可以使用網站管理員工具查看一下 Googlebot 在抓取重定向網頁時是否遇到問題。診斷下的網絡抓取頁列出了由於重定向錯誤導致 Googlebot 無法抓取的網址。

* `300(多種選擇)` 針對請求,服務器可執行多種操作。服務器可根據請求者 (`user-agent`) 選擇一項操作,或提供操作列表供請求者選擇。
* `301(永久移動)` 請求的網頁已永久移動到新位置。服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。您應使用此代碼告訴 Googlebot 某個網頁或網站已永久移動到新位置。
* `302(臨時移動)` 服務器目前從不同位置的網頁響應請求,但申請人應當繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的 301 代碼類似,會自動將請求者轉到不同的位置,但不應使用此代碼來告訴 Googlebot 頁面或網站已經移動,因爲 Googlebot 要繼續抓取原來的位置並編制索引。
* `303(查看其他位置)` 請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。對於除 HEAD 之外的所有請求,服務器會自動轉到其他位置。
* `304(未修改)` 自從上次請求後,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。如果網頁自請求者上次請求後再也沒有更改過,您應當將服務器配置爲返回此響應(稱爲 `If-Modified-Since HTTP` 標頭)。由於服務器可以告訴 Googlebot 自從上次抓取後網頁沒有變更,因此可節省帶寬和開銷。
* `305(使用代理)` 請求者只能使用代理訪問請求的網頁。如果服務器返回此響應,還表示請求者應當使用代理。
* `307(臨時重定向)` 服務器目前從不同位置的網頁響應請求,但請求者應當繼續使用原有位置來響應以後的請求。此代碼與響應 GET 和 HEAD 請求的 301 代碼類似,會自動將請求者轉到不同的位置,但您不應使用此代碼來告訴 Googlebot 某個網頁或網站已經移動,因爲 Googlebot 會繼續抓取原有位置並編制索引。
  1. 4xx 狀態碼
這些狀態碼錶示請求可能出錯,這妨礙了服務器的處理。

* `400(錯誤請求)` 服務器不理解請求的語法。
* `401(身份驗證錯誤)` 此頁要求授權。您可能不希望將此網頁納入索引。如果您的 Sitemap 中列出該網頁,您可以將其刪除。但如果您將其保留在您的 Sitemap 中,我們就不會抓取或索引該網頁(儘管該網頁將繼續保持錯誤狀態在此處列出)。如果我們將其作爲搜索抓取的一部分抓取,您可以在我們的網站管理員信息中查閱其原因。
* `403(禁止)` 服務器拒絕請求。如果您在 Googlebot 抓取您網站上的有效網頁時看到此狀態碼,可能是您的服務器或主機拒絕 Googlebot 訪問。
* `404(未找到)` 服務器找不到請求的網頁。例如,對於服務器上不存在的網頁經常會返回此代碼。
* `405(方法禁用)` 禁用請求中指定的方法。
* `406(不接受)` 無法使用請求的內容特性響應請求的網頁。
* `407(需要代理授權)` 此狀態碼與 401 類似,但指定請求者必須授權使用代理。如果服務器返回此響應,還表示請求者應當使用代理。
* `408(請求超時)` 服務器等候請求時發生超時。
* `409(衝突)` 服務器在完成請求時發生衝突。服務器必須在響應中包含有關衝突的信息。服務器在響應與前一個請求相沖突的 PUT 請求時可能會返回此代碼,以及兩個請求的差異列表。
* `410(已刪除)` 請求的資源永久刪除後,服務器返回此響應。該代碼與 404(未找到)代碼相似,但在資源以前存在而現在不存在的情況下,有時會用來替代 404 代碼。如果資源已永久刪除,您應當使用 301 指定資源的新位置。
* `411(需要有效長度)` 服務器不接受不含有效內容長度標頭字段的請求。
* `412(未滿足前提條件)` 服務器未滿足請求者在請求中設置的其中一個前提條件。
* `413(請求實體過大)` 服務器無法處理請求,因爲請求實體過大,超出服務器的處理能力。
* `414(請求的 URI 過長)` 請求的 URI(通常爲網址)過長,服務器無法處理。
* `415(不支持的媒體類型)` 請求的格式不受請求頁面的支持。
* `416(請求範圍不符合要求)` 如果頁面無法提供請求的範圍,則服務器會返回此狀態碼。
* `417(未滿足期望值)` 服務器未滿足"期望"請求標頭字段的要求。
  1. 5xx 狀態碼
這些狀態碼錶示服務器在處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。

* `500(服務器內部錯誤)` 服務器遇到錯誤,無法完成請求。
* `501(尚未實施)` 服務器不具備完成請求的功能。例如,服務器無法識別請求方法時則會返回此代碼。
* `502(錯誤網關)` 服務器作爲網關或代理,從上游服務器收到無效響應。
* `503(服務不可用)` 服務器目前無法使用(由於超載或停機維護)。通常,這只是暫時狀態。
* `504(網關超時)` 服務器作爲網關或代理,但是沒有及時從上游服務器收到請求。
*  `505(HTTP 版本不受支持)` 服務器不支持請求中所用的 HTTP 協議版本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章