这两天一直计划写一个通过模板img和xml文件实现KVM的快速部署的脚本 ,实现原理非常简单,就是通过复制img镜像到指定位置,并修改xml里的相关配置后,define guest主机,最后start即可。后来在网上也找了两个实现类似功能的脚本 ,一个是shell版的,一个是python版的。最终我这里结合了两者之间的一些特点在两者之间上做了一点整合和优化,出了一个增强版本,这里分享下,回头有时间再一并上传到github上去。

一、python脚本内容

如下:

  1#!/usr/bin/env python
  2###########################################
  3#Version 2.0
  4#order by www.361way.com
  5#Function Description:
  6#Batch automatically generated/delete VM
  7#1.Generated VM
  8##1.1.Copy VM img file
  9##1.2.Create VM XML file
 10###1.2.1.Update UUID
 11###1.2.2.Update MAC
 12###1.2.3.Update img path
 13###1.2.4.Update VM Name
 14#2.Start VM
 15#3.Delete VM
 16####################################################################
 17#import module
 18import shutil
 19import os,sys
 20from virtinst.util import *
 21import libvirt
 22import re
 23if sys.version_info < (2,5):
 24        import lxml.etree as ET
 25else:
 26        try:
 27              import xml.etree.cElementTree as ET
 28        except ImportError:
 29              import xml.etree.ElementTree as ET
 30#Define variables
 31template_img_path="/template/img"
 32template_xml_path="/template/xml"
 33vm_img_path="/file"
 34vm_xml_path="/etc/libvirt/qemu"
 35vm_file="/template/conf/newvm.ini"
 36uri="qemu:///system"
 37def file_exists(file):
 38    if os.path.exists(file):
 39        return 1
 40    else:
 41        return 0
 42def copy_vm_img_file(src_img_file,dst_img_file):
 43    print "Start Copy",src_img_file,"to",dst_img_file
 44    if file_exists(dst_img_file):
 45        print "File %s exists, abort" % dst_img_file
 46        sys.exit(1)
 47    shutil.copyfile(src_img_file,dst_img_file)
 48    print "Done!"
 49def start_vm(vm_xml_file,vm_name):
 50    try:
 51        conn = libvirt.open(uri)
 52    except Exception,e:
 53        print 'Faild to open connection to the hypervisor'
 54        sys.exit(1)
 55    create = True
 56    if create:
 57        xmlfile=open(vm_xml_file)
 58        xmldesc=xmlfile.read()
 59        xmlfile.close()
 60    try:
 61        vmname = conn.defineXML(xmldesc)
 62    except Exception,e:
 63        print "Failed to define %s:%s" %(vm_name,e)
 64        sys.exit(1)
 65    if vmname is None:
 66        print 'whoops this shouldnt happen!'
 67    try:
 68        vmname.create()
 69    except Exception,e:
 70        print "Failed to create %s:%s" %(vm_name,e)
 71        sys.exit(1)
 72    try:
 73        print "Domain 0:id %d running %s" %(vmname.ID(),vmname.name())
 74    except Exception,e:
 75        print e
 76    try:
 77        conn.close()
 78    except:
 79        print "Faild to close the connection!"
 80        sys.exit(1)
 81    print "Done!"
 82    print "="*100
 83def create_vm_xml_file(src_xml_file,vm_name,dst_img_file):
 84    config = ET.parse(src_xml_file)
 85    name = config.find('name')
 86    name.text = vm_name.strip()
 87    uuid = config.find('uuid')
 88    uuid.text = uuidToString(randomUUID())
 89    mem = config.find('memory')
 90    memkb = str(int(1024)*int(vm_mem.strip()))
 91    mem.text = memkb
 92    currentMemory = config.find('currentMemory')
 93    currentMemory.text = memkb
 94    vcpu = config.find('vcpu')
 95    vcpu.text = vm_vcpu.strip()
 96    mac = config.find('devices/interface/mac')
 97    mac.attrib['address'] = randomMAC(type='qemu')
 98    disk = config.find('devices/disk/source')
 99    disk.attrib['file']=dst_img_file
