Merge remote-tracking branch 'apache/4.15'

This commit is contained in:
Abhishek Kumar 2021-04-26 14:33:58 +05:30
commit 42c83b08f5
25 changed files with 262 additions and 178 deletions

View File

@ -55,8 +55,8 @@ public class MigrateVolumeCommand extends Command {
this.setWait(timeout);
}
public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool sourcePool, StoragePool targetPool, String hostGuidInTargetCluster) {
this(volumeId,volumePath,targetPool, null, Volume.Type.UNKNOWN, -1);
public MigrateVolumeCommand(long volumeId, String volumePath, String attachedVmName, StoragePool sourcePool, StoragePool targetPool, String hostGuidInTargetCluster) {
this(volumeId,volumePath,targetPool, attachedVmName, Volume.Type.UNKNOWN, -1);
this.sourcePool = new StorageFilerTO(sourcePool);
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
}

View File

@ -43,6 +43,7 @@ import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager;
import com.cloud.utils.fsm.NoTransitionException;
@ -260,4 +261,6 @@ public interface VirtualMachineManager extends Manager {
boolean unmanage(String vmUuid);
UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException;
Pair<Long, Long> findClusterAndHostIdForVm(long vmId);
}

View File

@ -2395,43 +2395,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
}
private Pair<Long, Long> findClusterAndHostIdForVm(VMInstanceVO vm) {
Long hostId = vm.getHostId();
Long clusterId = null;
// OfflineVmwareMigration: probably this is null when vm is stopped
if(hostId == null) {
hostId = vm.getLastHostId();
if (s_logger.isDebugEnabled()) {
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
}
}
if (hostId == null) {
List<VolumeVO> volumes = _volsDao.findByInstanceAndType(vm.getId(), Type.ROOT);
if (CollectionUtils.isNotEmpty(volumes)) {
for (VolumeVO rootVolume : volumes) {
if (rootVolume.getPoolId() != null) {
StoragePoolVO pool = _storagePoolDao.findById(rootVolume.getPoolId());
if (pool != null && pool.getClusterId() != null) {
clusterId = pool.getClusterId();
List<HostVO> hosts = _hostDao.findHypervisorHostInCluster(pool.getClusterId());
if (CollectionUtils.isNotEmpty(hosts)) {
hostId = hosts.get(0).getId();
break;
}
}
}
}
}
}
if (clusterId == null && hostId != null) {
HostVO host = _hostDao.findById(hostId);
if (host != null) {
clusterId = host.getClusterId();
}
}
return new Pair<>(clusterId, hostId);
}
private void migrateThroughHypervisorOrStorage(VMInstanceVO vm, Map<Volume, StoragePool> volumeToPool) throws StorageUnavailableException, InsufficientCapacityException {
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
Pair<Long, Long> vmClusterAndHost = findClusterAndHostIdForVm(vm);
@ -6129,4 +6092,53 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
_jobMgr.marshallResultObject(result));
}
private Pair<Long, Long> findClusterAndHostIdForVmFromVolumes(long vmId) {
Long clusterId = null;
Long hostId = null;
List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
for (VolumeVO volume : volumes) {
if (Volume.State.Ready.equals(volume.getState()) &&
volume.getPoolId() != null) {
StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId());
if (pool != null && pool.getClusterId() != null) {
clusterId = pool.getClusterId();
// hostId to be used only for sending commands, capacity check skipped
List<HostVO> hosts = _hostDao.findHypervisorHostInCluster(pool.getClusterId());
if (CollectionUtils.isNotEmpty(hosts)) {
hostId = hosts.get(0).getId();
break;
}
}
}
}
return new Pair<>(clusterId, hostId);
}
private Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm) {
Long hostId = vm.getHostId();
Long clusterId = null;
if(hostId == null) {
hostId = vm.getLastHostId();
if (s_logger.isDebugEnabled()) {
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
}
}
if (hostId == null) {
return findClusterAndHostIdForVmFromVolumes(vm.getId());
}
HostVO host = _hostDao.findById(hostId);
if (host != null) {
clusterId = host.getClusterId();
}
return new Pair<>(clusterId, hostId);
}
@Override
public Pair<Long, Long> findClusterAndHostIdForVm(long vmId) {
VMInstanceVO vm = _vmDao.findById(vmId);
if (vm == null) {
return new Pair<>(null, null);
}
return findClusterAndHostIdForVm(vm);
}
}

