Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3077,10 +3077,11 @@ public Pair<List<? extends GuestOSHypervisor>, Integer> listGuestOSMappingByCrit

if (osDisplayName != null) {
List<GuestOSVO> guestOSVOS = _guestOSDao.listLikeDisplayName(osDisplayName);
if (CollectionUtils.isNotEmpty(guestOSVOS)) {
List<Long> guestOSids = guestOSVOS.stream().map(mo -> mo.getId()).collect(Collectors.toList());
sc.addAnd(guestOsId, SearchCriteria.Op.IN, guestOSids.toArray());
if (CollectionUtils.isEmpty(guestOSVOS)) {
return new Pair<>(Collections.emptyList(), 0);
}
List<Long> guestOSids = guestOSVOS.stream().map(mo -> mo.getId()).collect(Collectors.toList());
sc.addAnd(guestOsId, SearchCriteria.Op.IN, guestOSids.toArray());
}

final Pair<List<GuestOSHypervisorVO>, Integer> result = _guestOSHypervisorDao.searchAndCount(sc, searchFilter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1732,11 +1732,12 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName, serviceOffering);
sourceVMwareInstance = sourceInstanceDetails.first();
isClonedInstance = sourceInstanceDetails.second();
guestOsId = resolveVmwareToKvmImportGuestOsId(guestOsId, sourceVMwareInstance);

// Ensure that the configured resource limits will not be exceeded before beginning the conversion process
checkVmResourceLimitsForUnmanagedInstanceImport(owner, sourceVMwareInstance, serviceOffering, template, reservations);

boolean isWindowsVm = sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows");
boolean isWindowsVm = StringUtils.containsIgnoreCase(sourceVMwareInstance.getOperatingSystem(), "windows");
if (isWindowsVm) {
checkConversionSupportOnHost(convertHost, sourceVMName, true, useVddk, details);
}
Expand Down Expand Up @@ -1792,6 +1793,104 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
}
}

protected Long resolveVmwareToKvmImportGuestOsId(Long requestedGuestOsId, UnmanagedInstanceTO sourceVMwareInstance) {
if (requestedGuestOsId != null || sourceVMwareInstance == null) {
return requestedGuestOsId;
}

String osDisplayName = sourceVMwareInstance.getOperatingSystem();
String osNameForHypervisor = sourceVMwareInstance.getOperatingSystemId();
String sourceHypervisorVersion = sourceVMwareInstance.getHostHypervisorVersion();

GuestOSHypervisor guestOSHypervisor = findVmwareGuestOsMappingByDisplayName(osDisplayName, sourceHypervisorVersion);
if (guestOSHypervisor == null) {
guestOSHypervisor = findVmwareGuestOsMappingByOsName(osNameForHypervisor, sourceHypervisorVersion);
}
if (guestOSHypervisor == null) {
guestOSHypervisor = findVmwareGuestOsMappingByDisplayName(osDisplayName, null);
}
if (guestOSHypervisor == null) {
guestOSHypervisor = findVmwareGuestOsMappingByOsName(osNameForHypervisor, null);
}
if (guestOSHypervisor != null) {
return guestOSHypervisor.getGuestOsId();
}

GuestOS guestOS = findVmwareGuestOsByDisplayName(osDisplayName);
return guestOS != null ? guestOS.getId() : null;
}

private GuestOSHypervisor findVmwareGuestOsMappingByDisplayName(String osDisplayName, String hypervisorVersion) {
List<GuestOS> guestOSes = listStrongMatchingGuestOses(osDisplayName);
for (GuestOS guestOS : guestOSes) {
GuestOSHypervisor guestOSHypervisor = guestOSHypervisorDao.findByOsIdAndHypervisor(
guestOS.getId(), Hypervisor.HypervisorType.VMware.toString(), hypervisorVersion);
if (guestOSHypervisor != null) {
return guestOSHypervisor;
}
}
return null;
}

private GuestOSHypervisor findVmwareGuestOsMappingByOsName(String osNameForHypervisor, String hypervisorVersion) {
if (StringUtils.isBlank(osNameForHypervisor)) {
return null;
}
return guestOSHypervisorDao.findByOsNameAndHypervisor(
osNameForHypervisor, Hypervisor.HypervisorType.VMware.toString(), hypervisorVersion);
}

