python2.7管理kvm虚拟主机

说明:

  • 脚本是基于 python 2.7.5 版本调试
  • 脚本使用到 libvirt 库,请安装好相应的库
  • kvm虚拟机需要 qemu-guest-agent sg3_utils sg3_utils-libs 安装包

kvm虚机依赖包

[root@localhost ~]# rpm -qa qemu-guest-agent sg3_utils sg3_utils-libs
qemu-guest-agent-2.12.0-3.el7.x86_64
sg3_utils-1.37-19.el7.x86_64
sg3_utils-libs-1.37-19.el7.x86_64

[root@localhost ~]# systemctl is-active qemu-guest-agent
active

没有安装上述三个包,获取IP地址会报错 Guest agent is not responding: QEMU guest agent is not connected

python依赖库

检查 libvirt

[root@localhost ~]# python
Python 2.7.5 (default, Oct 30 2018, 23:45:53) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import libvirt
>>> 

由与上述不一样,请安装好libvirt-python包即可

[root@localhost ~]# rpm -qa libvirt-python
libvirt-python-4.5.0-1.el7.x86_64

epel-release 仓库提供 libvirt-python

python代码

#!/usr/bin/python
# coding=utf-8
import sys
import re
import os
import time
import getopt
import libvirt

# 命令的帮助文档
def scriptHelp():
    print('''
    Usage:
        python test.py [-o operate] [-n vmnames] [-c cloneName] [-l list] [ip]

    -o operate: Operation of kvm virtual host, such as start, stop, restart, delete, clone..
    -n vmnames: The kvm virtual host being operated, Separate multiple hosts with commas
    -c cloneName: The parameters of the clone host must be used with the -o clone option and the -n parameter can only specify one host
    -l list: View the running status of all kvm virtual machines on the machine
    --ip: View all started kvm virtual host IP addresses

    Example:
      # Start multiple kvm virtual hosts
      kvmmanage -o start -n test01,test02

      # Shut down all kvm virtual hosts
      kvmmanage -o stop -n all

      # Quickly create kvm virtual host
      kvmmanage -o clone -n CentOS7 -c test01

      # delete kvm virtual host
      kvmmanage -o delete -n test01

      # View all started kvm virtual host IP addresses
      kvmmanage --ip
    ''')
    sys.exit()

# 连接 qemu 装饰器 
def conn_qemu(func):
    def inner(*args, **kwargs):
        global conn
        conn = createConnection()
        func(*args, **kwargs)
        closeConnection(conn)

    return inner

# 创建 qemu 连接
def createConnection():
    try:
        conn = libvirt.open("qemu:///system")
    except Exception as e:
        print("\033[1;31m[ERROR]\033[0m libvirtd service not running.")
    else:
        print("---Successfully opened connection to hypervisor---")
        return conn

# 关闭 qemu 连接
def closeConnection(conn):
    try:
        conn.close()
    except Exception as e:
        print(e)
    else:
        print("---Successfully closed the hypervisor connection---")

# 启动虚拟机
@conn_qemu
def startDomain(VmNames):
    # 处理批量启动所有虚拟机
    if len(VmNames) == 1 and VmNames[0] == "all":
        # 排除 except_vm_list 列表的虚拟机
        for name in except_vm_list:
            VmNames = conn.listDefinedDomains()
            VmNames.remove(name)
        # 如果为空,则说明所有主机处于启动状态
        if VmNames == []:
            print("\033[1;31m[ERROR]\033[0m All hosts have been started")
            return

    up_vm_list_name = []
    for id in conn.listDomainsID():
        up_vm_list_name.append(conn.lookupByID(id).name())

    for VmName in VmNames:
        if VmName in conn.listDefinedDomains():
            dom = conn.lookupByName(VmName)
            dom.create() 
            print("The virtual machine \033[1;36m%s\033[0m has executed the boot instruction"%VmName)
        elif VmName in up_vm_list_name:
            print("\033[1;31m[ERROR]\033[0m The virtual machine \033[1;36m%s\033[0m is started"%VmName)
            continue
        else:
            print("\033[1;31m[ERROR]\033[0m \033[1;36m%s\033[0m The VM name is incorrect"%VmName)
            continue

