server: trim autoscale Windows VM hostname (#11327)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Wei Zhou <weizhou@apache.org>
This commit is contained in:
Abhishek Kumar 2025-12-15 15:52:32 +01:00 committed by GitHub
parent 39d0d62fdd
commit da1c7cebf9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 111 additions and 14 deletions

View File

@ -16,9 +16,10 @@
// under the License.
package com.cloud.network.as;
import com.cloud.user.Account;
import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.user.Account;
public interface AutoScaleManager extends AutoScaleService {
ConfigKey<Integer> AutoScaleStatsInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class,
@ -63,7 +64,5 @@ public interface AutoScaleManager extends AutoScaleService {
void removeVmFromVmGroup(Long vmId);
String getNextVmHostName(AutoScaleVmGroupVO asGroup);
void checkAutoScaleVmGroupName(String groupName);
}

View File

@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import com.cloud.network.NetworkModel;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.affinity.AffinityGroupVO;
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
@ -113,6 +112,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.NetworkModel;
import com.cloud.network.as.AutoScaleCounter.AutoScaleCounterParam;
import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao;
import com.cloud.network.as.dao.AutoScalePolicyDao;
@ -146,7 +146,9 @@ import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.server.ResourceTag;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.template.TemplateManager;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
@ -280,6 +282,8 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
private NetworkOfferingDao networkOfferingDao;
@Inject
private VirtualMachineManager virtualMachineManager;
@Inject
GuestOSDao guestOSDao;
private static final String PARAM_ROOT_DISK_SIZE = "rootdisksize";
private static final String PARAM_DISK_OFFERING_ID = "diskofferingid";
@ -296,6 +300,10 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
protected static final String VM_HOSTNAME_PREFIX = "autoScaleVm-";
protected static final int VM_HOSTNAME_RANDOM_SUFFIX_LENGTH = 6;
// Windows OS has a limit of 15 characters for hostname
// https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/naming-conventions-for-computer-domain-site-ou
protected static final String WINDOWS_VM_HOSTNAME_PREFIX = "as-WinVm-";
private static final Long DEFAULT_HOST_ID = -1L;
ExecutorService groupExecutor;
@ -1821,13 +1829,15 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
List<Long> affinityGroupIdList = getVmAffinityGroupId(deployParams);
updateVmDetails(deployParams, customParameters);
String vmHostName = getNextVmHostName(asGroup);
Pair<String, String> vmHostAndDisplayName = getNextVmHostAndDisplayName(asGroup, template);
String vmHostName = vmHostAndDisplayName.first();
String vmDisplayName = vmHostAndDisplayName.second();
asGroup.setNextVmSeq(asGroup.getNextVmSeq() + 1);
autoScaleVmGroupDao.persist(asGroup);
if (zone.getNetworkType() == NetworkType.Basic) {
vm = userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, vmHostName,
vmHostName, diskOfferingId, dataDiskSize, null, null,
vmDisplayName, diskOfferingId, dataDiskSize, null, null,
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
null, null, true, null, affinityGroupIdList, customParameters, null, null, null,
null, true, overrideDiskOfferingId, null, null);
@ -1835,12 +1845,12 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
if (networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
Collections.emptyList())) {
vm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, null,
owner, vmHostName,vmHostName, diskOfferingId, dataDiskSize, null, null,
owner, vmHostName, vmDisplayName, diskOfferingId, dataDiskSize, null, null,
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
null, null, true, null, affinityGroupIdList, customParameters, null, null, null,
null, true, overrideDiskOfferingId, null, null, null);
} else {
vm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, vmHostName, vmHostName,
vm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, vmHostName, vmDisplayName,
diskOfferingId, dataDiskSize, null, null,
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
null, addrs, true, null, affinityGroupIdList, customParameters, null, null, null,
@ -1965,13 +1975,29 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
}
}
@Override
public String getNextVmHostName(AutoScaleVmGroupVO asGroup) {
String vmHostNameSuffix = "-" + asGroup.getNextVmSeq() + "-" +
RandomStringUtils.random(VM_HOSTNAME_RANDOM_SUFFIX_LENGTH, 0, 0, true, false, (char[])null, new SecureRandom()).toLowerCase();
protected boolean isWindowsOs(VirtualMachineTemplate template) {
GuestOSVO guestOSVO = guestOSDao.findById(template.getGuestOSId());
if (guestOSVO == null) {
return false;
}
String osName = StringUtils.firstNonBlank(guestOSVO.getName(), guestOSVO.getDisplayName());
if (StringUtils.isBlank(osName)) {
return false;
}
return osName.toLowerCase().contains("windows");
}
protected Pair<String, String> getNextVmHostAndDisplayName(AutoScaleVmGroupVO asGroup, VirtualMachineTemplate template) {
boolean isWindows = isWindowsOs(template);
String winVmHostNameSuffix = RandomStringUtils.random(VM_HOSTNAME_RANDOM_SUFFIX_LENGTH, 0, 0, true, false, (char[])null, new SecureRandom()).toLowerCase();
String vmHostNameSuffix = "-" + asGroup.getNextVmSeq() + "-" + winVmHostNameSuffix;
// Truncate vm group name because max length of vm name is 63
int subStringLength = Math.min(asGroup.getName().length(), 63 - VM_HOSTNAME_PREFIX.length() - vmHostNameSuffix.length());
return VM_HOSTNAME_PREFIX + asGroup.getName().substring(0, subStringLength) + vmHostNameSuffix;
String name = VM_HOSTNAME_PREFIX + asGroup.getName().substring(0, subStringLength) + vmHostNameSuffix;
if (!isWindows) {
return new Pair<>(name, name);
}
return new Pair<>(WINDOWS_VM_HOSTNAME_PREFIX + winVmHostNameSuffix, name);
}
@Override

View File

@ -135,8 +135,10 @@ import com.cloud.server.ResourceTag;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
@ -259,9 +261,10 @@ public class AutoScaleManagerImplTest {
LoadBalancingRulesService loadBalancingRulesService;
@Mock
VMInstanceDao vmInstanceDao;
@Mock
VirtualMachineManager virtualMachineManager;
@Mock
GuestOSDao guestOSDao;
AccountVO account;
UserVO user;
@ -420,6 +423,11 @@ public class AutoScaleManagerImplTest {
userDataDetails.put("0", new HashMap<>() {{ put("key1", "value1"); put("key2", "value2"); }});
Mockito.doReturn(userDataFinal).when(userVmMgr).finalizeUserData(any(), any(), any());
Mockito.doReturn(userDataFinal).when(userDataMgr).validateUserData(eq(userDataFinal), nullable(BaseCmd.HTTPMethod.class));
when(templateMock.getGuestOSId()).thenReturn(100L);
GuestOSVO guestOSMock = Mockito.mock(GuestOSVO.class);
when(guestOSDao.findById(anyLong())).thenReturn(guestOSMock);
when(guestOSMock.getName()).thenReturn("linux");
}
@After
@ -2495,4 +2503,68 @@ public class AutoScaleManagerImplTest {
Mockito.verify(userVmMgr).expunge(eq(userVmMock));
}
@Test
public void getNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForLinuxTemplate() {
when(asVmGroupMock.getName()).thenReturn(vmGroupName);
when(asVmGroupMock.getNextVmSeq()).thenReturn(1L);
Pair<String, String> result = autoScaleManagerImplSpy.getNextVmHostAndDisplayName(asVmGroupMock, templateMock);
String vmHostNamePattern = AutoScaleManagerImpl.VM_HOSTNAME_PREFIX + vmGroupName +
"-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
Assert.assertTrue(result.first().matches(vmHostNamePattern));
Assert.assertEquals(result.first(), result.second());
}
private void runGetNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplate() {
when(asVmGroupMock.getName()).thenReturn(vmGroupName);
when(asVmGroupMock.getNextVmSeq()).thenReturn(1L);
when(templateMock.getGuestOSId()).thenReturn(1L);
Pair<String, String> result = autoScaleManagerImplSpy.getNextVmHostAndDisplayName(asVmGroupMock, templateMock);
String vmHostNamePattern = AutoScaleManagerImpl.WINDOWS_VM_HOSTNAME_PREFIX + "[a-z]{6}";
Assert.assertTrue(result.first().matches(vmHostNamePattern));
Assert.assertEquals(15, result.first().length());
String vmDisplayHostNamePattern = AutoScaleManagerImpl.VM_HOSTNAME_PREFIX + vmGroupName +
"-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
Assert.assertTrue(result.second().matches(vmDisplayHostNamePattern));
Assert.assertTrue(result.second().length() <= 63);
Assert.assertTrue(result.second().endsWith(result.first().split("-")[2]));
}
@Test
public void getNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplateUsingGuestOsName() {
GuestOSVO guestOS = Mockito.mock(GuestOSVO.class);
when(guestOS.getName()).thenReturn("Windows Server");
when(guestOSDao.findById(1L)).thenReturn(guestOS);
runGetNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplate();
}
@Test
public void getNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplateUsingGuestOsDisplayName() {
GuestOSVO guestOS = Mockito.mock(GuestOSVO.class);
when(guestOS.getDisplayName()).thenReturn("Windows Server");
when(guestOSDao.findById(1L)).thenReturn(guestOS);
runGetNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplate();
}
@Test
public void getNextVmHostAndDisplayNameTruncatesGroupNameWhenExceedingMaxLength() {
when(asVmGroupMock.getName()).thenReturn(vmGroupNameWithMaxLength);
when(asVmGroupMock.getNextVmSeq()).thenReturn(1L);
Pair<String, String> result = autoScaleManagerImplSpy.getNextVmHostAndDisplayName(asVmGroupMock, templateMock);
Assert.assertTrue(result.first().length() <= 63);
Assert.assertTrue(result.second().length() <= 63);
}
@Test
public void getNextVmHostAndDisplayNameHandlesNullGuestOS() {
when(asVmGroupMock.getName()).thenReturn(vmGroupName);
when(asVmGroupMock.getNextVmSeq()).thenReturn(1L);
when(templateMock.getGuestOSId()).thenReturn(1L);
when(guestOSDao.findById(1L)).thenReturn(null);
Pair<String, String> result = autoScaleManagerImplSpy.getNextVmHostAndDisplayName(asVmGroupMock, templateMock);
String vmHostNamePattern = AutoScaleManagerImpl.VM_HOSTNAME_PREFIX + vmGroupName +
"-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
Assert.assertTrue(result.first().matches(vmHostNamePattern));
Assert.assertEquals(result.first(), result.second());
}
}