Libvirt 8.10.0 Python API 中文文档
libvirt Python API 提供了用于管理虚拟化环境的接口,允许使用 Python 脚本对虚拟机、网络、存储等资源进行管理。libvirt 8.10.0 版本的 Python 绑定与 C API 基本一一对应,将 C 函数映射为 Python 类的方法。主要包括以下类:virConnect
(连接)、virDomain
(域,即虚拟机)、virNetwork
(网络)、virStoragePool
(存储池)、virStorageVol
(存储卷)、virInterface
(网络接口)、virNodeDevice
(节点设备)、virSecret
(密钥)、virNWFilter
(网络过滤器)、virNWFilterBinding
(过滤器绑定)、virStream
(数据流)、virDomainSnapshot
(域快照)、virDomainCheckpoint
(域检查点)等。本文将按模块功能对上述类的方法和属性进行整理说明,并给出示例代码。请注意,libvirt API 的调用若出错通常会抛出 libvirt.libvirtError
异常,需要进行异常处理。
连接管理(virConnect 类)
连接是使用 libvirt API 的基础。应用程序首先需要获取一个到虚拟化主机的连接(virConnect
对象)才能进行后续操作。
**打开连接:**使用
libvirt.open()
或libvirt.openReadOnly()
或libvirt.openAuth()
函数来建立连接。其中:libvirt.open(uri=None)
:以读写权限打开到指定 URI 的连接。如果未提供 URI,则使用环境变量LIBVIRT_DEFAULT_URI
或 libvirt 配置默认值。
示例:import libvirt conn = libvirt.open("qemu:///system") if conn is None: raise Exception("无法打开到 qemu:///system 的连接") print("连接成功:", conn.getURI())
libvirt.openReadOnly(uri=None)
:以只读权限打开连接,只能执行查询操作,无法修改配置。
示例:conn_ro = libvirt.openReadOnly("qemu:///system") if conn_ro is None: raise Exception("无法以只读模式连接 hypervisor") print("只读连接:", conn_ro.getURI()) conn_ro.close()
libvirt.openAuth(uri, auth, flags)
:使用自定义认证方式打开连接。当连接需要交互式认证(如 SASL 密码)时使用,auth
参数提供回调。一般情况下,如果 URI 不需要交互认证,可直接使用open()
。
**关闭连接:**使用
virConnect.close()
方法关闭连接。当连接对象不再需要时,应调用此方法释放资源。libvirt 连接对象采用引用计数,每调用一次 open 函数获得一个引用,close 会减少引用计数。所有通过该连接获得的其他对象(域、网络等)也会持有连接的引用,因此在这些对象存在时调用 close 仍会返回正值表示引用尚未完全释放。通常关闭初始连接即可。
示例:conn = libvirt.open("qemu:///system") # ... 执行各种操作 ... ret = conn.close() print("关闭连接返回值:", ret) # 0 表示连接已关闭
连接属性和状态:
virConnect
提供了一系列方法获取主机和连接信息:conn.getURI()
:获取当前连接的 URI 字符串。conn.getHostname()
:获取连接到的虚拟化主机的主机名 (libvirt: Module libvirt-host from libvirt) (libvirt: Module libvirt-host from libvirt)。conn.getType()
:获取连接的虚拟机监控程序类型名称(例如"QEMU"
、"Xen"
等) (libvirt: Module libvirt-host from libvirt)。conn.getVersion()
:获取虚拟机监控程序软件的版本号(整数,格式为 major×1,000,000 + minor×1,000 + release) (libvirt: Module libvirt-host from libvirt)。conn.getLibVersion()
:获取远端 libvirt 库版本号(与conn.getVersion()
区别在于一个是hypervisor版本,一个是libvirt服务版本)。conn.getCapabilities()
:获取主机支持的虚拟化功能的 XML 描述(返回 XML 字符串,可解析出 CPU/设备支持情况等)。conn.isAlive()
:判断连接是否仍然存活,返回 1 表示存活,0 表示断开 (libvirt: Module libvirt-host from libvirt) (libvirt: Module libvirt-host from libvirt)。conn.isEncrypted()
:判断连接是否使用加密通道 (libvirt: Module libvirt-host from libvirt)。conn.isSecure()
:判断连接通道是否安全(加密或本地套接字等不易被窃听的通道) (libvirt: Module libvirt-host from libvirt) (libvirt: Module libvirt-host from libvirt)。
示例:
print("Hypervisor 类型:", conn.getType()) print("Hypervisor 版本:", conn.getVersion()) print("Libvirt 服务版本:", conn.getLibVersion()) print("主机名:", conn.getHostname()) if conn.isAlive(): print("连接存活,URI:", conn.getURI())
主机硬件信息:
conn.getInfo()
方法可以获取主机硬件概况,例如型号、内存、CPU 等信息。此方法返回列表,例如:[型号字符串, 内存大小(MiB), CPU数量, CPU频率(MHz), NUMA节点数, CPU插槽数, 每插槽核心数, 每核心线程数]
。
示例:info = conn.getInfo() print(f"主机型号: {info[0]}, 内存: {info[1]} MiB, CPU数: {info[2]}")
**检索虚拟机(域)对象:**通过连接可以查找和列出现有的虚拟机域:
conn.lookupByName(name)
:按名称获取正在定义的域对象,找不到则抛出异常。conn.lookupByUUIDString(uuid_str)
:通过 UUID 获取域。conn.lookupByID(id)
:通过运行中的域ID获取域(仅限运行中虚拟机)。conn.listAllDomains(flags=0)
:列出所有域对象列表。可选的flags
可以过滤仅活动或非活动域等 (libvirt: Module libvirt-domain-snapshot from libvirt)。返回的是virDomain
对象的列表。- 兼容旧接口:
conn.listDomainsID()
返回当前运行的域ID列表,conn.listDefinedDomains()
返回已定义但不在运行的域名称列表,conn.numOfDomains()
等方法提供类似信息,但更建议使用listAllDomains
一次性获取所有域对象。
示例:
try: dom0 = conn.lookupByName("Domain-0") print("找到Domain-0,UUID:", dom0.UUIDString()) except libvirt.libvirtError: print("Domain-0 未找到") # 列出所有虚拟机名称 for dom in conn.listAllDomains(): print("域名称:", dom.name(), "| 状态:", "运行中" if dom.isActive() else "已关闭")
**创建和定义虚拟机域:**可以使用连接对象创建新的虚拟机:
conn.defineXML(xml_desc)
:定义一个持久域(虚拟机),根据提供的 XML 描述注册虚拟机配置但不启动 (libvirt: Module libvirt-host from libvirt) (libvirt: Module libvirt-host from libvirt)。返回一个virDomain
对象。conn.createXML(xml_desc, flags=0)
:定义并启动一个临时域。根据 XML 创建虚拟机并立即启动,所创建的域默认是不持久的(除非 XML 中含有<on_crash>preserve</on_crash>
等配置)。
**示例:**定义并启动一个最小化配置的虚拟机(这里假设已准备好磁盘镜像):domain_xml = """ <domain type='kvm'> <name>testvm</name> <memory unit='KiB'>1048576</memory> <!-- 1GB 内存 --> <vcpu>1</vcpu> <os><type arch='x86_64'>hvm</type></os> <devices> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/var/lib/libvirt/images/testvm.qcow2'/> <target dev='vda' bus='virtio'/> </disk> <interface type='network'> <source network='default'/> </interface> </devices> </domain>""" dom = conn.createXML(domain_xml) # 创建并启动虚拟机 print("新域已启动:", dom.name(), dom.ID())
**注意:**虚拟机 XML 配置结构详见官方文档,各元素用于定义CPU、内存、设备、网络等。
**检索其他对象:**类似地,连接对象提供以下方法来获取和管理其他资源:
conn.listAllNetworks()
/conn.networkLookupByName()
:列出或查找虚拟网络(返回virNetwork
)。conn.listAllStoragePools()
/conn.storagePoolLookupByName()
:列出或查找存储池(返回virStoragePool
)。conn.listAllInterfaces()
/conn.interfaceLookupByName()
:列出或查找主机网络接口(返回virInterface
)。conn.listAllNodeDevices()
/conn.nodeDeviceLookupByName()
:列出或查找节点设备(PCI/USB等硬件设备,返回virNodeDevice
)。conn.listAllSecrets()
/conn.secretLookupByUUIDString()
/conn.secretLookupByUsage()
:列出或查找密钥对象 (virSecret
)。conn.listAllNWFilters()
/conn.nwfilterLookupByName()
:列出或查找网络过滤器 (virNWFilter
)。
这些方法返回相应资源的对象,可用于进一步操作。调用
listAll*
时可以使用标志过滤,例如仅活动或仅持久等(各资源类型的 flags 定义不同)。
虚拟机管理(virDomain 类)
virDomain
类表示一个虚拟机域对象,对应一个客户机虚拟机。通过 virConnect
获取 virDomain
对象后,可对虚拟机进行各种管理操作。
标识与基本信息:
dom.name()
:获取虚拟机名称。dom.UUIDString()
:获取虚拟机的 UUID 字符串格式。也可使用dom.UUID()
获取字节数组形式的 UUID。dom.ID()
:获取运行中虚拟机的ID(整数)。如果虚拟机关闭或未运行,则此方法可能返回 -1。dom.info()
:获取虚拟机概况信息,返回包含以下5项的元组: (状态, 最大内存, 当前内存, vCPU 数量, CPU 时间) (libvirt: Python API bindings)。其中状态为枚举值(0=无效,1=运行,2=封暂停,3=暂停,4=关机,5=崩溃,6=关闭),CPU时间为该域消耗的CPU时间(纳秒)。
示例:print(f"域名称:{dom.name()}, UUID:{dom.UUIDString()}, ID:{dom.ID()}") state, max_mem, mem, vcpus, cpu_time = dom.info() print(f"状态:{state}, 内存:{mem}/{max_mem} KB, vCPU:{vcpus}, CPU时间:{cpu_time} ns")
dom.isActive()
:返回 1 表示虚拟机当前正在运行 (libvirt: Module libvirt-domain from libvirt)。dom.isPersistent()
:返回 1 表示该虚拟机配置是持久定义的(即存在配置存档),0 表示为临时域。
启动和停止虚拟机:
dom.create()
:启动一个已定义但当前停止的虚拟机,相当于从关机状态开机。 (libvirt: Module libvirt-domain from libvirt)dom.shutdown()
:对虚拟机发送正常关机信号,试图让客户机操作系统关闭。执行此命令后域对象仍存在,但虚拟机将进入关机过程 (libvirt: Module libvirt-domain from libvirt)。注意虚拟机内部需要安装 QEMU guest agent 或 ACPI 电源管理支持才能响应关机信号,否则可能无效 (libvirt: Module libvirt-domain from libvirt)。dom.destroy()
:强制停止虚拟机,相当于拔掉电源 (libvirt: Module libvirt-domain from libvirt)。这不会释放域配置(如果是持久域),只是停止运行。dom.reboot(flags=0)
:重启虚拟机。可选的标志位可控制重启方式 (libvirt: Module libvirt-domain from libvirt) (libvirt: Module libvirt-domain from libvirt);若不加标志,libvirt 将使用默认方式尝试安全重启。dom.reset(flags=0)
:重置虚拟机,相当于瞬间复位电路(硬复位) (libvirt: Module libvirt-domain from libvirt)。通常用于调试故障,让虚拟机从头启动。dom.suspend()
:暂停虚拟机(挂起运行,将 CPU 执行冻结)。dom.resume()
:恢复已暂停的虚拟机。dom.shutdownFlags(flags)
和dom.destroyFlags(flags)
:这些方法允许指定标志执行更细粒度的关机/销毁操作,例如VIR_DOMAIN_SHUTDOWN_ACPI_POWER_BTN
等。8.10.0 中通常使用默认即可,无特殊需求可不使用 Flags 参数。
**示例:**下面演示启动、暂停、恢复和关闭一个虚拟机:
dom = conn.lookupByName("testvm") if dom.isActive() != 1: dom.create() # 启动虚拟机 print("状态:运行中" if dom.isActive() else "状态:已关闭") dom.suspend() print("暂停后状态:", "运行中" if dom.isActive() else "已暂停") dom.resume() print("恢复后状态:", "运行中" if dom.isActive() else "已暂停") dom.shutdown() # 请求正常关机
定义和注销虚拟机:
dom.undefine()
:注销一个已定义的虚拟机配置。如果虚拟机当前正在运行,undefine()
只会移除其持久配置但不停止虚拟机(该虚拟机变为临时域,关机后即消失)。若要在虚拟机运行时同时卸载配置,可传入标志VIR_DOMAIN_UNDEFINE_MANAGED_SAVE
等,以删除其托管保存状态等附属信息。dom.setAutostart(enable)
:设置虚拟机是否开机自动启动。enable
为 1 则开机自动运行,0 则不自动运行 (libvirt: Module libvirt-domain from libvirt)。可以通过dom.autostart()
来查询当前自动启动设置(部分版本提供此方法,也可以通过读取 XML 查看)。
示例:
dom = conn.lookupByName("testvm") dom.setAutostart(1) print("已设置", dom.name(), "开机自启") dom.undefine() print(dom.name(), "的配置已移除")
调整虚拟机资源:
dom.setMemory(memory_kb)
/dom.setMemoryFlags(memory_kb, flags)
:设置虚拟机内存大小 (libvirt: Module libvirt-domain from libvirt) (libvirt: Module libvirt-domain from libvirt)。memory_kb
为以KB为单位的内存数。例如修改内存为2GB可传入2097152
(=210241024)。使用 Flags 版本可以指定VIR_DOMAIN_AFFECT_LIVE
(运行时生效)或VIR_DOMAIN_AFFECT_CONFIG
(配置生效)或同时。dom.setMaxMemory(memory_kb)
:设置虚拟机可用的最大内存阈值(通常在关机状态下配置)。dom.setVcpus(vcpu_count)
/dom.setVcpusFlags(vcpu_count, flags)
:设置虚拟机的虚拟CPU数量。Flags 可指定对活动VM还是配置进行更改。dom.setMemoryStatsPeriod(period, flags)
:设置虚拟机收集内存统计的周期,period
为秒,0表示禁用内存统计 (libvirt: Module libvirt-domain from libvirt) (libvirt: Module libvirt-domain from libvirt)。
以上操作可能要求虚拟机处于特定状态或需要虚拟机配置支持(例如热插拔CPU/内存)。
获取性能与状态数据:
dom.memoryStats()
:获取虚拟机内存统计信息,例如实际使用内存、交换等,返回字典包含各项统计。需要虚拟机支持virtio-balloon
且启用统计开关。dom.maxMemory()
:获取可用的最大内存值(KB)。dom.maxVcpus()
:获取虚拟机支持的最大 vCPU 数。dom.blockStats(dev)
:获取虚拟机指定块设备(磁盘)的累积IO统计,例如读写字节和请求数等。参数dev
为设备名(如 "vda")。dom.interfaceStats(iface)
:获取指定网络接口的IO统计数据,例如收发字节包数。iface
为接口名称(如 "vnet0")。
修改虚拟机设备:
virDomain
提供方法可动态插入或移除虚拟机的虚拟硬件设备,这通常通过提供设备的XML片段来实现:dom.attachDevice(xml_desc)
:将设备(如磁盘、网络接口)按照给定XML添加到虚拟机。此方法影响当前运行状态,并默认不保存到配置。dom.detachDevice(xml_desc)
:移除给定XML描述的设备。dom.attachDeviceFlags(xml_desc, flags)
/dom.detachDeviceFlags(xml_desc, flags)
:带标志的添加/移除设备,常用标志有VIR_DOMAIN_AFFECT_LIVE
(热插拔,仅影响当前运行VM)和VIR_DOMAIN_AFFECT_CONFIG
(影响配置)。可以组合标志同时作用于当前和持久配置。
**示例:**为运行中的虚拟机添加一块额外磁盘:
disk_xml = """ <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/var/lib/libvirt/images/data.qcow2'/> <target dev='vdb' bus='virtio'/> </disk>""" if dom.isActive(): dom.attachDeviceFlags(disk_xml, libvirt.VIR_DOMAIN_AFFECT_LIVE) print("已挂载新磁盘至虚拟机。")
同理,可以构造网卡、USB 设备等 XML 执行 attach/detach。
**虚拟机快照管理:**libvirt 支持对虚拟机进行快照(snapshot)操作,以保存虚拟机在某一时刻的状态。
dom.snapshotCreateXML(xml_desc, flags=0)
:为虚拟机创建快照 (libvirt: Module libvirt-domain-snapshot from libvirt)。xml_desc
描述快照名称等信息(若为空则由系统自动命名)。常用 flag 如VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY
(仅磁盘快照)等。返回一个virDomainSnapshot
对象。dom.snapshotListNames(flags)
:列出快照名称列表。或使用dom.listAllSnapshots(flags)
获取virDomainSnapshot
对象列表 (libvirt: Module libvirt-domain-snapshot from libvirt)。dom.hasCurrentSnapshot(flags)
:判断是否有当前快照存在 (libvirt: Module libvirt-domain-snapshot from libvirt)。dom.snapshotCurrent(flags)
:获取当前(最近恢复或创建的)快照对象 (libvirt: Module libvirt-domain-snapshot from libvirt)。snap.getName()
:获取快照名称。snap.getXMLDesc()
获取快照XML定义等。- **恢复快照:**可以通过域对象或快照对象执行恢复操作:
dom.revertToSnapshot(snap, flags=0)
将虚拟机恢复到指定快照状态 (libvirt: Module libvirt-domain-snapshot from libvirt)。这会使虚拟机回到快照时刻的状态。也可以调用快照对象的snap.revertToSnapshot(flags)
达到同样效果。 - 删除快照:
snap.delete(flags=0)
删除该快照 (libvirt: Module libvirt-domain-snapshot from libvirt)。可使用标志控制是否递归删除子快照等(如VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN
)。
**示例:**创建一个名为 "snapshot1" 的快照并恢复:
# 创建快照 snap_xml = "<domainsnapshot><name>snapshot1</name></domainsnapshot>" snap = dom.snapshotCreateXML(snap_xml, 0) print("快照创建成功:", snap.getName()) # ... (进行一些虚拟机上的操作) ... # 恢复快照 dom.revertToSnapshot(snap) print("已恢复至快照:", snap.getName()) # 删除快照 snap.delete()
当创建系统快照时,虚拟机可以是运行中(系统会做磁盘快照并可选内存快照),恢复快照相当于回滚虚拟机状态。快照管理涉及磁盘存储和配置,操作前请了解其对数据的影响。
**增量备份检查点(Checkpoint):**libvirt 还引入了域检查点 (
virDomainCheckpoint
) 用于备份增量快照(如备份Guest增量变化)。主要接口类似快照:dom.checkpointCreateXML(xml, flags)
创建检查点,dom.listAllCheckpoints()
列出检查点,chkpt.delete()
删除检查点等。检查点可用于增量备份方案(例如 QEMU 提供的增量备份)。**迁移虚拟机:**libvirt 支持多种迁移接口,例如:
dom.migrate(dest_conn, flags, dest_uri, migrate_uri, bandwidth)
:将虚拟机迁移到另一个打开的连接(dest_conn
)上。dom.migrateToURI(uri, flags, dest_xml, bandwidth)
:直接迁移当前虚拟机到指定目标URI的libvirtd。dom.migrateToURI2()
:扩展接口,提供更多参数控制。
迁移操作较复杂,需确保目标主机环境、存储和网络配置匹配。这里不展开示例。
以上涵盖了 virDomain
对象的大部分常用方法。在实际开发中,可通过调用 dom.XMLDesc(flags)
来获取该虚拟机当前的完整 XML 描述,从而了解其配置或用于备份。对 virDomain
的任何修改操作,请合理设置 flags 以确保变更作用于所需的运行态或配置态。
虚拟网络管理(virNetwork 类)
virNetwork
类表示 libvirt 定义的虚拟网络(例如 NAT网络、桥接网络等)。这些网络通常在 libvirt 中用 XML 定义,如经典的 "default" 网络。通过 virConnect
可以定义、启动、停止虚拟网络。
定义和创建网络:
conn.networkDefineXML(xml_desc)
:定义一个虚拟网络配置,但并不启动 (libvirt: Module libvirt-network from libvirt)。返回virNetwork
对象。net.create()
:启动该网络,使其变为活动状态 (libvirt: Module libvirt-network from libvirt)。网络启动后,会在主机上创建相应的桥接口或开启 DHCP/DNS 服务(视网络配置而定)。conn.networkCreateXML(xml_desc)
:直接定义并启动网络 (libvirt: Module libvirt-network from libvirt)。类似于 domain 的 createXML,此方法创建一个临时网络(非持久)。
**示例:**定义一个名称为 "isolated-net" 的简单隔离网络:
network_xml = """ <network> <name>isolated-net</name> <bridge name='virbr10' stp='on' delay='0'/> <ip address='192.168.100.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.100.100' end='192.168.100.254'/> </dhcp> </ip> </network>""" net = conn.networkDefineXML(network_xml) net.create() # 启动网络 print("网络已启动:", net.name(), "桥接设备:", net.bridgeName())
上述 XML 定义了一个仅主机网络(NAT隔离网络),libvirt 会创建
virbr10
网桥,并在其上启用 DHCP 服务。基本属性和操作:
net.name()
:获取网络名称。net.UUIDString()
:获取网络 UUID。net.bridgeName()
:获取网络所使用的桥接接口名称(如 "virbr0")。net.isActive()
:判断网络是否处于启动状态。net.isPersistent()
:判断网络配置是否持久定义在系统中。net.getXMLDesc(flags=0)
:获取网络的XML描述字符串。net.destroy()
:停止一个活动的网络 (libvirt: Module libvirt-network from libvirt)。这将拆除其桥接设备、中断虚拟机该网络的连接。已连接该网络的虚拟机接口通常会断开。net.undefine()
:删除网络配置。如果网络在运行,通常需先 destroy 再 undefine。
自动启动设置:
net.setAutostart(enable)
:设置网络是否随libvirt守护进程启动自动启用。net.getAutostart()
:检查当前自动启动标志。
DHCP租约查询:
net.DHCPLeases(mac=None, flags=0)
:获取网络中DHCP租约信息。可以提供特定MAC地址过滤,也可传入 None 获取全部租约列表 (libvirt: Module libvirt-network from libvirt)。返回的通常是包含租约详情的列表,如每项包含 MAC、IP 地址、租约到期时间等字段。
示例:leases = net.DHCPLeases() # 获取所有DHCP租约 for lease in leases: print(f"MAC: {lease['mac']}, IP: {lease['ipaddr']}, 主机名: {lease.get('hostname','')}")
网络更新:
net.update(command, section, parentIndex, xml, flags)
方法可以对活动网络进行参数更新(如添加删除 DHCP 主机项、防火墙规则等),但较为复杂,这里不详细展开。
网络端口管理(virNetworkPort 类)
网络端口 (virNetworkPort
) 是针对某些网络技术(如 Open vSwitch 等)引入的对象,表示网络中的虚拟端口。它允许对网络中的端口进行独立管理。
创建网络端口:
net.createPortXML(xml_desc, flags=0)
:在网络上创建新端口 (libvirt: Module libvirt-network from libvirt)。XML 通常包含端口的 UUID、参数(如绑定的 MAC 等)。返回virNetworkPort
对象。net.listAllPorts()
:列出该网络下的所有端口对象列表。
网络端口操作:
port.getParameters(flags)
:获取端口的参数,如带宽设置等(返回字典)。port.getXMLDesc(flags)
:获取端口XML (libvirt: Module libvirt-network from libvirt)。port.getNetwork()
:获取此端口所属的virNetwork
对象。port.delete(flags=0)
:删除该网络端口 (libvirt: Module libvirt-network from libvirt)。删除后,与之关联的资源(如绑定的接口)将解除。
一般情况下,网络端口主要用于Open vSwitch等需要预先创建端口再分配给虚拟机的场景。常规的Linux桥接网络并不使用显式 network port 对象。
存储管理
libvirt 将虚拟机存储分为存储池(virStoragePool
)和存储卷(virStorageVol
)两级。存储池表示一类存储的集合(如目录、LVM卷组、iSCSI目标等),存储卷则是存储池中的一个具体存储单元(如镜像文件、LVM逻辑卷)。
存储池管理(virStoragePool 类)
定义和建立存储池:
conn.storagePoolDefineXML(xml_desc, flags=0)
:定义一个新的存储池但不启动 (libvirt: Module libvirt-storage from libvirt)。返回virStoragePool
对象。xml_desc
指定存储池类型(dir、logical、iscsi等)及其参数。pool.build(flags)
:构建存储池的基础结构 (libvirt: Module libvirt-storage from libvirt)。对于需要初始化的池(如创建目录、LVM PV),需调用此方法。flags
常用 0 或VIR_STORAGE_POOL_BUILD_NEW
。pool.create(flags=0)
:启动(激活)存储池 (libvirt: Module libvirt-storage from libvirt)。激活后,存储池即可使用,比如目录池会挂载或准备好路径。conn.storagePoolCreateXML(xml_desc, flags)
:直接定义并创建一个临时存储池并激活(不持久保存配置)。
**示例:**定义一个目录类型的存储池并启动:
pool_xml = """ <storagePool type='dir'> <name>mypool</name> <target> <path>/var/lib/libvirt/images/mypool</path> </target> </storagePool>""" pool = conn.storagePoolDefineXML(pool_xml) pool.build() # 创建目录 pool.create() # 激活池 print("存储池状态:", "运行中" if pool.isActive() else "未激活")
基本属性和操作:
pool.name()
:存储池名称。pool.UUIDString()
:存储池 UUID 字符串。pool.isActive()
:是否已激活(即正在使用)。pool.isPersistent()
:是否为持久定义的池。pool.info()
:获取存储池信息,返回元组 (状态, 容量(byte), 已用空间(byte), 可用空间(byte))。状态一般为0=未激活,1=构建中,2=已激活等。pool.getXMLDesc(flags)
:获取存储池定义 XML。pool.refresh(flags=0)
:刷新存储池内容信息。当存储池下可能有外部变化(新增或删除卷)时调用,更新 libvirt 对该池的认识。pool.autostart()
/pool.setAutostart(enable)
:查询或设置池的开机自动启动。pool.destroy()
:停止(取消激活)存储池。对目录池等会取消占用;对网络存储可能断开连接。pool.undefine()
:删除存储池配置(需先 destroy)。若指定VIR_STORAGE_POOL_DELETE_*
标志,可以在 undefine 时一并删除池内存储内容 (libvirt: Module libvirt-storage from libvirt)(慎用,可能删除数据)。
列出和查找存储池:
conn.listAllStoragePools()
返回所有存储池对象列表,可用 flags 过滤活动/非活动等。也可使用conn.storagePoolLookupByName()
或通过 UUID 查找特定池。
存储卷管理(virStorageVol 类)
创建存储卷:
pool.createXML(xml_desc, flags=0)
:在指定存储池中创建新的存储卷 (libvirt: Module libvirt-storage from libvirt)。xml_desc
描述卷名称、容量、格式等,例如:
返回一个<volume> <name>disk1.qcow2</name> <capacity unit="GB">10</capacity> <target><format type="qcow2"/></target> </volume>
virStorageVol
对象。pool.createXMLFrom(xml_desc, clone_vol, flags=0)
:从已有卷clone_vol
克隆创建新卷 (libvirt: Module libvirt-storage from libvirt)。用于快速复制模板镜像。
**示例:**创建一个1GB的QCOW2格式存储卷:
vol_xml = """ <volume> <name>testvol.qcow2</name> <capacity unit='GB'>1</capacity> <target><format type='qcow2'/></target> </volume>""" vol = pool.createXML(vol_xml) print("新建卷:", vol.name(), "路径:", vol.path())
列出和查找存储卷:
pool.listAllVolumes()
:列出存储池中的所有卷对象列表。pool.listVolumes()
:列出存储池内所有卷名称列表(旧接口)。conn.storageVolLookupByName(pool, vol_name)
:通过名称获取存储卷(需要提供所属池或在 XML 中全局唯一)。新版中通常通过池对象的 volumes 列表获取。conn.storageVolLookupByKey(key)
:按唯一键(通常是卷路径或标识符)查找卷。conn.storageVolLookupByPath(path)
:通过卷的物理路径查找卷。
卷属性和操作:
vol.name()
:卷名称。vol.key()
:卷的唯一键,一般对应后端标识(如文件路径、LVM UUID 等)。vol.path()
:卷的路径,如文件卷则为文件系统路径。vol.info()
:获取卷信息,返回 (类型, 容量(byte), 已用(byte))。类型一般为1=文件,2=块设备等。vol.getXMLDesc(flags)
:获取卷定义XML。vol.resize(capacity_bytes, flags=0)
:调整卷容量 (libvirt: Module libvirt-storage from libvirt)。支持的前提是后端允许扩展(如文件或逻辑卷)。flags可选VIR_STORAGE_VOL_RESIZE_ALLOCATE
等控制行为。vol.wipe(flags=0)
:安全擦除卷数据,将卷内容归零或随机填充,以防止数据恢复。flags 可指定擦除算法(如VIR_STORAGE_VOL_WIPE_ALG_ZERO
)。vol.delete(flags=0)
:删除该存储卷 (libvirt: Module libvirt-storage from libvirt)。文件卷将删除文件,LVM卷将销毁逻辑卷等。flags 可以指定VIR_STORAGE_VOL_DELETE_WITH_SNAPSHOTS
以删除相关快照。
数据上传与下载:
存储卷可以通过数据流 (virStream
) 进行读写:vol.upload(stream, offset, length, flags=0)
:将数据上传到卷 (libvirt: Module libvirt-storage from libvirt)。需要提供一个打开的virStream
流,以及上传起始偏移和长度。通常在创建空卷后,用该方法上传磁盘镜像数据。vol.download(stream, offset, length, flags=0)
:从卷下载数据到流 (libvirt: Module libvirt-storage from libvirt)。
**示例:**将本地文件上传到存储卷:
import os # 本地文件准备 file_path = "/tmp/upload.img" file_size = os.path.getsize(file_path) # 创建数据流 stream = conn.newStream(0) # 开始上传:offset=0, length为文件大小 vol.upload(stream, 0, file_size, 0) # 将本地文件内容写入流 with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): stream.send(chunk) stream.finish() print("文件上传完成:", vol.path())
上述过程:先用
conn.newStream()
获取一个数据流对象,将其传给vol.upload
开始上传,然后读取本地文件分块通过stream.send()
发送数据,最后调用stream.finish()
完成传输。如果中途要中止,可调用stream.abort()
。类似地,可用vol.download
+stream.recv
实现卷到本地的下载。
主机设备管理(virNodeDevice 类)
virNodeDevice
表示宿主机的物理或逻辑设备,例如PCI设备、USB设备、硬件加速器等。这些设备通常由 libvirt 枚举,用于设备直通(passthrough)等场景管理。
列出与查找设备:
conn.listAllNodeDevices(cap=None)
:列出所有主机设备对象,可选按能力类型过滤(如"pci"
、"usb"
等capability)。conn.nodeDeviceLookupByName(name)
:按名称获取设备对象,名称通常形如"pci_0000_04_00_0"
等标识。
设备信息:
dev.getName()
:获取设备名称。dev.getParent()
:获取父设备名称(如一个功能的父可能是物理函数)。dev.getXMLDesc(flags)
:获取设备的XML描述,其中包含该设备的各种能力(如设备ID、供应商ID、可用功能等)。dev.listCaps()
:获取该设备的能力列表(capabilities),例如["pci"]
或包含具体功能。dev.numOfCaps()
:能力数量。
设备操作:
dev.detach()
或dev.dettach()
(旧名称):将设备从主机操作系统分离 (libvirt: Module libvirt-nodedev from libvirt)。例如将主机PCI设备unbind出驱动,为直通给虚拟机做准备。成功执行后设备状态会变为未占用。dev.attach()
或dev.reAttach()
:将之前分离的设备重新附加回主机(重新绑定到驱动) (libvirt: Module libvirt-nodedev from libvirt)。dev.reset()
:对设备执行总线复位 (libvirt: Module libvirt-nodedev from libvirt)。通常在直通设备用完后,为安全起见可复位硬件。dev.destroy()
:某些可移除的逻辑设备可以 destroy(如 mdev 虚拟函数设备),表示将其移除。
**示例:**将一个PCI设备从主机分离用于直通,然后再重新附加:
dev = conn.nodeDeviceLookupByName("pci_0000_04_00_0") print("设备:", dev.getName(), "能力:", dev.listCaps()) dev.detach() # 将设备从主机驱动分离 # 此时可将该设备通过虚拟机的 <hostdev> 配置直通给虚拟机使用 # ...(省略将设备分配给VM的步骤)... dev.reAttach() # 将设备重新附加回主机 dev.reset() # 复位设备
上述流程适用于PCI直通场景。在设备重新附加后,主机可再次使用该设备。注意对于设备直通,通常需配合虚拟机配置中的
<hostdev>
元素使用,该元素引用主机设备的 XML 描述。
主机接口管理(virInterface 类)
virInterface
代表宿主机的物理或虚拟网络接口(网卡)。libvirt 可以管理主机接口的定义(主要用于网桥等自定义接口的创建)。
列出与查找接口:
conn.listAllInterfaces()
:列出所有接口对象(包括活动和非活动) (libvirt: Module libvirt-interface from libvirt)。flags 可以过滤仅活动接口等。conn.interfaceLookupByName(name)
:按接口名称获取对象(例如 "eth0", "br0" 等)。
接口定义与创建:
conn.interfaceDefineXML(xml_desc, flags=0)
:定义一个主机接口(通常是桥接、bond等) (libvirt: Module libvirt-interface from libvirt)。XML 描述接口类型和参数。返回virInterface
对象。iface.create(flags=0)
:激活接口 (libvirt: Module libvirt-interface from libvirt)。对物理接口而言可能是启用(相当于ifup
),对新定义的桥接等则创建并启动该接口。iface.destroy(flags=0)
:停用接口 (libvirt: Module libvirt-interface from libvirt)。iface.undefine()
:删除接口的定义 (libvirt: Module libvirt-interface from libvirt)。物理接口无法undefine,但自定义的(如桥)可以。
接口属性:
iface.name()
:接口名称。iface.MACString()
:接口的MAC地址字符串。iface.isActive()
:接口是否激活(up)。iface.getXMLDesc(flags)
:获取接口XML定义。
**示例:**停用一个接口并再次启用:
iface = conn.interfaceLookupByName("virbr0") if iface.isActive(): iface.destroy() print(iface.name(), "已停用") iface.create() print(iface.name(), "已重新启动,MAC地址:", iface.MACString())
(注:
virbr0
通常是默认网络的桥接接口,一般不建议随意停用;此仅为示例。)
接口管理主要用于需要通过 libvirt 创建自定义桥接网络或 VLAN 接口等高级用例。大多数情况下物理接口由操作系统网络管理,而非通过 libvirt 管理。
密钥与证书管理(virSecret 类)
virSecret
类用于管理保存在 libvirt 内部的密钥/密码数据。例如:用于存储磁盘加密秘钥、CEPH/RBD 用户密码、iSCSI CHAP 密码等。这样敏感信息可以由 libvirt 统一管理并在需要时提供给虚拟机或驱动程序。
定义密钥:
conn.secretDefineXML(xml_desc, flags=0)
:定义一个新的密钥 (libvirt: Module libvirt-secret from libvirt)。xml_desc
需指定<secret>
信息,包括usage
类型和标识。例如:
此处 usage 定义类型(ceph)和名称。<secret ephemeral='no' private='yes'> <description>Ceph client key</description> <usage type='ceph'> <name>client.libvirt secret</name> </usage> </secret>
ephemeral
表示密钥是否临时(重启后消失),private
表示内容是否对非root隐藏。返回virSecret
对象。
密钥查找:
conn.secretLookupByUUIDString(uuid_str)
:根据密钥的 UUID 获取对象。conn.secretLookupByUsage(usage_type, usage_id)
:根据用途类型和标识获取密钥 (libvirt: Module libvirt-secret from libvirt)。例如 usage_type=libvirt.VIR_SECRET_USAGE_TYPE_CEPH
,usage_id 为 Ceph 用户名。
操作与属性:
secret.UUIDString()
:获取密钥 UUID。secret.getUsageType()
:获取密钥用途类型(枚举值,对应 ceph/iscsi 等) (libvirt: Module libvirt-secret from libvirt)。secret.getUsageID()
:获取密钥用途标识字符串(如 Ceph 使用者名称或 iscsi 主机名等) (libvirt: Module libvirt-secret from libvirt)。secret.setValue(value: bytes, flags=0)
:设置密钥的实际值(数据) (libvirt: Module libvirt-secret from libvirt)。参数为字节串和长度,可以传入 Pythonbytes
对象。secret.getValue(flags=0)
:获取密钥值(字节串) (libvirt: Module libvirt-secret from libvirt)。通常需要有相应权限才能获取明文。secret.undefine()
:删除该密钥定义 (libvirt: Module libvirt-secret from libvirt)。删除后密钥值也随之销毁。
**示例:**创建一个静态密钥并设置值:
secret_xml = """ <secret private='no'> <usage type='iscsi'> <target>iqn.2021-04.com.example:storage</target> </usage> </secret>""" sec = conn.secretDefineXML(secret_xml) # 将十六进制字符串转换为二进制数据 secret_data = bytes.fromhex("5f4dcc3b5aa765d61d8327deb882cf99") sec.setValue(secret_data, 0) print("已保存密钥,UUID:", sec.UUIDString()) value = sec.getValue() # 读取密钥值 print("读取密钥值长度:", len(value)) sec.undefine()
上述代码示例定义了一个 iSCSI 类型的密钥并设置了一段示例值(十六进制字符串表示的二进制)。获取值时应谨慎对待,尤其当
private='yes'
时,非特权程序无法读取密钥明文。
网络过滤器管理(virNWFilter 类 及 virNWFilterBinding 类)
libvirt 网络过滤器用于定义虚拟机网络接口的过滤规则(如防火墙规则模板),例如典型的清理 ARP/路由通告的过滤器。virNWFilter
表示一个过滤器模板,可应用于多个接口;virNWFilterBinding
表示一个具体接口与过滤器的绑定实例。
定义过滤器:
conn.nwfilterDefineXML(xml_desc)
:定义一个新的网络过滤器 (libvirt: Module libvirt-nwfilter from libvirt)。XML 包含<filter>
名称及规则(可以嵌入 iptables 规则或 NWFilter 自有的规则语法)。返回virNWFilter
对象。filter.undefine()
:删除过滤器定义 (libvirt: Module libvirt-nwfilter from libvirt)。当没有接口绑定该过滤器时才能成功。
过滤器属性:
filter.name()
:名称。filter.UUIDString()
:UUID。filter.getXMLDesc()
:获取XML定义。
绑定过滤器:
conn.nwfilterBindingCreateXML(xml_desc, flags=0)
:创建一个过滤器绑定,将一个已定义的过滤器应用到某接口上 (libvirt: Module libvirt-nwfilter from libvirt)。XML 例如:
上述将名为 "clean-traffic" 的过滤器应用到接口 "vnet3"(某虚拟机的虚拟网卡)上。返回<filterbinding type='interface'> <filter name='clean-traffic'/> <interface name='vnet3'/> </filterbinding>
virNWFilterBinding
对象。binding.getFilterName()
:获取绑定使用的过滤器名称 (libvirt: Module libvirt-nwfilter from libvirt)。binding.getPortDev()
:获取绑定的设备名称(接口名) (libvirt: Module libvirt-nwfilter from libvirt)。binding.getXMLDesc()
:获取绑定XML。binding.delete()
:删除该绑定 (libvirt: Module libvirt-nwfilter from libvirt)。解除过滤器对接口的应用。
网络过滤器通常需在虚拟机定义的 <interface>
中引用才能自动应用。但也可在虚拟机运行时通过绑定机制动态应用或移除。
数据流(virStream 类)
virStream
提供了数据流通道,用于在客户端应用与 libvirt daemon 之间传输数据。例如在存储卷上传/下载、虚拟机快照备份、迁移等操作中使用。
创建流:
conn.newStream(flags=0)
:创建一个新的流对象 (libvirt: Module libvirt-stream from libvirt)。flags 保持0即可。返回virStream
。- 流创建后,一般需与特定的传输操作关联(如前述
vol.upload
/download
、dom.coreDump
等)。libvirt 会在后台将流与实际的资源连接,比如将流连接到远程文件描述符等。
发送与接收:
stream.send(data: bytes)
:发送数据,通过流写入。data
为字节串。返回实际发送的字节数。stream.recv(nbytes)
:从流读取最多nbytes
字节数据,返回字节串。可能返回少于请求的长度,要检查返回内容长度以判断结束。stream.sendAll(handler, opaque)
/stream.recvAll(handler, opaque)
:高级用法,一次性发送/接收所有数据,由回调函数提供/处理数据。Python 绑定中也可以简单地使用 while 循环 send/recv。stream.finish()
:正常关闭流,通知对端传输完成 (libvirt: Module libvirt-stream from libvirt)。对上传下载操作,finish 通知 libvirt 数据全部写入,从而结束会话。stream.abort()
:中止流传输。用于发生错误时取消传输。
**示例:**通过 stream 单独发送/接收数据:
st = conn.newStream(0) # 假设此时 st 关联到某资源,例如通过 vol.download() data = st.recv(1024) while data: # 处理接收到的数据... data = st.recv(1024) st.finish()
一般情况下不直接使用 stream 来收发自由数据,而是结合 libvirt 提供的功能接口使用。
事件回调:
高级场景下,可使用stream.eventAddCallback(events, callback, opaque)
注册回调,当流就绪可读写或发生EOS等事件时触发,用于异步IO。 (libvirt: Module libvirt-stream from libvirt)通常可用阻塞 read/write 简化处理,回调方式需要配合应用的事件循环。
错误处理与异常
libvirt Python API 调用失败时会抛出 libvirt.libvirtError
异常。可以通过捕获该异常获取错误信息,例如 err = libvirt.libvirtError
的实例有 err.get_error_code()
、err.get_error_message()
等方法来查看错误码和消息。此外,大多数对象方法在失败时返回 -1
或 None
也可作为判断依据。良好的实践是对每次调用进行错误处理,至少使用 try/except 捕获可能的 libvirtError
。
示例:
try:
dom = conn.lookupByName("nonexistent")
except libvirt.libvirtError as e:
print("错误:", e.get_error_message())
小结
以上内容涵盖了 libvirt 8.10.0 Python API 的主要类、方法和用法示例,包括连接、虚拟机、网络、存储、设备、密钥等各方面管理接口。开发者可以使用这些接口以编程方式完成几乎所有通过命令行工具(如 virsh)能够进行的操作。在实际使用中,请参考 libvirt 官方文档获取各方法所需 XML 配置的格式细节,并确保对可能的异常情况进行处理。通过本中文文档的整理,期望能够为 Python 开发者提供清晰的参考,方便地管理 KVM/QEMU 等虚拟化环境的各种资源。
评论区