/**
* vlove - web based virtual machine management
* Copyright (C) 2010 Limone Fresco Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package vlove.virt;
import java.io.File;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PreDestroy;
import javax.xml.xpath.XPathConstants;
import org.libvirt.Connect;
import org.libvirt.Domain;
import org.libvirt.LibvirtException;
import org.libvirt.Network;
import org.libvirt.StoragePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import vlove.VirtException;
import vlove.model.Capabilities;
import vlove.model.DomainState;
import vlove.model.InternalDomain;
import vlove.model.InternalStoragePool;
import vlove.model.StoragePoolState;
import vlove.util.XPathUtils;
import com.sun.management.OperatingSystemMXBean;
/**
* The meat and potatoes of vlove - the interface to libvirt.
*
* @author Michael Laccetti
*/
@SuppressWarnings("restriction")
public class VirtManager implements Serializable {
private static final Logger log = LoggerFactory.getLogger(VirtManager.class);
private boolean isConnected = false;
// Connection information
private String libvirtUrl;
private Connect connection;
// MBean
private OperatingSystemMXBean osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
public VirtManager() {
// empty, for Spring
}
private void checkConnected() {
if (!isConnected) {
log.error("Not connected to libvirt, failing operation.");
throw new RuntimeException("Not connected to libvirt, failing operation.");
}
}
/**
* Post-constructor init method - connects to libvirt.
*/
public void init() {
// System.setProperty("jna.library.path", "C:/Tools/dev/libvirt/bin");
// System.setProperty("java.library.path", "C:/Tools/dev/libvirt/bin");
final String os = System.getProperty("os.name");
log.debug("Loading driver for OS: {}", os);
String driver = null;
if (os != null && os.toLowerCase().contains("windows")) {
driver = "C:/Tools/dev/libvirt/bin/libvirt-0.dll";
} else if (os != null && os.toLowerCase().contains("linux")) {
driver = "/usr/lib/libvirt.so.0";
}
if (new File(driver).exists()) {
System.load(driver);
} else {
throw new RuntimeException(String.format("Could not load driver from %s.", driver));
}
// Set the default URL
libvirtUrl = "qemu:///session";
if (libvirtUrl == null || libvirtUrl.length() == 0) {
log.warn("No libvirt URL defined, setting to default: test:///default");
libvirtUrl = "test:///default";
}
}
/**
* Used to rebuild the connection to libvirt on the fly.
*
* @param newLibvirtUrl
* URL for libvirt
*/
public void init(String newLibvirtUrl) {
init();
}
/**
* Doesn't shut down a VM, but the VirtManager itself.
*/
@PreDestroy
public void shutdown() {
disconnect();
}
/**
* Check to see if we are connected to libvirt.
*
* @return
*/
public boolean isConnected() {
return isConnected;
}
/**
* Connect to libvirt using the URL pulled out during the init() process.
*
* @return
*/
public boolean connect() {
try {
log.debug("Attempting connection to {}.", libvirtUrl);
connection = new Connect(libvirtUrl);
isConnected = true;
log.debug("Connected to libvirt.");
return true;
} catch (LibvirtException le) {
log.error("Could not connect to libvirt.", le);
return false;
}
}
/**
* Disconnect from libvirt.
*/
public void disconnect() {
if (isConnected) {
try {
log.debug("Disconnecting from libvirt.");
connection.close();
} catch (LibvirtException le) {
log.warn("Could not close connection to libvirt.", le);
}
isConnected = false;
} else {
log.info("Not connected to libvirt, no need to disconnect.");
}
}
/**
* Retrieve the hardware capabilities of the host that libvirt is runnign on.
*
* @return
* @throws VirtException
*/
public Capabilities getCapabilities() throws VirtException {
checkConnected();
Capabilities c = new Capabilities();
try {
final String capabilities = connection.getCapabilities();
Document d = XPathUtils.loadDocument(capabilities.getBytes());
c.setCpuArch(((String) XPathUtils.parseXPathExpression(d, "/capabilities/host/cpu/arch", XPathConstants.STRING)));
c.setModel(((String) XPathUtils.parseXPathExpression(d, "/capabilities/host/cpu/model", XPathConstants.STRING)));
c.setVendor(((String) XPathUtils.parseXPathExpression(d, "/capabilities/host/cpu/vendor", XPathConstants.STRING)));
// To be added later, just dump it to the log for now:
Node t = (Node) XPathUtils.parseXPathExpression(d, "/capabilities/host/cpu/topology", XPathConstants.NODE);
Integer sockets = Integer.parseInt(t.getAttributes().getNamedItem("sockets").getNodeValue());
Integer cores = Integer.parseInt(t.getAttributes().getNamedItem("cores").getNodeValue());
Integer threads = Integer.parseInt(t.getAttributes().getNamedItem("threads").getNodeValue());
// log.debug("Topology - sockets: {}, cores: {}, threads: {}", new
// Object[] { sockets, cores, threads });
c.setNumProcs(sockets * cores * threads);
} catch (LibvirtException le) {
log.error("Could not retrieve capabilities from libvirt.", le);
throw new VirtException("Could not retrieve capabilities from libvirt.", le);
}
return c;
}
/**
* Retrieve all the VMs defined.
*
* @return
*/
public List<InternalDomain> getDomains() throws VirtException {
checkConnected();
try {
List<InternalDomain> domains = new ArrayList<>();
for (int did : connection.listDomains()) {
Domain d = connection.domainLookupByID(did);
domains.add(new InternalDomain(did, d.getUUIDString(), d.getName(), DomainState.valueOf(d.getInfo().state.toString()), osBean.getTotalPhysicalMemorySize(), d.getInfo().cpuTime, d.getInfo().memory));
}
for (String domainName : connection.listDefinedDomains()) {
Domain d = connection.domainLookupByName(domainName);
domains.add(new InternalDomain(0, d.getUUIDString(), d.getName(), DomainState.valueOf(d.getInfo().state.toString()), osBean.getTotalPhysicalMemorySize(), d.getInfo().cpuTime, d.getInfo().memory));
}
return domains;
} catch (LibvirtException le) {
log.error("Could not list domains.", le);
throw new VirtException("Could not list domains.", le);
}
}
/**
* Retrieve a specific VM, by numeric ID.
*
* @param domainId
* @return
* @throws VirtException
*/
public InternalDomain getDomain(Integer domainId) throws VirtException {
checkConnected();
try {
Domain d = connection.domainLookupByID(domainId);
return new InternalDomain(domainId, d.getUUIDString(), d.getName(), DomainState.valueOf(d.getInfo().state.toString()), osBean.getTotalPhysicalMemorySize(), d.getInfo().cpuTime, d.getInfo().memory);
} catch (LibvirtException le) {
throw new VirtException(String.format("Could not retrieve domain for ID %d.", domainId));
}
}
/**
* Retrieve a specific VM, by name.
*
* @param domainName
* @return
* @throws VirtException
*/
public InternalDomain getDomain(String domainName) throws VirtException {
checkConnected();
try {
Domain d = connection.domainLookupByName(domainName);
return new InternalDomain(d.getID(), d.getUUIDString(), d.getName(), DomainState.valueOf(d.getInfo().state.toString()), osBean.getTotalPhysicalMemorySize(), d.getInfo().cpuTime, d.getInfo().memory);
} catch (LibvirtException le) {
throw new VirtException(String.format("Could not retrieve domain for name %s.", domainName), le);
}
}
/**
* Retrieve a specific VM, by UUID.
*
* @param uuid
* @return
* @throws VirtException
*/
public InternalDomain getDomainByUUID(String uuid) throws VirtException {
try {
Domain d = connection.domainLookupByUUIDString(uuid);
return new InternalDomain(d.getID(), d.getUUIDString(), d.getName(), DomainState.valueOf(d.getInfo().state.toString()), osBean.getTotalPhysicalMemorySize(), d.getInfo().cpuTime, d.getInfo().memory);
} catch (LibvirtException le) {
throw new VirtException(String.format("Could not retrieve domain for UUID %s.", uuid));
}
}
/**
* Start a VM up.
*
* @param domainName
* @return
*/
// TODO convert from a PAIR to a TRIPLE so that we can capture the error
// message, if any
public Integer start(String domainName) throws VirtException {
checkConnected();
try {
Domain d = connection.domainLookupByName(domainName);
log.debug("Starting domain {}.", domainName);
d.create();
d = connection.domainLookupByName(domainName);
return d.getID();
} catch (LibvirtException le) {
log.error(String.format("Could not start domain %s.", domainName), le);
throw new VirtException(String.format("Could not start domain %s.", domainName), le);
}
}
/**
* Resume a paused VM.
*
* @param domainId
* @return
*/
public void resume(Integer domainId) throws VirtException {
checkConnected();
try {
Domain d = connection.domainLookupByID(domainId);
log.debug("Resuming domain {}.", domainId);
d.resume();
} catch (LibvirtException le) {
log.error(String.format("Could not resume domain %d.", domainId), le);
throw new VirtException(String.format("Could not resume domain %d.", domainId), le);
}
}
/**
* Pause a running VM.
*
* @param domainId
* @return
*/
public void pause(Integer domainId) throws VirtException {
checkConnected();
try {
log.debug("Pausing domain {}.", domainId);
Domain d = connection.domainLookupByID(domainId);
d.suspend();
} catch (LibvirtException le) {
log.error(String.format("Could not pause domain %d.", domainId), le);
throw new VirtException(String.format("Could not pause domain %d.", domainId), le);
}
}
/**
* Send the ACPI shutdown command to a running VM.
*
* @param domainId
* @return
*/
public void shutdown(Integer domainId) throws VirtException {
checkConnected();
try {
log.debug("Shutting down domain {}.", domainId);
Domain d = connection.domainLookupByID(domainId);
d.shutdown();
} catch (LibvirtException le) {
log.error(String.format("Could not shutdown domain %d.", domainId), le);
throw new VirtException(String.format("Could not shutdown domain %d.", domainId), le);
}
}
/**
* Forcibly terminate a running VM.
*
* @param domainId
* @return
*/
public void destroy(Integer domainId) throws VirtException {
checkConnected();
try {
log.debug("Destroying domain {}.", domainId);
Domain d = connection.domainLookupByID(domainId);
d.destroy();
} catch (LibvirtException le) {
log.error(String.format("Could not destroy domain %d.", domainId), le);
throw new VirtException(String.format("Could not destroy domain %d.", domainId), le);
}
}
/**
* Retrieve a list of storage pools.
*
* @return
* @throws VirtException
*/
public List<InternalStoragePool> getStoragePools() throws VirtException {
checkConnected();
try {
List<InternalStoragePool> pools = new ArrayList<>();
for (String poolName : connection.listStoragePools()) {
final StoragePool pool = connection.storagePoolLookupByName(poolName);
pools.add(new InternalStoragePool(pool.getName(), pool.getUUIDString(), pool.numOfVolumes(), StoragePoolState.valueOf(pool.getInfo().state.toString()), pool.getInfo().capacity, pool.getInfo().available, pool.getAutostart()));
}
return pools;
} catch (LibvirtException le) {
throw new VirtException("Could not retrieve list of storage pools.", le);
}
}
/**
* Retrieve a specific storage pool.
*
* @param name
* @return
* @throws VirtException
*/
public InternalStoragePool getStoragePool(String name) throws VirtException {
checkConnected();
try {
final StoragePool pool = connection.storagePoolLookupByName(name);
return new InternalStoragePool(pool.getName(), pool.getUUIDString(), pool.numOfVolumes(), StoragePoolState.valueOf(pool.getInfo().state.toString()), pool.getInfo().capacity, pool.getInfo().available, pool.getAutostart());
} catch (LibvirtException le) {
throw new VirtException("Could not retrieve storage pool.", le);
}
}
/**
* Retrieve all defined networks.
*
* @return
* @throws VirtException
*/
public List<String> getNetworks() throws VirtException {
checkConnected();
try {
return Arrays.asList(connection.listNetworks());
} catch (LibvirtException le) {
throw new VirtException("Could not retrieve list of networks.", le);
}
}
/**
* Retrieve a specific network.
*
* @param name
* @return
* @throws VirtException
*/
public Network getNetwork(String name) throws VirtException {
checkConnected();
try {
return connection.networkLookupByName(name);
} catch (LibvirtException le) {
throw new VirtException("Could not retrieve network information.", le);
}
}
}