cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kelv...@apache.org
Subject [26/27] git commit: updated refs/heads/master to ae23144
Date Wed, 04 Sep 2013 22:42:35 GMT
CLOUDSTACK-3237: add disk chain sync logic to handle out-of-band chain changes that could happen in storage live migration and VM snapshot operations


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

Branch: refs/heads/master
Commit: bae26665493769951a2432ee13c06fbd58aba193
Parents: e3a5b3f
Author: Kelven Yang <kelveny@gmail.com>
Authored: Mon Aug 26 17:45:54 2013 -0700
Committer: Kelven Yang <kelveny@gmail.com>
Committed: Wed Sep 4 14:49:46 2013 -0700

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/VolumeTO.java    |   4 +
 .../service/VolumeOrchestrationService.java     |   2 +
 .../com/cloud/vm/VirtualMachineManagerImpl.java |  18 +
 .../orchestration/VolumeOrchestrator.java       |  18 +
 .../vmware/resource/VmwareResource.java         | 702 ++++++++++++-------
 .../vmware/mo/VirtualMachineDiskInfo.java       |  42 ++
 .../mo/VirtualMachineDiskInfoBuilder.java       |  97 +++
 .../hypervisor/vmware/mo/VirtualMachineMO.java  |  27 +-
 8 files changed, 659 insertions(+), 251 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/api/src/com/cloud/agent/api/to/VolumeTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java
index a5681a0..fb08384 100644
--- a/api/src/com/cloud/agent/api/to/VolumeTO.java
+++ b/api/src/com/cloud/agent/api/to/VolumeTO.java
@@ -126,6 +126,10 @@ public class VolumeTO implements InternalIdentity {
     public String getChainInfo() {
         return chainInfo;
     }
+    
+    public void setChainInfo(String chainInfo) {
+    	this.chainInfo = chainInfo;
+    }
 
     public String getOsType() {
         return guestOsType;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
index 4e4a191..5f21eb5 100644
--- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
@@ -92,4 +92,6 @@ public interface VolumeOrchestrationService {
     boolean validateVolumeSizeRange(long size);
 
     StoragePool findStoragePool(DiskProfile dskCh, DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, Set<StoragePool> avoid);
+
+    void updateVolumeDiskChain(long volumeId, String path, String chainInfo);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index a5167fd..9976275 100755
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -46,6 +46,8 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.Listener;
@@ -76,8 +78,10 @@ import com.cloud.agent.api.StopAnswer;
 import com.cloud.agent.api.StopCommand;
 import com.cloud.agent.api.UnPlugNicAnswer;
 import com.cloud.agent.api.UnPlugNicCommand;
+import com.cloud.agent.api.to.DiskTO;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.agent.manager.Commands;
 import com.cloud.agent.manager.allocator.HostAllocator;
 import com.cloud.alert.AlertManager;
@@ -902,6 +906,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                             destHostId = finalHost.getId();
                         }
                         if (vmGuru.finalizeStart(vmProfile, destHostId, cmds, ctx)) {
+                            syncDiskChainChange(startAnswer);
+                            
                             if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) {
                                 throw new ConcurrentOperationException("Unable to transition to a new state.");
                             }
@@ -992,6 +998,18 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                     + "' (" + vm.getUuid() + "), see management server log for details");
         }
     }
+    
+    private void syncDiskChainChange(StartAnswer answer) {
+    	VirtualMachineTO vmSpec = answer.getVirtualMachine();
+    	
+    	for(DiskTO disk : vmSpec.getDisks()) {
+    		if(disk.getType() != Volume.Type.ISO) {
+	    		VolumeObjectTO vol = (VolumeObjectTO)disk.getData();
+	    		
+	    		volumeMgr.updateVolumeDiskChain(vol.getId(), vol.getPath(), vol.getChainInfo());
+    		}
+    	}
+    }
 
     @Override
     public void stop(String vmUuid) throws ResourceUnavailableException {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 0e98f48..f3183e6 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -1127,4 +1127,22 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
         VolumeVO vol = _volsDao.findById(volumeId);
         return dataStoreMgr.getPrimaryDataStore(vol.getPoolId()).getUuid();
     }
+    
+    public void updateVolumeDiskChain(long volumeId, String path, String chainInfo) {
+        VolumeVO vol = _volsDao.findById(volumeId);
+        boolean needUpdate = false;
+        if(!vol.getPath().equalsIgnoreCase(path))
+        	needUpdate = true;
+        
+        if(chainInfo != null && (vol.getChainInfo() == null || !chainInfo.equalsIgnoreCase(vol.getChainInfo())))
+        	needUpdate = true;
+        
+        if(needUpdate) {
+        	s_logger.info("Update volume disk chain info. vol: " + vol.getId() + ", " + vol.getPath() + " -> " + path 
+        		+ ", " + vol.getChainInfo() + " -> " + chainInfo);
+	        vol.setPath(path);
+	        vol.setChainInfo(chainInfo);
+	        _volsDao.update(volumeId, vol);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/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 03adbb0..ff04cad 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
@@ -278,6 +278,7 @@ import com.cloud.hypervisor.vmware.mo.ClusterMO;
 import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
 import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO;
 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.FeatureKeyConstants;
@@ -288,6 +289,8 @@ import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
 import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
 import com.cloud.hypervisor.vmware.mo.NetworkDetails;
 import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
+import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfo;
+import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
 import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
 import com.cloud.hypervisor.vmware.mo.VirtualSwitchType;
 import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
@@ -332,7 +335,6 @@ import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VirtualMachineName;
 import com.cloud.vm.VmDetailConstants;
 
-
 public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService {
     private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
 
@@ -1089,11 +1091,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             s_logger.error("Unexpected exception: " + e.toString(), e);
             return new Answer(cmd, false, "VPCLoadBalancerConfigCommand failed due to " + VmwareHelper.getExceptionMessage(e));
         }
-
-
     }
 
-
     protected Answer execute(final LoadBalancerConfigCommand cmd) {
 
         if ( cmd.getVpcId() != null ) {
@@ -1792,7 +1791,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             if (!result_gateway.first()) {
                 throw new InternalErrorException("Unable to configure source NAT for public IP address.");
             }
-
         }
     }
 
@@ -2292,6 +2290,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         }
         return new CheckS2SVpnConnectionsAnswer(cmd, true, result.second());
     }
