libvirt源码分析——virsh

libvirt是用来管理虚拟机或虚拟化功能的软件集合,主要包括:libvirt APIlibvirtd进程virsh工具集三部分。最初的目的是为不同的hypervisor提供统一的管理接口

libvirt是将最底层的直接在shell中输入命令来完成的操作进行了抽象封装,给应用程序开发人员提供了统一的,易用的接口。

libvirt版本:libvirt-4.9.0

libvirt层次体系结构

libviry_api

参照上图,来理一下通过virsh命令或接口创建虚拟机实例的代码执行路径:

  1. virsh命令或API接口c创建虚拟机 —— 接口层

    1
    virsh create vm.xml 或者 virDomainPtr virDomainCreateXML (virConnectPtr conn, const char * xmlDesc, unsigned int flags)

    file: src/libvirt-domain.c

  2. 调用libvirt提供的统一接口 —— 抽象驱动层

    1
    conn->driver->domainCreateXML(conn, xmlDesc, flags);  //此处的domainCreateXML即抽象的统一接口,这里并不需要关心底层的driver是kvm,还是xen
  3. 调用底层的相应虚拟化技术的接口 —— 具体驱动层

    1
    domainCreateXML = qemuDomainCreateXML; //如果driver=qemu,那么此处即调用的qemu注册到抽象驱动层上的函数qemuDomainCreateXML
  4. 拼装shell命令,并执行

抽象驱动层

目前,libvirt以下几种类型的抽象驱动,每一种类型的驱动代表某以功能模块的抽象封装:

  • 虚拟化驱动(virDriverPtr)
  • 虚拟网络驱动(virNetworkDriverPtr)
  • 物理网卡驱(virInterfaceDriverPtr)
  • 存储驱动(virStorageDriverPtr)
  • 监控驱动(virDeviceMonitorPtr)
  • 安全驱动(virSecretDriverPtr)
  • 过滤驱动(virNWFilterDriverPtr)
  • 状态驱动(virStateDriverPtr)

virsh start vm-name

1
virsh start vm-name

启动一个虚拟机

daemon进程(libvirtd )

该后台进程主要实现以下功能:

  1. 远程代理
    所有remote client发送来的命令,由该进程监测执行
  2. 本地环境初始化
    libvirt服务的启停,用户connection的响应等
  3. 根据环境注册各种Driver(qemu, xen, storage…)的实现
    不同虚拟化技术以Driver的形式实现,由于libvirt对外提供的是统一的接口,所以各个Driver就是实现这些接口, 即将Driver注册到libvirt中

virsh API调用

将libvirt API封装,以Command Line Interface提供的对外接口

1
2
$virsh define /path-vm-xml/vm_name.xml
$virst start vm-name

virsh命令与代码结构之间的关系:

文件名对应vshCmdDef变量对应virsh命令
virsh-domain-monitor.cdomMonitoringCmdsvirsh XX(虚拟机监控)
virsh-domain.cdomManagementCmdsvirsh XX(虚拟机操作)
virsh-host.chostAndHypervisorCmdsvirsh XX(虚拟机配置)
virsh-interface.cifaceCmdsvirsh iface-XX
virsh-network.cnetworkCmdsvirsh net-XX
virsh-nodedev.cnodedevCmdsvirsh net-XX
virsh-nwfilter.cnwfilterCmdsvirsh nwfilter-XX
virsh-pool.cstoragePoolCmdsvirsh pool-XX
virsh-secret.csecretCmdsvirsh secret-XX
virsh-snapshot.csnapshotCmdsvirsh snapshot-XX
virsh-volume.cstorageVolCmdsvirsh vol-XX

有了上面的表格我们就能够根据使用的virsh命令找到对应文件的对应vshCmdDef变量,在virsh中相关命令实现与具体API的调用文件相对于tools/virsh-domain.c <=> src/libvirt-domain.c

一个vshCmdDef结构对应一个virsh命令,其中vshCmdOptDef定义了命令的参数,vshCmdInfo定义了命令的帮助信息,bool (*handler) (vshControl *, const vshCmd *)定义了命令的处理函数。

domManagementCmds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const vshCmdDef domManagementCmds[] = {
{.name = "attach-device",
.handler = cmdAttachDevice,
.opts = opts_attach_device,
.info = info_attach_device,
.flags = 0
},
...
{.name = "start",
.handler = cmdStart,
.opts = opts_start,
.info = info_start,
.flags = 0
},
...

file: tools/virsh-domain.c

启动虚拟机的主要工作:

1
virst start vm-name

start命令的处理流程是cmdStart

1
2
3
4
5
6
7
8
9
10
static bool
cmdStart(vshControl *ctl, const vshCmd *cmd)
{
...
if ((nfds ?
virDomainCreateWithFiles(dom, nfds, fds, flags) :
virDomainCreateWithFlags(dom, flags)) == 0)
goto started;
...
}
  • virDomainCreateWithFiles: 启动已定义的域。如果调用成功,则域将从已定义的域池移动到正在运行的域池
  • virDomainCreateWithFlags: 启动已定义的域。如果调用成功,则域将从已定义的域池移动到正在运行的域池
1
2
virDomainCreateWithFiles
\-> conn->driver->domainCreateWithFiles
1
2
3
4
5
virDomainCreateWithFlags
\-> conn->driver->domainCreateWithFlags
\-> qemuDomainCreateWithFlags
\-> qemuProcessStart
\->

file: src/qemu/qemu_driver.c

libvirt for qemu

由于libvirt是将最底层需要执行的shell命令进行了抽象封装,供上层应用使用。因此在其封装的借口中必须存在一个shell运行环境

  • 底层qemu的命令何时被创建?
  • 底层shell环境的搭建?
  • 如何执行的该命令?

配置QEMU环境变量与参数

1
2
3
4
5
6
7
8
9
10
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
...
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
</devices>
<qemu:commandline>
<qemu:arg value='-newarg'/>
<qemu:env name='QEMU_ENV' value='VAL'/>
</qemu:commandline>
</domain>

1
2
3
<qemu:commandline xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<qemu:arg value='ARGUMENT'/>
</qemu:commandline>

在xml配置文件中必须指定<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>,因为默认的<domain type='kvm'>不支持qemu:commandline的标签

  • https://libvirt.org/drvqemu.html#qemucommand

参考

  • libvirt架构及源码分析
  • virsh commands cheatsheet to manage KVM guest virtual machines
  • How to pass QEMU command-line options through libvirt
  • QEMUSwitchToLibvirt(-s)