使用Python腳本操作快照
上一篇中介紹了KVM虛擬機各種快照的原理和命令行操作方法,由於磁盤外部快照最實用,所以本篇主要講怎麼利用Libvirt api操作磁盤外部快照。其中會涉及一些Libvirt api的基本用法,也會一起介紹。
操作環境
環境同上篇。Python與libvirt服務交互用的是libvirt模塊;操作虛擬機的XML描述文件用的是xml.dom模塊。
創建快照
我們要完成的功能是,給出一個虛擬機的名稱,創建這個虛擬機的磁盤快照。
首先建立與libvirt服務的連接,然後根據虛擬機名稱獲取該domain對象:
conn = libvirt.open("qemu:///system") dom = conn.lookupByName('vm') |
domain對象的方法snapshotCreateXML()實現了通過一個XML描述文件創建快照的功能,該方法接收的參數是一個描述快照的XML字符串(不是文件)和標誌位flags。快照的XML描述文件一般是下面這種格式:
<domainsnapshot> <name>snapshot01</name> <description>test api</description> <disks> <disk name='/path/diskname'> </disk> <disk name='/path/diskname'> </disk> </disks> </domainsnapshot> |
可以看出,構建快照的XML描述文件需要首先獲取到虛擬機的磁盤文件名,獲取方法是讀取並解析虛擬機的xml文件:
xml = dom.XMLDesc(0) doc = minidom.parseString(xml) disks = doc.getElementsByTagName('disk') for disk in disks: if disk.getAttribute('device') == 'disk': diskfile = disk.getElementsByTagName('source')[0].getAttribute('file') print diskfile |
這段代碼的輸出結果是:
[root@localhost snapshot]# python test.py /data/vm.img /data/data.img |
然後把磁盤文件的名字填到快照xml裏,存放在文件snapshot01.xml中:
<domainsnapshot> <name>snapshot01</name> <description>test api</description> <disks> <disk name='/data/vm.img'> </disk> <disk name='/data/data.img'> </disk> </disks> </domainsnapshot> |
另一個參數是標誌位flags,Libvirt定義了一系列標誌位控制創建快照的行爲,每一位的作用可以通過查看Libvirt官方文檔得知。Libvirt官方文檔只有C語言的api文檔,Python api的用法基本跟C語言的一致,所以不影響我們參考。標誌位的取值及含義如下:
#Restore or alter metadata VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE = 1 #With redefine, make snapshot current VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT = 2 #Make snapshot without remembering it VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA = 4 #Stop running guest after snapshot VIR_DOMAIN_SNAPSHOT_CREATE_HALT = 8 #disk snapshot, not system checkpoint VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY = 16 #reuse any existing external files VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT = 32 #use guest agent to quiesce all mounted file systems within the domain VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE = 64 #atomically avoid partial changes VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC = 128 #create the snapshot while the guest is running VIR_DOMAIN_SNAPSHOT_CREATE_LIVE = 256 |
我們需要用到的標誌有VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA,VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY和VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC,分別對應virsh snapshot-create-as命令的參數--disk-only,--no-metadata和--atmotic。多個標誌位疊加是通過二進制按位或操作,換算成十進制下的操作就是相加,所以flags的值爲4+16+128=148。
snapshotXML = open('snapshot01.xml','rb').read() dom.snapshotCreateXML(snapshotXML,flags=148) |
腳本內容彙總如下:
#!/usr/bin/python import libvirt import sys from xml.dom import minidom
def getDom(vm): try: conn = libvirt.open("qemu:///system") dom = conn.lookupByName(vm) return dom except Exception,e: print "Get domain object of vm %s failed: %s" % (vm,str(e)) sys.exit(1)
def getDiskfile(vm): dom = getDom(vm) xml = dom.XMLDesc(0) doc = minidom.parseString(xml) disks = doc.getElementsByTagName('disk') diskfiles = [] for disk in disks: if disk.getAttribute('device') == 'disk': diskfile = disk.getElementsByTagName('source')[0].getAttribute('file') diskfiles.append(diskfile) return diskfiles
def createXML(vm): diskfiles = getDiskfile(vm) xml = """<domainsnapshot> <name>snapshot01</name> <description>test api</description> <disks> <disk name='%s'> </disk> <disk name='%s'> </disk> </disks> </domainsnapshot>""" % (diskfiles[0],diskfiles[1]) with open('snapshot01.xml','w') as f: f.write(xml)
def createSnapshot(vm): dom = getDom(vm) snapshotXML = open('snapshot01.xml','rb').read() dom.snapshotCreateXML(snapshotXML,flags=148)
if __name__ == "__main__": createXML('vm') createSnapshot('vm') |
合併快照文件
首先爲虛擬機創建4個快照,現在磁盤文件形成了如下back chain(原理見上篇文章):
base<-snapshot01<-snapshot02<-snapshot03<-snapshot04*
我們要通過api把snapshot03合併到snapshot02,用到的方法是blockCommit(),該方法有3個必須提供的參數disk,base和top,分別對應virsh blockcommit命令的參數--path,--base和--top。把命令virsh blockcommit --domain vm –path vda --base /data/vm.snapshot02 --top /data/vm.snapshot03翻譯成Python代碼就是:
dom.blockCommit('vda','/data/vm.snapshot02','/data/vm.snapshot03') |
合併快照文件可能需要很長時間,但是blockCommit是異步的,執行完立即返回,如果我們想查看後臺的這個合併任務,需要用blockJobInfo()方法查看合併任務是否已完成。
腳本內容彙總如下:
#!/usr/bin/python import libvirt import sys
def getDom(vm): try: conn = libvirt.open("qemu:///system") dom = conn.lookupByName(vm) return dom except Exception,e: print "Get domain object of vm %s failed: %s" % (vm,str(e)) sys.exit(1)
if __name__ == "__main__": dom = getDom('vm') dom.blockCommit('vda','/data/vm.snapshot02','/data/vm.snapshot03') dom.blockCommit('vdb','/data/data.snapshot02','/data/data.snapshot03') |