Skip to content

Commit

Permalink
CLOUDSTACK-8715: Add VirtIO channel to all Instances for the Qemu Gue…
Browse files Browse the repository at this point in the history
…st Agent

This commit adds a additional VirtIO channel with the name 'org.qemu.guest_agent.0'
to all Instances.

With the Qemu Guest Agent the Hypervisor gains more control over the Instance if
these tools are present inside the Instance, for example:

* Power control
* Flushing filesystems

In the future this should allow safer snapshots on KVM since we can instruct the
Instance to flush the filesystems prior to snapshotting the disk.

More information: http://wiki.qemu.org/Features/QAPI/GuestAgent

Keep in mind that on Ubuntu AppArmor still needs to be disabled since the default
AppArmor profile doesn't allow libvirt to write into /var/lib/libvirt/qemu
  • Loading branch information
wido committed Oct 30, 2015
1 parent 930ef8d commit 9317ef7
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import com.cloud.exception.InternalErrorException;
import com.cloud.host.Host.Type;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ClockDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ConsoleDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuModeDef;
Expand All @@ -110,7 +111,6 @@
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef;
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtRequestWrapper;
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtUtilitiesHelper;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
Expand Down Expand Up @@ -1981,9 +1981,14 @@ So if getMinSpeed() returns null we fall back to getSpeed().
final SerialDef serial = new SerialDef("pty", null, (short)0);
devices.addDevice(serial);

/* Add a VirtIO channel for the Qemu Guest Agent tools */
devices.addDevice(new ChannelDef("org.qemu.guest_agent.0", ChannelDef.ChannelType.UNIX,
"/var/lib/libvirt/qemu/" + vmTO.getName() + ".org.qemu.guest_agent.0"));

/* Add a VirtIO channel for SystemVMs for communication and provisioning */
if (vmTO.getType() != VirtualMachine.Type.User) {
final VirtioSerialDef vserial = new VirtioSerialDef(vmTO.getName(), null);
devices.addDevice(vserial);
devices.addDevice(new ChannelDef(vmTO.getName() + ".vport", ChannelDef.ChannelType.UNIX,
"/var/lib/libvirt/qemu/" + vmTO.getName() + ".agent"));
}

