blob: bd5380cc160607087d3891712c2ee3cd3b91d642 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.storage.motion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
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.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
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.engine.subsystem.api.storage.VolumeService;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
import org.apache.cloudstack.framework.async.AsyncCallFuture;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.RemoteHostEndPoint;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.commons.collections.MapUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.springframework.stereotype.Component;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.MigrateAnswer;
import com.cloud.agent.api.MigrateCommand;
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
import com.cloud.agent.api.ModifyTargetsAnswer;
import com.cloud.agent.api.ModifyTargetsCommand;
import com.cloud.agent.api.PrepareForMigrationCommand;
import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageManager;
import com.cloud.storage.VMTemplateDetailVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDetailsVO;
import com.cloud.storage.dao.VMTemplateDetailsDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
@Component
public class StorPoolDataMotionStrategy implements DataMotionStrategy {
protected Logger logger = LogManager.getLogger(getClass());
@Inject
private SnapshotDataFactory _snapshotDataFactory;
@Inject
private DataStoreManager _dataStore;
@Inject
private ConfigurationDao _configDao;
@Inject
private EndPointSelector _selector;
@Inject
private TemplateDataStoreDao _templStoreDao;
@Inject
private ClusterDao _clusterDao;
@Inject
private HostDao _hostDao;
@Inject
private SnapshotDetailsDao _snapshotDetailsDao;
@Inject
private VMTemplateDetailsDao _vmTemplateDetailsDao;
@Inject
private SnapshotDataStoreDao _snapshotStoreDao;
@Inject
private StoragePoolDetailsDao _storagePoolDetails;
@Inject
private PrimaryDataStoreDao _storagePool;
@Inject
private VolumeDao _volumeDao;
@Inject
private VolumeDataFactory _volumeDataFactory;
@Inject
private VMInstanceDao _vmDao;
@Inject
private GuestOSDao _guestOsDao;
@Inject
private VolumeService _volumeService;
@Inject
private GuestOSCategoryDao _guestOsCategoryDao;
@Inject
private SnapshotDao _snapshotDao;
@Inject
private AgentManager _agentManager;
@Inject
private PrimaryDataStoreDao _storagePoolDao;
@Override
public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
DataObjectType srcType = srcData.getType();
DataObjectType dstType = destData.getType();
if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.TEMPLATE
&& StorPoolConfigurationManager.BypassSecondaryStorage.value()) {
SnapshotInfo sinfo = (SnapshotInfo) srcData;
VolumeInfo volume = sinfo.getBaseVolume();
StoragePoolVO storagePool = _storagePool.findById(volume.getPoolId());
if (!storagePool.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) {
return StrategyPriority.CANT_HANDLE;
}
String snapshotName = StorPoolHelper.getSnapshotName(sinfo.getId(), sinfo.getUuid(), _snapshotStoreDao,
_snapshotDetailsDao);
StorPoolUtil.spLog("StorPoolDataMotionStrategy.canHandle snapshot name=%s", snapshotName);
if (snapshotName != null) {
return StrategyPriority.HIGHEST;
}
}
return StrategyPriority.CANT_HANDLE;
}
@Override
public void copyAsync(DataObject srcData, DataObject destData, Host destHost,
AsyncCompletionCallback<CopyCommandResult> callback) {
SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO();
TemplateObjectTO template = (TemplateObjectTO) destData.getTO();
DataStore store = _dataStore.getDataStore(snapshot.getVolume().getDataStore().getUuid(),
snapshot.getVolume().getDataStore().getRole());
SnapshotInfo sInfo = _snapshotDataFactory.getSnapshot(snapshot.getId(), store);
VolumeInfo vInfo = sInfo.getBaseVolume();
SpConnectionDesc conn = StorPoolUtil.getSpConnection(vInfo.getDataStore().getUuid(),
vInfo.getDataStore().getId(), _storagePoolDetails, _storagePool);
String name = template.getUuid();
String volumeName = "";
String parentName = StorPoolHelper.getSnapshotName(sInfo.getId(), sInfo.getUuid(), _snapshotStoreDao,
_snapshotDetailsDao);
// TODO volume tags cs - template
SpApiResponse res = StorPoolUtil.volumeCreate(name, parentName, sInfo.getSize(), null, "no", "template", null,
conn);
CopyCmdAnswer answer = null;
String err = null;
if (res.getError() != null) {
logger.debug(String.format("Could not create volume from snapshot with ID=%s", snapshot.getId()));
StorPoolUtil.spLog("Volume create failed with error=%s", res.getError().getDescr());
err = res.getError().getDescr();
} else {
volumeName = StorPoolUtil.getNameFromResponse(res, true);
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sInfo.getId(), sInfo.getUuid());
snapshot.setPath(snapshotDetails.getValue());
Command backupSnapshot = new StorPoolBackupTemplateFromSnapshotCommand(snapshot, template,
StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, _configDao),
VirtualMachineManager.ExecuteInSequence.value());
try {
// final String snapName =
// StorpoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo)
// srcData).getPath(), true);
Long clusterId = StorPoolHelper.findClusterIdByGlobalId(parentName, _clusterDao);
EndPoint ep2 = clusterId != null
? RemoteHostEndPoint
.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, _hostDao))
: _selector.select(sInfo, destData);
if (ep2 == null) {
err = "No remote endpoint to send command, check if host or ssvm is down?";
} else {
answer = (CopyCmdAnswer) ep2.sendMessage(backupSnapshot);
if (answer != null && answer.getResult()) {
SpApiResponse resSnapshot = StorPoolUtil.volumeFreeze(volumeName, conn);
if (resSnapshot.getError() != null) {
logger.debug(String.format("Could not snapshot volume with ID=%s", snapshot.getId()));
StorPoolUtil.spLog("Volume freeze failed with error=%s", resSnapshot.getError().getDescr());
err = resSnapshot.getError().getDescr();
StorPoolUtil.volumeDelete(volumeName, conn);
} else {
StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(),
StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(res, false)), _templStoreDao);
}
} else {
err = "Could not copy template to secondary " + answer.getResult();
StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(res, true), conn);
}
}
} catch (CloudRuntimeException e) {
err = e.getMessage();
}
}
_vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorPoolUtil.SP_STORAGE_POOL_ID,
String.valueOf(vInfo.getDataStore().getId()), false));
StorPoolUtil.spLog("StorPoolDataMotionStrategy.copyAsync Creating snapshot=%s for StorPool template=%s",
volumeName, conn.getTemplateName());
final CopyCommandResult cmd = new CopyCommandResult(null, answer);
cmd.setResult(err);
callback.complete(cmd);
}
@Override
public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
return canHandleLiveMigrationOnStorPool(volumeMap, srcHost, destHost);
}
final StrategyPriority canHandleLiveMigrationOnStorPool(Map<VolumeInfo, DataStore> volumeMap, Host srcHost,
Host destHost) {
if (srcHost.getId() != destHost.getId() && isDestinationStorPoolPrimaryStorage(volumeMap)) {
return StrategyPriority.HIGHEST;
}
return StrategyPriority.CANT_HANDLE;
}
private boolean isDestinationStorPoolPrimaryStorage(Map<VolumeInfo, DataStore> volumeMap) {
if (MapUtils.isNotEmpty(volumeMap)) {
for (DataStore dataStore : volumeMap.values()) {
StoragePoolVO storagePoolVO = _storagePool.findById(dataStore.getId());
if (storagePoolVO == null
|| !storagePoolVO.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) {
return false;
}
}
} else {
return false;
}
return true;
}
@Override
public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMachineTO vmTO, Host srcHost,
Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
String errMsg = null;
String newVolName = null;
SpConnectionDesc conn = null;
try {
if (srcHost.getHypervisorType() != HypervisorType.KVM) {
throw new CloudRuntimeException(String.format("Invalid hypervisor type [%s]. Only KVM supported", srcHost.getHypervisorType()));
}
VMInstanceVO vmInstance = _vmDao.findById(vmTO.getId());
vmTO.setState(vmInstance.getState());
List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<MigrateDiskInfo>();
Map<String, MigrateCommand.MigrateDiskInfo> migrateStorage = new HashMap<>();
Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo = new HashMap<>();
for (Map.Entry<VolumeInfo, DataStore> entry : volumeDataStoreMap.entrySet()) {
VolumeInfo srcVolumeInfo = entry.getKey();
if (srcVolumeInfo.getPassphraseId() != null) {
throw new CloudRuntimeException(String.format("Cannot live migrate encrypted volume [%s] to StorPool", srcVolumeInfo.getName()));
}
DataStore destDataStore = entry.getValue();
VolumeVO srcVolume = _volumeDao.findById(srcVolumeInfo.getId());
StoragePoolVO destStoragePool = _storagePool.findById(destDataStore.getId());
VolumeVO destVolume = duplicateVolumeOnAnotherStorage(srcVolume, destStoragePool);
VolumeInfo destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
destVolumeInfo.processEvent(Event.MigrationCopyRequested);
destVolumeInfo.processEvent(Event.MigrationCopySucceeded);
destVolumeInfo.processEvent(Event.MigrationRequested);
conn = StorPoolUtil.getSpConnection(destDataStore.getUuid(), destDataStore.getId(), _storagePoolDetails,
_storagePool);
SpApiResponse resp = StorPoolUtil.volumeCreate(srcVolume.getUuid(), null, srcVolume.getSize(),
vmTO.getUuid(), null, "volume", srcVolume.getMaxIops(), conn);
if (resp.getError() == null) {
newVolName = StorPoolUtil.getNameFromResponse(resp, true);
}
String volumeName = StorPoolUtil.getNameFromResponse(resp, false);
destVolume.setPath(StorPoolUtil.devPath(volumeName));
_volumeDao.update(destVolume.getId(), destVolume);
destVolume = _volumeDao.findById(destVolume.getId());
destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
String destPath = generateDestPath(destHost, destStoragePool, destVolumeInfo);
MigrateCommand.MigrateDiskInfo migrateDiskInfo = configureMigrateDiskInfo(srcVolumeInfo, destPath);
migrateDiskInfoList.add(migrateDiskInfo);
migrateStorage.put(srcVolumeInfo.getPath(), migrateDiskInfo);
srcVolumeInfoToDestVolumeInfo.put(srcVolumeInfo, destVolumeInfo);
}
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
try {
Answer pfma = _agentManager.send(destHost.getId(), pfmc);
if (pfma == null || !pfma.getResult()) {
String details = pfma != null ? pfma.getDetails() : "null answer returned";
errMsg = String.format("Unable to prepare for migration due to the following: %s", details);
throw new AgentUnavailableException(errMsg, destHost.getId());
}
} catch (final OperationTimedoutException e) {
errMsg = String.format("Operation timed out due to %s", e.getMessage());
throw new AgentUnavailableException(errMsg, destHost.getId());
}
VMInstanceVO vm = _vmDao.findById(vmTO.getId());
boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId())
.getName().equalsIgnoreCase("Windows");
MigrateCommand migrateCommand = new MigrateCommand(vmTO.getName(),
destHost.getPrivateIpAddress(), isWindows, vmTO, true);
migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value());
migrateCommand.setMigrateStorage(migrateStorage);
migrateCommand.setMigrateStorageManaged(true);
migrateCommand.setMigrateDiskInfoList(migrateDiskInfoList);
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
migrateCommand.setAutoConvergence(kvmAutoConvergence);
MigrateAnswer migrateAnswer = (MigrateAnswer) _agentManager.send(srcHost.getId(), migrateCommand);
boolean success = migrateAnswer != null && migrateAnswer.getResult();
handlePostMigration(success, srcVolumeInfoToDestVolumeInfo, vmTO, destHost);
if (migrateAnswer == null) {
throw new CloudRuntimeException("Unable to get an answer to the migrate command");
}
if (!migrateAnswer.getResult()) {
errMsg = migrateAnswer.getDetails();
throw new CloudRuntimeException(errMsg);
}
} catch (AgentUnavailableException | OperationTimedoutException | CloudRuntimeException ex) {
errMsg = String.format(
"Copy volume(s) of VM [%s] to storage(s) [%s] and VM to host [%s] failed in StorPoolDataMotionStrategy.copyAsync. Error message: [%s].",
vmTO.getId(), srcHost.getId(), destHost.getId(), ex.getMessage());
logger.error(errMsg, ex);
throw new CloudRuntimeException(errMsg);
} finally {
if (errMsg != null) {
deleteVolumeOnFail(newVolName, conn);
}
CopyCmdAnswer copyCmdAnswer = new CopyCmdAnswer(errMsg);
CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
result.setResult(errMsg);
callback.complete(result);
}
}
private void deleteVolumeOnFail(String newVolName, SpConnectionDesc conn) {
if (newVolName != null && conn != null) {
StorPoolUtil.volumeDelete(newVolName, conn);
}
}
private VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePoolVO storagePoolVO) {
Long lastPoolId = volume.getPoolId();
VolumeVO newVol = new VolumeVO(volume);
newVol.setInstanceId(null);
newVol.setChainInfo(null);
newVol.setPath(null);
newVol.setFolder(null);
newVol.setPodId(storagePoolVO.getPodId());
newVol.setPoolId(storagePoolVO.getId());
newVol.setLastPoolId(lastPoolId);
return _volumeDao.persist(newVol);
}
private void handlePostMigration(boolean success, Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo,
VirtualMachineTO vmTO, Host destHost) {
if (!success) {
try {
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
pfmc.setRollback(true);
Answer pfma = _agentManager.send(destHost.getId(), pfmc);
if (pfma == null || !pfma.getResult()) {
String details = pfma != null ? pfma.getDetails() : "null answer returned";
String msg = "Unable to rollback prepare for migration due to the following: " + details;
throw new AgentUnavailableException(msg, destHost.getId());
}
} catch (Exception e) {
logger.debug("Failed to disconnect one or more (original) dest volumes", e);
}
}
for (Map.Entry<VolumeInfo, VolumeInfo> entry : srcVolumeInfoToDestVolumeInfo.entrySet()) {
VolumeInfo srcVolumeInfo = entry.getKey();
VolumeInfo destVolumeInfo = entry.getValue();
if (success) {
srcVolumeInfo.processEvent(Event.OperationSuccessed);
destVolumeInfo.processEvent(Event.OperationSuccessed);
_volumeDao.updateUuid(srcVolumeInfo.getId(), destVolumeInfo.getId());
VolumeVO volumeVO = _volumeDao.findById(destVolumeInfo.getId());
volumeVO.setFormat(ImageFormat.QCOW2);
_volumeDao.update(volumeVO.getId(), volumeVO);
try {
_volumeService.destroyVolume(srcVolumeInfo.getId());
srcVolumeInfo = _volumeDataFactory.getVolume(srcVolumeInfo.getId());
AsyncCallFuture<VolumeApiResult> destroyFuture = _volumeService.expungeVolumeAsync(srcVolumeInfo);
if (destroyFuture.get().isFailed()) {
logger.debug("Failed to clean up source volume on storage");
}
} catch (Exception e) {
logger.debug("Failed to clean up source volume on storage", e);
}
// Update the volume ID for snapshots on secondary storage
if (!_snapshotDao.listByVolumeId(srcVolumeInfo.getId()).isEmpty()) {
_snapshotDao.updateVolumeIds(srcVolumeInfo.getId(), destVolumeInfo.getId());
_snapshotStoreDao.updateVolumeIds(srcVolumeInfo.getId(), destVolumeInfo.getId());
}
} else {
try {
disconnectHostFromVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.getPath());
} catch (Exception e) {
logger.debug("Failed to disconnect (new) dest volume", e);
}
try {
_volumeService.revokeAccess(destVolumeInfo, destHost, destVolumeInfo.getDataStore());
} catch (Exception e) {
logger.debug("Failed to revoke access from dest volume", e);
}
destVolumeInfo.processEvent(Event.OperationFailed);
srcVolumeInfo.processEvent(Event.OperationFailed);
try {
_volumeService.destroyVolume(destVolumeInfo.getId());
destVolumeInfo = _volumeDataFactory.getVolume(destVolumeInfo.getId());
AsyncCallFuture<VolumeApiResult> destroyFuture = _volumeService.expungeVolumeAsync(destVolumeInfo);
if (destroyFuture.get().isFailed()) {
logger.debug("Failed to clean up dest volume on storage");
}
} catch (Exception e) {
logger.debug("Failed to clean up dest volume on storage", e);
}
}
}
}
private String generateDestPath(Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) {
return connectHostToVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.getPath());
}
private String connectHostToVolume(Host host, long storagePoolId, String iqn) {
ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, true);
return sendModifyTargetsCommand(modifyTargetsCommand, host.getId()).get(0);
}
private void disconnectHostFromVolume(Host host, long storagePoolId, String iqn) {
ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, false);
sendModifyTargetsCommand(modifyTargetsCommand, host.getId());
}
private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String iqn, boolean add) {
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
Map<String, String> details = new HashMap<>();
details.put(ModifyTargetsCommand.IQN, iqn);
details.put(ModifyTargetsCommand.STORAGE_TYPE, storagePool.getPoolType().name());
details.put(ModifyTargetsCommand.STORAGE_UUID, storagePool.getUuid());
details.put(ModifyTargetsCommand.STORAGE_HOST, storagePool.getHostAddress());
details.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePool.getPort()));
ModifyTargetsCommand cmd = new ModifyTargetsCommand();
List<Map<String, String>> targets = new ArrayList<>();
targets.add(details);
cmd.setTargets(targets);
cmd.setApplyToAllHostsInCluster(true);
cmd.setAdd(add);
cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC);
return cmd;
}
private List<String> sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) {
ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer) _agentManager.easySend(hostId, cmd);
if (modifyTargetsAnswer == null) {
throw new CloudRuntimeException("Unable to get an answer to the modify targets command");
}
if (!modifyTargetsAnswer.getResult()) {
String msg = "Unable to modify targets on the following host: " + hostId;
throw new CloudRuntimeException(msg);
}
return modifyTargetsAnswer.getConnectedPaths();
}
protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) {
return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(),
MigrateCommand.MigrateDiskInfo.DiskType.BLOCK, MigrateCommand.MigrateDiskInfo.DriverType.RAW,
MigrateCommand.MigrateDiskInfo.Source.DEV, destPath);
}
}