From 449b5daa70f033128f014942e9dcbacd15b09181 Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Thu, 31 Oct 2013 08:27:30 -0600 Subject: [PATCH] VMware changes related to managed storage --- .../vmware/manager/VmwareHostService.java | 3 + .../vmware/resource/VmwareResource.java | 69 ++++++++++++++++--- ...VmwareSecondaryStorageResourceHandler.java | 4 ++ .../resource/VmwareStorageProcessor.java | 51 +++++++++++++- .../SolidFirePrimaryDataStoreLifeCycle.java | 2 + .../cloud/storage/VolumeApiServiceImpl.java | 15 +++- .../vmware/mo/VirtualMachineMO.java | 16 ++++- 7 files changed, 147 insertions(+), 13 deletions(-) diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareHostService.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareHostService.java index d0147d194a6..d5a228411ce 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareHostService.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareHostService.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.hypervisor.vmware.manager; +import java.util.List; + import com.cloud.agent.api.Command; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; @@ -33,4 +35,5 @@ public interface VmwareHostService { String iqn, String initiatorChapName, String initiatorChapSecret, String mutualChapName, String mutualChapSecret) throws Exception; void createVmdk(Command cmd, DatastoreMO dsMo, String volumeDatastorePath, Long volumeSize) throws Exception; void handleDatastoreAndVmdkDetach(String iqn, String storageHost, int storagePort) throws Exception; + void removeManagedTargetsFromCluster(List managedIqns) throws Exception; } 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 6d3d34a27e0..a205fb6ae12 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 @@ -2985,7 +2985,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); - + Map details = vol.getDetails(); + boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED)); + Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); if(volumeDsDetails == null) throw new Exception("Primary datastore " + primaryStore.getUuid() + " is not mounted on host."); @@ -2997,17 +2999,25 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String disks[] = diskInfo.getDiskChain(); for(int i = 0; i < disks.length; i++) { DatastoreFile file = new DatastoreFile(disks[i]); - if(file.getDir() != null && file.getDir().isEmpty()) { + if (!isManaged && file.getDir() != null && file.getDir().isEmpty()) { s_logger.info("Perform run-time datastore folder upgrade. sync " + disks[i] + " to VM folder"); disks[i] = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( dcMo, vmMo.getName(), dsMo, file.getFileBaseName()); } } return disks; - } - - String datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( - dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); + } + + final String datastoreDiskPath; + + if (isManaged) { + datastoreDiskPath = dsMo.getDatastorePath(dsMo.getName() + ".vmdk"); + } + else { + datastoreDiskPath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder( + dcMo, vmMo.getName(), dsMo, volumeTO.getPath()); + } + if(!dsMo.fileExists(datastoreDiskPath)) { s_logger.warn("Volume " + volumeTO.getId() + " does not seem to exist on datastore, out of sync? path: " + datastoreDiskPath); } @@ -3223,7 +3233,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if(diskInfoBuilder != null) { VolumeObjectTO volume = (VolumeObjectTO)vol.getData(); - VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(volume.getPath()); + Map details = vol.getDetails(); + boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED)); + + VirtualMachineDiskInfo diskInfo = diskInfoBuilder.getDiskInfoByBackingFileBaseName(isManaged ? new DatastoreFile(volume.getPath()).getFileBaseName() : volume.getPath()); if(diskInfo != null) { s_logger.info("Found existing disk info from volume path: " + volume.getPath()); return diskInfo; @@ -3421,7 +3434,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); String poolUuid = primaryStore.getUuid(); if(poolMors.get(poolUuid) == null) { - ManagedObjectReference morDataStore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolUuid); + boolean isManaged = false; + String iScsiName = null; + Map details = vol.getDetails(); + + if (details != null) { + isManaged = Boolean.parseBoolean(details.get(DiskTO.MANAGED)); + iScsiName = details.get(DiskTO.IQN); + } + + ManagedObjectReference morDataStore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, isManaged ? VmwareResource.getDatastoreName(iScsiName) : poolUuid); if (morDataStore == null) { String msg = "Failed to get the mounted datastore for the volume's pool " + poolUuid; s_logger.error(msg); @@ -4534,7 +4556,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName()); if (!dsMo.fileExists(volumeDatastorePath)) { - createVmdk(cmd, dsMo, VmwareResource.getDatastoreName(cmd.get_iScsiName()), cmd.getVolumeSize()); + createVmdk(cmd, dsMo, volumeDatastorePath, cmd.getVolumeSize()); } } else { @@ -4592,6 +4614,35 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + @Override + public void removeManagedTargetsFromCluster(List iqns) throws Exception { + List lstManagedTargets = new ArrayList(); + + VmwareContext context = getServiceContext(); + VmwareHypervisorHost hyperHost = getHyperHost(context); + ManagedObjectReference morCluster = hyperHost.getHyperHostCluster(); + ClusterMO cluster = new ClusterMO(context, morCluster); + List> lstHosts = cluster.getClusterHosts(); + HostMO host = new HostMO(context, lstHosts.get(0).first()); + HostStorageSystemMO hostStorageSystem = host.getHostStorageSystemMO(); + + for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) { + if (hba instanceof HostInternetScsiHba) { + List lstTargets = ((HostInternetScsiHba)hba).getConfiguredStaticTarget(); + + if (lstTargets != null) { + for (HostInternetScsiHbaStaticTarget target : lstTargets) { + if (iqns.contains(target.getIScsiName())) { + lstManagedTargets.add(target); + } + } + } + } + } + + addRemoveInternetScsiTargetsToAllHosts(false, lstManagedTargets, lstHosts); + } + private void addRemoveInternetScsiTargetsToAllHosts(final boolean add, final List lstTargets, final List> lstHosts) throws Exception { VmwareContext context = getServiceContext(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index c84813f0b30..81f7bd9a7fa 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -360,4 +360,8 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe public void handleDatastoreAndVmdkDetach(String iqn, String storageHost, int storagePort) throws Exception { throw new OperationNotSupportedException(); } + + public void removeManagedTargetsFromCluster(List managedIqns) throws Exception { + throw new OperationNotSupportedException(); + } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 71ba4e9ff87..70a8769bcca 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -23,6 +23,7 @@ import java.io.OutputStreamWriter; import java.net.URI; import java.rmi.RemoteException; import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -79,6 +80,7 @@ import com.cloud.vm.VirtualMachine.State; import com.google.gson.Gson; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo; public class VmwareStorageProcessor implements StorageProcessor { private static final Logger s_logger = Logger.getLogger(VmwareStorageProcessor.class); @@ -1194,7 +1196,7 @@ public class VmwareStorageProcessor implements StorageProcessor { Map details = disk.getDetails(); morDs = hostService.getVmfsDatastore(hyperHost, VmwareResource.getDatastoreName(iScsiName), storageHost, storagePort, - VmwareResource.trimIqn(iScsiName), details.get(DiskTO.CHAP_INITIATOR_USERNAME), details.get(DiskTO.CHAP_INITIATOR_SECRET), + VmwareResource.trimIqn(iScsiName), details.get(DiskTO.CHAP_INITIATOR_USERNAME), details.get(DiskTO.CHAP_INITIATOR_SECRET), details.get(DiskTO.CHAP_TARGET_USERNAME), details.get(DiskTO.CHAP_TARGET_SECRET)); DatastoreMO dsMo = new DatastoreMO(hostService.getServiceContext(null), morDs); @@ -1202,7 +1204,7 @@ public class VmwareStorageProcessor implements StorageProcessor { String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName()); if (!dsMo.fileExists(volumeDatastorePath)) { - hostService.createVmdk(cmd, dsMo, VmwareResource.getDatastoreName(iScsiName), volumeTO.getSize()); + hostService.createVmdk(cmd, dsMo, volumeDatastorePath, volumeTO.getSize()); } } else { @@ -1498,6 +1500,14 @@ public class VmwareStorageProcessor implements StorageProcessor { vmMo.safePowerOff(_shutdown_waitMs); } + // call this before calling detachAllDisksExcept + // when expunging a VM, we need to see if any of its disks are serviced by managed storage + // if there is one or more disk serviced by managed storage, remove the iSCSI connection(s) + // don't remove the iSCSI connection(s) until the supported disk(s) is/are removed from the VM + // (removeManagedTargetsFromCluster should be called after detachAllDisksExcept and vm.destroy) + List virtualDisks = vmMo.getVirtualDisks(); + List managedIqns = getManagedIqnsFromVirtualDisks(virtualDisks); + List detachedDisks = vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ? diskInfo.getDiskDeviceBusName() : null); VmwareStorageLayoutHelper.moveVolumeToRootFolder(new DatacenterMO(context, morDc), detachedDisks); @@ -1505,6 +1515,9 @@ public class VmwareStorageProcessor implements StorageProcessor { // vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); vmMo.destroy(); + // this.hostService.handleDatastoreAndVmdkDetach(iScsiName, storageHost, storagePort); + this.hostService.removeManagedTargetsFromCluster(managedIqns); + for (NetworkDetails netDetails : networks) { if (netDetails.getGCTag() != null && netDetails.getGCTag().equalsIgnoreCase("true")) { if (netDetails.getVMMorsOnNetwork() == null || netDetails.getVMMorsOnNetwork().length == 1) { @@ -1555,6 +1568,40 @@ public class VmwareStorageProcessor implements StorageProcessor { } } + private List getManagedIqnsFromVirtualDisks(List virtualDisks) { + List managedIqns = new ArrayList(); + + if (virtualDisks != null) { + for (VirtualDisk virtualDisk : virtualDisks) { + if (virtualDisk.getBacking() instanceof VirtualDiskFlatVer2BackingInfo) { + VirtualDiskFlatVer2BackingInfo backingInfo = (VirtualDiskFlatVer2BackingInfo)virtualDisk.getBacking(); + String path = backingInfo.getFileName(); + + path = new DatastoreFile(path).getFileBaseName(); + + String search = "-"; + int index = path.indexOf(search); + + if (index > -1) { + path = path.substring(index + search.length()); + + index = path.lastIndexOf(search); + + if (index > -1) { + path = path.substring(0, index); + + if (path.startsWith("iqn.")) { + managedIqns.add(path); + } + } + } + } + } + } + + return managedIqns; + } + private Long restoreVolumeFromSecStorage(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName, String secStorageUrl, String secStorageDir, String backupName) throws Exception { diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java index 120a357b270..be43e1c6af6 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java @@ -317,10 +317,12 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC dataStoreHelper.attachZone(dataStore); List xenServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.XenServer, scope.getScopeId()); + List vmWareServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.VMware, scope.getScopeId()); List kvmHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.KVM, scope.getScopeId()); List hosts = new ArrayList(); hosts.addAll(xenServerHosts); + hosts.addAll(vmWareServerHosts); hosts.addAll(kvmHosts); for (HostVO host : hosts) { diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index d445381f2dd..28df21935f8 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -1233,6 +1233,19 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName(); boolean sendCommand = (vm.getState() == State.Running); + + Long hostId = vm.getHostId(); + + if (hostId == null) { + hostId = vm.getLastHostId(); + + HostVO host = _hostDao.findById(hostId); + + if (host != null && host.getHypervisorType() == HypervisorType.VMware) { + sendCommand = true; + } + } + Answer answer = null; if (sendCommand) { @@ -1251,7 +1264,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic cmd.set_iScsiName(volume.get_iScsiName()); try { - answer = _agentMgr.send(vm.getHostId(), cmd); + answer = _agentMgr.send(hostId, cmd); } catch (Exception e) { throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); } 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 a9536cfb544..784c0316e04 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1985,7 +1985,21 @@ public class VirtualMachineMO extends BaseMO { } throw new Exception("Unable to find device controller"); } - + + public List getVirtualDisks() throws Exception { + List virtualDisks = new ArrayList(); + + List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + for (VirtualDevice device : devices) { + if (device instanceof VirtualDisk) { + virtualDisks.add((VirtualDisk)device); + } + } + + return virtualDisks; + } + public List detachAllDisksExcept(String vmdkBaseName, String deviceBusName) throws Exception { List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");