private GuestOS findVmwareGuestOsByDisplayName(String osDisplayName) {
List<GuestOS> guestOSes = listStrongMatchingGuestOses(osDisplayName);
return CollectionUtils.isNotEmpty(guestOSes) ? guestOSes.get(0) : null;
}

private List<GuestOS> listStrongMatchingGuestOses(String osDisplayName) {
List<GuestOS> guestOSes = new ArrayList<>();
if (StringUtils.isBlank(osDisplayName)) {
return guestOSes;
}

GuestOS exactMatch = guestOSDao.findOneByDisplayName(osDisplayName);
if (exactMatch != null) {
guestOSes.add(exactMatch);
}

List<? extends GuestOS> candidates = guestOSDao.listLikeDisplayName(osDisplayName);
if (CollectionUtils.isEmpty(candidates)) {
return guestOSes;
}

for (GuestOS candidate : candidates) {
if (candidate == null || exactMatch != null && candidate.getId() == exactMatch.getId()) {
continue;
}
if (isStrongGuestOsNameMatch(candidate.getDisplayName(), osDisplayName)) {
guestOSes.add(candidate);
}
}
return guestOSes;
}

private boolean isStrongGuestOsNameMatch(String candidateName, String sourceName) {
String candidate = normalizeGuestOsName(candidateName);
String source = normalizeGuestOsName(sourceName);
if (StringUtils.isAnyBlank(candidate, source)) {
return false;
}
if (candidate.equals(source)) {
return true;
}
if (Math.min(candidate.length(), source.length()) < 8) {
return false;
}
return candidate.contains(source) || source.contains(candidate);
}

private String normalizeGuestOsName(String name) {
return StringUtils.defaultString(name).toLowerCase().replaceAll("[^a-z0-9]+", " ").trim();
}

