Skip to content

Commit 5c47649

Browse files
CLOUDSTACK-8239 - Adding support for virtio-scsi on KVM hosts
This adds support for virtio-scsi on KVM hosts, either for guests that are associated with a new os_type of 'Other PV Virtio-SCSI (64-bit)', or when a VM or template is regstered with a detail parameter rootDiskController=scsi. Update cloudstack add template dialog to allow for selecting rootDiskController with KVM Update cloudstack kvm virtio-scsi to enable discard=unmap
1 parent 850c07c commit 5c47649

File tree

7 files changed

+636
-54
lines changed

7 files changed

+636
-54
lines changed

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DevicesDef;
117117
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
118118
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.DeviceType;
119+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.DiscardType;
119120
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.DiskProtocol;
120121
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FeaturesDef;
121122
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FilesystemDef;
@@ -125,6 +126,7 @@
125126
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef;
126127
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
127128
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.GuestNetType;
129+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
128130
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
129131
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy;
130132
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VideoDef;
@@ -162,6 +164,7 @@
162164
import com.cloud.utils.ssh.SshHelper;
163165
import com.cloud.vm.VirtualMachine;
164166
import com.cloud.vm.VirtualMachine.PowerState;
167+
import com.cloud.vm.VmDetailConstants;
165168

