You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2016/04/06 09:37:09 UTC

git commit: updated refs/heads/4.5 to b0f3bea

Repository: cloudstack
Updated Branches:
  refs/heads/4.5 63b8de62e -> b0f3bea18


vmware: improve support for disk controllers

- Improve disk chain usage while attaching, migrating disks
- Gets root disk controller based diskDeviceBusName from volume's chain info

Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

This closes #1366


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/b0f3bea1
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/b0f3bea1
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/b0f3bea1

Branch: refs/heads/4.5
Commit: b0f3bea188bdf2bc889f74eeddd919878e5d170e
Parents: 63b8de6
Author: Rohit Yadav <ro...@shapeblue.com>
Authored: Mon Jan 25 17:56:01 2016 +0100
Committer: Rohit Yadav <ro...@shapeblue.com>
Committed: Wed Apr 6 13:06:32 2016 +0530

----------------------------------------------------------------------
 .../agent/api/storage/MigrateVolumeAnswer.java  |  9 ++++
 .../storage/command/AttachAnswer.java           | 17 +++++++
 .../motion/AncientDataMotionStrategy.java       |  4 ++
 .../vmware/resource/VmwareResource.java         | 31 ++++++++++---
 .../resource/VmwareStorageProcessor.java        | 28 +++++-------
 .../motion/VmwareStorageMotionStrategy.java     |  3 ++
 .../com/cloud/storage/VolumeApiServiceImpl.java | 40 ++++++++++++++++
 server/src/com/cloud/vm/UserVmManager.java      |  2 +
 server/src/com/cloud/vm/UserVmManagerImpl.java  |  9 ++++
 .../volume/VirtualMachineVolumeChainInfo.java   | 48 ++++++++++++++++++++
 .../vmware/mo/GuestOsDescriptorType.java        | 38 ++++++++++++++++
 .../hypervisor/vmware/mo/VirtualMachineMO.java  |  7 ++-
 12 files changed, 212 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java b/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java
