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 提供了一系列方法获取主机和连接信息:

    示例:

    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)操作,以保存虚拟机在某一时刻的状态。

    # 创建快照
    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 可以定义、启动、停止虚拟网络。

  • 定义和创建网络:

    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():列出该网络下的所有端口对象列表。
  • 网络端口操作:

    一般情况下,网络端口主要用于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) 进行读写:

    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" 等)。
  • 接口定义与创建:

  • 接口属性:

    • 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 类型和标识。例如:
      <secret ephemeral='no' private='yes'>
        <description>Ceph client key</description>
        <usage type='ceph'>
          <name>client.libvirt secret</name>
        </usage>
      </secret>
      
      此处 usage 定义类型(ceph)和名称。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_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 表示一个具体接口与过滤器的绑定实例。

  • 定义过滤器:

  • 过滤器属性:

    • filter.name():名称。
    • filter.UUIDString():UUID。
    • filter.getXMLDesc():获取XML定义。
  • 绑定过滤器:

网络过滤器通常需在虚拟机定义的 <interface> 中引用才能自动应用。但也可在虚拟机运行时通过绑定机制动态应用或移除。

数据流(virStream 类)

virStream 提供了数据流通道,用于在客户端应用与 libvirt daemon 之间传输数据。例如在存储卷上传/下载虚拟机快照备份迁移等操作中使用。

  • 创建流:

    • conn.newStream(flags=0):创建一个新的流对象 (libvirt: Module libvirt-stream from libvirt)。flags 保持0即可。返回 virStream
    • 流创建后,一般需与特定的传输操作关联(如前述 vol.upload/downloaddom.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() 等方法来查看错误码和消息。此外,大多数对象方法在失败时返回 -1None 也可作为判断依据。良好的实践是对每次调用进行错误处理,至少使用 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 等虚拟化环境的各种资源。