166169
/**
167170
* LibvirtComputingResource execute requests on the computing/routing host using
@@ -2059,6 +2062,19 @@ So if getMinSpeed() returns null we fall back to getSpeed().
20592062
final InputDef input = new InputDef("tablet", "usb");
20602063
devices.addDevice(input);
20612064

2065+
2066+
DiskDef.DiskBus busT = getDiskModelFromVMDetail(vmTO);
2067+
2068+
if (busT == null) {
2069+
busT = getGuestDiskModel(vmTO.getPlatformEmulator());
2070+
}
2071+
2072+
// If we're using virtio scsi, then we need to add a virtual scsi controller
2073+
if (busT == DiskDef.DiskBus.SCSI) {
2074+
final SCSIDef sd = new SCSIDef((short)0, 0, 0, 9, 0);
2075+
devices.addDevice(sd);
2076+
}
2077+
20622078
vm.addComp(devices);
20632079

20642080
return vm;
@@ -2142,23 +2158,16 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
21422158
}
21432159

21442160
// if params contains a rootDiskController key, use its value (this is what other HVs are doing)
2145-
DiskDef.DiskBus diskBusType = null;
2146-
final Map <String, String> params = vmSpec.getDetails();
2147-
if (params != null && params.get("rootDiskController") != null && !params.get("rootDiskController").isEmpty()) {
2148-
final String rootDiskController = params.get("rootDiskController");
2149-
s_logger.debug("Passed custom disk bus " + rootDiskController);
2150-
for (final DiskDef.DiskBus bus : DiskDef.DiskBus.values()) {
2151-
if (bus.toString().equalsIgnoreCase(rootDiskController)) {
2152-
s_logger.debug("Found matching enum for disk bus " + rootDiskController);
2153-
diskBusType = bus;
2154-
break;
2155-
}
2156-
}
2157-
}
2161+
DiskDef.DiskBus diskBusType = getDiskModelFromVMDetail(vmSpec);
21582162

21592163
if (diskBusType == null) {
21602164
diskBusType = getGuestDiskModel(vmSpec.getPlatformEmulator());
21612165
}
2166+
2167+
// I'm not sure why previously certain DATADISKs were hard-coded VIRTIO and others not, however this
2168+
// maintains existing functionality with the exception that SCSI will override VIRTIO.
2169+
DiskDef.DiskBus diskBusTypeData = (diskBusType == DiskDef.DiskBus.SCSI) ? diskBusType : DiskDef.DiskBus.VIRTIO;
2170+
21622171
final DiskDef disk = new DiskDef();
21632172
if (volume.getType() == Volume.Type.ISO) {
21642173
if (volPath == null) {
@@ -2170,6 +2179,11 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
21702179
} else {
21712180
final int devId = volume.getDiskSeq().intValue();
21722181

2182+
if (diskBusType == DiskDef.DiskBus.SCSI ) {
2183+
disk.setQemuDriver(true);
2184+
disk.setDiscard(DiscardType.UNMAP);
2185+
}
2186+
21732187
if (pool.getType() == StoragePoolType.RBD) {
21742188
/*
21752189
For RBD pools we use the secret mechanism in libvirt.
@@ -2188,7 +2202,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
21882202
disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusType);
21892203
} else {
21902204
if (volume.getType() == Volume.Type.DATADISK) {
2191-
disk.defFileBasedDisk(physicalDisk.getPath(), devId, DiskDef.DiskBus.VIRTIO, DiskDef.DiskFmtType.QCOW2);
2205+
disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData, DiskDef.DiskFmtType.QCOW2);
21922206
} else {
21932207
disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.DiskFmtType.QCOW2);
21942208
}
@@ -2216,6 +2230,7 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
22162230
disk.setCacheMode(DiskDef.DiskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString().toUpperCase()));
22172231
}
22182232
}
2233+
22192234
vm.getDevices().addDevice(disk);
22202235
}
22212236

@@ -2334,13 +2349,13 @@ public synchronized String attachOrDetachDisk(final Connect conn,
23342349
DiskDef diskdef = null;
23352350
final KVMStoragePool attachingPool = attachingDisk.getPool();
23362351
try {
2337-
if (!attach) {
2338-
dm = conn.domainLookupByName(vmName);
2339-
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
2340-
final String xml = dm.getXMLDesc(0);
2341-
parser.parseDomainXML(xml);
2342-
disks = parser.getDisks();
2352+
dm = conn.domainLookupByName(vmName);
2353+
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
2354+
final String domXml = dm.getXMLDesc(0);
2355+
parser.parseDomainXML(domXml);
2356+
disks = parser.getDisks();
23432357

2358+
if (!attach) {
23442359
for (final DiskDef disk : disks) {
23452360
final String file = disk.getDiskPath();
23462361
if (file != null && file.equalsIgnoreCase(attachingDisk.getPath())) {
@@ -2352,17 +2367,31 @@ public synchronized String attachOrDetachDisk(final Connect conn,
23522367
throw new InternalErrorException("disk: " + attachingDisk.getPath() + " is not attached before");
23532368
}
23542369
} else {
2370+
DiskDef.DiskBus busT = DiskDef.DiskBus.VIRTIO;
2371+
for (final DiskDef disk : disks) {
2372+
if (disk.getDeviceType() == DeviceType.DISK) {
2373+
if (disk.getBusType() == DiskDef.DiskBus.SCSI) {
2374+
busT = DiskDef.DiskBus.SCSI;
2375+
}
2376+
break;
2377+
}
2378+
}
2379+
23552380
diskdef = new DiskDef();
2381+
if (busT == DiskDef.DiskBus.SCSI) {
2382+
diskdef.setQemuDriver(true);
2383+
diskdef.setDiscard(DiscardType.UNMAP);
2384+
}
23562385
if (attachingPool.getType() == StoragePoolType.RBD) {
23572386
diskdef.defNetworkBasedDisk(attachingDisk.getPath(), attachingPool.getSourceHost(), attachingPool.getSourcePort(), attachingPool.getAuthUserName(),
2358-
attachingPool.getUuid(), devId, DiskDef.DiskBus.VIRTIO, DiskProtocol.RBD, DiskDef.DiskFmtType.RAW);
2387+
attachingPool.getUuid(), devId, busT, DiskProtocol.RBD, DiskDef.DiskFmtType.RAW);
23592388
} else if (attachingPool.getType() == StoragePoolType.Gluster) {
23602389
diskdef.defNetworkBasedDisk(attachingDisk.getPath(), attachingPool.getSourceHost(), attachingPool.getSourcePort(), null,
2361-
null, devId, DiskDef.DiskBus.VIRTIO, DiskProtocol.GLUSTER, DiskDef.DiskFmtType.QCOW2);
2390+
null, devId, busT, DiskProtocol.GLUSTER, DiskDef.DiskFmtType.QCOW2);
23622391
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
2363-
diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, DiskDef.DiskBus.VIRTIO, DiskDef.DiskFmtType.QCOW2);
2392+
diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, busT, DiskDef.DiskFmtType.QCOW2);
23642393
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) {
2365-
diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, DiskDef.DiskBus.VIRTIO);
2394+
diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, busT);
23662395
}
23672396
if (bytesReadRate != null && bytesReadRate > 0) {
23682397
diskdef.setBytesReadRate(bytesReadRate);
@@ -2961,19 +2990,8 @@ private String getHypervisorPath(final Connect conn) {
29612990
}
29622991

29632992
boolean isGuestPVEnabled(final String guestOSName) {
2964-
if (guestOSName == null) {
2965-
return false;
2966-
}
2967-
if (guestOSName.startsWith("Ubuntu") || guestOSName.startsWith("Fedora 13") || guestOSName.startsWith("Fedora 12") || guestOSName.startsWith("Fedora 11") ||
2968-
guestOSName.startsWith("Fedora 10") || guestOSName.startsWith("Fedora 9") || guestOSName.startsWith("CentOS 5.3") || guestOSName.startsWith("CentOS 5.4") ||
2969-
guestOSName.startsWith("CentOS 5.5") || guestOSName.startsWith("CentOS") || guestOSName.startsWith("Fedora") ||
2970-
guestOSName.startsWith("Red Hat Enterprise Linux 5.3") || guestOSName.startsWith("Red Hat Enterprise Linux 5.4") ||
2971-
guestOSName.startsWith("Red Hat Enterprise Linux 5.5") || guestOSName.startsWith("Red Hat Enterprise Linux 6") || guestOSName.startsWith("Debian GNU/Linux") ||
2972-
guestOSName.startsWith("FreeBSD 10") || guestOSName.startsWith("Oracle") || guestOSName.startsWith("Other PV")) {
2973-
return true;
2974-
} else {
2975-
return false;
2976-
}
2993+
DiskDef.DiskBus db = getGuestDiskModel(guestOSName);
2994+
return db != DiskDef.DiskBus.IDE;
29772995
}
29782996

29792997
public boolean isCentosHost() {
@@ -2984,14 +3002,42 @@ public boolean isCentosHost() {
29843002
}
29853003
}
29863004

3005+
public DiskDef.DiskBus getDiskModelFromVMDetail(final VirtualMachineTO vmTO) {
3006+
Map<String, String> details = vmTO.getDetails();
3007+
if (details == null) {
3008+
return null;
3009+
}
3010+
3011+
final String rootDiskController = details.get(VmDetailConstants.ROOT_DISK_CONTROLLER);
3012+
if (StringUtils.isNotBlank(rootDiskController)) {
3013+
s_logger.debug("Passed custom disk bus " + rootDiskController);
3014+
for (final DiskDef.DiskBus bus : DiskDef.DiskBus.values()) {
3015+
if (bus.toString().equalsIgnoreCase(rootDiskController)) {
3016+
s_logger.debug("Found matching enum for disk bus " + rootDiskController);
3017+
return bus;
3018+
}
3019+
}
3020+
}
3021+
return null;
3022+
}
3023+
29873024
private DiskDef.DiskBus getGuestDiskModel(final String platformEmulator) {
2988-
if (isGuestPVEnabled(platformEmulator)) {
3025+
if (platformEmulator == null) {
3026+
return DiskDef.DiskBus.IDE;
3027+
} else if (platformEmulator.startsWith("Other PV Virtio-SCSI")) {
3028+
return DiskDef.DiskBus.SCSI;
3029+
} else if (platformEmulator.startsWith("Ubuntu") || platformEmulator.startsWith("Fedora 13") || platformEmulator.startsWith("Fedora 12") || platformEmulator.startsWith("Fedora 11") ||
3030+
platformEmulator.startsWith("Fedora 10") || platformEmulator.startsWith("Fedora 9") || platformEmulator.startsWith("CentOS 5.3") || platformEmulator.startsWith("CentOS 5.4") ||
3031+
platformEmulator.startsWith("CentOS 5.5") || platformEmulator.startsWith("CentOS") || platformEmulator.startsWith("Fedora") ||
3032+
platformEmulator.startsWith("Red Hat Enterprise Linux 5.3") || platformEmulator.startsWith("Red Hat Enterprise Linux 5.4") ||
3033+
platformEmulator.startsWith("Red Hat Enterprise Linux 5.5") || platformEmulator.startsWith("Red Hat Enterprise Linux 6") || platformEmulator.startsWith("Debian GNU/Linux") ||
3034+
platformEmulator.startsWith("FreeBSD 10") || platformEmulator.startsWith("Oracle") || platformEmulator.startsWith("Other PV")) {
29893035
return DiskDef.DiskBus.VIRTIO;
29903036
} else {
29913037
return DiskDef.DiskBus.IDE;
29923038
}
2993-
}
29943039

3040+
}
29953041
private void cleanupVMNetworks(final Connect conn, final List<InterfaceDef> nics) {
29963042
if (nics != null) {
29973043
for (final InterfaceDef nic : nics) {

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,23 @@ public String toString() {
545545
}
546546
}
547547

548+
public enum DiscardType {
549+
IGNORE("ignore"), UNMAP("unmap");
550+
String _discardType;
551+
DiscardType(String discardType) {
552+
_discardType = discardType;
553+
}
554+
555+
@Override
556+
public String toString() {
557+
if (_discardType == null) {
558+
return "ignore";
559+
}
560+
return _discardType;
561+
}
562+
563+
}
564+
548565
private DeviceType _deviceType; /* floppy, disk, cdrom */
549566
private DiskType _diskType;
550567
private DiskProtocol _diskProtocol;
@@ -566,6 +583,15 @@ public String toString() {
566583
private DiskCacheMode _diskCacheMode;
567584
private String _serial;
568585
private boolean qemuDriver = true;
586+
private DiscardType _discard = DiscardType.IGNORE;
587+
588+
public DiscardType getDiscard() {
589+
return _discard;
590+
}
591+
592+
public void setDiscard(DiscardType discard) {
593+
this._discard = discard;
594+
}
569595

570596
public void setDeviceType(DeviceType deviceType) {
571597
_deviceType = deviceType;
@@ -764,7 +790,11 @@ public String toString() {
764790
diskBuilder.append(">\n");
765791
if(qemuDriver) {
766792
diskBuilder.append("<driver name='qemu'" + " type='" + _diskFmtType
767-
+ "' cache='" + _diskCacheMode + "' " + "/>\n");
793+
+ "' cache='" + _diskCacheMode + "' ");
794+
if(_discard != null && _discard != DiscardType.IGNORE) {
795+
diskBuilder.append("discard='" + _discard.toString() + "' ");
796+
}
797+
diskBuilder.append("/>\n");
768798
}
769799

770800
if (_diskType == DiskType.FILE) {
@@ -1345,6 +1375,37 @@ public String toString() {
13451375
}
13461376
}
13471377

1378+
public static class SCSIDef {
1379+
private short index = 0;
1380+
private int domain = 0;
1381+
private int bus = 0;
1382+
private int slot = 9;
1383+
private int function = 0;
1384+
1385+
public SCSIDef(short index, int domain, int bus, int slot, int function) {
1386+
this.index = index;
1387+
this.domain = domain;
1388+
this.bus = bus;
1389+
this.slot = slot;
1390+
this.function = function;
1391+
}
1392+
1393+
public SCSIDef() {
1394+
1395+
}
1396+
1397+
@Override
1398+
public String toString() {
1399+
StringBuilder scsiBuilder = new StringBuilder();
1400+
1401+
scsiBuilder.append(String.format("<controller type='scsi' index='%d' mode='virtio-scsi'>\n", this.index ));
1402+
scsiBuilder.append(String.format("<address type='pci' domain='0x%04X' bus='0x%02X' slot='0x%02X' function='0x%01X'/>\n",
1403+
this.domain, this.bus, this.slot, this.function ) );
1404+
scsiBuilder.append("</controller>");
1405+
return scsiBuilder.toString();
1406+
}
1407+
}
1408+
13481409
public static class InputDef {
13491410
private final String _type; /* tablet, mouse */
13501411
private final String _bus; /* ps2, usb, xen */

0 commit comments

Comments
 (0)