# 关闭虚拟机
@conn_qemu
def stopDomain(VmNames):
    up_vm_list_name = []
    for id in conn.listDomainsID():
        up_vm_list_name.append(conn.lookupByID(id).name())

    if len(VmNames) == 1 and VmNames[0] == "all":
        VmNames = up_vm_list_name
        if VmNames == []:
            print("\033[1;31m[ERROR]\033[0m All hosts have been powered off")

    for VmName in VmNames:
        if VmName in conn.listDefinedDomains():
            print("\033[1;31m[ERROR]\033[0m The virtual machine \033[1;36m%s\033[0m is shut down"%VmName)
            continue
        elif VmName in up_vm_list_name:
            dom = conn.lookupByName(VmName)
            dom.destroy()
            print("The shutdown command has been executed for the virtual machine \033[1;36m%s\033[0m"%VmName)
        else:
            print("\033[1;31m[ERROR]\033[0m \033[1;36m%s\033[0m The VM name is incorrect"%VmName)
            continue            

# 重启虚拟机
@conn_qemu
def restartDomain(VmNames):
    up_vm_list_name = []
    for id in conn.listDomainsID():
        up_vm_list_name.append(conn.lookupByID(id).name())

    if len(VmNames) == 1 and VmNames[0] == "all":
        VmNames = up_vm_list_name
        if VmNames == []:
            print("\033[1;31m[ERROR]\033[0m All hosts have been powered off")

    for VmName in VmNames:
        if VmName in up_vm_list_name:
            dom = conn.lookupByName(VmName)
            dom.reboot()
            print("The virtual machine \033[1;36m%s\033[0m has executed the restart instruction"%VmName)
        elif VmName in conn.listDefinedDomains():
            print("\033[1;31m[ERROR]\033[0m \033[1;36m%s\033[0m The VM is stopped. Procedure"%VmName)
            continue
        else:
            print("\033[1;31m[ERROR]\033[0m \033[1;36m%s\033[0m The VM name is incorrect"%VmName)
            continue

# 删除虚拟机
def deleteDomain(VmNames):
    warn = raw_input("[Dangerous operation] Are you sure you want to delete the kvm virtual machine? [y/n]: ").strip()
    if warn not in ["y", "Y"]:
        print("The input option is not y, and the deletion action is not performed...")
        sys.exit()
    stopDomain(VmNames)
    for VmName in VmNames:
        delete_cmd = '''virsh undefine %s && rm -rf /var/lib/libvirt/images/%s.qcow2''' % (VmName, VmName)
        print("[CMD] %s" % delete_cmd)
        if os.system(delete_cmd) != 0:
            print("\033[1;31m[ERROR]\033[0m delete failed...")

# 克隆虚拟机
def cloneDomain(vmNames, cloneNames):
    for cloneName in cloneNames:
        clone_cmd = "virt-clone -o %s -n %s -f /var/lib/libvirt/images/%s.qcow2" % (vmNames[0], cloneName, cloneName)
        print("[CMD] %s" % clone_cmd)
        if os.system(clone_cmd) != 0:
            print("\033[1;31m[ERROR]\033[0m clone failed...")

# 查看所有虚拟机状态
@conn_qemu
def getVmStatus():
    down_vm_list_name = conn.listDefinedDomains()
    print("\033[1;32mVirtual host not started [ total of %s ]:\033[0m \n%s\n"%(len(down_vm_list_name), sorted(down_vm_list_name)))

    up_vm_list_name = []
    for id in conn.listDomainsID():
        up_vm_list_name.append(conn.lookupByID(id).name())
    print("\033[1;32mStart virtual host [ total of %s ]:\033[0m \n%s" % (len(up_vm_list_name), sorted(up_vm_list_name))) 