+    
     protected Answer execute(CheckRouterCommand cmd) {
         if (s_logger.isDebugEnabled()) {
             s_logger.debug("Executing resource CheckRouterCommand: " + _gson.toJson(cmd));
@@ -2520,6 +2519,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
 
         return validatedDisks.toArray(new DiskTO[0]);
     }
+    
+    private static DiskTO getIsoDiskTO(DiskTO[] disks) {
+        for (DiskTO vol : disks) {
+            if (vol.getType() == Volume.Type.ISO) {
+                return vol;
+            }
+        }
+        return null;
+    }
 
     protected ScaleVmAnswer execute(ScaleVmCommand cmd) {
 
@@ -2569,23 +2577,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
     }
 
     protected StartAnswer execute(StartCommand cmd) {
-
         if (s_logger.isInfoEnabled()) {
             s_logger.info("Executing resource StartCommand: " + _gson.toJson(cmd));
         }
-
+        
         VirtualMachineTO vmSpec = cmd.getVirtualMachine();
-        String vmInternalCSName = null;
-        String vmNameOnVcenter = null;
-        if (vmSpec.getHostName() != null) {
-            vmInternalCSName = vmSpec.getName();
-            if (_instanceNameFlag == true)
-                vmNameOnVcenter = vmSpec.getHostName();
-            else
-                vmNameOnVcenter = vmSpec.getName();
-        } else {
-            vmNameOnVcenter = vmInternalCSName = vmSpec.getName();
-        }
+        
+        Pair<String, String> names = composeVmNames(vmSpec);
+        String vmInternalCSName = names.first();
+        String vmNameOnVcenter = names.second();
+        
         // Thus, vmInternalCSName always holds i-x-y, the cloudstack generated internal VM name.
         State state = State.Stopped;
         VmwareContext context = getServiceContext();
@@ -2597,10 +2598,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 _vms.put(vmInternalCSName, State.Starting);
             }
 
-            VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.valueOf(vmSpec.getDetails().get(VmDetailConstants.NIC_ADAPTER));
-            if(s_logger.isDebugEnabled())
-                s_logger.debug("VM " + vmInternalCSName + " will be started with NIC device type: " + nicDeviceType);
-
             VmwareHypervisorHost hyperHost = getHyperHost(context);
             DiskTO[] disks = validateDisks(vmSpec.getDisks());
             assert (disks.length > 0);
@@ -2621,12 +2618,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             }
             
             DatacenterMO dcMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter());
-
+            VirtualMachineDiskInfoBuilder diskInfoBuilder = null;
             VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName);
             if (vmMo != null) {
                 s_logger.info("VM " + vmInternalCSName + " already exists, tear down devices for reconfiguration");
                 if (getVmState(vmMo) != State.Stopped)
                     vmMo.safePowerOff(_shutdown_waitMs);
+             
+                // retrieve disk information before we tear down
+                diskInfoBuilder = vmMo.getDiskInfoBuilder();
                 vmMo.tearDownDevices(new Class<?>[] { VirtualDisk.class, VirtualEthernetCard.class });
                 vmMo.ensureScsiDeviceController();
             } else {
@@ -2643,6 +2643,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
 
                     if (getVmState(vmMo) != State.Stopped)
                         vmMo.safePowerOff(_shutdown_waitMs);
+                    
+                    diskInfoBuilder = vmMo.getDiskInfoBuilder();
                     vmMo.tearDownDevices(new Class<?>[] { VirtualDisk.class, VirtualEthernetCard.class });
                     vmMo.ensureScsiDeviceController();
                 } else {
@@ -2656,6 +2658,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                     }
 
                     assert (vmSpec.getMinSpeed() != null) && (rootDiskDataStoreDetails != null);
+
+                    if(rootDiskDataStoreDetails.second().folderExists(String.format("[%s]", rootDiskDataStoreDetails.second().getName()), vmNameOnVcenter)) {
+                    	s_logger.warn("WARN!!! Folder already exists on datastore for new VM " + vmNameOnVcenter + ", erase it");
+                    	rootDiskDataStoreDetails.second().deleteFile(String.format("[%s] %s/", rootDiskDataStoreDetails.second().getName(), 
+                    		vmNameOnVcenter), dcMo.getMor(), false);
+                    }
+                    
                     if (!hyperHost.createBlankVm(vmNameOnVcenter, vmInternalCSName, vmSpec.getCpus(), vmSpec.getMaxSpeed().intValue(),
                             vmSpec.getMinSpeed(), vmSpec.getLimitCpuUse(),(int)(vmSpec.getMaxRam()/(1024*1024)), ramMb,
                             translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(), rootDiskDataStoreDetails.first(), false)) {
@@ -2670,63 +2679,43 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             }
             
             int totalChangeDevices = disks.length + nics.length;
+            
             DiskTO volIso = null;
             if (vmSpec.getType() != VirtualMachine.Type.User) {
                 // system VM needs a patch ISO
                 totalChangeDevices++;
             } else {
-                for (DiskTO vol : disks) {
-                    if (vol.getType() == Volume.Type.ISO) {
-                        volIso = vol;
-                        break;
-                    }
-                }
-
+            	volIso = getIsoDiskTO(disks);
                 if (volIso == null)
                     totalChangeDevices++;
             }
 
             VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
             int ramMb = (int) (vmSpec.getMinRam() / (1024 * 1024));