final VideoDef videoCard = new VideoDef(_videoHw, _videoRam);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
Expand All @@ -41,6 +42,7 @@ public class LibvirtDomainXMLParser {
private static final Logger s_logger = Logger.getLogger(LibvirtDomainXMLParser.class);
private final List<InterfaceDef> interfaces = new ArrayList<InterfaceDef>();
private final List<DiskDef> diskDefs = new ArrayList<DiskDef>();
private final List<ChannelDef> channels = new ArrayList<ChannelDef>();
private Integer vncPort;
private String desc;

Expand Down Expand Up @@ -171,6 +173,25 @@ public boolean parseDomainXML(String domXML) {
interfaces.add(def);
}

NodeList ports = devices.getElementsByTagName("channel");
for (int i = 0; i < ports.getLength(); i++) {
Element channel = (Element)ports.item(i);

String type = channel.getAttribute("type");
String path = getAttrValue("source", "path", channel);
String name = getAttrValue("target", "name", channel);
String state = getAttrValue("target", "state", channel);

ChannelDef def = null;
if (state == null || state.length() == 0) {
def = new ChannelDef(name, ChannelDef.ChannelType.valueOf(type.toUpperCase()), path);
} else {
def = new ChannelDef(name, ChannelDef.ChannelType.valueOf(type.toUpperCase()), ChannelDef.ChannelState.valueOf(state.toUpperCase()), path);
}

channels.add(def);
}

Element graphic = (Element)devices.getElementsByTagName("graphics").item(0);

if (graphic != null) {
Expand Down Expand Up @@ -234,6 +255,10 @@ public List<DiskDef> getDisks() {
return diskDefs;
}

public List<ChannelDef> getChannels() {
return channels;
}

public String getDescription() {
return desc;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1161,25 +1161,95 @@ public String toString() {
}
}

public static class VirtioSerialDef {
private final String _name;
public static class ChannelDef {
enum ChannelType {
UNIX("unix"), SERIAL("serial");
String _type;

ChannelType(String type) {
_type = type;
}

@Override
public String toString() {
return _type;
}
}

enum ChannelState {
DISCONNECTED("disconnected"), CONNECTED("connected");
String _type;

ChannelState(String type) {
_type = type;
}

@Override
public String toString() {
return _type;
}
}

private String _name;
private String _path;
private ChannelType _type;
private ChannelState _state;

public ChannelDef(String name, ChannelType type) {
_name = name;
_type = type;
}

public ChannelDef(String name, ChannelType type, String path) {
_name = name;
_path = path;
_type = type;
}

public ChannelDef(String name, ChannelType type, ChannelState state) {
_name = name;
_state = state;
_type = type;
}

public VirtioSerialDef(String name, String path) {
public ChannelDef(String name, ChannelType type, ChannelState state, String path) {
_name = name;
_path = path;
_state = state;
_type = type;
}

public ChannelType getChannelType() {
return _type;
}

public ChannelState getChannelState() {
return _state;
}

public String getName() {
return _name;
}

public String getPath() {
return _path;
}

@Override
public String toString() {
StringBuilder virtioSerialBuilder = new StringBuilder();
virtioSerialBuilder.append("<channel type='" + _type.toString() + "'>\n");
if (_path == null) {
_path = "/var/lib/libvirt/qemu";
virtioSerialBuilder.append("<source mode='bind'/>\n");
} else {
virtioSerialBuilder.append("<source mode='bind' path='" + _path + "'/>\n");
}
virtioSerialBuilder.append("<channel type='unix'>\n");
virtioSerialBuilder.append("<source mode='bind' path='" + _path + "/" + _name + ".agent'/>\n");
virtioSerialBuilder.append("<target type='virtio' name='" + _name + ".vport'/>\n");
virtioSerialBuilder.append("<address type='virtio-serial'/>\n");
if (_state == null) {
virtioSerialBuilder.append("<target type='virtio' name='" + _name + "'/>\n");
} else {
virtioSerialBuilder.append("<target type='virtio' name='" + _name + "' state='" + _state.toString() + "'/>\n");
}
virtioSerialBuilder.append("</channel>\n");
return virtioSerialBuilder.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.KVMHABase.NfsStoragePool;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtRequestWrapper;
Expand Down Expand Up @@ -332,6 +333,9 @@ private void verifyVm(final VirtualMachineTO to, final LibvirtVMDef vm) {
assertXpath(domainDoc, "/domain/devices/input/@type", "tablet");
assertXpath(domainDoc, "/domain/devices/input/@bus", "usb");

assertNodeExists(domainDoc, "/domain/devices/channel");
assertXpath(domainDoc, "/domain/devices/channel/@type", ChannelDef.ChannelType.UNIX.toString());

assertXpath(domainDoc, "/domain/memory/text()", String.valueOf( to.getMaxRam() / 1024 ));
assertXpath(domainDoc, "/domain/currentMemory/text()", String.valueOf( to.getMinRam() / 1024 ));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.List;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;

public class LibvirtDomainXMLParserTest extends TestCase {

Expand All @@ -38,6 +39,11 @@ public void testDomainXMLParser() {
InterfaceDef.NicModel ifModel = InterfaceDef.NicModel.VIRTIO;
InterfaceDef.GuestNetType ifType = InterfaceDef.GuestNetType.BRIDGE;

ChannelDef.ChannelType channelType = ChannelDef.ChannelType.UNIX;
ChannelDef.ChannelState channelState = ChannelDef.ChannelState.DISCONNECTED;
String guestAgentPath = "/var/lib/libvirt/qemu/guest-agent.org.qemu.guest_agent.0";
String guestAgentName = "org.qemu.guest_agent.0";

String diskLabel ="vda";
String diskPath = "/var/lib/libvirt/images/my-test-image.qcow2";

Expand Down Expand Up @@ -144,7 +150,7 @@ public void testDomainXMLParser() {
"</console>" +
"<channel type='unix'>" +
"<source mode='bind' path='/var/lib/libvirt/qemu/s-2970-VM.agent'/>" +
"<target type='virtio' name='s-2970-VM.vport'/>" +
"<target type='virtio' name='s-2970-VM.vport' state='disconnected'/>" +
"<alias name='channel0'/>" +
"<address type='virtio-serial' controller='0' bus='0' port='1'/>" +
"</channel>" +
Expand All @@ -164,6 +170,12 @@ public void testDomainXMLParser() {
"<alias name='balloon0'/>" +
"<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>" +
"</memballoon>" +
"<channel type='unix'>" +
"<source mode='bind' path='" + guestAgentPath + "'/>" +
"<target type='virtio' name='" + guestAgentName + "'/>" +
"<alias name='channel0'/>" +
"<address type='virtio-serial' controller='0' bus='0' port='1'/>" +
"</channel>" +
"</devices>" +
"<seclabel type='none'/>" +
"</domain>";
Expand All @@ -185,6 +197,16 @@ public void testDomainXMLParser() {
assertEquals(deviceType, disks.get(diskId).getDeviceType());
assertEquals(diskFormat, disks.get(diskId).getDiskFormatType());

List<ChannelDef> channels = parser.getChannels();
for (int i = 0; i < channels.size(); i++) {
assertEquals(channelType, channels.get(i).getChannelType());
assertEquals(channelType, channels.get(i).getChannelType());
}

assertEquals(channelState, channels.get(0).getChannelState());
assertEquals(guestAgentPath, channels.get(1).getPath());
assertEquals(guestAgentName, channels.get(1).getName());

List<InterfaceDef> ifs = parser.getInterfaces();
for (int i = 0; i < ifs.size(); i++) {
assertEquals(ifModel, ifs.get(i).getModel());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import junit.framework.TestCase;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.utils.Pair;

public class LibvirtVMDefTest extends TestCase {
Expand Down Expand Up @@ -112,4 +113,18 @@ public void testHypervEnlightDef() {
assertTrue((hostOsVersion.first() == 6 && hostOsVersion.second() >= 5) || (hostOsVersion.first() >= 7));
}

public void testChannelDef() {
ChannelDef.ChannelType type = ChannelDef.ChannelType.UNIX;
ChannelDef.ChannelState state = ChannelDef.ChannelState.CONNECTED;
String name = "v-136-VM.vport";
String path = "/var/lib/libvirt/qemu/" + name;

ChannelDef channelDef = new ChannelDef(name, type, state, path);

assertEquals(state, channelDef.getChannelState());
assertEquals(type, channelDef.getChannelType());
assertEquals(name, channelDef.getName());
assertEquals(path, channelDef.getPath());
}

}

0 comments on commit 9317ef7

Please sign in to comment.