diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java index a5681a003dd..fb08384187e 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; 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 4e4a1917db6..5f21eb50046 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 avoid); + + void updateVolumeDiskChain(long volumeId, String path, String chainInfo); } diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index a5167fdb082..9976275888c 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 { 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 0e98f48729c..f3183e6ed90 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); + } + } } 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 03adbb00b64..ff04cad9577 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 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) + continue; + + controllerKey = getDiskController(vol, vmSpec, ideControllerKey, scsiControllerKey); - if (vol.getType() != Volume.Type.ISO) { - VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); - Pair 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); + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + Pair 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 extraOptions = new ArrayList(); - 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. = 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 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 dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria); - - DistributedVirtualPort vmDvPort = null; - List usedVlans = new ArrayList(); - 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 dvPortConfigSpecs = new ArrayList(); - 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> 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 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 + private Pair 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(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 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 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. = 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 extraOptions, VirtualMachineTO vmSpec) { + // we no longer to validation anymore + for(Map.Entry 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 dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria); + + DistributedVirtualPort vmDvPort = null; + List usedVlans = new ArrayList(); + 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 dvPortConfigSpecs = new ArrayList(); + 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 validateVmDetails(Map vmDetails) { Map validatedDetails = new HashMap(); @@ -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 listForSort = new ArrayList(); 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 listForSort = new ArrayList(); 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(); 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 00000000000..5a5060da315 --- /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; + } +} 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 00000000000..41c6db686b7 --- /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> disks; + + public VirtualMachineDiskInfoBuilder() { + disks = new HashMap>(); + } + + public void addDisk(String diskDeviceBusName, String diskBackingFilePath) { + List chain = getDiskChainContainer(diskDeviceBusName); + chain.add(diskBackingFilePath); + } + + public int getDiskCount() { + return disks.keySet().size(); + } + + public List getAllDiskInfo() { + List infoList = new ArrayList(); + for(Map.Entry> 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 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> 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 getDiskChainContainer(String diskDeviceBusName) { + assert(diskDeviceBusName != null); + List chain = disks.get(diskDeviceBusName); + if(chain == null) { + chain = new ArrayList(); + disks.put(diskDeviceBusName, chain); + } + return chain; + } + + private static boolean chainContains(List chain, String diskBackingFileBaseName) { + for(String backing : chain) { + DatastoreFile file = new DatastoreFile(backing); + + if(file.getFileBaseName().contains(diskBackingFileBaseName)) + return true; + } + + return false; + } +} 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 149c4d0c26e..e2a20e6fde9 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 devices = (List)_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> 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 deviceList = new ArrayList(); List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");