+            String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value();
+            
             VmwareHelper.setBasicVmConfig(vmConfigSpec, vmSpec.getCpus(), vmSpec.getMaxSpeed(),
                     vmSpec.getMinSpeed(),(int) (vmSpec.getMaxRam()/(1024*1024)), ramMb,
-                    translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value(), vmSpec.getLimitCpuUse());
-            String guestOsId = translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs()).value();
+                    guestOsId, vmSpec.getLimitCpuUse());
+            
             // Check for hotadd settings
             vmConfigSpec.setMemoryHotAddEnabled(vmMo.isMemoryHotAddSupported(guestOsId));
             vmConfigSpec.setCpuHotAddEnabled(vmMo.isCpuHotAddSupported(guestOsId));
-
-            if ("true".equals(vmSpec.getDetails().get(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG))) {
-                s_logger.debug("Nested Virtualization enabled in configuration, checking hypervisor capability");
-                ManagedObjectReference hostMor = vmMo.getRunningHost().getMor();
-                ManagedObjectReference computeMor = context.getVimClient().getMoRefProp(hostMor, "parent");
-                ManagedObjectReference environmentBrowser =
-                        context.getVimClient().getMoRefProp(computeMor, "environmentBrowser");
-                HostCapability hostCapability = context.getService().queryTargetCapabilities(environmentBrowser, hostMor);
-                Boolean nestedHvSupported = hostCapability.isNestedHVSupported();
-                if (nestedHvSupported == null) {
-                    // nestedHvEnabled property is supported only since VMware 5.1. It's not defined for earlier versions.
-                    s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName());
-                } else if (nestedHvSupported.booleanValue()) {
-                    s_logger.debug("Hypervisor supports nested virtualization, enabling for VM " + vmSpec.getName());
-                    vmConfigSpec.setNestedHVEnabled(true);
-                }
-                else {
-                    s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName());
-                    vmConfigSpec.setNestedHVEnabled(false);
-                }
-            }
+            configNestedHVSupport(vmMo, vmSpec, vmConfigSpec);
 
             VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[totalChangeDevices];
             int i = 0;
             int ideUnitNumber = 0;
-            int scsiUnitNumber =0;
+            int scsiUnitNumber = 0;
             int nicUnitNumber = 0;
             int ideControllerKey = vmMo.getIDEDeviceControllerKey();
             int scsiControllerKey = vmMo.getScsiDeviceControllerKey();
             int controllerKey;
-            String datastoreDiskPath;
 
+            //
+            // Setup ISO device
+            //
+            
             // prepare systemvm patch ISO
             if (vmSpec.getType() != VirtualMachine.Type.User) {
                 // attach ISO (for patching of system VM)
@@ -2758,8 +2747,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                     deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT);
                 }
             } else {
-                // we will always plugin a CDROM device
-
+                // Note: we will always plug a CDROM device
                 if (volIso != null) {
                     TemplateObjectTO iso = (TemplateObjectTO)volIso.getData();
 
@@ -2805,55 +2793,58 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                     }
                 }
             }
+            
             i++;
-            for (DiskTO vol : sortVolumesByDeviceId(disks)) {
+            
+            //
+            // Setup ROOT/DATA disk devices
+            //
+            DiskTO[] sortedDisks = sortVolumesByDeviceId(disks);
+            for (DiskTO vol : sortedDisks) {
                 deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
 
-                if (vol.getType() == Volume.Type.ISO) {
-                    controllerKey = ideControllerKey;
-                } else {
-                    if(vol.getType() == Volume.Type.ROOT) {
-                        if(vmSpec.getDetails() != null && vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null)
-                        {
-                            if(vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi"))
-                                controllerKey = scsiControllerKey;
-                            else
-                                controllerKey = ideControllerKey;
-                        } else {
-                            controllerKey = scsiControllerKey;
-                        }
-                    } else {
-                        // DATA volume always use SCSI device
-                        controllerKey = scsiControllerKey;
-                    }
-                }
-
-                if (vol.getType() != Volume.Type.ISO) {
-                    VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData();
-                    PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore();
-                    Pair<ManagedObjectReference, DatastoreMO> volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid());
-                    assert (volumeDsDetails != null);
-                    VirtualDevice device;
-                    
-                    datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dcMo, vmNameOnVcenter, volumeDsDetails.second(), 
-                    	volumeTO.getPath());
-                    device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, new String[] { datastoreDiskPath }, volumeDsDetails.first(),
-                        (controllerKey==ideControllerKey)?ideUnitNumber++:scsiUnitNumber++, i + 1);
-                    
-                    deviceConfigSpecArray[i].setDevice(device);
-                    deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD);
+                if (vol.getType() == Volume.Type.ISO)
+                	continue;
+                
+                controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey);
 
+                VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData();
+                PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore();
+                Pair<ManagedObjectReference, DatastoreMO> volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid());
+                assert (volumeDsDetails != null);
+                VirtualDevice device;
+                
+                String[] diskChain = syncDiskChain(dcMo, vmMo, vmSpec, 
+                    	vol, diskInfoBuilder,
+                    	dataStoresDetails,
+                    	(controllerKey == ideControllerKey) ? true : false, 
+                    	0, 	// currently only support bus 0
+                    	(controllerKey == ideControllerKey) ? ideUnitNumber : scsiUnitNumber);
+                
+                device = VmwareHelper.prepareDiskDevice(vmMo, controllerKey, 
+                	diskChain, 
+                	volumeDsDetails.first(),
+                    (controllerKey == ideControllerKey) ? ideUnitNumber++ : scsiUnitNumber++, i + 1);
+                
+                deviceConfigSpecArray[i].setDevice(device);
+                deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD);
 
-                    if(s_logger.isDebugEnabled())
-                        s_logger.debug("Prepare volume at new device " + _gson.toJson(device));
+                if(s_logger.isDebugEnabled())
+                    s_logger.debug("Prepare volume at new device " + _gson.toJson(device));
 
-                    i++;
-                }
+                i++;
             }
 
+            //
+            // Setup NIC devices
+            //
             VirtualDevice nic;
             int nicMask = 0;
             int nicCount = 0;
