KVM Development Guide
Overview
This guide will go through how to configure your Java development environment, connect to KVM and provide some Java code examples for interfacing with KVM. Upon completing this guide you should be off and running with developing application interfaces for KVM. As mentioned in previous post KVM is made up of three components: KVM (hardware acceleration), Qemu (hypervisor) and Libvirt (management library). We will be purely focusing on Libvirt as it is the management library for KVM.
Development Environment Setup
This guide assumes you will be using Eclipse and Maven for jar package management. If you aren't no big deal, you can always download libraries and add them to CLASSPATH manually.
Configure KVM
Before we begin it is important to ensure KVM is properly configured to allow remote access over TCP. For instructions on doing this refer to KVM Installation and Configuration.
Configure Maven
Add JNA and Libvirt dependencies to pom.xml. Be careful which versions you use. Make sure the JNA version is not newer than the Libvirt version else you will have incompatibility issues.
[code language="xml"]
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.libvirt</groupId>
<artifactId>libvirt</artifactId>
<version>0.4.9</version>
</dependency>
[/code]
Instruct Maven to download and make these jars available to Eclipse by running the following command from the root project folder:
-
mvn clean compile
Note: if you choose not to use Maven, you can simply download the JNA / Libvirt libraries and add them to your projects CLASSPATH in Eclipse.
Connecting to Libvirt
Now that we have configured Libvirt to allow remote TCP connections and have our Java libraries setup we can start coding!
Libvirt connection class
Below we will create a class which we can instantiate with a hostname. The class will provide a getConnection() method that returns the connection to a KVM host.
[code language="java"]
import org.libvirt.Connect;
import org.libvirt.ConnectAuth;
import org.libvirt.ConnectAuthDefault;
import org.libvirt.LibvirtException;
public class KvmConnection {
private String hostname;
public KvmConnection(String host) {
this.hostname = host;
}
public Connect getConnection() throws Exception {
Connect conn = null;
try {
ConnectAuth defaultAuth = new ConnectAuthDefault();
conn = new Connect("qemu+tcp://" + hostname + "/system", defaultAuth, 0);
} catch (LibvirtException e) {
throw new Exception(e.getMessage(), e);
}
return conn;
}
}
[/code]
Note: the above example is using SASL authentication. Other authentication mechanisms such as TLS are possible but the URI does need to change if the authentication mechanism changes.
Working with Domains
In Libvirt Domains are Virtual Machines. Before we can perform APIs against a domain we must retrieve the Domain object which can be done by name, id or other methods. Libvirt also has APIs that return a list of active or inactive Domains. If we don't know the name or id of a Domain we would first get a list of all Domain Ids or names. In addition to the below code examples, the API reference for Libvirt Domains is also a good resource.
Listing Domains
The below method returns a list of active and inactive Domain objects:
[code language="java"]
public List<Domain> getDomains(Connect conn) throws Exception {
List<Domain> domainList = new ArrayList<Domain>();
try {
int[] activeDomainIds = conn.listDomains();
String[] inactiveDomainNames = conn.listDefinedDomains();
if (activeDomainIds.length > 0) {
for (int id : activeDomainIds) {
Domain domain = conn.domainLookupByID(id);
domainList.add(domain);
}
}
if (inactiveDomainNames.length > 0) {
for (String name : inactiveDomainNames) {
Domain domain = conn.domainLookupByName(name);
domainList.add(domain);
}
}
} catch (Exception e) {
throw new Exception(e.getMessage(), e);
}
return domainList;
}
[/code]
Domain status
A domain can be either active, inactive or error. These are represented as integers in Libvirt. The below method will get the status of a Domain and save it as an Enum:
[code language="java"]
public VmStatusEnum getVmStatus(int status) throws Exception {
VmStatusEnum vmState = null;
try {
if (status == 1) {
vmState = VmStatusEnum.ON;
} else if (status == 0) {
vmState = VmStatusEnum.OFF;
} else {
vmState = VmStatusEnum.ERROR;
}
} catch (Exception e) {
throw new Exception(e.getMessage(), e);
}
return vmState;
}
[/code]
Creating Domain Snapshot
The below code creates a Domain snapshot based on a given Connection object, Domain name and Snapshot name:
[code language="java"]
public void createSnapshot(Connect conn, String vmName, String snapshotName) throws Exception {
if (snapshotName == null || snapshotName.isEmpty()) {
snapshotName = vmName + "_snapshot";
}
String description = "KVM snapshot created by XYZ";
try {
Domain domain = conn.domainLookupByName(vmName);
int status = domain.isActive();
VmStatusEnum vmStatusEnum = getVmStatus(status);
if (vmStatusEnum.equals(VmStatusEnum.ON)) {
String snapshotXML = "<domainsnapshot><name>" + snapshotName + "</name><description>" + description + "</description></domainsnapshot>";
domain.snapshotCreateXML(snapshotXML);
} else if (vmStatusEnum.equals(VmStatusEnum.ERROR)) {
throw new Exception(String.format(VM_STATUS_ERROR, vmName));
}
} catch (LibvirtException e) {
LOGGER.error(String.format(SNAPSHOT_CREATE_FAILED, snapshotName, vmName));
throw new Exception(e.getMessage(), e);
}
LOGGER.info(String.format(SNAPSHOT_CREATE_SUCCESS, snapshotName, vmName));
}
[/code]
Working with Storage Pools
Libvirt uses Storage Pools to store images and virtual disks for Virtual Machines. Virtual disks are presented to Virtual Machines as block devices regardless of the underlying storage pool type. There are many different types of storage pools which can be configured. Aside from NFS, iSCSI and FC there is also integration to support Ceph RDB pools, Gluster pools and even ZFS pools. In addition to below code example the API reference for Libvirt Storage is a good resource.
Listing Storage Pools
Below is a method that returns a list of active and inactive Storage Pools:
[code language="java"]
public List<StoragePool> getStoragePools(Connect conn) throws Exception {
List<StoragePool> storagePoolList = new ArrayList();
try {
String[] activeStoragePools = conn.listStoragePools();
String[] inactiveStoragePools = conn.listDefinedStoragePools();
if (activeStoragePools.length > 0) {
for (String name : activeStoragePools) {
StoragePool storagePool = conn.storagePoolLookupByName(name);
storagePoolList.add(storagePool);
}
}
if (inactiveStoragePools.length > 0) {
for (String name : inactiveStoragePools) {
StoragePool storagePool = conn.storagePoolLookupByName(name);
storagePoolList.add(storagePool);
}
}
} catch (Exception e) {
throw new Exception(e.getMessage(), e);
}
return storagePoolList;
}
[/code]
Conclusion
Through these examples you should have a good understanding of how to work with KVM using Java or even other languages. As far as working with multiple KVM hypervisors, check out oVirt which is open source management software built around KVM (similar in concept to vCenter for VMware environments). Red Hat who heavily contributes to upstream oVirt community uses it as the basis for Red Hat Enterprise Virtualization Management. I will be covering oVirt and Red Hat Enterprise Virtualization in later posts so stay tuned! As always if you have questions or need some help feel free to leave a post or get in contact with me directly. Happy Libvirting and safe travels!
(c) 2015 Keith Tenzer