index 3db3e86..43ce4a6 100644
--- a/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java
+++ b/core/src/com/cloud/agent/api/storage/MigrateVolumeAnswer.java
@@ -24,6 +24,7 @@ import com.cloud.agent.api.Command;
 
 public class MigrateVolumeAnswer extends Answer {
     private String volumePath;
+    private String volumeChain;
 
     public MigrateVolumeAnswer(Command command, boolean success, String details, String volumePath) {
         super(command, success, details);
@@ -38,4 +39,12 @@ public class MigrateVolumeAnswer extends Answer {
     public String getVolumePath() {
         return volumePath;
     }
+
+    public String getVolumeChainInfo() {
+        return volumeChain;
+    }
+
+    public void setVolumeChainInfo(String volumeChain) {
+        this.volumeChain = volumeChain;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java b/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java
index 8073689..16452af 100644
--- a/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java
+++ b/core/src/org/apache/cloudstack/storage/command/AttachAnswer.java
@@ -22,8 +22,11 @@ package org.apache.cloudstack.storage.command;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.to.DiskTO;
 
+import java.util.Map;
+
 public class AttachAnswer extends Answer {
     private DiskTO disk;
+    private Map<String, String> diskDetails;
 
     public AttachAnswer() {
         super(null);
@@ -34,6 +37,12 @@ public class AttachAnswer extends Answer {
         setDisk(disk);
     }
 
+    public AttachAnswer(DiskTO disk, Map<String, String> diskDetails) {
+        super(null);
+        setDisk(disk);
+        setDiskDetails(diskDetails);
+    }
+
     public AttachAnswer(String errMsg) {
         super(null, false, errMsg);
     }
@@ -45,4 +54,12 @@ public class AttachAnswer extends Answer {
     public void setDisk(DiskTO disk) {
         this.disk = disk;
     }
+
+    public Map<String, String> getDiskDetails() {
+        return diskDetails;
+    }
+
+    public void setDiskDetails(Map<String, String> diskDetails) {
+        this.diskDetails = diskDetails;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index 7e572c3..c9c35b4 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -407,6 +407,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
             VolumeVO volumeVo = volDao.findById(volume.getId());
             Long oldPoolId = volume.getPoolId();
             volumeVo.setPath(((MigrateVolumeAnswer)answer).getVolumePath());
+            String chainInfo = ((MigrateVolumeAnswer) answer).getVolumeChainInfo();
+            if (chainInfo != null) {
+                volumeVo.setChainInfo(chainInfo);
+            }
             volumeVo.setPodId(destPool.getPodId());
             volumeVo.setPoolId(destPool.getId());
             volumeVo.setLastPoolId(oldPoolId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 008f3a3..e9fca8c 100755
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -33,7 +33,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
 import java.util.TimeZone;
@@ -228,6 +227,7 @@ import com.cloud.hypervisor.vmware.mo.DatacenterMO;
 import com.cloud.hypervisor.vmware.mo.DatastoreFile;
 import com.cloud.hypervisor.vmware.mo.DatastoreMO;
 import com.cloud.hypervisor.vmware.mo.DiskControllerType;
+import com.cloud.hypervisor.vmware.mo.GuestOsDescriptorType;
 import com.cloud.hypervisor.vmware.mo.FeatureKeyConstants;
 import com.cloud.hypervisor.vmware.mo.HostMO;
 import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
@@ -1449,7 +1449,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 s_logger.error(msg);
                 throw new Exception(msg);
             }
-
+            String guestOsId = translateGuestOsIdentifierEx(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator());
             DiskTO[] disks = validateDisks(vmSpec.getDisks());
             assert (disks.length > 0);
             NicTO[] nics = vmSpec.getNics();
@@ -1562,7 +1562,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                         tearDownVm(vmMo);
                     }else if (!hyperHost.createBlankVm(vmNameOnVcenter, vmInternalCSName, vmSpec.getCpus(), vmSpec.getMaxSpeed().intValue(),
                             getReservedCpuMHZ(vmSpec), vmSpec.getLimitCpuUse(), (int)(vmSpec.getMaxRam() / (1024 * 1024)), getReservedMemoryMb(vmSpec),
-                            translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value(), rootDiskDataStoreDetails.first(), false, controllerInfo, systemVm)) {
+                            guestOsId, rootDiskDataStoreDetails.first(), false, controllerInfo, systemVm)) {
                         throw new Exception("Failed to create VM. vmName: " + vmInternalCSName);
                     }
                 }
@@ -1586,7 +1586,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             }
 
             VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
-            String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value();
 
             VmwareHelper.setBasicVmConfig(vmConfigSpec, vmSpec.getCpus(), vmSpec.getMaxSpeed(),
                     getReservedCpuMHZ(vmSpec), (int)(vmSpec.getMaxRam() / (1024 * 1024)), getReservedMemoryMb(vmSpec),
@@ -3357,6 +3356,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 s_logger.debug("Successfully consolidated disks of VM " + vmName + ".");
             }
 
+            VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
             // Update and return volume path for every disk because that could have changed after migration
             for (Pair<VolumeTO, StorageFilerTO> entry : volToFiler) {
                 volume = entry.first();
@@ -3365,8 +3365,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 for (VirtualDisk disk : disks) {
                     if (volumeDeviceKey.get(volumeId) == disk.getKey()) {
                         VolumeObjectTO newVol = new VolumeObjectTO();
+                        String newPath = vmMo.getVmdkFileBaseName(disk);
+                        String poolName = entry.second().getUuid().replace("-", "");
+                        VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(newPath, poolName);
                         newVol.setId(volumeId);
-                        newVol.setPath(vmMo.getVmdkFileBaseName(disk));
+                        newVol.setChainInfo(this._gson.toJson(diskInfo));
+                        newVol.setPath(newPath);
                         volumeToList.add(newVol);
                         break;
                     }
@@ -3498,7 +3502,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                     }
             }
 
-            return new MigrateVolumeAnswer(cmd, true, null, volumePath);
+            VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
+            String chainInfo = _gson.toJson(diskInfoBuilder.getDiskInfoByBackingFileBaseName(volumePath, poolTo.getUuid().replace("-", "")));
+            MigrateVolumeAnswer answer = new MigrateVolumeAnswer(cmd, true, null, volumePath);
+            answer.setVolumeChainInfo(chainInfo);
+            return answer;
         } catch (Exception e) {
             String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
             s_logger.error(msg, e);
@@ -4672,6 +4680,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         return VirtualMachineGuestOsIdentifier.OTHER_GUEST;
     }
 
+    private String translateGuestOsIdentifierEx(String cpuArchitecture, String guestOs, String cloudGuestOs) {
+        if (cloudGuestOs != null) {
+            GuestOsDescriptorType guestOsId = GuestOsDescriptorType.fromValue(cloudGuestOs);
+            if (guestOsId != null) {
+                s_logger.debug("Found guest OS mapping name for guest os: " + guestOs);
+                return guestOsId.toString();
+            }
+        }
+        return translateGuestOsIdentifier(cpuArchitecture, guestOs, cloudGuestOs).value();
+    }
+
     private HashMap<String, HostVmStateReportEntry> getHostVmStateReport() throws Exception {
         VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
index bc454e7..2b710f5 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
@@ -1345,24 +1345,18 @@ public class VmwareStorageProcessor implements StorageProcessor {
             AttachAnswer answer = new AttachAnswer(disk);
 
             if (isAttach) {
-                String dataDiskController = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER);
-                String rootDiskController = controllerInfo.get(VmDetailConstants.ROOK_DISK_CONTROLLER);
-                DiskControllerType rootDiskControllerType = DiskControllerType.getType(rootDiskController);
-
-                if (dataDiskController == null) {
-                    dataDiskController = getLegacyVmDataDiskController();
-                } else if ((rootDiskControllerType == DiskControllerType.lsilogic) ||
-                           (rootDiskControllerType == DiskControllerType.lsisas1068) ||
-                           (rootDiskControllerType == DiskControllerType.pvscsi) ||
-                           (rootDiskControllerType == DiskControllerType.buslogic)) {
-                    //TODO: Support mix of SCSI controller types for single VM. If root disk is already over
-                    //a SCSI controller then use the same for data volume as well. This limitation will go once mix
-                    //of SCSI controller types for single VM.
-                    dataDiskController = rootDiskController;
-                } else if (DiskControllerType.getType(dataDiskController) == DiskControllerType.osdefault) {
-                    dataDiskController = vmMo.getRecommendedDiskController(null);
+                Map<String, String> diskDetails = new HashMap<String, String>();
+                String diskController = null;
+                if (controllerInfo != null) {
+                    diskController = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER);
                 }
-                vmMo.attachDisk(new String[] {datastoreVolumePath}, morDs, dataDiskController);
+                if (diskController == null) {
+                    diskController = getLegacyVmDataDiskController();
+                }
+                if (DiskControllerType.getType(diskController) == DiskControllerType.osdefault) {
+                    diskController = vmMo.getRecommendedDiskController(null);
+                }
+                vmMo.attachDisk(new String[] {datastoreVolumePath}, morDs, diskController);
             } else {
                 vmMo.removeAllSnapshots();
                 vmMo.detachDisk(datastoreVolumePath, false);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
index da9764d..4b77aab 100644
--- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
+++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
@@ -207,6 +207,9 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
                     VolumeVO volumeVO = volDao.findById(volume.getId());
                     Long oldPoolId = volumeVO.getPoolId();
                     volumeVO.setPath(volumeTo.getPath());
+                    if (volumeTo.getChainInfo() != null) {
+                        volumeVO.setChainInfo(volumeTo.getChainInfo());
+                    }
                     volumeVO.setLastPoolId(oldPoolId);
                     volumeVO.setFolder(pool.getPath());
                     volumeVO.setPodId(pool.getPodId());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index fa2888a..68798f2 100644
--- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ExecutionException;
 
 import javax.inject.Inject;
 
+import org.apache.cloudstack.utils.volume.VirtualMachineVolumeChainInfo;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
@@ -96,16 +97,19 @@ import com.cloud.exception.StorageUnavailableException;
 import com.cloud.gpu.GPU;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorCapabilitiesVO;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
 import com.cloud.org.Grouping;
+import com.cloud.serializer.GsonHelper;
 import com.cloud.service.dao.ServiceOfferingDetailsDao;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.SnapshotDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.VolumeDetailsDao;
 import com.cloud.storage.snapshot.SnapshotApiService;
 import com.cloud.storage.snapshot.SnapshotManager;
 import com.cloud.template.TemplateManager;
@@ -134,6 +138,7 @@ import com.cloud.utils.db.UUIDManager;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.fsm.NoTransitionException;
 import com.cloud.utils.fsm.StateMachine2;
+import com.cloud.vm.UserVmManager;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
@@ -154,6 +159,8 @@ import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
 import com.cloud.vm.snapshot.VMSnapshotVO;
 import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
 
 public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService, VmWorkJobHandler {
     private final static Logger s_logger = Logger.getLogger(VolumeApiServiceImpl.class);
@@ -176,6 +183,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
     @Inject
     VolumeDao _volsDao;
     @Inject
+    VolumeDetailsDao _volDetailDao;
+    @Inject
     HostDao _hostDao;
     @Inject
     SnapshotDao _snapshotDao;
@@ -225,6 +234,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
     VmWorkJobDao _workJobDao;
     @Inject
     ClusterDetailsDao _clusterDetailsDao;
+    @Inject
+    UserVmManager _userVmMgr;
+    protected Gson _gson;
 
     private List<StoragePoolAllocator> _storagePoolAllocators;
 
@@ -238,6 +250,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
 
     protected VolumeApiServiceImpl() {
         _volStateMachine = Volume.State.getStateMachine();
+        _gson = GsonHelper.getGsonLogger();
     }
 
     /*
@@ -1781,6 +1794,23 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
                                 throw new InvalidParameterValueException("Cannot migrate ROOT volume of a stopped VM to a storage pool in a different VMware datacenter");
                             }
                         }
+
+                        String rootVolChainInfo = vol.getChainInfo();
+                        if ((vm.getType().equals(VirtualMachine.Type.User)) && (rootVolChainInfo != null) && (!rootVolChainInfo.isEmpty())) {
+                            String rootDiskController = null;
+                            try {
+                                VirtualMachineVolumeChainInfo infoInChain = _gson.fromJson(rootVolChainInfo, VirtualMachineVolumeChainInfo.class);
+                                if (infoInChain != null) {
+                                    rootDiskController = infoInChain.getControllerFromDeviceBusName();
+                                }
+                                UserVmVO userVmVo = _userVmDao.findById(vm.getId());
+                                if ((rootDiskController != null) && (!rootDiskController.isEmpty())) {
+                                    _userVmDao.loadDetails(userVmVo);
+                                    _userVmMgr.persistDeviceBusInfo(userVmVo, rootDiskController);
+                                }
+                            } catch (JsonParseException ignored) {
+                            }
+                        }
                     }
                 }
             }
@@ -2325,6 +2355,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
             controllerInfo.put(VmDetailConstants.ROOK_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.ROOK_DISK_CONTROLLER));
             controllerInfo.put(VmDetailConstants.DATA_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.DATA_DISK_CONTROLLER));
             cmd.setControllerInfo(controllerInfo);
+            s_logger.debug("Attach volume id:" + volumeToAttach.getId() +  " on VM id:" + vm.getId() + " has controller info:" + controllerInfo);
 
             try {
                 answer = (AttachAnswer)_agentMgr.send(hostId, cmd);
@@ -2349,6 +2380,15 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
 
                     _volsDao.update(volumeToAttach.getId(), volumeToAttach);
                 }
+
+                if (host.getHypervisorType() == Hypervisor.HypervisorType.VMware) {
+                    Map<String, String> diskDetails = answer.getDiskDetails();
+                    for (Map.Entry<String, String> detail : diskDetails.entrySet()) {
+                        VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeToAttach.getId(), detail.getKey(), detail.getValue(), true);
+                        _volDetailDao.persist(volumeDetailVo);
+                    }
+                }
+
             } else {
                 _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), deviceId);
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/server/src/com/cloud/vm/UserVmManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java
index 324547f..204619a 100755
--- a/server/src/com/cloud/vm/UserVmManager.java
+++ b/server/src/com/cloud/vm/UserVmManager.java
@@ -111,4 +111,6 @@ public interface UserVmManager extends UserVmService {
     public void removeCustomOfferingDetails(long vmId);
 
     void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType);
+
+    void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 35aafb8..e3ca9e6 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -5028,6 +5028,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         }
     }
 
+    public void persistDeviceBusInfo(UserVmVO vm, String rootDiskController) {
+        String existingVmRootDiskController = vm.getDetail(VmDetailConstants.ROOK_DISK_CONTROLLER);
+        if (((existingVmRootDiskController == null) || (existingVmRootDiskController.isEmpty())) && (rootDiskController != null) && (!rootDiskController.isEmpty())) {
+            vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, rootDiskController);
+            _vmDao.saveDetails(vm);
+            s_logger.debug("Persisted device bus information rootDiskController=" + rootDiskController + " for vm: " + vm.getDisplayName());
+        }
+    }
+
     @Override
     public String getConfigComponentName() {
         return UserVmManager.class.getSimpleName();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java
----------------------------------------------------------------------
diff --git a/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java b/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java
new file mode 100644
index 0000000..ac1648b
--- /dev/null
+++ b/utils/src/org/apache/cloudstack/utils/volume/VirtualMachineVolumeChainInfo.java
@@ -0,0 +1,48 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.utils.volume;
+
+public class VirtualMachineVolumeChainInfo {
+    String diskDeviceBusName;
+    String[] diskChain;
+
+    public String getDiskDeviceBusName() {
+        return this.diskDeviceBusName;
+    }
+
+    public void setDiskDeviceBusName(String diskDeviceBusName) {
+        this.diskDeviceBusName = diskDeviceBusName;
+    }
+
+    public String[] getDiskChain() {
+        return this.diskChain;
+    }
+
+    public void setDiskChain(String[] diskChain) {
+        this.diskChain = diskChain;
+    }
+
+    public String getControllerFromDeviceBusName() {
+        if ((this.diskDeviceBusName == null) || (this.diskDeviceBusName.isEmpty()) || (!this.diskDeviceBusName.contains(":"))) {
+            return null;
+        }
+        return this.diskDeviceBusName.substring(0, this.diskDeviceBusName.indexOf(":") - 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/vmware-base/src/com/cloud/hypervisor/vmware/mo/GuestOsDescriptorType.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/GuestOsDescriptorType.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/GuestOsDescriptorType.java
new file mode 100644
index 0000000..364bd87
--- /dev/null
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/GuestOsDescriptorType.java
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.hypervisor.vmware.mo;
+
+public enum GuestOsDescriptorType
+{
+  windows9Guest,
+  windows9_64Guest,
+  windows9Server64Guest,
+  rhel7Guest,
+  rhel7_64Guest;
+
+  public static GuestOsDescriptorType fromValue(String value) {
+    if (value == null) {
+      return null;
+    }
+    GuestOsDescriptorType guestOsType = null;
+    try {
+      guestOsType = valueOf(value);
+    } catch (IllegalArgumentException e) {
+    }
+    return guestOsType;
+  }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b0f3bea1/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
index d61cb2e..86c4f47 100644
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
@@ -2112,8 +2112,12 @@ public class VirtualMachineMO extends BaseMO {
         List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
                 getDynamicProperty(_mor, "config.hardware.device");
 
+        String deviceList = "";
         if (devices != null && devices.size() > 0) {
             for (VirtualDevice device : devices) {
+                if (device != null) {
+                    deviceList += String.format(" Device %s: info:%s controller key:%s;", device.getKey(), device.getDeviceInfo(), device.getControllerKey());
+                }
                 if ((DiskControllerType.getType(diskController) == DiskControllerType.lsilogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
                         && device instanceof VirtualLsiLogicController) {
                     return ((VirtualLsiLogicController)device).getKey();
@@ -2131,7 +2135,8 @@ public class VirtualMachineMO extends BaseMO {
         }
 
         assert (false);
-        throw new Exception(diskController + " Controller Not Found");
+        throw new Exception("Scsi disk controller of type " + diskController + " not found among configured devices:" + deviceList +
+                ". Unable to find and return scsi disk controller key.");
     }
 
     public int getScsiDiskControllerKeyNoException(String diskController) throws Exception {