View File

@ -33,6 +33,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -42,7 +43,6 @@ import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException;
@Component
@ -57,7 +57,9 @@ public class DataMotionServiceImpl implements DataMotionService {
@Override
public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
if (srcData.getDataStore() == null || destData.getDataStore() == null) {
throw new CloudRuntimeException("can't find data store");
String errMsg = "can't find data store";
invokeCallback(errMsg, callback);
return;
}
if (srcData.getDataStore().getDriver().canCopy(srcData, destData)) {
@ -73,8 +75,10 @@ public class DataMotionServiceImpl implements DataMotionService {
// OfflineVmware volume migration
// Cleanup volumes from target and reset the state of volume at source
cleanUpVolumesForFailedMigrations(srcData, destData);
throw new CloudRuntimeException("Can't find strategy to move data. " + "Source: " + srcData.getType().name() + " '" + srcData.getUuid() + ", Destination: " +
destData.getType().name() + " '" + destData.getUuid() + "'");
String errMsg = "Can't find strategy to move data. " + "Source: " + srcData.getType().name() + " '" + srcData.getUuid() + ", Destination: " +
destData.getType().name() + " '" + destData.getUuid() + "'";
invokeCallback(errMsg, callback);
return;
}
strategy.copyAsync(srcData, destData, destHost, callback);
@ -112,10 +116,22 @@ public class DataMotionServiceImpl implements DataMotionService {
volumeIds.add(volumeInfo.getUuid());
}
throw new CloudRuntimeException("Can't find strategy to move data. " + "Source Host: " + srcHost.getName() + ", Destination Host: " + destHost.getName() +
", Volume UUIDs: " + StringUtils.join(volumeIds, ","));
String errMsg = "Can't find strategy to move data. " + "Source Host: " + srcHost.getName() + ", Destination Host: " + destHost.getName() +
", Volume UUIDs: " + StringUtils.join(volumeIds, ",");
invokeCallback(errMsg, callback);
return;
}
strategy.copyAsync(volumeMap, vmTo, srcHost, destHost, callback);
}
private void invokeCallback(String errMsg, AsyncCompletionCallback<CopyCommandResult> callback) {
CopyCmdAnswer copyCmdAnswer = new CopyCmdAnswer(errMsg);
CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
result.setResult(errMsg);
callback.complete(result);
}
}

View File