+            VirtualEthernetCardType nicDeviceType = VirtualEthernetCardType.valueOf(vmSpec.getDetails().get(VmDetailConstants.NIC_ADAPTER));
+            if(s_logger.isDebugEnabled())
+                s_logger.debug("VM " + vmInternalCSName + " will be started with NIC device type: " + nicDeviceType);
+            
             for (NicTO nicTo : sortNicsByDeviceId(nics)) {
                 s_logger.info("Prepare NIC device based on NicTO: " + _gson.toJson(nicTo));
 
@@ -2890,170 +2881,47 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
 
             vmConfigSpec.getDeviceChange().addAll(Arrays.asList(deviceConfigSpecArray));
 
+            //
+            // Setup VM options
+            //
+            
             // pass boot arguments through machine.id & perform customized options to VMX
-
             ArrayList<OptionValue> extraOptions = new ArrayList<OptionValue>();
-            OptionValue newVal = new OptionValue();
-            newVal.setKey("machine.id");
-            newVal.setValue(vmSpec.getBootArgs());
-            extraOptions.add(newVal);
-
-            newVal = new OptionValue();
-            newVal.setKey("devices.hotplug");
-            newVal.setValue("true");
-            extraOptions.add(newVal);
-
-            /**
-             * Extra Config : nvp.vm-uuid = uuid
-             *  - Required for Nicira NVP integration
-             */
-            newVal = new OptionValue();
-            newVal.setKey("nvp.vm-uuid");
-            newVal.setValue(vmSpec.getUuid());
-            extraOptions.add(newVal);
-
-            /**
-             * Extra Config : nvp.iface-id.<num> = uuid
-             *  - Required for Nicira NVP integration
-             */
-            int nicNum = 0;
-            for (NicTO nicTo : sortNicsByDeviceId(nics)) {
-                if (nicTo.getUuid() != null) {
-                    newVal = new OptionValue();
-                    newVal.setKey("nvp.iface-id." + nicNum);
-                    newVal.setValue(nicTo.getUuid());
-                    extraOptions.add(newVal);
-                }
-                nicNum++;
-            }
-
-            for(Map.Entry<String, String> entry : validateVmDetails(vmSpec.getDetails()).entrySet()) {
-                newVal = new OptionValue();
-                newVal.setKey(entry.getKey());
-                newVal.setValue(entry.getValue());
-                extraOptions.add(newVal);
-            }
+            configBasicExtraOption(extraOptions, vmSpec);
+            configNvpExtraOption(extraOptions, vmSpec);
+            configCustomExtraOption(extraOptions, vmSpec);
 
+            // config VNC
             String keyboardLayout = null;
             if(vmSpec.getDetails() != null)
                 keyboardLayout = vmSpec.getDetails().get(VmDetailConstants.KEYBOARD);
-            vmConfigSpec.getExtraConfig().addAll(Arrays.asList(configureVnc(extraOptions.toArray(new OptionValue[0]), hyperHost, vmInternalCSName, vmSpec.getVncPassword(), keyboardLayout)));
-
+            vmConfigSpec.getExtraConfig().addAll(
+            	Arrays.asList(
+            		configureVnc(
+            			extraOptions.toArray(new OptionValue[0]), 
+            			hyperHost, vmInternalCSName, vmSpec.getVncPassword(), keyboardLayout
+            		)
+            	)
+            );
+
+            //
+            // Configure VM
+            //
             if (!vmMo.configureVm(vmConfigSpec)) {
                 throw new Exception("Failed to configure VM before start. vmName: " + vmInternalCSName);
             }
 
+            //
+            // Post Configuration
+            //
+            
             vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMask));
