server: Fix VM import DB sequence issue on import failure (#11659)

* Fix VM import DB sequence issue on import failure

* Remove ununsed imports

* Refactor to avoid duplicating the next ID for VM sequence
This commit is contained in:
Nicolas Vazquez 2025-09-23 03:48:18 -03:00 committed by GitHub
parent 40dec99659
commit 1a223fd2ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 70 deletions

View File

@ -503,7 +503,31 @@ public interface UserVmService {
void collectVmNetworkStatistics (UserVm userVm);
UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceName, final String displayName, final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
/**
* Import VM into CloudStack
* @param zone importing zone
* @param host importing host
* @param template template for the imported VM
* @param instanceNameInternal set to null to CloudStack to autogenerate from the next available VM ID on database
* @param displayName display name for the imported VM
* @param owner owner of the imported VM
* @param userData user data for the imported VM
* @param caller caller account
* @param isDisplayVm true to display the imported VM
* @param keyboard keyboard distribution for the imported VM
* @param accountId account ID
* @param userId user ID
* @param serviceOffering service offering for the imported VM
* @param sshPublicKey ssh key for the imported VM
* @param hostName the name for the imported VM
* @param hypervisorType hypervisor type for the imported VM
* @param customParameters details for the imported VM
* @param powerState power state of the imported VM
* @param networkNicMap network to nic mapping
* @return the imported VM
* @throws InsufficientCapacityException in case of errors
*/
UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceNameInternal, final String displayName, final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKey,
final String hostName, final HypervisorType hypervisorType, final Map<String, String> customParameters,
final VirtualMachine.PowerState powerState, final LinkedHashMap<String, List<NicProfile>> networkNicMap) throws InsufficientCapacityException;

View File

@ -8989,34 +8989,47 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
private String getInternalName(long accountId, long vmId) {
String instanceSuffix = _configDao.getValue(Config.InstanceName.key());
if (instanceSuffix == null) {
instanceSuffix = "DEFAULT";
}
return VirtualMachineName.getVmName(vmId, accountId, instanceSuffix);
}
@Override
public UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceName, final String displayName,
public UserVm importVM(final DataCenter zone, final Host host, final VirtualMachineTemplate template, final String instanceNameInternal, final String displayName,
final Account owner, final String userData, final Account caller, final Boolean isDisplayVm, final String keyboard,
final long accountId, final long userId, final ServiceOffering serviceOffering, final String sshPublicKeys,
final String hostName, final HypervisorType hypervisorType, final Map<String, String> customParameters,
final VirtualMachine.PowerState powerState, final LinkedHashMap<String, List<NicProfile>> networkNicMap) throws InsufficientCapacityException {
if (zone == null) {
throw new InvalidParameterValueException("Unable to import virtual machine with invalid zone");
}
if (host == null && hypervisorType == HypervisorType.VMware) {
throw new InvalidParameterValueException("Unable to import virtual machine with invalid host");
}
return Transaction.execute((TransactionCallbackWithException<UserVm, InsufficientCapacityException>) status -> {
if (zone == null) {
throw new InvalidParameterValueException("Unable to import virtual machine with invalid zone");
}
if (host == null && hypervisorType == HypervisorType.VMware) {
throw new InvalidParameterValueException("Unable to import virtual machine with invalid host");
}
final long id = _vmDao.getNextInSequence(Long.class, "id");
final long id = _vmDao.getNextInSequence(Long.class, "id");
String instanceName = StringUtils.isBlank(instanceNameInternal) ?
getInternalName(owner.getAccountId(), id) :
instanceNameInternal;
if (hostName != null) {
// Check is hostName is RFC compliant
checkNameForRFCCompliance(hostName);
}
if (hostName != null) {
// Check is hostName is RFC compliant
checkNameForRFCCompliance(hostName);
}
final String uuidName = _uuidMgr.generateUuid(UserVm.class, null);
final Host lastHost = powerState != VirtualMachine.PowerState.PowerOn ? host : null;
final Boolean dynamicScalingEnabled = checkIfDynamicScalingCanBeEnabled(null, serviceOffering, template, zone.getId());
return commitUserVm(true, zone, host, lastHost, template, hostName, displayName, owner,
null, null, userData, null, null, isDisplayVm, keyboard,
accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKeys, networkNicMap,
id, instanceName, uuidName, hypervisorType, customParameters,
null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId(), null);
final String uuidName = _uuidMgr.generateUuid(UserVm.class, null);
final Host lastHost = powerState != VirtualMachine.PowerState.PowerOn ? host : null;
final Boolean dynamicScalingEnabled = checkIfDynamicScalingCanBeEnabled(null, serviceOffering, template, zone.getId());
return commitUserVm(true, zone, host, lastHost, template, hostName, displayName, owner,
null, null, userData, null, null, isDisplayVm, keyboard,
accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKeys, networkNicMap,
id, instanceName, uuidName, hypervisorType, customParameters,
null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId(), null);
});
}
@Override

View File

@ -133,7 +133,6 @@ import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VirtualMachineName;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.VmDetailConstants;
@ -1111,13 +1110,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
}
private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceName, final DataCenter zone, final Cluster cluster, final HostVO host,
private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceNameInternal, final DataCenter zone, final Cluster cluster, final HostVO host,
final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId,
final ServiceOfferingVO serviceOffering, final Map<String, Long> dataDiskOfferingMap,
final Map<String, Long> nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
final Map<String, String> details, final boolean migrateAllowed, final boolean forced, final boolean isImportUnmanagedFromSameHypervisor) {
logger.debug(LogUtils.logGsonWithoutException("Trying to import VM [%s] with name [%s], in zone [%s], cluster [%s], and host [%s], using template [%s], service offering [%s], disks map [%s], NICs map [%s] and details [%s].",
unmanagedInstance, instanceName, zone, cluster, host, template, serviceOffering, dataDiskOfferingMap, nicNetworkMap, details));
unmanagedInstance, displayName, zone, cluster, host, template, serviceOffering, dataDiskOfferingMap, nicNetworkMap, details));
UserVm userVm = null;
ServiceOfferingVO validatedServiceOffering = null;
try {
@ -1129,8 +1128,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
String internalCSName = unmanagedInstance.getInternalCSName();
if (StringUtils.isEmpty(internalCSName)) {
internalCSName = instanceName;
if (StringUtils.isEmpty(instanceNameInternal)) {
internalCSName = instanceNameInternal;
}
Map<String, String> allDetails = new HashMap<>(details);
if (validatedServiceOffering.isDynamic()) {
@ -1142,18 +1141,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
if (!migrateAllowed && host != null && !hostSupportsServiceOfferingAndTemplate(host, validatedServiceOffering, template)) {
throw new InvalidParameterValueException(String.format("Service offering: %s or template: %s is not compatible with host: %s of unmanaged VM: %s", serviceOffering.getUuid(), template.getUuid(), host.getUuid(), instanceName));
throw new InvalidParameterValueException(String.format("Service offering: %s or template: %s is not compatible with host: %s of unmanaged VM: %s", serviceOffering.getUuid(), template.getUuid(), host.getUuid(), displayName));
}
// Check disks and supplied disk offerings
List<UnmanagedInstanceTO.Disk> unmanagedInstanceDisks = unmanagedInstance.getDisks();
if (CollectionUtils.isEmpty(unmanagedInstanceDisks)) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", instanceName));
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", displayName));
}
Pair<UnmanagedInstanceTO.Disk, List<UnmanagedInstanceTO.Disk>> rootAndDataDisksPair = getRootAndDataDisks(unmanagedInstanceDisks, dataDiskOfferingMap);
final UnmanagedInstanceTO.Disk rootDisk = rootAndDataDisksPair.first();
final List<UnmanagedInstanceTO.Disk> dataDisks = rootAndDataDisksPair.second();
if (rootDisk == null || StringUtils.isEmpty(rootDisk.getController())) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed. Unable to retrieve root disk details for VM: %s ", instanceName));
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed. Unable to retrieve root disk details for VM: %s ", displayName));
}
if (cluster.getHypervisorType() == Hypervisor.HypervisorType.KVM) {
Long rootDiskOfferingId = validatedServiceOffering.getDiskOfferingId();
@ -1203,13 +1202,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
validatedServiceOffering, null, hostName,
cluster.getHypervisorType(), allDetails, powerState, null);
} catch (InsufficientCapacityException ice) {
String errorMsg = String.format("Failed to import VM [%s] due to [%s].", instanceName, ice.getMessage());
String errorMsg = String.format("Failed to import VM [%s] due to [%s].", displayName, ice.getMessage());
logger.error(errorMsg, ice);
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg);
}
if (userVm == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName));
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", displayName));
}
List<Pair<DiskProfile, StoragePool>> diskProfileStoragePoolList = new ArrayList<>();
try {
@ -1239,9 +1238,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
deviceId++;
}
} catch (Exception e) {
logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e);
logger.error(String.format("Failed to import volumes while importing vm: %s", displayName), e);
cleanupFailedImportVM(userVm);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage())));
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", displayName, StringUtils.defaultString(e.getMessage())));
}
try {
int nicIndex = 0;
@ -1252,9 +1251,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
nicIndex++;
}
} catch (Exception e) {
logger.error(String.format("Failed to import NICs while importing vm: %s", instanceName), e);
logger.error(String.format("Failed to import NICs while importing vm: %s", displayName), e);
cleanupFailedImportVM(userVm);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage())));
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", displayName, StringUtils.defaultString(e.getMessage())));
}
if (migrateAllowed) {
userVm = migrateImportedVM(host, template, validatedServiceOffering, userVm, owner, diskProfileStoragePoolList);
@ -1668,8 +1667,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
checkConversionSupportOnHost(convertHost, sourceVMName, true);
}
String instanceName = getGeneratedInstanceName(owner);
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, sourceVMwareInstance, nicNetworkMap, nicIpAddressMap, forced);
checkNetworkingBeforeConvertingVmwareInstance(zone, owner, displayName, hostName, sourceVMwareInstance, nicNetworkMap, nicIpAddressMap, forced);
UnmanagedInstanceTO convertedInstance;
if (cmd.getForceMsToImportVmFiles() || !conversionSupportAnswer.isOvfExportSupported()) {
// Uses MS for OVF export to temporary conversion location
@ -1690,14 +1688,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
sanitizeConvertedInstance(convertedInstance, sourceVMwareInstance);
UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null,
UserVm userVm = importVirtualMachineInternal(convertedInstance, null, zone, destinationCluster, null,
template, displayName, hostName, caller, owner, userId,
serviceOffering, dataDiskOfferingMap,
nicNetworkMap, nicIpAddressMap,
details, false, forced, false);
long timeElapsedInSecs = (System.currentTimeMillis() - importStartTime) / 1000;
logger.debug(String.format("VMware VM %s imported successfully to CloudStack instance %s (%s), Time taken: %d secs, OVF files imported from %s, Source VMware VM details - OS: %s, PowerState: %s, Disks: %s, NICs: %s",
sourceVMName, instanceName, displayName, timeElapsedInSecs, (ovfTemplateOnConvertLocation != null)? "MS" : "KVM Host", sourceVMwareInstance.getOperatingSystem(), sourceVMwareInstance.getPowerState(), sourceVMwareInstance.getDisks(), sourceVMwareInstance.getNics()));
sourceVMName, displayName, displayName, timeElapsedInSecs, (ovfTemplateOnConvertLocation != null)? "MS" : "KVM Host", sourceVMwareInstance.getOperatingSystem(), sourceVMwareInstance.getPowerState(), sourceVMwareInstance.getDisks(), sourceVMwareInstance.getNics()));
return userVm;
} catch (CloudRuntimeException e) {
logger.error(String.format("Error importing VM: %s", e.getMessage()), e);
@ -1714,7 +1712,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
}
private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String instanceName,
private void checkNetworkingBeforeConvertingVmwareInstance(DataCenter zone, Account owner, String displayName,
String hostName, UnmanagedInstanceTO sourceVMwareInstance,
Map<String, Long> nicNetworkMap,
Map<String, Network.IpAddresses> nicIpAddressMap,
@ -1742,9 +1740,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
boolean autoImport = ipAddresses != null && ipAddresses.getIp4Address() != null && ipAddresses.getIp4Address().equalsIgnoreCase("auto");
checkUnmanagedNicAndNetworkMacAddressForImport(network, nic, forced);
checkUnmanagedNicAndNetworkForImport(instanceName, nic, network, zone, owner, autoImport, Hypervisor.HypervisorType.KVM);
checkUnmanagedNicAndNetworkHostnameForImport(instanceName, nic, network, hostName);
checkUnmanagedNicIpAndNetworkForImport(instanceName, nic, network, ipAddresses);
checkUnmanagedNicAndNetworkForImport(displayName, nic, network, zone, owner, autoImport, Hypervisor.HypervisorType.KVM);
checkUnmanagedNicAndNetworkHostnameForImport(displayName, nic, network, hostName);
checkUnmanagedNicIpAndNetworkForImport(displayName, nic, network, ipAddresses);
}
}
@ -1758,15 +1756,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
}
private String getGeneratedInstanceName(Account owner) {
long id = vmDao.getNextInSequence(Long.class, "id");
String instanceSuffix = configurationDao.getValue(Config.InstanceName.key());
if (instanceSuffix == null) {
instanceSuffix = "DEFAULT";
}
return VirtualMachineName.getVmName(id, owner.getId(), instanceSuffix);
}
private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, UnmanagedInstanceTO sourceVMwareInstance) {
convertedInstance.setCpuCores(sourceVMwareInstance.getCpuCores());
convertedInstance.setCpuSpeed(sourceVMwareInstance.getCpuSpeed());
@ -2512,10 +2501,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
VirtualMachine.PowerState powerState = VirtualMachine.PowerState.PowerOff;
String internalName = getInternalName(owner.getAccountId());
try {
userVm = userVmManager.importVM(zone, null, template, internalName, displayName, owner,
userVm = userVmManager.importVM(zone, null, template, null, displayName, owner,
null, caller, true, null, owner.getAccountId(), userId,
serviceOffering, null, hostName,
Hypervisor.HypervisorType.KVM, allDetails, powerState, null);
@ -2654,10 +2641,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
profiles.add(nicProfile);
networkNicMap.put(network.getUuid(), profiles);
String internalName = getInternalName(owner.getAccountId());
try {
userVm = userVmManager.importVM(zone, null, template, internalName, displayName, owner,
userVm = userVmManager.importVM(zone, null, template, null, displayName, owner,
null, caller, true, null, owner.getAccountId(), userId,
serviceOffering, null, hostName,
Hypervisor.HypervisorType.KVM, allDetails, powerState, networkNicMap);
@ -2868,15 +2853,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return getRemoteVmsAnswer.getUnmanagedInstances();
}
private String getInternalName(long accounId) {
String instanceSuffix = configurationDao.getValue(Config.InstanceName.key());
if (instanceSuffix == null) {
instanceSuffix = "DEFAULT";
}
long vmId = userVmDao.getNextInSequence(Long.class, "id");
return VirtualMachineName.getVmName(vmId, accounId, instanceSuffix);
}
@Override
public String getConfigComponentName() {
return UnmanagedVMsManagerImpl.class.getSimpleName();

View File

@ -507,7 +507,6 @@ public class UnmanagedVMsManagerImplTest {
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
when(templateDao.findByName(anyString())).thenReturn(template);
HostVO host = Mockito.mock(HostVO.class);
when(userVmDao.getNextInSequence(Long.class, "id")).thenReturn(1L);
DeployDestination mockDest = Mockito.mock(DeployDestination.class);
when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest);
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);
@ -593,7 +592,6 @@ public class UnmanagedVMsManagerImplTest {
String tmplFileName = "5b8d689a-e61a-4ac3-9b76-e121ff90fbd3";
long newVmId = 2L;
long networkId = 1L;
when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId);
ClusterVO cluster = mock(ClusterVO.class);
when(cluster.getId()).thenReturn(clusterId);
@ -744,7 +742,6 @@ public class UnmanagedVMsManagerImplTest {
when(hostDao.findById(anyLong())).thenReturn(host);
NetworkOffering netOffering = Mockito.mock(NetworkOffering.class);
when(entityMgr.findById(NetworkOffering.class, 0L)).thenReturn(netOffering);
when(userVmDao.getNextInSequence(Long.class, "id")).thenReturn(1L);
DeployDestination mockDest = Mockito.mock(DeployDestination.class);
when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest);
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);