手机版
你好,游客 登录 注册
背景:
阅读新闻

libvirt Java API使用教程

libvirt Java API使用详解

[日期:2017-06-28] 来源:csdn.net/kyyee  作者:kyyee [字体: ]

要想通过 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 端口 16509
tcp_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/ 物理空间。

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);
        }
    }

图1

这段代码也证实了 libvirt 提供的默认 StoragePool 的名称为:default。

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。

图2

图3

从图中可以看出,获取到的磁盘容量有部分损失,作为监控宿主机和分配资源使用还是可行的。

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));
    }

创建 storage pool 除了可调用 StoragePool storagePoolCreateXML(String xmlDesc, int flags) 方法外,还可调用 StoragePool storagePoolDefineXML(String xml, int flags) 。
具体的,storagePoolCreateXML 创建的是一个非持久化的 pool ,删除它只需调用 destroy(),或者重启宿主机,就会消失,而 storagePoolDefineXML 定义的是一个持久化的 pool,除非明确调用 undefine(),否则它一直存在。

管理客户机(虚拟机)镜像

前面讲了这么多,都是为了给创建虚拟机镜像做铺垫。libvirt 提供的 StorageVolume 就是专门用来管理客户机物理硬盘的,由于 StorageVolume 与 StoragePool 关系密切,前面的内容是实现创建虚拟机镜像的基础。

下面将根据讲解 StoragePool 的层次结构来讲解 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.105: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);
        }
    }

图4

图5

这里有一个文件是 iso 文件,被过滤了,没有显示。

StoragePool 对象提供了 1 种方式获得 StorageVol 对象:

  • StorageVol storageVolLookupByName(String name)
    通过唯一的名字获取 storage volume。

通过唯一的名字获取 storage volume 的示例代码:

    private void getStorageVolumebyName(@RequestParam String name) throws LibvirtException {
        logger.info("get storage volume 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");
        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("存储卷的类型:{}", 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。

图6

图7

获取客户机(虚拟机)磁盘容量非常准确,我给客户机分配的磁盘空间就是 15 GB,将这个 api 作为监控客户机(虚拟机)使用完全没有问题。

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.105: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 派生(克隆)一个镜像,但是要保证派生(克隆)的镜像的名称和路径唯一,否则派生(克隆)不成功。

克隆 storage volume 的示例代码:

    private void cloneStorageVolume() throws LibvirtException, DocumentException {
        logger.info("clone storage volume 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-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 位基镜像。

删除 storage volume 示例代码:

    private void deleteStorageVolume() throws LibvirtException, DocumentException {
        logger.info("delete storage volume execute succeeded");
        Connect connect = new Connect("qemu+tcp://192.168.10.105:16509/system");

        StoragePool storagePool = connect.storagePoolLookupByName("default");
        StorageVol storageVol = storagePool.storageVolLookupByName("kvmdemo.qcow2");
        logger.info("存储卷名称:{}", storageVol.getName());
        storageVol.free();
        storageVol.wipe();
        storageVol.delete(0);
    }

未完待续,后续将继续发布客户机创建、启动、关机、强制关机、重启、强制重启;宿主机监控,客户机监控等。

linux
【内容导航】
第1页:KVM/QEMU区别与libvirt简介 第2页:u 16.04安装QEMU与libvirt
第3页:libvirt C/Java API介绍与jna/libvirt Java SDK引入 第4页:libvirt Java API使用详解
第5页:解决Unable to load library 'virt'
相关资讯       libvirt  libvirt Java API 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款