libvirt Java API用法连载之libvirt Java API使用详解(四)
来源:互联网 发布:新浪短域名 编辑:程序博客网 时间:2024/05/16 02:54
要想通过 libvirt 创建虚拟机,首先要和 Hypervisor 建立连接。libvirt Java API提供了 Connect 对象来建立连接。连接成功后,所有操作都是在该连接上实现,如 libvirt Java API 提供了 NodeInfo 对象来管理宿主机(节点),提供了 Domain、DomainInfo、MemoryStatistic(用于客户机内存监控) 对象来管理客户机(虚拟机);提供了 StoragePool、StoragePoolInfo 对象来管理宿主机(节点)硬盘,提供了 StorageVolume、StorageVolumeInfo 对象来管理客户机(虚拟机)硬盘;提供了 Interface、Network 对象来管理网络。
Linux 系统环境
虚拟机:VMware 12.1.0
系统版本:Ubuntu 16.04 64 位
Connect 建立连接
Connect 对象的构造函数有 3 个重载方法。
- Connect(String uri)
此方法建立的连接支持读写。 - Connect(String uri, boolean readOnly)
此方法建立的连接可设置是否只读。 - Connect(String uri, ConnectAuth auth, int flags)
此方法建立的连接可传入一个安全验证对象。
构造函数中的 uri 参数可以是本地 URI,也可以是远程 URI。远程 URI 可以建立到远程宿主机(节点)的连接。
本地 URI
URI 的格式如下:
driver:///path
- driver 表示虚拟化引擎如 qemu、xen、test 等。
- path 表示资源范围,有两个值:system 和 session,其中 system 表示所有虚拟化资源,session 表示当前用户的虚拟化资源,通常使用 system。
示例代码:
private void localConnect() throws LibvirtException { logger.info("local connect execute succeeded"); Connect connect = new Connect("qemu:///system", true); logger.info("连接到的宿主机的主机名:{}", connect.getHostName()); logger.info("JNI连接的libvirt库版本号:{}", connect.getLibVirVersion()); logger.info("连接的URI:{}", connect.getURI()); }
本地调用可用于熟悉 libvirt Java API,远程调用才是本系列文章的重点。
远程 URI
URI 的格式如下:
driver[+transport]://[username@][hostname][:port]/[path][?extraparameters]
- driver 表示虚拟化引擎如 qemu、xen、test 等。
- transport 表示传输协议如 tcp 等。
- username 表示要连接的远程宿主机的用户名,示例代码为:kyyee。(可不填)
- hostname 表示要连接的远程宿主机的 IP 地址,示例代码为:192.168.10.105。
- port 表示要连接的远程宿主机的端口。(如果传输协议需要的话)
- path 表示资源范围,有两个值:system 和 session,其中 system 表示所有虚拟化资源,session 表示当前用户的虚拟化资源,通常使用 system。
- extraparameters 表示使用特定传输协议的额外参数。
transport 除支持 tcp 外还支持的传输协议有:tls,unix,ssh,ext,libssh2,libssh,如果没有特别说明,默认的传输协议是tls。有关这些协议的更多说明可参考官网,传送门。
extraparameters 的更多用法可参考官网,传送门。
下文所有的示例代码均使用 tcp 的方式连接宿主机。
tcp 是一种未加密的 TCP/IP socket,不推荐在生产环境使用,本系列文章使用 tcp 只是为了方便测试。
libvirt 默认配置没有开启 tcp 传输协议支持。按照如下方式可以开启 TCP 连接。
修改 /etc/libvirt/libvirtd.conf
sudo gedit /etc/libvirt/libvirtd.conf
# 禁用 tls 传输协议listen_tls = 0# 启用 tcp 传输协议listen_tcp = 1# tcp 端口 16509tcp_port = "16509"listen_addr = "0.0.0.0"# 关闭 tcp 认证auth_tcp = "none"
修改/etc/default/libvirt-bin
sudo gedit /etc/default/libvirt-bin
start_libvirtd="yes"libvirtd_opts="-d -l"
libvirtd.conf 的更多配置方式可参考官网,传送门
重启 libvirtd
sudo /etc/init.d/libvirt-bin restart
重启成功后就可以使用 tcp 传输协议连接到 libvirtd 了,但像上面这样设置后连接是无安全认证的,不推荐在生产环境使用。
认证授权的更多用法可参考官网,传送门。
示例代码远程连接 libvirtd 的 uri 为:qemu+tcp://192.168.10.105:16509/system
连接示例代码:
private void remoteConnectByTcp() throws LibvirtException { logger.info("remote connect execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.105:16509/system", true); logger.info("连接到的宿主机的主机名:{}", connect.getHostName()); logger.info("JNI连接的libvirt库版本号:{}", connect.getLibVirVersion()); logger.info("连接的URI:{}", connect.getURI()); logger.info("连接到的宿主机的剩余内存:{}", connect.getFreeMemory()); logger.info("连接到的宿主机的最大Cpu输了:{}", connect.getMaxVcpus(null)); logger.info("hypervisor的名称:{}", connect.getType()); logger.info("hypervisor的容量(返回的是一个xml字符串,用处不大):\n{}", connect.getCapabilities()); }
Connect 对象的 getMaxVcpus(String type) 的方法,这个地方传入的参数可以是 null,也可以是虚拟机对应 xml 文件中元素的 type 属性,也就是说 qemu 是 kvm ,xen 是 xen,推荐直接填 null。
Capabilities 描述 xml 的更多信息可参考官网:传送门
管理镜像
QEMU 自带一个虚拟机磁盘管理工具 qemu-img,它可以用来创建或克隆虚拟机,KVM 虚拟机常用的镜像格式有 raw 和 qcow2,raw 是简单的二进制镜像文件格式,qcow2 是主流的一种虚拟化镜像文件格式。qcow2 比 raw 的优势在于:更小的磁盘空间占用,支持写时复制,支持快照,支持压缩和加密。
使用 qemu-img 创建或克隆镜像不是本系列文章的重点,下面将用示例代码的方式演示如何调用 libvirt Java API 创建和克隆镜像。
创建镜像是在宿主机上,准确的说是在宿主机的物理硬盘上创建镜像,而 libvirt 提供的 StoragePool 就是专门用来管理宿主机物理硬盘的。
管理宿主机物理硬盘
libvirt 为我们提供了一个默认的 StoragePool,名称叫 default,对应宿主机的 /var/lib/libvirt/images/ 物理空间。
遍历 StoragePool
libvirt 提供了一个接口可以遍历宿主机的 StoragePool。
遍历示例代码:
private void listStoragePool() throws LibvirtException { logger.info("list storage pool execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.105:16509/system", true); String[] pools = connect.listStoragePools(); logger.info("存储池个数:{}", pools.length); for (String pool : pools) { logger.info("存储池名称:{}", pool); } }
这段代码也证实了 libvirt 提供的默认 StoragePool 的名称为:default。
查询 StoragePool
Connect 对象提供了 4 种方式获得 StoragePool 对象,除去一种已过时的方法,还有:
- StoragePool storagePoolLookupByName(String name)
通过唯一的名称获取 storage pool。 - StoragePool storagePoolLookupByUUID(UUID uuid)
通过全局唯一的 id 获取 storage pool。 - StoragePool storagePoolLookupByUUIDString(String UUID)
通过全局唯一的 id 获取 storage pool。
通过唯一的名称获取 storage pool 的示例代码:
private void getStoragePoolbyName(@RequestParam String name) throws LibvirtException { logger.info("get storage pool by name execute succeeded"); logger.info("request parameter:{}",name); Connect connect = new Connect("qemu+tcp://192.168.10.105:16509/system", true); StoragePool storagePool = connect.storagePoolLookupByName("default"); StoragePoolInfo storagePoolInfo = storagePool.getInfo(); logger.info("存储池的状态:{}", storagePoolInfo.state); logger.info("存储池的容量:{}GB", storagePoolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的可用容量:{}GB", storagePoolInfo.available / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的已用容量:{}GB", storagePoolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的描述xml:\n {}", storagePool.getXMLDesc(0)); }
备注:这几个属性取得的容量值的单位都是bytes。
从图中可以看出,获取到的磁盘容量有部分损失,作为监控宿主机和分配资源使用还是可行的。
创建 StoragePool
StoragePool 对象的方法 String getXMLDesc(int flags) 可以取得 StoragePool 配置描述 xml,可依照这个描述自定义 StoragePool 配置描述 xml。
示例 StoragePool 配置描述 xml:
<?xml version="1.0" encoding="UTF-8"?><pool type="dir"> <name>virtimages</name> <!--名称必须唯一--> <source> </source> <capacity unit='GiB'>40</capacity> <!--StoragePool 的容量--> <allocation>0</allocation> <!--StoragePool 的已用容量--> <target> <path>/home/kyyee/images</path> <!--StoragePool 在宿主机的路径--> <permissions> <!--权限--> <mode>0711</mode> <owner>0</owner> <group>0</group> </permissions> </target></pool>
StoragePool 配置描述 xml 的更多用法可参考官网:传送门
有了 StoragePool 配置描述 xml,就可以很轻松的创建或定义 StoragePool 了。
创建 storage pool 的示例代码(此代码未验证):
private void createStoragePool() throws LibvirtException, DocumentException { logger.info("create storage pool execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.105:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo-storage-pool.xml")); String xmlDesc = document.asXML(); StoragePool storagePool = connect.storagePoolCreateXML(xmlDesc, 0); StoragePoolInfo storagePoolInfo = storagePool.getInfo(); logger.info("存储池的状态:{}", storagePoolInfo.state); logger.info("存储池的容量:{}GB", storagePoolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的可用容量:{}GB", storagePoolInfo.available / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的已用容量:{}GB", storagePoolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的描述xml:\n {}", storagePool.getXMLDesc(0)); }
定义 StoragePool
定义 storage pool 的示例代码(此代码未验证):
private void defineStoragePool() throws LibvirtException, DocumentException { logger.info("define storage pool execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo-storage-pool.xml")); String xmlDesc = document.asXML(); logger.info("defineStoragePool description:\n{}", xmlDesc); StoragePool storagePool = connect.storagePoolDefineXML(xmlDesc, 0); StoragePoolInfo storagePoolInfo = storagePool.getInfo(); logger.info("存储池的状态:{}", storagePoolInfo.state); logger.info("存储池的容量:{}GB", storagePoolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的可用容量:{}GB", storagePoolInfo.available / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的已用容量:{}GB", storagePoolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储池的描述xml:\n {}", storagePool.getXMLDesc(0)); }
创建 storage pool 除了可调用 StoragePool storagePoolCreateXML(String xmlDesc, int flags) 方法外,还可调用 StoragePool storagePoolDefineXML(String xml, int flags) 。
具体的,storagePoolCreateXML 创建的是一个临时的 pool ,删除它只需调用 destroy(),或者重启宿主机,就会消失;而 storagePoolDefineXML 定义的是一个持久化的 pool,除非明确调用 undefine(),否则它一直存在。
删除 StoragePool
删除 storage pool 示例代码:
private void deleteStoragePool() throws LibvirtException, DocumentException { logger.info("delete storage pool execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); StoragePool storagePool = connect.storagePoolLookupByName("default"); logger.info("存储池名称:{}", storagePool.getName());// storagePool.free(); storagePool.destroy(); storagePool.undefine(); }
管理客户机(虚拟机)镜像
前面讲了这么多,都是为了给创建虚拟机镜像做铺垫。libvirt 提供的 StorageVolume 就是专门用来管理客户机物理硬盘的,由于 StorageVolume 与 StoragePool 关系密切,前面的内容是实现创建虚拟机镜像的基础。
下面将根据讲解 StoragePool 的层次结构来讲解 StorageVolume。
遍历 StorageVolume
libvirt 提供了一个接口可以遍历 StoragePool 下的所有的 StorageVolume。
遍历示例代码:
private void listStorageVolume() throws LibvirtException { logger.info("list storage volume execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system", true); StoragePool storagePool = connect.storagePoolLookupByName("default"); String[] volumes = storagePool.listVolumes(); logger.info("存储卷个数:{}", volumes.length); for (String volume : volumes) { if (volume.contains("iso")) continue; // 过滤掉 iso 文件 logger.info("存储卷名称:{}", volume); } }
这里有一个文件是 iso 文件,被过滤了,没有显示。
查询 StorageVolume
StoragePool 对象提供了 1 种方式获得 StorageVol 对象:
- StorageVol storageVolLookupByName(String name)
通过唯一的名称获取 storage volume。
通过唯一的名称获取 storage volume 的示例代码:
private void getStorageVolumebyName() throws LibvirtException { logger.info("get storage volume by name execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system", true); StoragePool storagePool = connect.storagePoolLookupByName("default"); String[] volumes = storagePool.listVolumes(); for (String volume : volumes) { if (volume.contains("iso")) continue; // 过滤掉 iso 文件 StorageVol storageVol = storagePool.storageVolLookupByName(volume); StorageVolInfo storageVolInfo = storageVol.getInfo(); logger.info("存储卷名称:{}", volume); logger.info("存储卷的类型:{}", storageVolInfo.type); logger.info("存储卷的容量:{} GB", storageVolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的可用容量:{} GB", (storageVolInfo.capacity - storageVolInfo.allocation) / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的已用容量:{} GB", storageVolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的描述xml:\n {}", storageVol.getXMLDesc(0)); } }
备注:这几个属性取得的容量值的单位都是bytes。
获取客户机(虚拟机)磁盘容量非常准确,我给客户机分配的磁盘空间就是 15 GB,将这个 api 作为监控客户机(虚拟机)使用完全没有问题。
创建 StorageVolume
StorageVol 对象的方法 String getXMLDesc(int flags) 可以取得 StorageVol 配置描述 xml,可依照这个描述自定义 StorageVol 配置描述 xml。
示例 StorageVol 配置描述 xml:
<?xml version="1.0" encoding="UTF-8"?><volume type='file'> <name>kvmdemo.qcow2</name> <!--名称必须唯一--> <source> </source> <capacity unit='GiB'>10</capacity> <!--StorageVol 的容量--> <allocation>0</allocation> <!--StorageVol 的已用容量--> <target> <path>/var/lib/libvirt/images/kvmdemo.qcow2</path> <!--StorageVol 在宿主机的路径--> <format type='qcow2'/> <!--文件类型,通常都是 qcow2,也可以是 raw--> <permissions> <!--权限--> <mode>0600</mode> <owner>0</owner> <group>0</group> </permissions> </target></volume>
StorageVol 配置描述 xml 的更多用法可参考官网:传送门
有了 StorageVol 配置描述 xml,就可以很轻松的创建或克隆 StorageVol 了。
创建 storage volume 的示例代码:
private void createStorageVolume() throws LibvirtException, DocumentException { logger.info("create storage volume execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo-storage-vol.xml")); String xmlDesc = document.asXML(); logger.info("createStorageVolume description:\n{}", xmlDesc); StoragePool storagePool = connect.storagePoolLookupByName("default"); logger.info("This could take some times at least 3min..."); StorageVol storageVol = storagePool.storageVolCreateXML(xmlDesc, 0); logger.info("create success"); logger.info("createStorageVolume name:{}", storageVol.getName()); logger.info("createStorageVolume path:{}", storageVol.getPath()); StorageVolInfo storageVolInfo = storageVol.getInfo(); logger.info("存储卷的类型:{}", storageVolInfo.type); logger.info("存储卷的容量:{} GB", storageVolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的可用容量:{} GB", (storageVolInfo.capacity - storageVolInfo.allocation) / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的已用容量:{} GB", storageVolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的描述xml:\n {}", storageVol.getXMLDesc(0)); }
这里需要一个 xml 工具包来操作 xml 文件,示例代码使用的是 dom4j
创建 storage volume 除了可调用 StorageVol storageVolCreateXML(String xmlDesc, int flags) 方法外,还可调用 StorageVol storageVolCreateXMLFrom(String xmlDesc, StorageVol cloneVolume, int flags) 。
具体的,storageVolCreateXML 创建的一个全新的镜像,如果虚拟机基于这个镜像文件创建,那么还需要挂载光盘驱动器安装操作系统才能使用虚拟机,而 storageVolCreateXMLFrom 基于传入的 cloneVolume 派生(克隆)一个镜像,但是要保证派生(克隆)的镜像的名称和路径唯一,否则派生(克隆)不成功。
克隆 StorageVolume
克隆 storage volume 的示例代码:
private void cloneStorageVolume() throws LibvirtException, DocumentException { logger.info("clone storage volume execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo-storage-vol.xml")); String xmlDesc = document.asXML(); logger.info("createStorageVolume description:\n{}", xmlDesc); StoragePool storagePool = connect.storagePoolLookupByName("default"); // 克隆的基镜像,这个镜像需要自己制作,可使用 virt-manager 制作基镜像,本示例代码采用的基镜像是 Ubuntu 16.04 64位 StorageVol genericVol = storagePool.storageVolLookupByName("generic.qcow2"); logger.info("This could take some times at least 3min..."); StorageVol storageVol = storagePool.storageVolCreateXMLFrom(xmlDesc, genericVol, 0); logger.info("clone success"); logger.info("createStorageVolume name:{}", storageVol.getName()); logger.info("createStorageVolume path:{}", storageVol.getPath()); StorageVolInfo storageVolInfo = storageVol.getInfo(); logger.info("存储卷的类型:{}", storageVolInfo.type); logger.info("存储卷的容量:{} GB", storageVolInfo.capacity / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的可用容量:{} GB", (storageVolInfo.capacity - storageVolInfo.allocation) / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的已用容量:{} GB", storageVolInfo.allocation / 1024.00 / 1024.00 / 1024.00); logger.info("存储卷的描述xml:\n {}", storageVol.getXMLDesc(0)); }
克隆镜像的基镜像需要自己制作,可使用 virt-manager 制作基镜像,本示例代码使用 virt-manager 制作的 Ubuntu 16.04 64 位基镜像。
删除 StorageVolume
删除 storage volume 示例代码:
private void deleteStorageVolume() throws LibvirtException, DocumentException { logger.info("delete storage volume execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); StoragePool storagePool = connect.storagePoolLookupByName("default"); StorageVol storageVol = storagePool.storageVolLookupByName("kvmdemo.qcow2"); logger.info("存储卷名称:{}", storageVol.getName()); storageVol.wipe(); storageVol.delete(0); }
管理虚拟机
前面讲了那么多“废话”,终于到了最关键的时候,接下来我们将调用 libvirt Java API 创建虚拟机。但是在这之前,我们还有一件重要的事情,需将宿主机(节点)的网卡设置为桥接模式。
将宿主机(节点)网络设置为桥接模式
固定 IP 的桥接模式
笔者使用的是这种方式,优势在于 IP 固定,方便开发,不用担心 IP 更改,造成服务不可用,但是配置稍复杂。
控制台输入如下命令。
sudo gedit /etc/network/interfaces
打开 gedit,默认情况下有如下文本:
# interfaces(5) file used by ifup(8) and ifdown(8)auto loiface lo inet loopback
末尾换行追加如下文本:
auto ens33iface ens33 inet manualauto br0iface br0 inet staticaddress 192.168.10.231netmask 255.255.255.0broadcast 192.168.10.255gateway 192.168.10.1dns-nameserver 192.168.10.1bridge_ports ens33bridge_stp offbridge_fd 0bridge_maxwait 0
address 是你希望设置的 IP 地址,桥接模式下的 IP 需与物理主机的 IP 在同一网段。
dns-nameserver 是 DNS 地址,与物理主机的 DNS 地址一致即可,如果不配置 dns-nameserver 将无法识别公网 url,如 http://blog.csdn.net/kyyee。
保存退出后,控制台输入如下命令重启网络。
sudo /etc/init.d/networking restart
DHCP 获取 IP 等信息的桥接模式
这种方式,宿主机(节点)直接从 DHCP 服务器获取 IP 等信息,免于配置 IP 等信息,缺点就是 IP 随时可能会变。
控制台输入如下命令。
sudo gedit /etc/network/interfaces
打开 gedit,默认情况下有如下文本:
# interfaces(5) file used by ifup(8) and ifdown(8)auto loiface lo inet loopback
末尾换行追加如下文本:
auto ens33iface ens33 inet manualauto br0iface br0 inet dhcpbridge_ports ens33bridge_stp offbridge_fd 0
保存退出后,控制台输入如下命令重启网络。
sudo /etc/init.d/networking restart
管理虚拟机生命周期
在管理镜像的章节,我们创建了一个名为:kvmdemo.qcow2的镜像,现在这个镜像终于派上用场了,接下来的虚拟机将使用该镜像作为自己的虚拟硬盘,libvirt Java API 提供的与虚拟机生命周期相关的对象是 Domian。
下面将根据讲解 StorageVolume 的层次结构来讲解 Domain。
遍历宿主机上的客户机(虚拟机)
libvirt 提供了一个接口可以遍历 Connect 下的所有的 Domain。
遍历示例代码:
private void listDomain() throws LibvirtException { logger.info("list domain execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system", true); int[] idsOfDomain = connect.listDomains(); logger.info("正在运行的虚拟机个数:{}", idsOfDomain.length); for (int id : idsOfDomain) { logger.info("正在运行的虚拟机id:{}", id); } String[] namesOfDefinedDomain = connect.listDefinedDomains(); logger.info("已定义未运行的虚拟机个数:{}", namesOfDefinedDomain.length); for (String name : namesOfDefinedDomain) { logger.info("已定义未运行的虚拟机名称:{}", name); } }
查询客户机(虚拟机)
Connect 对象提供了 5 种方式获得 Domain 对象:
- Domain domainLookupByID(int id)
通过唯一的 id 获取客户机(虚拟机)。 - Domain domainLookupByName(String name)
通过唯一的名称获取客户机(虚拟机)。 - Domain domainLookupByUUID(int[] UUID)
通过唯一的 UUID 获取客户机(虚拟机)。 - Domain domainLookupByUUID(UUID uuid)
通过唯一的 UUID 获取客户机(虚拟机)。 - Domain domainLookupByUUIDString(String UUID)
通过唯一的 UUID 获取客户机(虚拟机)。
这几个通过 UUID 获取客户机(虚拟机)的方法,传入的参数有些微差异,这是根据你定义虚拟机时使用的 UUID 规则决定的,本系列文章主要使用名称获取客户机(虚拟机),不会使用 UUID 获取客户机(虚拟机),这里不在过多介绍。
通过唯一的id和名称获取 domain 的示例代码:
private void getDomainbyIdOrName() throws LibvirtException { logger.info("get domain by id execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system", true); int[] idsOfDomain = connect.listDomains(); logger.info("正在运行的虚拟机个数:{}", idsOfDomain.length); for (int id : idsOfDomain) { Domain domain = connect.domainLookupByID(id); logger.info("虚拟机的id:{}", domain.getID()); logger.info("虚拟机的uuid:{}", domain.getUUIDString()); logger.info("虚拟机的名称:{}", domain.getName()); logger.info("虚拟机的是否自动启动:{}", domain.getAutostart()); logger.info("虚拟机的状态:{}", domain.getInfo().state); } String[] uuidsOfDefinedDomain = connect.listDefinedDomains(); logger.info("已定义未运行的虚拟机个数:{}", uuidsOfDefinedDomain.length); for (String uuid : uuidsOfDefinedDomain) { Domain domain = connect.domainLookupByName(uuid); logger.info("虚拟机的id:{}", domain.getID()); logger.info("虚拟机的uuid:{}", domain.getUUIDString()); logger.info("虚拟机的名称:{}", domain.getName()); logger.info("虚拟机的是否自动启动:{}", domain.getAutostart()); logger.info("虚拟机的状态:{}", domain.getInfo().state); } }
创建客户机(虚拟机)
Domain 对象的方法 String getXMLDesc(int flags) 可以取得客户机(虚拟机)配置描述 xml,可依照这个描述自定义客户机(虚拟机)配置描述 xml。
示例客户机(虚拟机)配置描述 xml:
<?xml version="1.0" encoding="UTF-8"?><domain type='kvm'> <name>kvmdemo</name> <!--名称必须唯一--> <uuid>c6e408f3-7750-47ca-8bd1-d19837271472</uuid> <!--uuid必须唯一,可使用 java.util.UUID 随机生成--> <memory unit='MiB'>512</memory> <!--最大可用内存配置--> <currentMemory unit='MiB'>512</currentMemory> <vcpu placement='static'>1</vcpu> <!--配置cpu--> <os> <type arch='x86_64' machine='pc'>hvm</type> <boot dev='hd'/> <!--硬盘启动--> <boot dev='cdrom'/> <!--光驱启动--> </os> <features> <acpi/> <apic/> <pae/> </features> <clock offset='localtime'/> <on_poweroff>destroy</on_poweroff> <on_reboot>restart</on_reboot> <on_crash>restart</on_crash> <devices> <emulator>/usr/bin/qemu-system-x86_64</emulator> <!--模拟器所在路径,视自己情况配置--> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/var/lib/libvirt/images/kvmdemo.qcow2'/> <!--虚拟硬盘配置,这个地方填生成的镜像文件所在的路径即可--> <target dev='hda' bus='ide'/> </disk> <!--<disk type='file' device='cdrom'> <source file='/var/lib/libvirt/images/ubuntu-16.04-desktop-amd64.iso'/> <target dev='hdb' bus='ide'/> <readonly/> </disk>--> <interface type='bridge'> <!--网络配置,本示例配置成桥接模式--> <mac address='52:54:00:f4:06:03'/> <!--mac 地址必须唯一--> <source bridge='br0'/> </interface> <console type='pty'> <!--控制台配置,如果需要使用 virsh 命令登陆虚拟机,则必须添加--> <target port='0'/> </console> <input type='tablet' bus='usb'/> <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <graphics type='vnc' autoport='yes' keymap='en-us' listen='0.0.0.0'/> <!--VNC配置,autoport="yes"表示自动分配VNC端口,推荐使用,listen="0.0.0.0"表示监听所有IP--> <memballoon model="virtio"> <!--内存监控配置,添加此配置,才能正常取得内存使用情况--> <stats period="10"/><!--每10s钟收集一次--> </memballoon> </devices></domain>
关于如何随机生成 Mac 地址可查看我的另一篇博文:传送门
客户机(虚拟机)配置描述 xml 的更多用法可参考官网:传送门
有了客户机(虚拟机)配置描述 xml,就可以很容易的创建或定义客户机(虚拟机)了。
创建客户机(虚拟机)的示例代码:
private void createDomain() throws LibvirtException, DocumentException { logger.info("create domain execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo.xml")); String xmlDesc = document.asXML(); logger.info("createDomain description:\n{}", xmlDesc); Domain domain = connect.domainCreateXML(xmlDesc, 0); logger.info("虚拟机的id:{}", domain.getID()); logger.info("虚拟机的uuid:{}", domain.getUUIDString()); logger.info("虚拟机的名称:{}", domain.getName()); logger.info("虚拟机的是否自动启动:{}", domain.getAutostart()); logger.info("虚拟机的状态:{}", domain.getInfo().state); logger.info("虚拟机的描述xml:\n{}", domain.getXMLDesc(0)); }
定义客户机(虚拟机)
定义客户机(虚拟机)示例代码:
private void defineDomain() throws LibvirtException, DocumentException { logger.info("define domain execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); // xml 文件 => Dom4j 文档 => String SAXReader reader = new SAXReader(); Document document = reader.read(new File("xml/kvmdemo.xml")); String xmlDesc = document.asXML(); logger.info("defineDomain description:\n{}", xmlDesc); Domain domain = connect.domainDefineXML(xmlDesc);// domain.abortJob(); // 是否随宿主机开机自动启动 domain.setAutostart(false); domain.create(); // 定义完后直接启动 logger.info("虚拟机的id:{}", domain.getID()); logger.info("虚拟机的uuid:{}", domain.getUUIDString()); logger.info("虚拟机的名称:{}", domain.getName()); logger.info("虚拟机的是否自动启动:{}", domain.getAutostart()); logger.info("虚拟机的状态:{}", domain.getInfo().state); logger.info("虚拟机的描述xml:\n{}", domain.getXMLDesc(0)); }
这里需要一个 xml 工具包来操作 xml 文件,示例代码使用的是 dom4j
这里之所以列出了 Domain domainCreateXML(String xmlDesc, int flags) 和 Domain domainDefineXML(String xmlDesc) 的示例代码,是由于这两个方法在官方文档中的说明不够详细。
具体的,domainCreateXML 创建的客户机(虚拟机)是一个临时的,当调用 destroy() 或者宿主机重启后,就会消失,这和 storagePoolCreateXML 类似;而 domainDefineXML 定义的是一个持久化的客户机(虚拟机),除非明确调用 undefine(),否则它一直存在,此外,domainDefineXML 会覆盖之前的定义,但是有些操作会阻止这个操作,比如 block copy 操作,要先使用 abortJob() 操作取消这些块拷贝操作。
删除客户机(虚拟机)
删除客户机(虚拟机)示例代码:
private void undefineDomain() throws LibvirtException, DocumentException { logger.info("undefine domain execute succeeded"); Connect connect = new Connect("qemu+tcp://192.168.10.231:16509/system"); Domain domain = connect.domainLookupByName("kvmdemo"); logger.info("虚拟机的id:{}", domain.getID()); logger.info("虚拟机的uuid:{}", domain.getUUIDString()); logger.info("虚拟机的名称:{}", domain.getName()); logger.info("虚拟机的是否自动启动:{}", domain.getAutostart()); logger.info("虚拟机的状态:{}", domain.getInfo().state); domain.destroy(); // 强制关机 domain.undefine(); }
未完待续,后续将继续发布客户机启动、关机、强制关机、重启、强制重启;宿主机监控,客户机监控等。
- libvirt Java API用法连载之libvirt Java API使用详解(四)
- libvirt Java API用法连载之libvirt C/Java API介绍与jna/libvirt Java SDK引入(三)
- libvirt Java API用法连载之KVM/QEMU区别与libvirt简介(一)
- libvirt Java API用法连载之Ubuntu16.04安装QEMU与libvirt(二)
- libvirt Java API用法连载之解决Unable to load library 'virt'(番外)
- 使用libvirt管理kvm(API篇)
- 使用python-libvirt API 迁移
- libvirt API 简介(一)
- libvirt API 简介(二)
- libvirt API学习
- libvirt API学习笔记
- libvirt API初探
- libvirt API 简介
- libvirt API简介
- Python libvirt domain api
- libvirt 部分API 介绍
- libvirt 部分API 介绍
- libvirt API学习报告
- kendoGrid jquery 数据列表加载
- 大气飞行姿态动力学大作业
- Android消息机制-Handler(二)
- centos设置nginx开机启动
- 最简单的计算GBK偏移量 java版 取模时用到
- libvirt Java API用法连载之libvirt Java API使用详解(四)
- 通过出牌牌组判断斗地主的出牌
- DeepLab笔记(未完待续)
- Android——内容提供者
- java使用jbarcode生成条形码
- 湖南省第七届大学生计算机程序设计竞赛---机器人的指令
- 从心出发,华展云2.0荣耀发布
- 将项目部署到Linux时,提示表不存在?
- JFinal下使用websocket(转载+精简+排坑)