mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Timeout config to copy the disks of remote KVM instance while importing the instance from an external host (#9213)
* Added timeout config to copy the disks of remote KVM instance while importing the instance from an external host * Updated copy config units to mins * Cleanup remote converted file and local file when copy failed
This commit is contained in:
parent
097359bef9
commit
5ab23cd9c9
@ -30,6 +30,15 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService,
|
|||||||
"If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " +
|
"If set to true, do not remove VM nics (and its MAC addresses) when unmanaging a VM, leaving them allocated but not reserved. " +
|
||||||
"If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone);
|
"If set to false, nics are removed and MAC addresses can be reassigned", true, ConfigKey.Scope.Zone);
|
||||||
|
|
||||||
|
ConfigKey<Integer> RemoteKvmInstanceDisksCopyTimeout = new ConfigKey<>(Integer.class,
|
||||||
|
"remote.kvm.instance.disks.copy.timeout",
|
||||||
|
"Advanced",
|
||||||
|
"30",
|
||||||
|
"Timeout (in mins) to prepare and copy the disks of remote KVM instance while importing the instance from an external host",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Global,
|
||||||
|
null);
|
||||||
|
|
||||||
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
|
static boolean isSupported(Hypervisor.HypervisorType hypervisorType) {
|
||||||
return hypervisorType == VMware || hypervisorType == KVM;
|
return hypervisorType == VMware || hypervisorType == KVM;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,14 +23,11 @@ import com.cloud.agent.api.to.StorageFilerTO;
|
|||||||
|
|
||||||
@LogLevel(LogLevel.Log4jLevel.Trace)
|
@LogLevel(LogLevel.Log4jLevel.Trace)
|
||||||
public class CopyRemoteVolumeCommand extends Command {
|
public class CopyRemoteVolumeCommand extends Command {
|
||||||
|
|
||||||
String remoteIp;
|
String remoteIp;
|
||||||
String username;
|
String username;
|
||||||
String password;
|
String password;
|
||||||
String srcFile;
|
String srcFile;
|
||||||
|
|
||||||
String tmpPath;
|
String tmpPath;
|
||||||
|
|
||||||
StorageFilerTO storageFilerTO;
|
StorageFilerTO storageFilerTO;
|
||||||
|
|
||||||
public CopyRemoteVolumeCommand(String remoteIp, String username, String password) {
|
public CopyRemoteVolumeCommand(String remoteIp, String username, String password) {
|
||||||
|
|||||||
@ -5336,20 +5336,31 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||||||
/*
|
/*
|
||||||
Scp volume from remote host to local directory
|
Scp volume from remote host to local directory
|
||||||
*/
|
*/
|
||||||
public String copyVolume(String srcIp, String username, String password, String localDir, String remoteFile, String tmpPath) {
|
public String copyVolume(String srcIp, String username, String password, String localDir, String remoteFile, String tmpPath, int timeoutInSecs) {
|
||||||
|
String outputFile = UUID.randomUUID().toString();
|
||||||
try {
|
try {
|
||||||
String outputFile = UUID.randomUUID().toString();
|
|
||||||
StringBuilder command = new StringBuilder("qemu-img convert -O qcow2 ");
|
StringBuilder command = new StringBuilder("qemu-img convert -O qcow2 ");
|
||||||
command.append(remoteFile);
|
command.append(remoteFile);
|
||||||
command.append(" "+tmpPath);
|
command.append(" " + tmpPath);
|
||||||
command.append(outputFile);
|
command.append(outputFile);
|
||||||
s_logger.debug("Converting remoteFile: "+remoteFile);
|
s_logger.debug(String.format("Converting remote disk file: %s, output file: %s%s (timeout: %d secs)", remoteFile, tmpPath, outputFile, timeoutInSecs));
|
||||||
SshHelper.sshExecute(srcIp, 22, username, null, password, command.toString());
|
SshHelper.sshExecute(srcIp, 22, username, null, password, command.toString(), timeoutInSecs * 1000);
|
||||||
s_logger.debug("Copying remoteFile to: "+localDir);
|
s_logger.debug("Copying converted remote disk file " + outputFile + " to: " + localDir);
|
||||||
SshHelper.scpFrom(srcIp, 22, username, null, password, localDir, tmpPath+outputFile);
|
SshHelper.scpFrom(srcIp, 22, username, null, password, localDir, tmpPath + outputFile);
|
||||||
s_logger.debug("Successfully copyied remoteFile to: "+localDir+"/"+outputFile);
|
s_logger.debug("Successfully copied converted remote disk file to: " + localDir + "/" + outputFile);
|
||||||
return outputFile;
|
return outputFile;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
String deleteRemoteConvertedFileCmd = String.format("rm -f %s%s", tmpPath, outputFile);
|
||||||
|
SshHelper.sshExecute(srcIp, 22, username, null, password, deleteRemoteConvertedFileCmd);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileUtils.deleteQuietly(new File(localDir + "/" + outputFile));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,6 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||||
String result = null;
|
|
||||||
String srcIp = command.getRemoteIp();
|
String srcIp = command.getRemoteIp();
|
||||||
String username = command.getUsername();
|
String username = command.getUsername();
|
||||||
String password = command.getPassword();
|
String password = command.getPassword();
|
||||||
@ -56,23 +55,25 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<
|
|||||||
KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr();
|
KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr();
|
||||||
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
||||||
String dstPath = pool.getLocalPath();
|
String dstPath = pool.getLocalPath();
|
||||||
|
int timeoutInSecs = command.getWait();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||||
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath);
|
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
|
||||||
s_logger.debug("Volume Copy Successful");
|
s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
|
||||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
|
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
|
||||||
final String path = vol.getPath();
|
final String path = vol.getPath();
|
||||||
long size = getVirtualSizeFromFile(path);
|
long size = getVirtualSizeFromFile(path);
|
||||||
return new CopyRemoteVolumeAnswer(command, "", filename, size);
|
return new CopyRemoteVolumeAnswer(command, "", filename, size);
|
||||||
} else {
|
} else {
|
||||||
return new Answer(command, false, "Unsupported Storage Pool");
|
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
|
||||||
|
return new Answer(command, false, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
s_logger.error("Error while copying file from remote host: "+ e.getMessage());
|
s_logger.error("Error while copying volume file from remote host: " + e.getMessage(), e);
|
||||||
return new Answer(command, false, result);
|
String msg = "Failed to copy volume due to: " + e.getMessage();
|
||||||
|
return new Answer(command, false, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -135,6 +135,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
|||||||
import org.apache.cloudstack.userdata.UserDataManager;
|
import org.apache.cloudstack.userdata.UserDataManager;
|
||||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
import org.apache.cloudstack.vm.UnmanagedVMsManager;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
@ -557,6 +558,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.key());
|
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.key());
|
||||||
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.key());
|
configValuesForValidation.add(StorageManager.STORAGE_POOL_CLIENT_MAX_CONNECTIONS.key());
|
||||||
configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING);
|
configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING);
|
||||||
|
configValuesForValidation.add(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.key());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void weightBasedParametersForValidation() {
|
private void weightBasedParametersForValidation() {
|
||||||
|
|||||||
@ -794,13 +794,19 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
copyRemoteVolumeCommand.setTempPath(tmpPath);
|
copyRemoteVolumeCommand.setTempPath(tmpPath);
|
||||||
|
int copyTimeout = UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.value();
|
||||||
|
if (copyTimeout <= 0) {
|
||||||
|
copyTimeout = Integer.valueOf(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.defaultValue());
|
||||||
|
}
|
||||||
|
int copyTimeoutInSecs = copyTimeout * 60;
|
||||||
|
copyRemoteVolumeCommand.setWait(copyTimeoutInSecs);
|
||||||
Answer answer = agentManager.easySend(dest.getHost().getId(), copyRemoteVolumeCommand);
|
Answer answer = agentManager.easySend(dest.getHost().getId(), copyRemoteVolumeCommand);
|
||||||
if (!(answer instanceof CopyRemoteVolumeAnswer)) {
|
if (!(answer instanceof CopyRemoteVolumeAnswer)) {
|
||||||
throw new CloudRuntimeException("Error while copying volume");
|
throw new CloudRuntimeException("Error while copying volume of remote instance: " + answer.getDetails());
|
||||||
}
|
}
|
||||||
CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer;
|
CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer;
|
||||||
if(!copyRemoteVolumeAnswer.getResult()) {
|
if(!copyRemoteVolumeAnswer.getResult()) {
|
||||||
throw new CloudRuntimeException("Error while copying volume");
|
throw new CloudRuntimeException("Unable to copy volume of remote instance");
|
||||||
}
|
}
|
||||||
diskProfile.setSize(copyRemoteVolumeAnswer.getSize());
|
diskProfile.setSize(copyRemoteVolumeAnswer.getSize());
|
||||||
DiskProfile profile = volumeManager.updateImportedVolume(type, diskOffering, vm, template, deviceId,
|
DiskProfile profile = volumeManager.updateImportedVolume(type, diskOffering, vm, template, deviceId,
|
||||||
@ -813,7 +819,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
Volume.Type type, VirtualMachineTemplate template,
|
Volume.Type type, VirtualMachineTemplate template,
|
||||||
Long deviceId, Long hostId, String diskPath, DiskProfile diskProfile) {
|
Long deviceId, Long hostId, String diskPath, DiskProfile diskProfile) {
|
||||||
List<StoragePoolVO> storagePools = primaryDataStoreDao.findLocalStoragePoolsByHostAndTags(hostId, null);
|
List<StoragePoolVO> storagePools = primaryDataStoreDao.findLocalStoragePoolsByHostAndTags(hostId, null);
|
||||||
|
|
||||||
if(storagePools.size() < 1) {
|
if(storagePools.size() < 1) {
|
||||||
throw new CloudRuntimeException("Local Storage not found for host");
|
throw new CloudRuntimeException("Local Storage not found for host");
|
||||||
}
|
}
|
||||||
@ -826,7 +831,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
return new Pair<>(profile, storagePool);
|
return new Pair<>(profile, storagePool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Pair<DiskProfile, StoragePool> importKVMSharedDisk(VirtualMachine vm, DiskOffering diskOffering,
|
private Pair<DiskProfile, StoragePool> importKVMSharedDisk(VirtualMachine vm, DiskOffering diskOffering,
|
||||||
Volume.Type type, VirtualMachineTemplate template,
|
Volume.Type type, VirtualMachineTemplate template,
|
||||||
Long deviceId, Long poolId, String diskPath, DiskProfile diskProfile) {
|
Long deviceId, Long poolId, String diskPath, DiskProfile diskProfile) {
|
||||||
@ -838,7 +842,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
return new Pair<>(profile, storagePool);
|
return new Pair<>(profile, storagePool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Pair<DiskProfile, StoragePool> importDisk(UnmanagedInstanceTO.Disk disk, VirtualMachine vm, Cluster cluster, DiskOffering diskOffering,
|
private Pair<DiskProfile, StoragePool> importDisk(UnmanagedInstanceTO.Disk disk, VirtualMachine vm, Cluster cluster, DiskOffering diskOffering,
|
||||||
Volume.Type type, String name, Long diskSize, Long minIops, Long maxIops, VirtualMachineTemplate template,
|
Volume.Type type, String name, Long diskSize, Long minIops, Long maxIops, VirtualMachineTemplate template,
|
||||||
Account owner, Long deviceId) {
|
Account owner, Long deviceId) {
|
||||||
@ -2335,7 +2338,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName()));
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Map<Volume, StoragePool> storage = dest.getStorageForDisks();
|
Map<Volume, StoragePool> storage = dest.getStorageForDisks();
|
||||||
Volume volume = volumeDao.findById(diskProfile.getVolumeId());
|
Volume volume = volumeDao.findById(diskProfile.getVolumeId());
|
||||||
StoragePool storagePool = storage.get(volume);
|
StoragePool storagePool = storage.get(volume);
|
||||||
@ -2355,7 +2357,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
}
|
}
|
||||||
diskProfile.setSize(checkVolumeAnswer.getSize());
|
diskProfile.setSize(checkVolumeAnswer.getSize());
|
||||||
|
|
||||||
|
|
||||||
List<Pair<DiskProfile, StoragePool>> diskProfileStoragePoolList = new ArrayList<>();
|
List<Pair<DiskProfile, StoragePool>> diskProfileStoragePoolList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
long deviceId = 1L;
|
long deviceId = 1L;
|
||||||
@ -2376,7 +2377,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
return userVm;
|
return userVm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException {
|
private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException {
|
||||||
NetworkVO defaultNetwork = null;
|
NetworkVO defaultNetwork = null;
|
||||||
|
|
||||||
@ -2503,6 +2503,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[]{UnmanageVMPreserveNic};
|
return new ConfigKey<?>[]{
|
||||||
|
UnmanageVMPreserveNic,
|
||||||
|
RemoteKvmInstanceDisksCopyTimeout
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,12 +38,18 @@ import com.cloud.utils.Pair;
|
|||||||
public class SshHelper {
|
public class SshHelper {
|
||||||
private static final int DEFAULT_CONNECT_TIMEOUT = 180000;
|
private static final int DEFAULT_CONNECT_TIMEOUT = 180000;
|
||||||
private static final int DEFAULT_KEX_TIMEOUT = 60000;
|
private static final int DEFAULT_KEX_TIMEOUT = 60000;
|
||||||
|
private static final int DEFAULT_WAIT_RESULT_TIMEOUT = 120000;
|
||||||
|
|
||||||
private static final Logger s_logger = Logger.getLogger(SshHelper.class);
|
private static final Logger s_logger = Logger.getLogger(SshHelper.class);
|
||||||
|
|
||||||
public static Pair<Boolean, String> sshExecute(String host, int port, String user, File pemKeyFile, String password, String command) throws Exception {
|
public static Pair<Boolean, String> sshExecute(String host, int port, String user, File pemKeyFile, String password, String command) throws Exception {
|
||||||
|
|
||||||
return sshExecute(host, port, user, pemKeyFile, password, command, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT, 120000);
|
return sshExecute(host, port, user, pemKeyFile, password, command, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT, DEFAULT_WAIT_RESULT_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pair<Boolean, String> sshExecute(String host, int port, String user, File pemKeyFile, String password, String command, int waitResultTimeoutInMs) throws Exception {
|
||||||
|
|
||||||
|
return sshExecute(host, port, user, pemKeyFile, password, command, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT, waitResultTimeoutInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String localFile, String fileMode)
|
public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String localFile, String fileMode)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user