@ -69,7 +69,11 @@ public class TemplateDataFactoryImpl implements TemplateDataFactory {
@Override
public TemplateInfo getTemplateOnPrimaryStorage(long templateId, DataStore store, String configuration) {
VMTemplateVO templ = imageDataDao.findById(templateId);
VMTemplateVO templ = imageDataDao.findByIdIncludingRemoved(templateId);
if (templ == null) {
s_logger.error("Could not find a template with id " + templateId);
return null;
}
if (store.getRole() == DataStoreRole.Primary) {
VMTemplateStoragePoolVO templatePoolVO = templatePoolDao.findByPoolTemplate(store.getId(), templateId, configuration);
if (templatePoolVO != null) {

View File

@ -207,48 +207,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
@Override public VirtualMachineTO implement(VirtualMachineProfile vm) {
vmwareVmImplementer.setGlobalNestedVirtualisationEnabled(VmwareEnableNestedVirtualization.value());
vmwareVmImplementer.setGlobalNestedVPerVMEnabled(VmwareEnableNestedVirtualizationPerVM.value());
return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm), getClusterId(vm.getId()));
}
private Long getClusterIdFromVmVolume(long vmId) {
Long clusterId = null;
List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
if (CollectionUtils.isNotEmpty(volumes)) {
for (VolumeVO rootVolume : volumes) {
if (rootVolume.getPoolId() != null) {
StoragePoolVO pool = _storagePoolDao.findById(rootVolume.getPoolId());
if (pool != null && pool.getClusterId() != null) {
clusterId = pool.getClusterId();
break;
}
}
}
}
return clusterId;
}
private Long getClusterId(long vmId) {
Long clusterId = null;
Long hostId = null;
VMInstanceVO vm = _vmDao.findById(vmId);
if (vm != null) {
hostId = _vmDao.findById(vmId).getHostId();
}
if (vm != null && hostId == null) {
// If VM is in stopped state then hostId would be undefined. Hence read last host's Id instead.
hostId = _vmDao.findById(vmId).getLastHostId();
}
HostVO host = null;
if (hostId != null) {
host = _hostDao.findById(hostId);
}
if (host != null) {
clusterId = host.getClusterId();
} else {
clusterId = getClusterIdFromVmVolume(vmId);
}
return clusterId;
return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm), vmManager.findClusterAndHostIdForVm(vm.getId()).first());
}
@Override @DB public Pair<Boolean, Long> getCommandHostDelegation(long hostId, Command cmd) {
@ -446,7 +405,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
@Override public Map<String, String> getClusterSettings(long vmId) {
Map<String, String> details = new HashMap<String, String>();
Long clusterId = getClusterId(vmId);
Long clusterId = vmManager.findClusterAndHostIdForVm(vmId).first();
if (clusterId != null) {
details.put(VmwareReserveCpu.key(), VmwareReserveCpu.valueIn(clusterId).toString());
details.put(VmwareReserveMemory.key(), VmwareReserveMemory.valueIn(clusterId).toString());
@ -1114,7 +1073,6 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
List<Command> commands = new ArrayList<Command>();
// OfflineVmwareMigration: specialised migration command
List<VolumeTO> vols = new ArrayList<>();
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerTo = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
Long poolClusterId = null;
for (Map.Entry<Volume, StoragePool> entry : volumeToPool.entrySet()) {
@ -1126,10 +1084,9 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
poolClusterId = pool.getClusterId();
}
volumeToFilerTo.add(new Pair<VolumeTO, StorageFilerTO>(volumeTo, filerTo));
vols.add(volumeTo);
}
final Long destClusterId = poolClusterId;
final Long srcClusterId = getClusterId(vm.getId());
final Long srcClusterId = vmManager.findClusterAndHostIdForVm(vm.getId()).first();
final boolean isInterClusterMigration = isInterClusterMigration(destClusterId, srcClusterId);
MigrateVmToPoolCommand migrateVmToPoolCommand = new MigrateVmToPoolCommand(vm.getInstanceName(),
volumeToFilerTo, getHostGuidInTargetCluster(isInterClusterMigration, destClusterId), true);

View File

@ -34,11 +34,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.cloud.hypervisor.vmware.mo.VirtualStorageObjectManagerMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
import com.vmware.vim25.BaseConfigInfoDiskFileBackingInfo;
import com.vmware.vim25.VStorageObject;
import com.vmware.vim25.VirtualDiskType;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
import org.apache.cloudstack.storage.command.AttachAnswer;
import org.apache.cloudstack.storage.command.AttachCommand;
@ -84,7 +79,9 @@ import com.cloud.hypervisor.vmware.mo.HostMO;
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.VirtualMachineDiskInfoBuilder;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VirtualStorageObjectManagerMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.resource.VmwareResource;
import com.cloud.hypervisor.vmware.util.VmwareContext;
@ -104,6 +101,7 @@ import com.cloud.vm.VirtualMachine.PowerState;
import com.cloud.vm.VmDetailConstants;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.vmware.vim25.BaseConfigInfoDiskFileBackingInfo;
import com.vmware.vim25.DatastoreHostMount;
import com.vmware.vim25.HostHostBusAdapter;
import com.vmware.vim25.HostInternetScsiHba;
@ -122,11 +120,13 @@ import com.vmware.vim25.HostUnresolvedVmfsResignatureSpec;
import com.vmware.vim25.HostUnresolvedVmfsVolume;
import com.vmware.vim25.InvalidStateFaultMsg;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.VStorageObject;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualDiskType;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VmConfigInfo;
import com.vmware.vim25.VmfsDatastoreExpandSpec;
@ -2683,15 +2683,14 @@ public class VmwareStorageProcessor implements StorageProcessor {
List<VirtualDisk> virtualDisks = vmMo.getVirtualDisks();
List<String> managedDatastoreNames = getManagedDatastoreNamesFromVirtualDisks(virtualDisks);
// Preserve other disks of the VM
List<String> detachedDisks = vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ? diskInfo.getDiskDeviceBusName() : null);
VmwareStorageLayoutHelper.moveVolumeToRootFolder(new DatacenterMO(context, morDc), detachedDisks);
// let vmMo.destroy to delete volume for us
// vmMo.tearDownDevices(new Class<?>[] { VirtualDisk.class, VirtualEthernetCard.class });
if (isManaged) {
List<String> detachedDisks = vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ? diskInfo.getDiskDeviceBusName() : null);
VmwareStorageLayoutHelper.moveVolumeToRootFolder(new DatacenterMO(context, morDc), detachedDisks);
vmMo.unregisterVm();
}
else {
} else {
vmMo.destroy();
}

View File

@ -31,11 +31,9 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
@ -67,6 +65,8 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
@Component
@ -77,13 +77,13 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
@Inject
VolumeDao volDao;
@Inject
VolumeDataFactory volFactory;
@Inject
PrimaryDataStoreDao storagePoolDao;
@Inject
VMInstanceDao instanceDao;
@Inject
private HostDao hostDao;
HostDao hostDao;
@Inject
VirtualMachineManager vmManager;
@Override
public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
@ -91,8 +91,7 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
if (isOnVmware(srcData, destData)
&& isOnPrimary(srcData, destData)
&& isVolumesOnly(srcData, destData)
&& isIntraPodOrZoneWideStoreInvolved(srcData, destData)
&& isDettached(srcData)) {
&& isDetachedOrAttachedToStoppedVM(srcData)) {
if (s_logger.isDebugEnabled()) {
String msg = String.format("%s can handle the request because %d(%s) and %d(%s) share the pod"
, this.getClass()
@ -107,20 +106,14 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
return StrategyPriority.CANT_HANDLE;
}
private boolean isIntraPodOrZoneWideStoreInvolved(DataObject srcData, DataObject destData) {
DataStore srcStore = srcData.getDataStore();
StoragePoolVO srcPool = storagePoolDao.findById(srcStore.getId());
DataStore destStore = destData.getDataStore();
StoragePoolVO destPool = storagePoolDao.findById(destStore.getId());
if (srcPool.getPodId() != null && destPool.getPodId() != null) {
return srcPool.getPodId().equals(destPool.getPodId());
}
return (ScopeType.ZONE.equals(srcPool.getScope()) || ScopeType.ZONE.equals(destPool.getScope()));
private boolean isAttachedToStoppedVM(Volume volume) {
VMInstanceVO vm = instanceDao.findById(volume.getInstanceId());
return vm != null && VirtualMachine.State.Stopped.equals(vm.getState());
}
private boolean isDettached(DataObject srcData) {
private boolean isDetachedOrAttachedToStoppedVM(DataObject srcData) {
VolumeVO volume = volDao.findById(srcData.getId());
return volume.getInstanceId() == null;
return volume.getInstanceId() == null || isAttachedToStoppedVM(volume);
}
private boolean isVolumesOnly(DataObject srcData, DataObject destData) {
@ -138,11 +131,46 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
&& HypervisorType.VMware.equals(destData.getTO().getHypervisorType());
}
private Pair<Long, String> getHostIdForVmAndHostGuidInTargetCluster(DataObject srcData, DataObject destData) {
StoragePool sourcePool = (StoragePool) srcData.getDataStore();
ScopeType sourceScopeType = srcData.getDataStore().getScope().getScopeType();
StoragePool targetPool = (StoragePool) destData.getDataStore();
ScopeType targetScopeType = destData.getDataStore().getScope().getScopeType();
private String getHostGuidInTargetCluster (Long sourceClusterId,
StoragePool targetPool,
ScopeType targetScopeType) {
String hostGuidInTargetCluster = null;
if (ScopeType.CLUSTER.equals(targetScopeType) && !sourceClusterId.equals(targetPool.getClusterId())) {
// Without host vMotion might fail between non-shared storages with error similar to,
// https://kb.vmware.com/s/article/1003795
List<HostVO> hosts = hostDao.findHypervisorHostInCluster(targetPool.getClusterId());
if (CollectionUtils.isNotEmpty(hosts)) {
hostGuidInTargetCluster = hosts.get(0).getGuid();
}
if (hostGuidInTargetCluster == null) {
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable target host for VM placement while migrating between storage pools of different cluster without shared storages");
}
}
return hostGuidInTargetCluster;
}
private VirtualMachine getVolumeVm(DataObject srcData) {
if (srcData instanceof VolumeInfo) {
return ((VolumeInfo)srcData).getAttachedVM();
}
VolumeVO volume = volDao.findById(srcData.getId());
return volume.getInstanceId() == null ? null : instanceDao.findById(volume.getInstanceId());
}
private Pair<Long, String> getHostIdForVmAndHostGuidInTargetClusterForAttachedVm(VirtualMachine vm,
StoragePool targetPool,
ScopeType targetScopeType) {
Pair<Long, Long> clusterAndHostId = vmManager.findClusterAndHostIdForVm(vm.getId());
if (clusterAndHostId.second() == null) {
throw new CloudRuntimeException(String.format("Offline Migration failed, unable to find host for VM: %s", vm.getUuid()));
}
return new Pair<>(clusterAndHostId.second(), getHostGuidInTargetCluster(clusterAndHostId.first(), targetPool, targetScopeType));
}
private Pair<Long, String> getHostIdForVmAndHostGuidInTargetClusterForWorkerVm(StoragePool sourcePool,
ScopeType sourceScopeType,
StoragePool targetPool,
ScopeType targetScopeType) {
Long hostId = null;
String hostGuidInTargetCluster = null;
if (ScopeType.CLUSTER.equals(sourceScopeType)) {
@ -151,17 +179,7 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
if (hostId == null) {
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable host for worker VM placement in the cluster of storage pool: " + sourcePool.getName());
}
if (ScopeType.CLUSTER.equals(targetScopeType) && !sourcePool.getClusterId().equals(targetPool.getClusterId())) {
// Without host vMotion might fail between non-shared storages with error similar to,
// https://kb.vmware.com/s/article/1003795
List<HostVO> hosts = hostDao.findHypervisorHostInCluster(targetPool.getClusterId());
if (CollectionUtils.isNotEmpty(hosts)) {
hostGuidInTargetCluster = hosts.get(0).getGuid();
}
if (hostGuidInTargetCluster == null) {
throw new CloudRuntimeException("Offline Migration failed, unable to find suitable target host for worker VM placement while migrating between storage pools of different cluster without shared storages");
}
}
hostGuidInTargetCluster = getHostGuidInTargetCluster(sourcePool.getClusterId(), targetPool, sourceScopeType);
} else if (ScopeType.CLUSTER.equals(targetScopeType)) {
hostId = findSuitableHostIdForWorkerVmPlacement(targetPool.getClusterId());
if (hostId == null) {
@ -171,6 +189,19 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
return new Pair<>(hostId, hostGuidInTargetCluster);
}
private Pair<Long, String> getHostIdForVmAndHostGuidInTargetCluster(VirtualMachine vm,
DataObject srcData,
StoragePool sourcePool,
DataObject destData,
StoragePool targetPool) {
ScopeType sourceScopeType = srcData.getDataStore().getScope().getScopeType();
ScopeType targetScopeType = destData.getDataStore().getScope().getScopeType();
if (vm != null) {
return getHostIdForVmAndHostGuidInTargetClusterForAttachedVm(vm, targetPool, targetScopeType);
}
return getHostIdForVmAndHostGuidInTargetClusterForWorkerVm(sourcePool, sourceScopeType, targetPool, targetScopeType);
}
@Override
public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
if (srcHost.getHypervisorType() == HypervisorType.VMware && destHost.getHypervisorType() == HypervisorType.VMware) {
@ -205,12 +236,15 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
// OfflineVmwareMigration: we shouldn't be here as we would have refused in the canHandle call
throw new UnsupportedOperationException();
}
Pair<Long, String> hostIdForVmAndHostGuidInTargetCluster = getHostIdForVmAndHostGuidInTargetCluster(srcData, destData);
Long hostId = hostIdForVmAndHostGuidInTargetCluster.first();
VirtualMachine vm = getVolumeVm(srcData);
StoragePool sourcePool = (StoragePool) srcData.getDataStore();
StoragePool targetPool = (StoragePool) destData.getDataStore();
Pair<Long, String> hostIdForVmAndHostGuidInTargetCluster =
getHostIdForVmAndHostGuidInTargetCluster(vm, srcData, sourcePool, destData, targetPool);
Long hostId = hostIdForVmAndHostGuidInTargetCluster.first();
MigrateVolumeCommand cmd = new MigrateVolumeCommand(srcData.getId()
, srcData.getTO().getPath()
, vm != null ? vm.getInstanceName() : null
, sourcePool
, targetPool
, hostIdForVmAndHostGuidInTargetCluster.second());
@ -253,9 +287,11 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
VolumeVO sourceVO = volDao.findById(srcData.getId());
sourceVO.setState(Volume.State.Ready);
volDao.update(sourceVO.getId(), sourceVO);
destinationVO.setState(Volume.State.Expunged);
destinationVO.setRemoved(new Date());
volDao.update(destinationVO.getId(), destinationVO);
if (destinationVO.getId() != sourceVO.getId()) {
destinationVO.setState(Volume.State.Expunged);
destinationVO.setRemoved(new Date());
volDao.update(destinationVO.getId(), destinationVO);
}
throw new CloudRuntimeException("unexpected answer from hypervisor agent: " + answer.getDetails());
}
MigrateVolumeAnswer ans = (MigrateVolumeAnswer) answer;
@ -264,6 +300,7 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
s_logger.debug(String.format(format, ans.getVolumePath(), destData.getId()));
}
// OfflineVmwareMigration: update the volume with new pool/volume path
destinationVO.setPoolId(destData.getDataStore().getId());
destinationVO.setPath(ans.getVolumePath());
volDao.update(destinationVO.getId(), destinationVO);
}

View File

@ -16,6 +16,13 @@
// under the License.
package org.apache.cloudstack.storage.motion;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ -23,17 +30,6 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.MigrateWithStorageAnswer;
import com.cloud.agent.api.MigrateWithStorageCommand;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
@ -63,12 +59,18 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.MigrateWithStorageAnswer;
import com.cloud.agent.api.MigrateWithStorageCommand;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@ -87,7 +89,9 @@ public class VmwareStorageMotionStrategyTest {
@Inject
VMInstanceDao instanceDao;
@Inject
private HostDao hostDao;
HostDao hostDao;
@Inject
VirtualMachineManager vmManager;
CopyCommandResult result;
@ -268,6 +272,11 @@ public class VmwareStorageMotionStrategyTest {
return Mockito.mock(HostDao.class);
}
@Bean
public VirtualMachineManager vmManager() {
return Mockito.mock(VirtualMachineManager.class);
}
public static class Library implements TypeFilter {
@Override
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {

View File

@ -663,6 +663,9 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
if (loadBalancer == null) {
throw new InvalidParameterException("Invalid Load balancer Id:" + cmd.getLbRuleId());
}
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
List<LBStickinessPolicyVO> stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false);
for (LBStickinessPolicyVO stickinessPolicy : stickinessPolicies) {
if (stickinessPolicy.getId() == cmd.getEntityId()) {
@ -717,6 +720,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements
if (loadBalancer == null) {
throw new InvalidParameterException("Invalid Load balancer Id:" + cmd.getLbRuleId());
}
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
FirewallRule.State backupState = loadBalancer.getState();
loadBalancer.setState(FirewallRule.State.Add);
_lbDao.persist(loadBalancer);

View File

@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import com.cloud.dc.Pod;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
@ -104,6 +103,7 @@ import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.Pod;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.Domain;
import com.cloud.event.ActionEvent;
@ -1835,13 +1835,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
@Override
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPDATE, eventDescription = "updating volume", async = true)
public Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long entityOwnerId, String chainInfo) {
Account caller = CallContext.current().getCallingAccount();
VolumeVO volume = _volsDao.findById(volumeId);
if (volume == null) {
throw new InvalidParameterValueException("The volume id doesn't exist");
}
/* Does the caller have authority to act on this volume? */
_accountMgr.checkAccess(caller, null, true, volume);
if (path != null) {
volume.setPath(path);
}
@ -2065,6 +2068,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume")
public Volume detachVolumeViaDestroyVM(long vmId, long volumeId) {
Account caller = CallContext.current().getCallingAccount();
Volume volume = _volsDao.findById(volumeId);
// Permissions check
_accountMgr.checkAccess(caller, null, true, volume);
return orchestrateDetachVolumeFromVM(vmId, volumeId);
}
@ -2254,6 +2261,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
@Override
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_MIGRATE, eventDescription = "migrating volume", async = true)
public Volume migrateVolume(MigrateVolumeCmd cmd) {
Account caller = CallContext.current().getCallingAccount();
Long volumeId = cmd.getVolumeId();
Long storagePoolId = cmd.getStoragePoolId();
@ -2262,6 +2270,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
throw new InvalidParameterValueException("Failed to find the volume id: " + volumeId);
}
_accountMgr.checkAccess(caller, null, true, vol);
if (vol.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException("Volume must be in ready state");
}
@ -2279,13 +2289,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
}
// Check that Vm to which this volume is attached does not have VM Snapshots
// OfflineVmwareMigration: considder if this is needed and desirable
// OfflineVmwareMigration: consider if this is needed and desirable
if (vm != null && _vmSnapshotDao.findByVm(vm.getId()).size() > 0) {
throw new InvalidParameterValueException("Volume cannot be migrated, please remove all VM snapshots for VM to which this volume is attached");
}
// OfflineVmwareMigration: extract this block as method and check if it is subject to regression
if (vm != null && vm.getState() == State.Running) {
if (vm != null && State.Running.equals(vm.getState())) {
// Check if the VM is GPU enabled.
if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported");
@ -2321,10 +2331,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (!liveMigrateVolume) {
throw new InvalidParameterValueException("Volume needs to be detached from VM");
}
if (!cmd.isLiveMigrate()) {
throw new InvalidParameterValueException("The volume " + vol + "is attached to a vm and for migrating it " + "the parameter livemigrate should be specified");
}
}
if (liveMigrateVolume && !cmd.isLiveMigrate()) {
throw new InvalidParameterValueException("The volume " + vol + "is attached to a vm and for migrating it " + "the parameter livemigrate should be specified");
if (vm != null &&
HypervisorType.VMware.equals(vm.getHypervisorType()) &&
State.Stopped.equals(vm.getState())) {
// For VMware, use liveMigrateVolume=true so that it follows VmwareStorageMotionStrategy
liveMigrateVolume = true;
}
StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
@ -2361,7 +2378,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
getStoragePoolTags(destPool), vol.getName(), vol.getUuid(), diskOffering.getTags()));
}
if (liveMigrateVolume && destPool.getClusterId() != null && srcClusterId != null) {
if (liveMigrateVolume && State.Running.equals(vm.getState()) &&
destPool.getClusterId() != null && srcClusterId != null) {
if (!srcClusterId.equals(destPool.getClusterId())) {
throw new InvalidParameterValueException("Cannot migrate a volume of a virtual machine to a storage pool in a different cluster");
}
@ -2629,11 +2647,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
private Snapshot takeSnapshotInternal(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup)
throws ResourceAllocationException {
Account caller = CallContext.current().getCallingAccount();
VolumeInfo volume = volFactory.getVolume(volumeId);
if (volume == null) {
throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
}
_accountMgr.checkAccess(caller, null, true, volume);
if (volume.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot.");
}
@ -2650,6 +2671,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
}
if (vm != null) {
_accountMgr.checkAccess(caller, null, true, vm);
// serialize VM operation
AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {

View File

@ -335,6 +335,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
Boolean display = cmd.getDisplay();
SnapshotPolicyVO policyVO = _snapshotPolicyDao.findById(id);
VolumeInfo volume = volFactory.getVolume(policyVO.getVolumeId());
if (volume == null) {
throw new InvalidParameterValueException("No such volume exist");
}
// does the caller have the authority to act on this volume
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
if (display != null) {
boolean previousDisplay = policyVO.isDisplay();
policyVO.setDisplay(display);

View File

@ -1648,6 +1648,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
final Long accountId = CallContext.current().getCallingAccountId();
SnapshotVO snapshot = null;
VolumeVO volume = null;
Account caller = CallContext.current().getCallingAccount();
try {
TemplateInfo tmplInfo = _tmplFactory.getTemplate(templateId, DataStoreRole.Image);
@ -1686,6 +1687,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
throw new CloudRuntimeException("Cannot find snapshot " + snapshotId + " on secondary and could not create backup");
}
}
_accountMgr.checkAccess(caller, null, true, snapInfo);
DataStore snapStore = snapInfo.getDataStore();
if (snapStore != null) {
@ -1696,7 +1698,11 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
future = _tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store);
} else if (volumeId != null) {
VolumeInfo volInfo = _volFactory.getVolume(volumeId);
if (volInfo == null) {
throw new InvalidParameterValueException("No such volume exist");
}
_accountMgr.checkAccess(caller, null, true, volInfo);
future = _tmpltSvr.createTemplateFromVolumeAsync(volInfo, tmplInfo, store);
} else {
throw new CloudRuntimeException("Creating private Template need to specify snapshotId or volumeId");

View File

@ -2487,10 +2487,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
@DB
@ActionEvent(eventType = EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY, eventDescription = "register for the developer API keys")
public String[] createApiKeyAndSecretKey(final long userId) {
Account caller = getCurrentCallingAccount();
User user = getUserIncludingRemoved(userId);
if (user == null) {
throw new InvalidParameterValueException("Unable to find user by id");
}
Account account = _accountDao.findById(user.getAccountId());
checkAccess(caller, null, true, account);
final String[] keys = new String[2];
Transaction.execute(new TransactionCallbackNoReturn() {
@Override

View File

@ -50,6 +50,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.affinity.AffinityGroupService;
@ -3000,6 +3001,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
checkForUnattachedVolumes(vmId, volumesToBeDeleted);
validateVolumes(volumesToBeDeleted);
final ControlledEntity[] volumesToDelete = volumesToBeDeleted.toArray(new ControlledEntity[0]);
_accountMgr.checkAccess(ctx.getCallingAccount(), null, true, volumesToDelete);
stopVirtualMachine(vmId, VmDestroyForcestop.value());
detachVolumesFromVm(volumesToBeDeleted);

View File

@ -23,7 +23,7 @@
<p class="required required-label">{{ $t('label.required') }}</p>
<a-select style="width: 100%" @change="handleChangeDomain" v-model="domainId">
<a-select-option v-for="(domain, index) in domainsList" :value="domain.id" :key="index">
{{ domain.name }}
{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</a-spin>

View File

@ -39,7 +39,7 @@
<p class="form__label"><span class="required">*</span>{{ $t('label.domain') }}</p>
<a-select @change="changeDomain" v-model="selectedDomain" :defaultValue="selectedDomain">
<a-select-option v-for="domain in domains" :key="domain.name" :value="domain.id">
{{ domain.path }}
{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</div>

View File

@ -49,7 +49,7 @@
:placeholder="apiParams.domainid.description"
@change="val => { this.handleDomainChanged(this.domains[val]) }">
<a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex">
{{ opt.name || opt.description }}
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>

View File

@ -145,7 +145,7 @@
rules: [{ required: true, message: $t('message.error.select') }] }]"
:placeholder="apiParams.domainid.description">
<a-select-option v-for="domain in domainsList" :key="domain.id">
{{ domain.name }}
{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</a-form-item>

View File

@ -125,7 +125,7 @@
v-decorator="['domainid']"
:placeholder="apiParams.domainid.description">
<a-select-option v-for="domain in domainsList" :key="domain.id">
{{ domain.name }}
{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</a-form-item>
@ -218,7 +218,7 @@ export default {
domainid: null
}
},
beforeCreate () {
created () {
this.form = this.$form.createForm(this)
this.apiConfig = this.$store.getters.apis.createUser || {}
this.apiParams = {}

View File

@ -92,7 +92,7 @@
rules: [{ required: true, message: `${$t('label.required')}` }]
}]"
>
<a-select-option v-for="domain in domains" :key="domain.id" :value="domain.id">{{ domain.name }}</a-select-option>
<a-select-option v-for="domain in domains" :key="domain.id" :value="domain.id">{{ domain.path || domain.name || domain.description }}</a-select-option>
</a-select>
</a-form-item>

View File

@ -125,7 +125,7 @@
<a-select-option
v-for="domain in domains"
:key="domain.id"
:value="domain.id">{{ domain.name }}
:value="domain.id">{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</div>
@ -187,7 +187,7 @@
<a-select-option
v-for="domain in domains"
:key="domain.id"
:value="domain.id">{{ domain.name }}
:value="domain.id">{{ domain.path || domain.name || domain.description }}
</a-select-option>
</a-select>
</a-form-item>

View File

@ -97,7 +97,7 @@
:placeholder="this.$t('label.domainid')"
@change="val => { this.handleDomainChange(this.domains[val]) }">
<a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex">
{{ opt.name || opt.description }}
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>

View File

@ -97,7 +97,7 @@
:placeholder="this.$t('label.domainid')"
@change="val => { this.handleDomainChange(this.domains[val]) }">
<a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex">
{{ opt.name || opt.description }}
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>

View File

@ -207,7 +207,7 @@
:placeholder="this.$t('label.domainid')"
@change="val => { this.handleDomainChange(this.domains[val]) }">
<a-select-option v-for="(opt, optIndex) in this.domains" :key="optIndex">
{{ opt.name || opt.description }}
{{ opt.path || opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>