这两天一直计划写一个通过模板img和xml文件实现KVM的快速部署的脚本 ,实现原理非常简单,就是通过复制img镜像到指定位置,并修改xml里的相关配置后,define guest主机,最后start即可。后来在网上也找了两个实现类似功能的脚本 ,一个是shell版的,一个是python版的。最终我这里结合了两者之间的一些特点在两者之间上做了一点整合和优化,出了一个增强版本,这里分享下,回头有时间再一并上传到github上去。
一、python脚本内容
如下:
#!/usr/bin/env python ########################################### #Version 2.0 #order by www.361way.com #Function Description: #Batch automatically generated/delete VM #1.Generated VM ##1.1.Copy VM img file ##1.2.Create VM XML file ###1.2.1.Update UUID ###1.2.2.Update MAC ###1.2.3.Update img path ###1.2.4.Update VM Name #2.Start VM #3.Delete VM #################################################################### #import module import shutil import os,sys from virtinst.util import * import libvirt import re if sys.version_info < (2,5): import lxml.etree as ET else: try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET #Define variables template_img_path="/template/img" template_xml_path="/template/xml" vm_img_path="/file" vm_xml_path="/etc/libvirt/qemu" vm_file="/template/conf/newvm.ini" uri="qemu:///system" def file_exists(file): if os.path.exists(file): return 1 else: return 0 def copy_vm_img_file(src_img_file,dst_img_file): print "Start Copy",src_img_file,"to",dst_img_file if file_exists(dst_img_file): print "File %s exists, abort" % dst_img_file sys.exit(1) shutil.copyfile(src_img_file,dst_img_file) print "Done!" def start_vm(vm_xml_file,vm_name): try: conn = libvirt.open(uri) except Exception,e: print 'Faild to open connection to the hypervisor' sys.exit(1) create = True if create: xmlfile=open(vm_xml_file) xmldesc=xmlfile.read() xmlfile.close() try: vmname = conn.defineXML(xmldesc) except Exception,e: print "Failed to define %s:%s" %(vm_name,e) sys.exit(1) if vmname is None: print 'whoops this shouldnt happen!' try: vmname.create() except Exception,e: print "Failed to create %s:%s" %(vm_name,e) sys.exit(1) try: print "Domain 0:id %d running %s" %(vmname.ID(),vmname.name()) except Exception,e: print e try: conn.close() except: print "Faild to close the connection!" sys.exit(1) print "Done!" print "="*100 def create_vm_xml_file(src_xml_file,vm_name,dst_img_file): config = ET.parse(src_xml_file) name = config.find('name') name.text = vm_name.strip() uuid = config.find('uuid') uuid.text = uuidToString(randomUUID()) mem = config.find('memory') memkb = str(int(1024)*int(vm_mem.strip())) mem.text = memkb currentMemory = config.find('currentMemory') currentMemory.text = memkb vcpu = config.find('vcpu') vcpu.text = vm_vcpu.strip() mac = config.find('devices/interface/mac') mac.attrib['address'] = randomMAC(type='qemu') disk = config.find('devices/disk/source') disk.attrib['file']=dst_img_file vm_xml_name=vm_name.strip() + '.xml' vm_xml_file=os.path.join(vm_xml_path,vm_xml_name) if file_exists(vm_xml_file): print "File %s exists, abort" % vm_xml_file sys.exit(1) config.write(vm_xml_file) print "Created vm config file %s" % vm_xml_file #print "Use disk image %s, you must create it from the template disk: %s" % (disk_image, disk_old) print "Done!" #Function 2 Start VM print "Start VM" start_vm(vm_xml_file,vm_name) def delete_file(file_name): if file_exists(file_name): os.unlink(file_name) def delete_vm(vm_name): vmimg=vm_name+".img" vmxml=vm_name+".xml" img_file=os.path.join(vm_img_path,vmimg) xml_file=os.path.join(vm_xml_path,vmxml) try: conn = libvirt.open(uri) except Exception,e: print 'Faild to open connection to the hypervisor' sys.exit(1) try: server=conn.lookupByName(vm_name) except Exception,e: print e sys.exit(1) if server.isActive(): print "VM %s will be shutdown!" %vm_name try: #server.shutdown()#VM need install acpid server.destroy() except Exception,e: print e sys.exit(1) print "VM %s will be delete!" %vm_name try: server.undefine() except Exception,e: print e sys.exit(1) delete_file(img_file) delete_file(xml_file) try: conn.close() except: print "Faild to close the connection!" sys.exit(1) else: print "VM %s will be delete!" %vm_name try: server.undefine() except Exception,e: print e sys.exit(1) delete_file(img_file) delete_file(xml_file) print "Done" print "="*100 #Open config file fh=open(vm_file) vm_config=fh.readlines() fh.close() for line in vm_config: passline=re.compile("#.*") if re.search(passline,line)!=None: continue (action,vm_name,src_file,xml_file,vm_mem,vm_vcpu)=line.strip().split(",") if action=='add': src_img_file=os.path.join(template_img_path,src_file) dst_img_file=os.path.join(vm_img_path,vm_name.strip()+".img") src_xml_file=os.path.join(template_xml_path,xml_file) if not (file_exists(src_img_file) and file_exists(src_xml_file)): print "File %s or %s not exists,abort!" %(src_img_file,src_xml_file) sys.exit(1) #Function1.1 Copy VM img file print "Copy Template VM image file" copy_vm_img_file(src_img_file,dst_img_file) #Function1.2 Create VM XML file print "Create VM Xml file" create_vm_xml_file(src_xml_file,vm_name,dst_img_file) elif action=="delete": #Function3 Delete VM print "Delete VM" delete_vm(vm_name)
上面脚本的大部分代码都是取自 坏男孩的python的脚本 ,我在其基础上主要做了两点变更:
1、使用cElementTree取代原代码中的ElementTree ,这个相较后者效率更高,并且包含在原python标准库里,在目前流行的linux发行版源里自带的python也都支持 。
2、增加了配置文件中的参数关于内存和vcpu的配置选项 。不过此处写的有点小bug,即后面的参数不存在时没有提示信息,也没有设置默认参数或异常处理 。
二、目录结构及使用
具体脚本的目录结构如下:
[root@localhost /]# tree /template /template ├── conf │ ├── newvm.ini │ ├── newvm.ini.bak │ └── vm.ini ├── img │ └── template.qcow2 ├── template1.py ├── template2.py └── xml ├── template_qcow2.xml └── template.xml
其中,template1.py是坏男孩的原版的这里不再贴出,template2.py是我修改后的版本,代码已经在上面贴出 。img目录存放的img镜像文件,xml目录就于存放与img对应的xml文件。python脚本通过读取ini文件进行批量的增加或删除 ,这里比较下新旧两个版本的配置文件:
[root@localhost conf]# cat newvm.ini #Action,Vm_name,Template_img_file,Template_xml_file,VM_mem,VM_vcpu delete,test03,template.qcow2,template_qcow2.xml,0,0 [root@localhost conf]# cat vm.ini #Action,Vm_name,Template_img_file,Template_xml_file add,test02,template.qcow2,template_qcow2.xml [root@localhost conf]# cat newvm_add.ini #Action,Vm_name,Template_img_file,Template_xml_file,VM_mem,VM_vcpu add,test03,template.qcow2,template_qcow2.xml,2048,2 add,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 。
四、参考文档
后期功能扩展也可以参考下以下链接的相文文档:
Create new KVM guest from template 坏男孩的python版代码里和这里很多也基本相同
使用 Python 为 KVM 编写脚本( 来自IBM developerworks )