/**
* Check whether the conversion storage pool exists and is suitable for the conversion or not.
* Secondary storage is only allowed when forceConvertToPool is false.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSHypervisorVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
Expand All @@ -147,6 +149,8 @@
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.GuestOSHypervisorDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplateDao;
Expand Down Expand Up @@ -249,6 +253,10 @@ public class UnmanagedVMsManagerImplTest {
private ImportVmTasksManager importVmTasksManager;
@Mock
private SnapshotDao snapshotDao;
@Mock
private GuestOSDao guestOSDao;
@Mock
private GuestOSHypervisorDao guestOSHypervisorDao;

@Mock
private VMInstanceVO virtualMachine;
Expand Down Expand Up @@ -730,6 +738,88 @@ public void testGetTemplateForImportInstanceDefaultTemplate() {
Assert.assertEquals(defaultTemplateName, templateForImportInstance.getName());
}

@Test
public void testResolveVmwareToKvmImportGuestOsIdKeepsRequestedGuestOsId() {
Long resolvedGuestOsId = unmanagedVMsManager.resolveVmwareToKvmImportGuestOsId(1L, instance);

Assert.assertEquals(Long.valueOf(1L), resolvedGuestOsId);
Mockito.verifyNoInteractions(guestOSDao, guestOSHypervisorDao);
}

@Test
public void testResolveVmwareToKvmImportGuestOsIdPrefersDisplayNameMapping() {
String osDisplayName = "Debian GNU/Linux 11 (64-bit)";
instance.setOperatingSystem(osDisplayName);
instance.setOperatingSystemId("otherLinux64Guest");
instance.setHostHypervisorVersion("8.0.3");

GuestOSVO guestOS = mock(GuestOSVO.class);
when(guestOS.getId()).thenReturn(10L);
when(guestOSDao.findOneByDisplayName(osDisplayName)).thenReturn(guestOS);
when(guestOSDao.listLikeDisplayName(osDisplayName)).thenReturn(List.of(guestOS));

GuestOSHypervisorVO guestOSHypervisor = mock(GuestOSHypervisorVO.class);
when(guestOSHypervisor.getGuestOsId()).thenReturn(20L);
when(guestOSHypervisorDao.findByOsIdAndHypervisor(10L, Hypervisor.HypervisorType.VMware.toString(), "8.0.3")).thenReturn(guestOSHypervisor);

Long resolvedGuestOsId = unmanagedVMsManager.resolveVmwareToKvmImportGuestOsId(null, instance);

Assert.assertEquals(Long.valueOf(20L), resolvedGuestOsId);
Mockito.verify(guestOSHypervisorDao, Mockito.never()).findByOsNameAndHypervisor(anyString(), anyString(), anyString());
}

@Test
public void testResolveVmwareToKvmImportGuestOsIdFallsBackToConfiguredGuestOsIdentifier() {
String osDisplayName = "Unmatched Runtime OS";
instance.setOperatingSystem(osDisplayName);
instance.setOperatingSystemId("ubuntu64Guest");
instance.setHostHypervisorVersion("8.0.3");
when(guestOSDao.findOneByDisplayName(osDisplayName)).thenReturn(null);
when(guestOSDao.listLikeDisplayName(osDisplayName)).thenReturn(Collections.emptyList());

GuestOSHypervisorVO guestOSHypervisor = mock(GuestOSHypervisorVO.class);
when(guestOSHypervisor.getGuestOsId()).thenReturn(30L);
when(guestOSHypervisorDao.findByOsNameAndHypervisor("ubuntu64Guest", Hypervisor.HypervisorType.VMware.toString(), "8.0.3")).thenReturn(guestOSHypervisor);

Long resolvedGuestOsId = unmanagedVMsManager.resolveVmwareToKvmImportGuestOsId(null, instance);

Assert.assertEquals(Long.valueOf(30L), resolvedGuestOsId);
}

@Test
public void testResolveVmwareToKvmImportGuestOsIdFallsBackToGuestOsTypeMatch() {
String osDisplayName = "Debian GNU/Linux 11 (64-bit)";
instance.setOperatingSystem(osDisplayName);
instance.setOperatingSystemId("otherLinux64Guest");
instance.setHostHypervisorVersion("8.0.3");

GuestOSVO guestOS = mock(GuestOSVO.class);
when(guestOS.getId()).thenReturn(40L);
when(guestOSDao.findOneByDisplayName(osDisplayName)).thenReturn(guestOS);
when(guestOSDao.listLikeDisplayName(osDisplayName)).thenReturn(List.of(guestOS));

Long resolvedGuestOsId = unmanagedVMsManager.resolveVmwareToKvmImportGuestOsId(null, instance);

Assert.assertEquals(Long.valueOf(40L), resolvedGuestOsId);
}

@Test
public void testResolveVmwareToKvmImportGuestOsIdAvoidsWeakGuestOsTypeMatch() {
String osDisplayName = "Linux";
instance.setOperatingSystem(osDisplayName);
instance.setOperatingSystemId(null);
instance.setHostHypervisorVersion("8.0.3");

GuestOSVO guestOS = mock(GuestOSVO.class);
when(guestOS.getDisplayName()).thenReturn("Other Linux (64-bit)");
when(guestOSDao.findOneByDisplayName(osDisplayName)).thenReturn(null);
when(guestOSDao.listLikeDisplayName(osDisplayName)).thenReturn(List.of(guestOS));

Long resolvedGuestOsId = unmanagedVMsManager.resolveVmwareToKvmImportGuestOsId(null, instance);

Assert.assertNull(resolvedGuestOsId);
}

private enum VcenterParameter {
EXISTING,
EXTERNAL,
Expand Down
2 changes: 1 addition & 1 deletion ui/src/views/tools/ImportUnmanagedInstance.vue
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}">
<a-select-option v-for="mapping in resource.guestOsMappings" :key="mapping.ostypeid" :label="mapping.osdisplayname">
<a-select-option v-for="mapping in resource.guestOsMappings" :key="mapping.ostypeid" :value="mapping.ostypeid" :label="mapping.osdisplayname">
<span>
{{ mapping.osdisplayname }}
</span>
Expand Down
118 changes: 112 additions & 6 deletions ui/src/views/tools/ManageInstances.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1379,18 +1379,121 @@ export default {
this.fetchInstances()
}
},
async fetchGuestOsMappings (osIdentifier, hypervisorVersion) {
const params = {}
params.hypervisor = 'VMware'
params.hypervisorversion = hypervisorVersion
params.osnameforhypervisor = osIdentifier
async fetchGuestOsMappingsByParams (params) {
return await getAPI('listGuestOsMapping', params).then(json => {
return json.listguestosmappingresponse?.guestosmapping || []
}).catch(error => {
this.$notifyError(error)
return []
})
},
normalizeGuestOsName (name) {
return (name || '')
.toLowerCase()
.replace(/^microsoft\s+/, '')
.replace(/[^a-z0-9]+/g, ' ')
.replace(/\s+/g, ' ')
.trim()
},
isStrongGuestOsNameMatch (osType, osDisplayName) {
const candidate = this.normalizeGuestOsName(osType.description || osType.osdisplayname)
const source = this.normalizeGuestOsName(osDisplayName)
if (!candidate || !source) {
return false
}
if (candidate === source) {
return true
}
if (Math.min(candidate.length, source.length) < 8) {
return false
}
return candidate.includes(source) || source.includes(candidate)
},
async fetchGuestOsTypeFallbackMappings (osDisplayName) {
if (!osDisplayName || !('listOsTypes' in this.$store.getters.apis)) {
return []
}
return await getAPI('listOsTypes', {
description: osDisplayName
}).then(json => {
const osTypes = json.listostypesresponse?.ostype || []
return osTypes
.filter(osType => this.isStrongGuestOsNameMatch(osType, osDisplayName))
.map(osType => {
return {
ostypeid: osType.id,
osdisplayname: osType.description || osType.osdisplayname
}
})
}).catch(error => {
this.$notifyError(error)
return []
})
},
filterGuestOsMappings (mappings, params, osDisplayName) {
if (!mappings || mappings.length === 0) {
return []
}

const osNameForHypervisor = (params.osnameforhypervisor || '').toLowerCase()
let filteredMappings = mappings
if (osNameForHypervisor) {
filteredMappings = mappings.filter(mapping => (mapping.osnameforhypervisor || '').toLowerCase() === osNameForHypervisor)
}

if (osDisplayName) {
const displayNameMatches = filteredMappings.filter(mapping => this.isStrongGuestOsNameMatch(mapping, osDisplayName))
if (displayNameMatches.length > 0) {
return displayNameMatches
}
if (params.osdisplayname) {
return []
}
}

return filteredMappings
},
async fetchGuestOsMappings (osIdentifier, osDisplayName, hypervisorVersion) {
const lookups = []
if (osIdentifier) {
lookups.push({
hypervisor: 'VMware',
hypervisorversion: hypervisorVersion,
osnameforhypervisor: osIdentifier
})
}
if (osIdentifier) {
lookups.push({
hypervisor: 'VMware',
osnameforhypervisor: osIdentifier
})
}
if (osDisplayName) {
lookups.push({
hypervisor: 'VMware',
hypervisorversion: hypervisorVersion,
osdisplayname: osDisplayName
})
}
if (osDisplayName) {
lookups.push({
hypervisor: 'VMware',
osdisplayname: osDisplayName
})
}

for (const params of lookups) {
if (!params.hypervisorversion) {
delete params.hypervisorversion
}
const mappings = await this.fetchGuestOsMappingsByParams(params)
const filteredMappings = this.filterGuestOsMappings(mappings, params, osDisplayName)
if (filteredMappings.length > 0) {
return filteredMappings
}
}
return await this.fetchGuestOsTypeFallbackMappings(osDisplayName)
},
fetchVmwareInstanceForKVMMigration (vmname, hostname) {
const params = {}
this.loadingGuestOsMappings = true
Expand All @@ -1411,7 +1514,10 @@ export default {
this.selectedUnmanagedInstance = response.unmanagedinstance[0]
this.selectedUnmanagedInstance.ostypename = this.selectedUnmanagedInstance.osdisplayname
this.selectedUnmanagedInstance.state = this.selectedUnmanagedInstance.powerstate
this.selectedUnmanagedInstance.guestOsMappings = await this.fetchGuestOsMappings(this.selectedUnmanagedInstance.osid, this.selectedUnmanagedInstance.hypervisorversion)
this.selectedUnmanagedInstance.guestOsMappings = await this.fetchGuestOsMappings(
this.selectedUnmanagedInstance.osid,
this.selectedUnmanagedInstance.osdisplayname,
this.selectedUnmanagedInstance.hypervisorversion)
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
Expand Down
Loading