本章節主要講解運維工程師比較感興趣的知識,那就是運維批量管理,在Python下有paramiko、fabric和pexpect這三個模塊可幫助運維實現自動化部署、批量執行命令、文件傳輸等常規任務,接下來一塊兒看看它們的使用方法吧!python
18.1 paramikomysql
paramiko模塊是基於Python實現的SSH遠程安全鏈接,用於SSH遠程執行命令、文件傳輸等功能。web
默認Python沒有,須要手動安裝:pip install paramiko正則表達式
如安裝失敗,能夠嘗試yum安裝:yum install python-paramikosql
18.1.1 SSH密碼認證遠程執行命令shell
#!/usr/bin/python # -*- coding: utf-8 -*- import paramiko import sys hostname = '192.168.1.215' port = 22 username = 'root' password = '123456' client = paramiko.SSHClient() # 綁定實例 client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(hostname, port, username, password, timeout=5) stdin, stdout, stderr = client.exec_command('df -h') # 執行bash命令 result = stdout.read() error = stderr.read() # 判斷stderr輸出是否爲空,爲空則打印執行結果,不爲空打印報錯信息 if not error: print result else: print error client.close()
18.1.2 私鑰認證遠程執行命令windows
#!/usr/bin/python # -*- coding: utf-8 -*- import paramiko import sys hostname = '192.168.1.215' port = 22 username = 'root' key_file = '/root/.ssh/id_rsa' cmd = " ".join(sys.argv[1:]) def ssh_conn(command): client = paramiko.SSHClient() key = paramiko.RSAKey.from_private_key_file(key_file) client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(hostname, port, username, pkey=key) stdin, stdout, stderr = client.exec_command(command) # 標準輸入,標準輸出,錯誤輸出 result = stdout.read() error = stderr.read() if not error: print result else: print error client.close() if __name__ == "__main__": ssh_conn(cmd)
18.1.3 上傳文件到遠程服務器api
#!/usr/bin/python # -*- coding: utf-8 -*- import os, sys import paramiko hostname = '192.168.1.215' port = 22 username = 'root' password = '123456' local_path = '/root/test.txt' remote_path = '/opt/test.txt' if not os.path.isfile(local_path): print local_path + " file not exist!" sys.exit(1) try: s = paramiko.Transport((hostname, port)) s.connect(username = username, password=password) except Exception as e: print e sys.exit(1) sftp = paramiko.SFTPClient.from_transport(s) # 使用put()方法把本地文件上傳到遠程服務器 sftp.put(local_path, remote_path) # 簡單測試是否上傳成功 try: # 若是遠程主機有這個文件則返回一個對象,不然拋出異常 sftp.file(remote_path) print "上傳成功." except IOError: print "上傳失敗!" finally: s.close()
18.1.4 從遠程服務器下載文件安全
#!/usr/bin/python # -*- coding: utf-8 -*- import os, sys import paramiko hostname = '192.168.1.215' port = 22 username = 'root' password = '123456' local_path = '/root/test.txt' remote_path = '/opt/test.txt' try: s = paramiko.Transport((hostname, port)) s.connect(username=username, password=password) sftp = paramiko.SFTPClient.from_transport(s) except Exception as e: print e sys.exit(1) try: # 判斷遠程服務器是否有這個文件 sftp.file(remote_path) # 使用get()方法從遠程服務器拉去文件 sftp.get(remote_path, local_path) except IOError as e: print remote_path + "remote file not exist!" sys.exit(1) finally: s.close() # 測試是否下載成功 if os.path.isfile(local_path): print "下載成功." else: print "下載失敗!"
18.1.5 上傳目錄到遠程服務器bash
paramiko模塊並無實現直接上傳目錄的類,已經知道了如何上傳文件,再寫一個上傳目錄的代碼就簡單了,利用os庫的os.walk()方法遍歷目錄,再一個個上傳:
#!/usr/bin/python # -*- coding: utf-8 -*- import os, sys import paramiko hostname = '192.168.1.215' port = 22 username = 'root' password = '123456' local_path = '/root/abc' remote_path = '/opt/abc' # 去除路徑後面正斜槓 if local_path[-1] == '/': local_path = local_path[0:-1] if remote_path[-1] == '/': remote_path = remote_path[0:-1] file_list = [] if os.path.isdir(local_path): for root, dirs, files in os.walk(local_path): for file in files: # 獲取文件絕對路徑 file_path = os.path.join(root, file) file_list.append(file_path) else: print path + "Directory not exist!" sys.exit(1) try: s = paramiko.Transport((hostname, port)) s.connect(username=username, password=password) sftp = paramiko.SFTPClient.from_transport(s) except Exception as e: print e for local_file in file_list: # 替換目標目錄 remote_file = local_file.replace(local_path, remote_path) remote_dir = os.path.dirname(remote_file) # 若是遠程服務器沒目標目錄則建立 try: sftp.stat(remote_dir) except IOError: sftp.mkdir(remote_dir) print "%s -> %s" % (local_file, remote_file) sftp.put(local_file, remote_file) s.close()
sftp是安全文件傳輸協議,提供一種安全的加密方法,sftp是SSH的一部分,SFTPClient類實現了sftp客戶端,經過已創建的SSH通道傳輸文件,與其餘的操做,以下:
sftp.getcwd() | 返回當前工做目錄 |
sftp.chdir(path) | 改變工做目錄 |
sftp.chmod(path, mode) | 修改權限 |
sftp.chown(path, uid, gid) | 設置屬主屬組 |
sftp.close() | 關閉sftp |
sftp.file(filename, mode='r', bufsize=-1) | 讀取文件 |
sftp.from_transport(s) | 建立SFTP客戶端通道 |
sftp.listdir(path='.') | 列出目錄,返回一個列表 |
sftp.listdir_attr(path='.') | 列出目錄,返回一個SFTPAttributes列表 |
sftp.mkdir(path, mode=511) | 建立目錄 |
sftp.normalize(path) | 返回規範化path |
sftp.open(filename, mode='r', bufsize=-1) | 在遠程服務器打開文件 |
sftp.put(localpath, remotepath, callback=None) | localpath文件上傳到遠程服務器remotepath |
sftp.get(remotepath, localpath, callback=None) | 從遠程服務器remotepath拉文件到本地localpath |
sftp.readlink(path) | 返回一個符號連接目標 |
sftp.remove(path) | 刪除文件 |
sftp.rename(oldpath, newpath) | 重命名文件或目錄 |
sftp.rmdir(path) | 刪除目錄 |
sftp.stat(path) | 返回遠程服務器文件信息(返回一個對象的屬性) |
sftp.truncate(path, size) | 截取文件大小 |
sftp.symlink(source, dest) | 建立一個軟連接(快捷方式) |
sftp.unlink(path) | 刪除軟連接 |
博客地址:http://lizhenliang.blog.51cto.com
QQ羣:323779636(Shell/Python運維開發羣)
18.2 fabric
fabric模塊是在paramiko基礎上又作了一層封裝,操做起來更方便。主要用於多臺主機批量執行任務。
默認Python沒有,須要手動安裝:pip install fabric
如安裝失敗,能夠嘗試yum安裝:yum install fabric
Fabric經常使用API:
API類 |
描述 |
示例 |
local | 執行本地命令 | local('uname -s') |
lcd | 切換本地目錄 | lcd('/opt') |
run | 執行遠程命令 | run('uname -s') |
cd | 切換遠程目錄 | cd('/opt') |
sudo | sudo方式執行遠程命令 | sudo('/etc/init.d/httpd start') |
put | 上傳本地文件或目錄到遠程主機 | put(remote_path, local_path) |
get | 從遠程主機下載文件或目錄到本地 | put(local_path, remote_path) |
open_shell | 打開一個shell,相似於SSH鏈接到了遠程主機 | open_shell("ifconfig eth0") |
prompt | 得到用戶輸入信息 | prompt('Please input user password: ') |
confirm | 得到提示信息確認 | confirm('Continue[Y/N]?') |
reboot | 重啓遠程主機 | reboot() |
@task | 函數裝飾器,引用說明函數可調用,不然不可見 | |
@runs_once | 函數裝飾器,函數只會執行一次 |
當咱們寫好fabric腳本後,須要用fab命令調用執行任務。
命令格式:fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...
fab命令有如下經常使用選項:
選項 |
描述 |
-l | 打印可用的命令(函數) |
--set=KEY=VALUE,... | 逗號分隔,設置環境變量 |
--shortlist | 簡短打印可用命令 |
-c PATH | 指定本地配置文件 |
-D | 不加載用戶known_hosts文件 |
-f PATH | 指定fabfile文件 |
-g HOST | 逗號分隔要操做的主機 |
-i PATH | 指定私鑰文件 |
-k | 不加載來自~/.ssh下的私鑰文件 |
-p PASSWORD | 使用密碼認證and/or sudo |
-P | 默認爲並行執行方法 |
--port=PORT | 指定SSH鏈接端口 |
-R ROLES | 根據角色操做,逗號分隔 |
-s SHELL | 指定新shell,默認是'/bin/bash -l -c' |
--show=LEVELS | 以逗號分隔的輸出 |
--ssh-config-path=PATH | SSH配置文件路徑 |
-t N | 設置鏈接超時時間,單位秒 |
-T N | 設置遠程命令超時時間,單位秒 |
-u USER | 鏈接遠程主機用戶名 |
-x HOSTS | 以逗號分隔排除主機 |
-z INT | 併發進程數 |
18.2.1 本地執行命令
from fabric.api import local def command(): local('ls') # fab command [localhost] local: ls fabfile.py fabfile.pyc tab.py tab.pyc Done.
使用fab命令調用,默認尋找當前目錄的fabfile.py文件。
18.2.2 遠程執行命令
from fabric.api import run def command(): run('ls') # fab -H 192.168.1.120 -u user command [192.168.1.120] Executing task 'command' [192.168.1.120] run: ls [192.168.1.120] Login password for 'user': [192.168.1.120] out: access.log a.py [192.168.1.120] out: Done. Disconnecting from 192.168.1.120... done.
若是在多臺主機執行,只須要-H後面的IP以逗號分隔便可。
18.2.3 給腳本函數傳入位置參數
from fabric.api import run def hello(name="world"): print("Hello %s!" % name) # fab -H localhost hello [localhost] Executing task 'hello' Hello world! Done. # fab -H localhost hello:name=Python [localhost] Executing task 'hello' Hello Python! Done.
18.2.4 主機列表組
from fabric.api import run, env env.hosts = ['root@192.168.1.120:22', 'root@192.168.1.130:22'] env.password = '123.com' env.exclude_hosts = ['root@192.168.1.120:22'] # 排除主機 def command(): run('ls')
env做用是定義fabfile全局設定,相似於變量。還有一些經常使用的屬性:
env屬性 |
描述 |
示例 |
env.hosts | 定義目標主機 | env.hosts = ['192.168.1.120:22'] |
env.exclude_hosts | 排除指定主機 | env.exclude_hosts = '[192.168.1.1]' |
env.user | 定義用戶名 | env.user='root' |
env.port | 定義端口 | env.port='22' |
env.password | 定義密碼 | env.password='123' |
env.passwords | 定義多個密碼,不一樣主機對應不一樣密碼 | env.passwords = {'root@192.168.1.120:22': '123'} |
env.gateway | 定義網關 | env.gateway='192.168.1.2' |
env.roledefs | 定義角色分組 | env.roledef = {'web':['192.168.1.11'], 'db':['192.168.1.12']} |
env.deploy_release_dir | 自定義全局變量,格式:env.+ '變量名' | env.var |
18.2.5 定義角色分組
# vi install.py from fabric.api import run, env env.roledefs = { 'web': ['192.168.1.10', '192.168.1.20'], 'db': ['192.168.1.30', '192.168.1.40'] } env.password = '123' @roles('web') def task1(): run('yum install httpd -y') @roles('db') def task2(): run('yum install mysql-server -y') def deploy(): execute(task1) execute(task2) # fab -f install.py deploy
18.2.6 上傳目錄到遠程主機
from fabric.api import * env.hosts = ['192.168.1.120'] env.user = 'user' env.password = '123.com' def task(): put('/root/abc', '/home/user') run('ls -l /home/user') # fab task
18.2.7 從遠程主機下載目錄
from fabric.api import * env.hosts = ['192.168.1.120'] env.user = 'user' env.password = '123.com' def task(): get('/home/user/b', '/opt') local('ls -l /opt') # fab task
18.2.8 打印顏色,有助於關鍵地方醒目
from fabric.colors import * def show(): print green('Successful.') print red('Failure!') print yellow('Warning.') # fab show
通過上面演示fabric主要相關功能,是否是以爲很適合批量自動部署呢!沒錯,經過編寫簡單的腳本,便可完成複雜的操做。
博客地址:http://lizhenliang.blog.51cto.com
QQ羣:323779636(Shell/Python運維開發羣)
18.3 pexpect
pexpect是一個用來啓動子程序,並使用正則表達式對程序輸出作出特定響應,以此實現與其自動交互的Python模塊。暫不支持Windows下的Python環境執行。
這裏主要講解run()函數和spawn()類,能完成自動交互,下面簡單瞭解下它們使用。
18.3.1 run()
run()函數用來運行bash命令,相似於os模塊中的system()函數。
參數:run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)
例1:執行ls命令 >>> import pexpect >>> pexpect.run("ls") 例2:得到命令狀態返回值 >>> command_output, exitstatus = pexpect.run("ls", withexitstatus=1)
command_outout是執行結果,exitstatus是退出狀態值。
18.3.2 spawn()
spawn()是pexpect模塊主要的類,實現啓動子程序,使用pty.fork()生成子進程,並調用exec()系列函數執行命令。
參數:spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)
spawn()類幾個經常使用函數:
expect(pattern, timeout=-1, searchwindowsize=None) | 匹配正則表達式,pattern能夠是正則表達式。 |
send(s) | 給子進程發送一個字符串 |
sendline(s='') | 就像send(),但添加了一個換行符(os.lineseq) |
sendcontrol(char) | 發送一個控制符,好比ctrl-c、ctrl-d |
例子:ftp交互
用ftp命令登陸是這樣的,須要手動輸入用戶名和密碼,才能登陸進去。
# ftp 192.168.1.10 Connected to 192.168.1.10 (192.168.1.10). 220-FileZilla Server version 0.9.46 beta 220-written by Tim Kosse (tim.kosse@filezilla-project.org) 220 Please visit http://sourceforge.net/projects/filezilla/ Name (192.168.1.10:root): yunwei 331 Password required for yunwei Password: 230 Logged on Remote system type is UNIX. ftp>
下面咱們用pexpect幫咱們完成輸入用戶名和密碼:
import pexpect child = pexpect.spawn('ftp 192.168.1.10') child.expect('Name .*: ') child.sendline('yunwei') child.expect('Password:') child.sendline('yunweipass') child.expect('ftp> ') child.sendline('ls') child.sendline('bye') child.expect(pexpect.EOF) # pexpect.EOF程序打印提示信息 print child.before # 保存命令執行結果
手動輸入時,是來自鍵盤的標準輸入,而pexpect是先匹配到關鍵字,再向子進程發送字符串。
pexpect.EOF打印提示信息,child.before保存的是命令執行結果。
經過上面的例子想必你已經知道pexpect主要功能了,在交互場景下頗有用,這裏就講解這麼多了,目的是給你們提供一個自動交互實現思路。
小結:
經過對Python下paramiko、fabric和pexpect模塊使用,它們各有本身擅長的一面。
paramiko:方便嵌套系統平臺中,擅長遠程執行命令,文件傳輸。
fabric:方便與shell腳本結合,擅長批量部署,任務管理。
pexpect:擅長自動交互,好比ssh、ftp、telnet。