-
-            /**
-             * We need to configure the port on the DV switch after the host is
-             * connected. So make this happen between the configure and start of
-             * the VM
-             */
-            int nicIndex = 0;
-            for (NicTO nicTo : sortNicsByDeviceId(nics)) {
-                if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) {
-                    // We need to create a port with a unique vlan and pass the key to the nic device
-                    s_logger.trace("Nic " + nicTo.toString() + " is connected to an NVP logicalswitch");
-                    VirtualDevice nicVirtualDevice = vmMo.getNicDeviceByIndex(nicIndex);
-                    if (nicVirtualDevice == null) {
-                        throw new Exception("Failed to find a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad
-                    }
-                    VirtualDeviceBackingInfo backing = nicVirtualDevice.getBacking();
-                    if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) {
-                        // This NIC is connected to a Distributed Virtual Switch
-                        VirtualEthernetCardDistributedVirtualPortBackingInfo portInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing;
-                        DistributedVirtualSwitchPortConnection port = portInfo.getPort();
-                        String portKey = port.getPortKey();
-                        String portGroupKey = port.getPortgroupKey();
-                        String dvSwitchUuid = port.getSwitchUuid();
-
-                        s_logger.debug("NIC " + nicTo.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey);
-
-                        ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager();
-                        ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid);
-
-                        // Get all ports
-                        DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria();
-                        criteria.setInside(true);
-                        criteria.getPortgroupKey().add(portGroupKey);
-                        List<DistributedVirtualPort> dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria);
-
-                        DistributedVirtualPort vmDvPort = null;
-                        List<Integer> usedVlans = new ArrayList<Integer>();
-                        for (DistributedVirtualPort dvPort : dvPorts) {
-                            // Find the port for this NIC by portkey
-                            if (portKey.equals(dvPort.getKey())) {
-                                vmDvPort = dvPort;
-                            }
-                            VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort
-                                    .getConfig().getSetting();
-                            VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings
-                                    .getVlan();
-                            s_logger.trace("Found port " + dvPort.getKey()
-                                    + " with vlan " + vlanId.getVlanId());
-                            if (vlanId.getVlanId() > 0
-                                    && vlanId.getVlanId() < 4095) {
-                                usedVlans.add(vlanId.getVlanId());
-                            }
-                        }
-
-                        if (vmDvPort == null) {
-                            throw new Exception("Empty port list from dvSwitch for nic " + nicTo.toString());
-                        }
-
-                        DVPortConfigInfo dvPortConfigInfo = vmDvPort
-                                .getConfig();
-                        VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPortConfigInfo.getSetting();
-
-                        VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan();
-                        BoolPolicy blocked = settings.getBlocked();
-                        if (blocked.isValue() == Boolean.TRUE) {
-                            s_logger.trace("Port is blocked, set a vlanid and unblock");
-                            DVPortConfigSpec dvPortConfigSpec = new DVPortConfigSpec();
-                            VMwareDVSPortSetting edittedSettings = new VMwareDVSPortSetting();
-                            // Unblock
-                            blocked.setValue(Boolean.FALSE);
-                            blocked.setInherited(Boolean.FALSE);
-                            edittedSettings.setBlocked(blocked);
-                            // Set vlan
-                            for (i = 1; i < 4095; i++) {
-                                if (!usedVlans.contains(i))
-                                    break;
-                            }
-                            vlanId.setVlanId(i); // FIXME should be a determined
-                            // based on usage
-                            vlanId.setInherited(false);
-                            edittedSettings.setVlan(vlanId);
-
-                            dvPortConfigSpec.setSetting(edittedSettings);
-                            dvPortConfigSpec.setOperation("edit");
-                            dvPortConfigSpec.setKey(portKey);
-                            List<DVPortConfigSpec> dvPortConfigSpecs = new ArrayList<DVPortConfigSpec>();
-                            dvPortConfigSpecs.add(dvPortConfigSpec);
-                            ManagedObjectReference task = vmMo.getContext().getVimClient().getService().reconfigureDVPortTask(dvSwitch, dvPortConfigSpecs);
-                            if (!vmMo.getContext().getVimClient().waitForTask(task)) {
-                                throw new Exception(
-                                        "Failed to configure the dvSwitch port for nic "
-                                                + nicTo.toString());
-                            }
-                            s_logger.debug("NIC " + nicTo.toString()
-                                    + " connected to vlan " + i);
-                        } else {
-                            s_logger.trace("Port already configured and set to vlan " + vlanId.getVlanId());
-                        }
-                    } else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) {
-                        // This NIC is connected to a Virtual Switch
-                        // Nothing to do
-                    }
-                    else {
-                        s_logger.error("nic device backing is of type " + backing.getClass().getName());
-                        throw new Exception("Incompatible backing for a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad
-                    }
-                }
-                nicIndex++;
-            }
-
+            postNvpConfigBeforeStart(vmMo, vmSpec);
+            postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, ideControllerKey, scsiControllerKey);
+            
+            //
+            // Power-on VM
+            //
             if (!vmMo.powerOn()) {
                 throw new Exception("Failed to start VM. vmName: " + vmInternalCSName + " with hostname " + vmNameOnVcenter);
             }
@@ -3079,7 +2947,340 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
             }
         }
     }
+    
+    // return the finalized disk chain for startup, from top to bottom
+    private String[] syncDiskChain(DatacenterMO dcMo, VirtualMachineMO vmMo, VirtualMachineTO vmSpec, 
+    	DiskTO vol, VirtualMachineDiskInfoBuilder diskInfoBuilder,
+    	HashMap<String ,Pair<ManagedObjectReference, DatastoreMO>> dataStoresDetails,
+    	boolean ideController, int deviceBusNumber, int deviceUnitNumber) throws Exception {
+    	
+        VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData();
+        PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore();
+        
+        String deviceBusName;
+        if(ideController)
+        	deviceBusName = String.format("ide%d:%d", deviceBusNumber, deviceUnitNumber);
+        else
+        	deviceBusName = String.format("scsi%d:%d", deviceBusNumber, deviceUnitNumber);
+        
+        Pair<ManagedObjectReference, DatastoreMO> volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid());
+        if(volumeDsDetails == null)
+        	throw new Exception("Primary datastore " + primaryStore.getUuid() + " is not mounted on host.");
+        DatastoreMO dsMo = volumeDsDetails.second();
+
+        String datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(
+        	dcMo, vmMo.getName(), dsMo, volumeTO.getPath());
+
+        if(!dsMo.fileExists(datastoreDiskPath)) {
+    		if(s_logger.isInfoEnabled())
+    			s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath);
+    		
+            if(diskInfoBuilder != null && diskInfoBuilder.getDiskCount() > 0) {
+            	// we will always on-disk info from vCenter in this case
+            	VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName);
+            	if(diskInfo != null) {
+            		if(s_logger.isInfoEnabled())
+            			s_logger.info("Volume " + volumeTO.getId() + " does not seem to exist on datastore. use on-disk chain: " + 
+            				_gson.toJson(diskInfo));
+            		
+            		return diskInfo.getDiskChain();
+            	} else {
+            		s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore. on-disk may be out of sync as well. disk device info: " + deviceBusName);
+            	}
+            }
+            
+            // last resort, try chain info stored in DB
+            if(volumeTO.getChainInfo() != null) {
+            	VirtualMachineDiskInfo diskInfo = _gson.fromJson(volumeTO.getChainInfo(), VirtualMachineDiskInfo.class);
+            	if(diskInfo != null) {
+            		s_logger.info("Use chain info from DB: " + volumeTO.getChainInfo());
+            		return diskInfo.getDiskChain();
+            	}
+            	
+            	throw new Exception("Volume " + volumeTO.getId() + " does not seem to exist on datastore. Broken disk chain");
+            }
+    	}
+        
+    	return new String[] { datastoreDiskPath }; 
+    }
 
