Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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);
}
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 Down Expand Up @@ -1810,26 +1814,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 @@ -1954,13 +1960,27 @@ public void updateVmDetails(Map<String, String> deployParams, Map<String, String
}
}

@Override
public String getNextVmHostName(AutoScaleVmGroupVO asGroup) {
protected Pair<String, String> getNextVmHostAndDisplayName(AutoScaleVmGroupVO asGroup, VirtualMachineTemplate template) {
template.getGuestOSId();
GuestOSVO guestOSVO = guestOSDao.findById(template.getGuestOSId());
boolean isWindows = guestOSVO != null && guestOSVO.getName().toLowerCase().contains("windows");
String vmHostNameSuffix = "-" + asGroup.getNextVmSeq() + "-" +
RandomStringUtils.random(VM_HOSTNAME_RANDOM_SUFFIX_LENGTH, 0, 0, true, false, (char[])null, new SecureRandom()).toLowerCase();
// 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);
}
String hostName = name.substring(Math.max(0, name.length() - 15));
if (Character.isLetterOrDigit(hostName.charAt(0))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems digit it not supported as first char ? @shwstppr

public void checkNameForRFCCompliance(String name) {
if (!NetUtils.verifyDomainNameLabel(name, true)) {
throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', "
+ "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit");
}
}

return new Pair<>(hostName, name);
}
String temp = name.substring(0, Math.max(0, name.length() - 15)).replaceAll("[^a-zA-Z0-9]", "");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edge case: all of the last 15 chars are special chars or whitespace. I think we better do a replace first and then take the last 15.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaanHoogland I don't think this case can happen.
The only user-defined part in the VM name is the name of the autoscale group, and we've checks for that to only contain letters, numbers and hyphen. checkAutoScaleVmGroupName` does that

if (!temp.isEmpty()) {
return new Pair<>(temp.charAt(temp.length() - 1) + hostName.substring(1), name);
}
return new Pair<>('a' + hostName.substring(1), 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,53 @@ 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());
}

@Test
public void getNextVmHostAndDisplayNameGeneratesCorrectHostAndDisplayNameForWindowsTemplate() {
GuestOSVO guestOS = Mockito.mock(GuestOSVO.class);
when(asVmGroupMock.getName()).thenReturn(vmGroupName);
when(asVmGroupMock.getNextVmSeq()).thenReturn(1L);
when(templateMock.getGuestOSId()).thenReturn(1L);
when(guestOS.getName()).thenReturn("Windows Server");
when(guestOSDao.findById(1L)).thenReturn(guestOS);
Pair<String, String> result = autoScaleManagerImplSpy.getNextVmHostAndDisplayName(asVmGroupMock, templateMock);
String vmHostNamePattern = AutoScaleManagerImpl.VM_HOSTNAME_PREFIX + vmGroupName +
"-" + asVmGroupMock.getNextVmSeq() + "-[a-z]{6}";
Assert.assertTrue(result.second().matches(vmHostNamePattern));
Assert.assertEquals(15, result.first().length());
Assert.assertTrue(result.second().endsWith(result.first()));
}

@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