# 获取已启动虚拟机ip地址
@conn_qemu
def getVmAddress():
    # 获取虚机名称
    up_vm_list_name = []
    for id in conn.listDomainsID():
        up_vm_list_name.append(conn.lookupByID(id).name())
    up_vm_list_name = sorted(up_vm_list_name)

    # 获取虚机ip地址
    up_vm_list_ip = []
    for name in up_vm_list_name:
        IP = []
        dom = conn.lookupByName(name)
        try:
            ifaces = dom.interfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT, 0)
        except Exception as e:
            IP.append("Null")
        else:
            for key, value in ifaces.items():
                # print(conn.lookupByID(id).name(), key, value)
                if re.search("lo|docker.*|kube-ipvs.*|tunl.*|cali.*", key) != None or value["addrs"] == None:
                    # print(conn.lookupByID(id).name(), key, value)
                    continue
                else:
                    for ip in value["addrs"]:
                        # print(conn.lookupByID(id).name(), key, ip)
                        if re.search("2[1-8]", str(ip["prefix"])) != None:
                            IP.append(ip[r'addr'])
                        elif ip["prefix"] == 64 and ip["addr"] != "::1":
                            IP.append(ip["addr"])
        up_vm_list_ip.append(IP) 

    # 判断是否有虚机运行中
    if up_vm_list_name == []:
        print("All the VM hosts are not started")
        return

    # 统计up_vm_list_name列表元素的字符数
    a = [len(nums) for nums in up_vm_list_name]

    # 统计up_vm_list_ip列表元素的字符数
    b = []
    for host_ip in up_vm_list_ip:
        c = [len(nums) for nums in host_ip]
        b.append(sum(c))

    list_ip_num_many = len(up_vm_list_ip[b.index(max(b))])
    if max(b) + list_ip_num_many*2 >= 12:
        print("{:>3}{}+".format("+", "-"*(max(a) + max(b) + list_ip_num_many*2+3 )))
        print("{:>3}{:^{}}|{:^{}}|".format("|", "Hostname", max(a) + 2, "IP Address", max(b) + list_ip_num_many*2 ))
        print("{:>3}{}+".format("+", "-"*(max(a) + max(b) + list_ip_num_many*2+3 )))
        for i, host_ip in enumerate(up_vm_list_ip):
            print("{:>3}{:^{}}|{:^{}}|".format("|", up_vm_list_name[i], max(a) + 2, "__".join(host_ip), max(b) + list_ip_num_many*2))
        print("{:>3}{}+".format("+", "-"*(max(a)+max(b) + list_ip_num_many*2+3)))
    else:
        print("{:>3}{}+".format("+", "-"*(max(a) + 15)))
        print("{:>3}{:^{}}| {:^{}} |".format("|", "Hostname", max(a) + 2, "IP Address", max(b) + list_ip_num_many*2 ))
        print("{:>3}{}+".format("+", "-"*(max(a) + 15)))
        for i, host_ip in enumerate(up_vm_list_ip):
            print("{:>3}{:^{}}|{:^{}}|".format("|", up_vm_list_name[i], max(a) + 2, "__".join(host_ip), 12))
        print("{:>3}{}+".format("+", "-"*(max(a) + 15)))

# 操作虚机
def operateType(operate, vmNames, cloneNames=''):
    if operate == "start":
        startDomain(vmNames)
    elif operate == "stop":
        stopDomain(vmNames)
    elif operate == "restart":
        restartDomain(vmNames)
    elif operate == "delete":
        deleteDomain(vmNames)
    elif operate == "clone":
        cloneDomain(vmNames, cloneNames)        
    else:
        scriptHelp()

# 判断参数及调用函数
def main(operate, vmNames, cloneNames, NoValueParameter):
    if len(NoValueParameter) >= 2:
        scriptHelp()
    elif len(NoValueParameter) == 1 and operate == '' and cloneNames == '':
        if NoValueParameter[0] == "help":
            scriptHelp()
        elif NoValueParameter[0] == "list":
            getVmStatus()
        elif NoValueParameter[0] == "ip":
            getVmAddress()
    elif operate == "clone" and vmNames != '':
        if len(vmNames) != 1:
            print("\033[1;31m[ERROR]\033[0m Only one virtual machine can be specified as the template machine (-n parameter)\n")
        elif cloneNames == '':
            print("\033[1;31m[ERROR]\033[0m For cloning operations, the -c parameter cannot be null")
        else:
            operateType(operate, vmNames, cloneNames)
    elif operate != '' and vmNames != '':
        operateType(operate, vmNames)
    else:
        scriptHelp()
    
# 获取命令的传递参数
def getScriptParameter(args):
    try:
        opts, args = getopt.getopt(args, "hlo:n:c:", ["help", "operate=", "vmnames=", "list", "clonename=", "ip"])
    except getopt.GetoptError:
        scriptHelp()
    
    operate = vmNames = cloneNames = ""
    NoValueParameter = []
    for opt, arg in opts:
        if opt in ("-o", "--operate"):
            operate = arg
        elif opt in ("-n", "--vmnames"):
            vmNames = arg.split(",")
        elif opt in ("-c", "--clonename"):
            cloneNames = arg.split(",")
        elif opt in ("-l", "--list"):
            NoValueParameter.append("list")
        elif opt in ("--ip"):
            NoValueParameter.append("ip")
        elif opt in ("-h", "--help"):
            NoValueParameter.append("help")

    return operate, vmNames, cloneNames, NoValueParameter

if __name__ == '__main__':
    # 操作主机时,使用all表示启动所有主机,但是排除except_vm_list外...
    except_vm_list=["CentOS7"]

    start_time = time.time()
    if len(sys.argv) <= 1:
        scriptHelp()

    operate, vmNames, cloneNames, NoValueParameter = getScriptParameter(sys.argv[1:])
    main(operate, vmNames, cloneNames, NoValueParameter)

    print("\nThis operation takes %.3f seconds" % (time.time() - start_time))
posted @   jiaxzeng  阅读(353)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示