+    // Pair<internal CS name, vCenter display name>
+    private Pair<String, String> composeVmNames(VirtualMachineTO vmSpec) {
+        String vmInternalCSName = null;
+        String vmNameOnVcenter = null;
+        if (vmSpec.getHostName() != null) {
+            vmInternalCSName = vmSpec.getName();
+            if (_instanceNameFlag == true)
+                vmNameOnVcenter = vmSpec.getHostName();
+            else
+                vmNameOnVcenter = vmSpec.getName();
+        } else {
+            vmNameOnVcenter = vmInternalCSName = vmSpec.getName();
+        }
+    
+        return new Pair<String, String>(vmInternalCSName, vmNameOnVcenter);
+    }
+    
+    private static void configNestedHVSupport(VirtualMachineMO vmMo, 
+    	VirtualMachineTO vmSpec, VirtualMachineConfigSpec vmConfigSpec) throws Exception {
+    	
+    	VmwareContext context = vmMo.getContext();
+        if ("true".equals(vmSpec.getDetails().get(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG))) {
+        	if(s_logger.isDebugEnabled())
+        		s_logger.debug("Nested Virtualization enabled in configuration, checking hypervisor capability");
+            
+        	ManagedObjectReference hostMor = vmMo.getRunningHost().getMor();
+            ManagedObjectReference computeMor = context.getVimClient().getMoRefProp(hostMor, "parent");
+            ManagedObjectReference environmentBrowser = context.getVimClient().getMoRefProp(computeMor, "environmentBrowser");
+            HostCapability hostCapability = context.getService().queryTargetCapabilities(environmentBrowser, hostMor);
+            Boolean nestedHvSupported = hostCapability.isNestedHVSupported();
+            if (nestedHvSupported == null) {
+                // nestedHvEnabled property is supported only since VMware 5.1. It's not defined for earlier versions.
+                s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName());
+            } else if (nestedHvSupported.booleanValue()) {
+                s_logger.debug("Hypervisor supports nested virtualization, enabling for VM " + vmSpec.getName());
+                vmConfigSpec.setNestedHVEnabled(true);
+            }
+            else {
+                s_logger.warn("Hypervisor doesn't support nested virtualization, unable to set config for VM " +vmSpec.getName());
+                vmConfigSpec.setNestedHVEnabled(false);
+            }
+        }
+    }
+    
+    private static void configBasicExtraOption(List<OptionValue> extraOptions, VirtualMachineTO vmSpec) {
+        OptionValue newVal = new OptionValue();
+        newVal.setKey("machine.id");
+        newVal.setValue(vmSpec.getBootArgs());
+        extraOptions.add(newVal);
+
+        newVal = new OptionValue();
+        newVal.setKey("devices.hotplug");
+        newVal.setValue("true");
+        extraOptions.add(newVal);
+    }
+    
+    private static void configNvpExtraOption(List<OptionValue> extraOptions, VirtualMachineTO vmSpec) {
+        /**
+         * Extra Config : nvp.vm-uuid = uuid
+         *  - Required for Nicira NVP integration
+         */
+    	OptionValue newVal = new OptionValue();
+        newVal.setKey("nvp.vm-uuid");
+        newVal.setValue(vmSpec.getUuid());
+        extraOptions.add(newVal);
+
+        /**
+         * Extra Config : nvp.iface-id.<num> = uuid
+         *  - Required for Nicira NVP integration
+         */
+        int nicNum = 0;
+        for (NicTO nicTo : sortNicsByDeviceId(vmSpec.getNics())) {
+            if (nicTo.getUuid() != null) {
+                newVal = new OptionValue();
+                newVal.setKey("nvp.iface-id." + nicNum);
+                newVal.setValue(nicTo.getUuid());
+                extraOptions.add(newVal);
+            }
+            nicNum++;
+        }
+    }
+
+    private static void configCustomExtraOption(List<OptionValue> extraOptions, VirtualMachineTO vmSpec) {
+    	// we no longer to validation anymore
+        for(Map.Entry<String, String> entry : vmSpec.getDetails().entrySet()) {
+        	OptionValue newVal = new OptionValue();
+            newVal.setKey(entry.getKey());
+            newVal.setValue(entry.getValue());
+            extraOptions.add(newVal);
+        }
+    }
+    
+    private static void postNvpConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec) throws Exception {
+        /**
+         * We need to configure the port on the DV switch after the host is
+         * connected. So make this happen between the configure and start of
+         * the VM
+         */
+        int nicIndex = 0;
+        for (NicTO nicTo : sortNicsByDeviceId(vmSpec.getNics())) {
+            if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) {
+                // We need to create a port with a unique vlan and pass the key to the nic device
+                s_logger.trace("Nic " + nicTo.toString() + " is connected to an NVP logicalswitch");
+                VirtualDevice nicVirtualDevice = vmMo.getNicDeviceByIndex(nicIndex);
+                if (nicVirtualDevice == null) {
+                    throw new Exception("Failed to find a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad
+                }
+                VirtualDeviceBackingInfo backing = nicVirtualDevice.getBacking();
+                if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) {
+                    // This NIC is connected to a Distributed Virtual Switch
+                    VirtualEthernetCardDistributedVirtualPortBackingInfo portInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing;
+                    DistributedVirtualSwitchPortConnection port = portInfo.getPort();
+                    String portKey = port.getPortKey();
+                    String portGroupKey = port.getPortgroupKey();
+                    String dvSwitchUuid = port.getSwitchUuid();
+
+                    s_logger.debug("NIC " + nicTo.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey);
+
+                    ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager();
+                    ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid);
+
+                    // Get all ports
+                    DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria();
+                    criteria.setInside(true);
+                    criteria.getPortgroupKey().add(portGroupKey);
+                    List<DistributedVirtualPort> dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria);
+
+                    DistributedVirtualPort vmDvPort = null;
+                    List<Integer> usedVlans = new ArrayList<Integer>();
+                    for (DistributedVirtualPort dvPort : dvPorts) {
+                        // Find the port for this NIC by portkey
+                        if (portKey.equals(dvPort.getKey())) {
+                            vmDvPort = dvPort;
+                        }
+                        VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPort
+                                .getConfig().getSetting();
+                        VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings
+                                .getVlan();
+                        s_logger.trace("Found port " + dvPort.getKey()
+                                + " with vlan " + vlanId.getVlanId());
+                        if (vlanId.getVlanId() > 0
+                                && vlanId.getVlanId() < 4095) {
+                            usedVlans.add(vlanId.getVlanId());
+                        }
+                    }
+
+                    if (vmDvPort == null) {
+                        throw new Exception("Empty port list from dvSwitch for nic " + nicTo.toString());
+                    }
+
+                    DVPortConfigInfo dvPortConfigInfo = vmDvPort
+                            .getConfig();
+                    VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPortConfigInfo.getSetting();
+
+                    VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan();
+                    BoolPolicy blocked = settings.getBlocked();
+                    if (blocked.isValue() == Boolean.TRUE) {
+                        s_logger.trace("Port is blocked, set a vlanid and unblock");
+                        DVPortConfigSpec dvPortConfigSpec = new DVPortConfigSpec();
+                        VMwareDVSPortSetting edittedSettings = new VMwareDVSPortSetting();
+                        // Unblock
+                        blocked.setValue(Boolean.FALSE);
+                        blocked.setInherited(Boolean.FALSE);
+                        edittedSettings.setBlocked(blocked);
+                        // Set vlan
+                        int i;
+                        for (i = 1; i < 4095; i++) {
+                            if (!usedVlans.contains(i))
+                                break;
+                        }
+                        vlanId.setVlanId(i); // FIXME should be a determined
+                        // based on usage
+                        vlanId.setInherited(false);
+                        edittedSettings.setVlan(vlanId);
+
+                        dvPortConfigSpec.setSetting(edittedSettings);
+                        dvPortConfigSpec.setOperation("edit");
+                        dvPortConfigSpec.setKey(portKey);
+                        List<DVPortConfigSpec> dvPortConfigSpecs = new ArrayList<DVPortConfigSpec>();
+                        dvPortConfigSpecs.add(dvPortConfigSpec);
+                        ManagedObjectReference task = vmMo.getContext().getVimClient().getService().reconfigureDVPortTask(dvSwitch, dvPortConfigSpecs);
+                        if (!vmMo.getContext().getVimClient().waitForTask(task)) {
+                            throw new Exception(
+                                    "Failed to configure the dvSwitch port for nic "
+                                            + nicTo.toString());
+                        }
+                        s_logger.debug("NIC " + nicTo.toString()
+                                + " connected to vlan " + i);
+                    } else {
+                        s_logger.trace("Port already configured and set to vlan " + vlanId.getVlanId());
+                    }
+                } else if (backing instanceof VirtualEthernetCardNetworkBackingInfo) {
+                    // This NIC is connected to a Virtual Switch
+                    // Nothing to do
+                }
+                else {
+                    s_logger.error("nic device backing is of type " + backing.getClass().getName());
+                    throw new Exception("Incompatible backing for a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad
+                }
+            }
+            nicIndex++;
+        }
+    }
+    
+    private static int getDiskController(DiskTO vol, VirtualMachineTO vmSpec, int ideControllerKey, int scsiControllerKey) {
+    	int controllerKey;
+    	
+        if(vol.getType() == Volume.Type.ROOT) {
+            if(vmSpec.getDetails() != null && vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER) != null)
+            {
+                if(vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER).equalsIgnoreCase("scsi"))
+                    controllerKey = scsiControllerKey;
+                else
+                    controllerKey = ideControllerKey;
+            } else {
+                controllerKey = scsiControllerKey;
+            }
+        } else {
+            // DATA volume always use SCSI device
+            controllerKey = scsiControllerKey;
+        }
+    	
+        return controllerKey;
+    }
+    
+    private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks,
+    	int ideControllerKey, int scsiControllerKey) throws Exception {
+
+    	VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
+    	int controllerKey;
+        int ideUnitNumber = 1;				// we always count in IDE device first
+        int scsiUnitNumber = 0;
+    	
+    	for(DiskTO vol: sortedDisks) {
+    		VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData();
+    		
+            if (vol.getType() == Volume.Type.ISO)
+                continue;
+
+            controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey);
+            
+            String deviceBusName;
+            if(controllerKey == ideControllerKey)
+            	deviceBusName = String.format("ide%d:%d", 0, ideUnitNumber++);
+            else
+            	deviceBusName = String.format("scsi%d:%d", 0, scsiUnitNumber++);
+            
+    		VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(deviceBusName);
+    		assert(diskInfo != null);
+    		
+    		String[] diskChain = diskInfo.getDiskChain();
+    		assert(diskChain.length > 0);
+    		
+    		DatastoreFile file = new DatastoreFile(diskChain[0]);
+    		if(!file.getFileBaseName().equalsIgnoreCase(volumeTO.getPath())) {
+    			if(s_logger.isInfoEnabled())
+    				s_logger.info("Detected disk-chain top file change on volume: " + volumeTO.getId() + " "
+    					+ volumeTO.getPath() + " -> " + file.getFileBaseName());
+    		}
+    		
+    		VolumeObjectTO volInSpec = getVolumeInSpec(vmSpec, volumeTO);
+    		volInSpec.setPath(file.getFileBaseName());
+    		volInSpec.setChainInfo(_gson.toJson(diskInfo));
+    	}
+    }
+    
+    private static VolumeObjectTO getVolumeInSpec(VirtualMachineTO vmSpec, VolumeObjectTO srcVol) {
+    	for(DiskTO disk : vmSpec.getDisks()) {
+    		VolumeObjectTO vol = (VolumeObjectTO)disk.getData();
+    		if(vol.getId() == srcVol.getId())
+    			return vol;
+    	}
+    	
+    	return null;
+    }
+    
+    @Deprecated
     private Map<String, String> validateVmDetails(Map<String, String> vmDetails) {
 
         Map<String, String> validatedDetails = new HashMap<String, String>();
@@ -3105,7 +3306,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         return validatedDetails;
     }
 