100    vm_xml_name=vm_name.strip() + '.xml'
101    vm_xml_file=os.path.join(vm_xml_path,vm_xml_name)
102    if file_exists(vm_xml_file):
103        print "File %s exists, abort" % vm_xml_file
104        sys.exit(1)
105    config.write(vm_xml_file)
106    print "Created vm config file %s" % vm_xml_file
107    #print "Use disk image %s, you must create it from the template disk: %s" % (disk_image, disk_old)
108    print "Done!"
109    #Function 2 Start VM
110    print "Start VM"
111    start_vm(vm_xml_file,vm_name)
112def delete_file(file_name):
113    if file_exists(file_name):
114        os.unlink(file_name)
115def delete_vm(vm_name):
116    vmimg=vm_name+".img"
117    vmxml=vm_name+".xml"
118    img_file=os.path.join(vm_img_path,vmimg)
119    xml_file=os.path.join(vm_xml_path,vmxml)
120    try:
121        conn = libvirt.open(uri)
122    except Exception,e:
123        print 'Faild to open connection to the hypervisor'
124        sys.exit(1)
125    try:
126        server=conn.lookupByName(vm_name)
127    except Exception,e:
128        print e
129        sys.exit(1)
130    if server.isActive():
131        print "VM %s will be shutdown!" %vm_name
132        try:
133            #server.shutdown()#VM need install acpid
134            server.destroy()
135        except Exception,e:
136            print e
137            sys.exit(1)
138        print "VM %s will be delete!" %vm_name
139        try:
140            server.undefine()
141        except Exception,e:
142            print e
143            sys.exit(1)
144        delete_file(img_file)
145        delete_file(xml_file)
146        try:
147            conn.close()
148        except:
149            print "Faild to close the connection!"
150            sys.exit(1)
151    else:
152        print "VM %s will be delete!" %vm_name
153        try:
154            server.undefine()
155        except Exception,e:
156            print e
157            sys.exit(1)
158        delete_file(img_file)
159        delete_file(xml_file)
160    print "Done"
161    print "="*100
162#Open config file
163fh=open(vm_file)
164vm_config=fh.readlines()
165fh.close()
166for line in vm_config:
167    passline=re.compile("#.*")
168    if re.search(passline,line)!=None:
169        continue
170    (action,vm_name,src_file,xml_file,vm_mem,vm_vcpu)=line.strip().split(",")
171    if action=='add':
172        src_img_file=os.path.join(template_img_path,src_file)
173        dst_img_file=os.path.join(vm_img_path,vm_name.strip()+".img")
174        src_xml_file=os.path.join(template_xml_path,xml_file)
175        if not (file_exists(src_img_file) and file_exists(src_xml_file)):
176            print "File %s or %s not exists,abort!" %(src_img_file,src_xml_file)
177            sys.exit(1)
178        #Function1.1 Copy VM img file
179        print "Copy Template VM image file"
180        copy_vm_img_file(src_img_file,dst_img_file)
181        #Function1.2 Create VM XML file
182        print "Create VM Xml file"
183        create_vm_xml_file(src_xml_file,vm_name,dst_img_file)
184    elif action=="delete":
185        #Function3 Delete VM
186        print "Delete VM"
187        delete_vm(vm_name)

上面脚本的大部分代码都是取自 坏男孩的python的脚本 ,我在其基础上主要做了两点变更:

1、使用cElementTree取代原代码中的ElementTree ,这个相较后者效率更高,并且包含在原python标准库里,在目前流行的linux发行版源里自带的python也都支持 。

2、增加了配置文件中的参数关于内存和vcpu的配置选项 。不过此处写的有点小bug,即后面的参数不存在时没有提示信息,也没有设置默认参数或异常处理 。

二、目录结构及使用

具体脚本的目录结构如下:

 1[root@localhost /]# tree /template
 2/template
 3├── conf
 4│   ├── newvm.ini
 5│   ├── newvm.ini.bak
 6│   └── vm.ini
 7├── img
 8│   └── template.qcow2
 9├── template1.py
10├── template2.py
11└── xml
12    ├── template_qcow2.xml
13    └── template.xml

其中,template1.py是坏男孩的原版的这里不再贴出,template2.py是我修改后的版本,代码已经在上面贴出 。img目录存放的img镜像文件,xml目录就于存放与img对应的xml文件。python脚本通过读取ini文件进行批量的增加或删除 ,这里比较下新旧两个版本的配置文件:

 1[root@localhost conf]# cat newvm.ini
 2#Action,Vm_name,Template_img_file,Template_xml_file,VM_mem,VM_vcpu
 3delete,test03,template.qcow2,template_qcow2.xml,0,0
 4[root@localhost conf]# cat vm.ini
 5#Action,Vm_name,Template_img_file,Template_xml_file
 6add,test02,template.qcow2,template_qcow2.xml
 7[root@localhost conf]# cat newvm_add.ini
 8#Action,Vm_name,Template_img_file,Template_xml_file,VM_mem,VM_vcpu
 9add,test03,template.qcow2,template_qcow2.xml,2048,2
10add,test04,template.qcow2,template_qcow2.xml,512,1

删除虚拟机时由于用不到mem和vcpu的值,所以可以直接补0,如果不给参的话,会报错 。增删多个虚拟机时,可以写多行。

三、关于ip、主机名、Guid等

这里脚本的做法很类似于冷迁移 ,而后面还有一些需要执行的东西需要操作,如IP地址、主机名、GUID很多值会和原主机一样 ,以一些场景下这些值相同会有一些问题。处理这些问题时也有两种方法,一种是通过libguestfs-tools 工具,直接在上面的脚本里增强,在复制完img后,直接修改img文件里的内容,这里也提供下libguestfs-tools官方提供的python操作的示例页

不过利用libguestfs-tools工具时,针对windows操作系统时可能有就有点麻烦,比如修改IP地址,更改GUID通过libguestfs实现就会很麻烦 。所以这里可以选择第二种设想,利用guest主机start以后,利用脚本或批处理去实现 ,当然这个可以是手动,也可以是自动的(在开机选项里,在调用完成后再删除自身) 。这个后期想法的东西在网上也到了类似功能代码 Cloning VMs with KVM

四、参考文档

后期功能扩展也可以参考下以下链接的相文文档:

通过python-libvirt管理KVM虚拟机

libvirt.org官方python API篇

Create new KVM guest from template 坏男孩的python版代码里和这里很多也基本相同

使用 Python 为 KVM 编写脚本( 来自IBM developerworks )

shell 版实现类似功能脚本