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 @@ -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,
Expand Down Expand Up @@ -63,7 +64,5 @@ public interface AutoScaleManager extends AutoScaleService {

void removeVmFromVmGroup(Long vmId);

String getNextVmHostName(AutoScaleVmGroupVO asGroup);

void checkAutoScaleVmGroupName(String groupName);
}
46 changes: 36 additions & 10 deletions server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

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;
Expand Down Expand Up @@ -113,6 +112,7 @@
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;
Expand Down Expand Up @@ -146,7 +146,9 @@
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;
Expand Down Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -1821,26 +1829,28 @@ protected UserVm createNewVM(AutoScaleVmGroupVO asGroup) {
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);
} else {
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,
Expand Down Expand Up @@ -1965,13 +1975,29 @@ public void updateVmDetails(Map<String, String> deployParams, Map<String, String
}
}

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@
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;
Expand Down Expand Up @@ -259,9 +261,10 @@ public class AutoScaleManagerImplTest {
LoadBalancingRulesService loadBalancingRulesService;
@Mock
VMInstanceDao vmInstanceDao;

@Mock
VirtualMachineManager virtualMachineManager;
@Mock
GuestOSDao guestOSDao;

AccountVO account;
UserVO user;
Expand Down Expand Up @@ -420,6 +423,11 @@ public void setUp() {
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
Expand Down Expand Up @@ -2495,4 +2503,68 @@ public void destroyVm() {

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());
}
}
Loading