-    private NicTO[] sortNicsByDeviceId(NicTO[] nics) {
+    private static NicTO[] sortNicsByDeviceId(NicTO[] nics) {
 
         List<NicTO> listForSort = new ArrayList<NicTO>();
         for (NicTO nic : nics) {
@@ -3128,7 +3329,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         return listForSort.toArray(new NicTO[0]);
     }
 
-    private DiskTO[] sortVolumesByDeviceId(DiskTO[] volumes) {
+    private static DiskTO[] sortVolumesByDeviceId(DiskTO[] volumes) {
 
         List<DiskTO> listForSort = new ArrayList<DiskTO>();
         for (DiskTO vol : volumes) {
@@ -6358,6 +6559,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
 	            cfmMo.ensureCustomFieldDef("Network", CustomFieldConstants.CLOUD_GC);
 	            cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_UUID);
 	            cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_NIC_MASK);
+	            cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_VM_INTERNAL_NAME);
 	
 	            VmwareHypervisorHost hostMo = this.getHyperHost(context);
 	            _hostName = hostMo.getHyperHostName();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java
new file mode 100644
index 0000000..5a5060d
--- /dev/null
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfo.java
@@ -0,0 +1,42 @@
+// 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 class VirtualMachineDiskInfo {
+	String diskDeviceBusName;
+	String[] diskChain;
+	
+	public VirtualMachineDiskInfo() {
+	}
+	
+	public String getDiskDeviceBusName() {
+		return diskDeviceBusName;
+	}
+
+	public void setDiskDeviceBusName(String diskDeviceBusName) {
+		this.diskDeviceBusName = diskDeviceBusName;
+	}
+
+	public String[] getDiskChain() {
+		return diskChain;
+	}
+
+	public void setDiskChain(String[] diskChain) {
+		this.diskChain = diskChain;
+	}
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java
new file mode 100644
index 0000000..41c6db6
--- /dev/null
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineDiskInfoBuilder.java
@@ -0,0 +1,97 @@
+// 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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class VirtualMachineDiskInfoBuilder {
+	Map<String, List<String>> disks;
+	
+	public VirtualMachineDiskInfoBuilder() {
+		disks = new HashMap<String, List<String>>(); 
+	}
+	
+	public void addDisk(String diskDeviceBusName, String diskBackingFilePath) {
+		List<String> chain = getDiskChainContainer(diskDeviceBusName);
+		chain.add(diskBackingFilePath);
+	}
+	
+	public int getDiskCount() {
+		return disks.keySet().size();
+	}
+	
+	public List<VirtualMachineDiskInfo> getAllDiskInfo() {
+		List<VirtualMachineDiskInfo> infoList = new ArrayList<VirtualMachineDiskInfo>();
+		for(Map.Entry<String, List<String>> entry : disks.entrySet()) {
+			VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo();
+			diskInfo.setDiskDeviceBusName(entry.getKey());
+			diskInfo.setDiskChain(entry.getValue().toArray(new String[1]));
+			infoList.add(diskInfo);
+		}
+		return infoList;
+	}
+	
+	public VirtualMachineDiskInfo getDiskInfoByDeviceBusName(String diskDeviceBusName) {
+		List<String> chain = disks.get(diskDeviceBusName);
+		if(chain != null && chain.size() > 0) {
+			VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo();
+			diskInfo.setDiskDeviceBusName(diskDeviceBusName);
+			diskInfo.setDiskChain(chain.toArray(new String[1]));
+			return diskInfo;
+		}
+		
+		return null;
+	}
+	
+	public VirtualMachineDiskInfo getDiskInfoByBackingFileBaseName(String diskBackingFileBaseName) {
+		for(Map.Entry<String, List<String>> entry : disks.entrySet()) {
+			if(chainContains(entry.getValue(), diskBackingFileBaseName)) {
+				VirtualMachineDiskInfo diskInfo = new VirtualMachineDiskInfo();
+				diskInfo.setDiskDeviceBusName(entry.getKey());
+				diskInfo.setDiskChain(entry.getValue().toArray(new String[1]));
+				return diskInfo;
+			}
+		}
+		
+		return null;
+	}
+	
+	private List<String> getDiskChainContainer(String diskDeviceBusName) {
+		assert(diskDeviceBusName != null);
+		List<String> chain = disks.get(diskDeviceBusName);
+		if(chain == null) {
+			chain = new ArrayList<String>();
+			disks.put(diskDeviceBusName, chain);
+		}
+		return chain;
+	}
+	
+	private static boolean chainContains(List<String> chain, String diskBackingFileBaseName) {
+		for(String backing : chain) {
+			DatastoreFile file = new DatastoreFile(backing);
+			
+			if(file.getFileBaseName().contains(diskBackingFileBaseName))
+				return true;
+		}
+		
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bae26665/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 149c4d0..e2a20e6 100644
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
@@ -1847,6 +1847,31 @@ public class VirtualMachineMO extends BaseMO {
 
 		return null;
 	}
+	
+	public VirtualMachineDiskInfoBuilder getDiskInfoBuilder() throws Exception {
+		VirtualMachineDiskInfoBuilder builder = new VirtualMachineDiskInfoBuilder();
+		
+		List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
+		
+		if(devices != null && devices.size() > 0) {
+			for(VirtualDevice device : devices) {
+				if(device instanceof VirtualDisk) {
+					VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking();
+					if(backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
+						VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
+						
+						while(diskBackingInfo != null) {
+							String deviceBusName = getDeviceBusName(devices, device);
+							builder.addDisk(deviceBusName, diskBackingInfo.getFileName());
+							diskBackingInfo = diskBackingInfo.getParent();
+						}
+					}
+				}
+			}
+		}
+		
+		return builder;
+	}
 
 	@Deprecated
 	public List<Pair<String, ManagedObjectReference>> getDiskDatastorePathChain(VirtualDisk disk, boolean followChain) throws Exception {
@@ -1938,7 +1963,7 @@ public class VirtualMachineMO extends BaseMO {
 		}
 		throw new Exception("Unable to find device controller");
 	}
-
+	
 	public VirtualDisk[] getAllDiskDevice() throws Exception {
 		List<VirtualDisk> deviceList = new ArrayList<VirtualDisk>();
 		List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");


Mime
View raw message