You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2023/10/12 13:16:45 UTC
[cloudstack] branch main updated: Allow password definition during VM deploy (#6947)
This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 0c14e4603d8 Allow password definition during VM deploy (#6947)
0c14e4603d8 is described below
commit 0c14e4603d80f6db909218602dbdc0c9b801bda0
Author: Stephan Krug <st...@icloud.com>
AuthorDate: Thu Oct 12 10:16:37 2023 -0300
Allow password definition during VM deploy (#6947)
Co-authored-by: Stephan Krug <st...@scclouds.com.br>
Co-authored-by: dahn <da...@gmail.com>
Co-authored-by: GaOrtiga <49...@users.noreply.github.com>
Co-authored-by: Gabriel Pordeus Santos <ga...@gmail.com>
Co-authored-by: Gabriel <ga...@scclouds.com.br>
---
.../api/command/user/vm/DeployVMCmd.java | 8 +++
.../com/cloud/network/as/AutoScaleManagerImpl.java | 2 +-
.../main/java/com/cloud/vm/UserVmManagerImpl.java | 62 ++++++++++++++++------
.../cloud/network/as/AutoScaleManagerImplTest.java | 3 --
.../java/com/cloud/vm/UserVmManagerImplTest.java | 61 +++++++++++++++++++++
5 files changed, 116 insertions(+), 20 deletions(-)
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index c140a1e1c22..1cbe28f4dde 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -105,6 +105,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
@Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine")
private String displayName;
+ @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.",
+ since="4.19.0.0")
+ protected String password;
+
//Owner information
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.")
private String accountName;
@@ -464,6 +468,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
return zoneId;
}
+ public String getPassword() {
+ return password;
+ }
+
public List<Long> getNetworkIds() {
if (MapUtils.isNotEmpty(vAppNetworks)) {
if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) {
diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
index de8a3ff3c83..2f69ac6e9ba 100644
--- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java
@@ -1964,7 +1964,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
private boolean startNewVM(long vmId) {
try {
CallContext.current().setEventDetails("Vm Id: " + vmId);
- userVmMgr.startVirtualMachine(vmId, null, null, null);
+ userVmMgr.startVirtualMachine(vmId, null, new HashMap<>(), null);
} catch (final ResourceUnavailableException ex) {
s_logger.warn("Exception: ", ex);
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 23be3facd25..e4dc03a72fd 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -135,6 +135,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.w3c.dom.Document;
@@ -1174,9 +1175,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private UserVm forceRebootVirtualMachine(long vmId, long hostId, boolean enterSetup) {
try {
if (stopVirtualMachine(vmId, false) != null) {
- Map<VirtualMachineProfile.Param,Object> params = null;
+ Map<VirtualMachineProfile.Param,Object> params = new HashMap<>();
if (enterSetup) {
- params = new HashMap();
params.put(VirtualMachineProfile.Param.BootIntoSetup, Boolean.TRUE);
}
return startVirtualMachine(vmId, null, null, hostId, params, null, false).first();
@@ -4878,6 +4878,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (cmd.getBootIntoSetup() != null) {
additionalParams.put(VirtualMachineProfile.Param.BootIntoSetup, cmd.getBootIntoSetup());
}
+
+ if (StringUtils.isNotBlank(cmd.getPassword())) {
+ additionalParams.put(VirtualMachineProfile.Param.VmPassword, cmd.getPassword());
+ }
+
return startVirtualMachine(vmId, podId, clusterId, hostId, diskOfferingMap, additionalParams, cmd.getDeploymentPlanner());
}
@@ -5268,21 +5273,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
@Override
- public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
- throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
+ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams,
+ String deploymentPlannerToUse) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
return startVirtualMachine(vmId, null, null, hostId, additionalParams, deploymentPlannerToUse);
}
@Override
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId,
- Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
+ @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true);
}
@Override
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId,
- Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, boolean isExplicitHost)
+ @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, boolean isExplicitHost)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
// Input validation
final Account callerAccount = CallContext.current().getCallingAccount();
@@ -5381,15 +5386,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Check that the password was passed in and is valid
template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
- String password = "saved_password";
- if (template.isEnablePassword()) {
- if (vm.getDetail("password") != null) {
- password = DBEncryptionUtil.decrypt(vm.getDetail("password"));
- } else {
- password = _mgr.generateRandomPassword();
- vm.setPassword(password);
- }
- }
+ String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template);
if (!validPassword(password)) {
throw new InvalidParameterValueException("A valid password for this virtual machine was not provided.");
@@ -5402,7 +5399,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password);
}
- if(null != additionalParams && additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
+ if(additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
if (! HypervisorType.VMware.equals(vm.getHypervisorType())) {
throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType());
}
@@ -5445,6 +5442,39 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return vmParamPair;
}
+ /**
+ * If the template is password enabled and the VM already has a password, returns it.
+ * If the template is password enabled and the VM does not have a password, sets the password to the password defined by the user and returns it. If no password is informed,
+ * sets it to a random password and returns it.
+ * If the template is not password enabled, returns saved_password.
+ * @param newPassword The new password informed by the user in order to set the password of the VM.
+ * @param vm The VM to retrieve the password from.
+ * @param template The template to be checked if the password is enabled or not.
+ * @return The password of the VM or saved_password.
+ */
+ protected String getCurrentVmPasswordOrDefineNewPassword(String newPassword, UserVmVO vm, VMTemplateVO template) {
+ String password = "saved_password";
+
+ if (template.isEnablePassword()) {
+ if (vm.getDetail("password") != null) {
+ s_logger.debug(String.format("Decrypting VM [%s] current password.", vm));
+ password = DBEncryptionUtil.decrypt(vm.getDetail("password"));
+ } else if (StringUtils.isNotBlank(newPassword)) {
+ s_logger.debug(String.format("A password for VM [%s] was informed. Setting VM password to value defined by user.", vm));
+ password = newPassword;
+ vm.setPassword(password);
+ } else {
+ s_logger.debug(String.format("Setting VM [%s] password to a randomly generated password.", vm));
+ password = _mgr.generateRandomPassword();
+ vm.setPassword(password);
+ }
+ } else if (StringUtils.isNotBlank(newPassword)) {
+ s_logger.debug(String.format("A password was informed; however, the template [%s] is not password enabled. Ignoring the parameter.", template));
+ }
+
+ return password;
+ }
+
private Map<VirtualMachineProfile.Param, Object> createParameterInParameterMap(Map<VirtualMachineProfile.Param, Object> params, Map<VirtualMachineProfile.Param, Object> parameterMap, VirtualMachineProfile.Param parameter,
Object parameterValue) {
if (s_logger.isTraceEnabled()) {
diff --git a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
index 0c65f1a4694..aaf0f254d41 100644
--- a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java
@@ -99,7 +99,6 @@ import com.cloud.vm.UserVmService;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
-import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.VmStats;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.UserVmDao;
@@ -1495,8 +1494,6 @@ public class AutoScaleManagerImplTest {
when(autoScaleVmGroupDao.updateState(vmGroupId, AutoScaleVmGroup.State.ENABLED, AutoScaleVmGroup.State.SCALING)).thenReturn(true);
when(autoScaleVmGroupDao.updateState(vmGroupId, AutoScaleVmGroup.State.SCALING, AutoScaleVmGroup.State.ENABLED)).thenReturn(true);
Mockito.doReturn(virtualMachineId).when(autoScaleManagerImplSpy).createNewVM(asVmGroupMock);
- Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVm = Mockito.mock(Pair.class);
- when(userVmMgr.startVirtualMachine(virtualMachineId, null, null, null)).thenReturn(startVm);
when(asVmGroupMock.getLoadBalancerId()).thenReturn(loadBalancerId);
when(lbVmMapDao.listByLoadBalancerId(loadBalancerId)).thenReturn(Arrays.asList(loadBalancerVMMapMock));
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index a58ce358dda..7886e70920c 100644
--- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
@@ -38,6 +38,7 @@ import com.cloud.network.NetworkModel;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.ServiceOffering;
+import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
@@ -213,6 +214,12 @@ public class UserVmManagerImplTest {
@Mock
AccountVO account;
+ @Mock
+ VMTemplateVO vmTemplateVoMock;
+
+ @Mock
+ ManagementService managementServiceMock;
+
@Mock
private ServiceOfferingVO serviceOffering;
@@ -1068,4 +1075,58 @@ public class UserVmManagerImplTest {
Mockito.verify(userVmDao).findById(vmId);
Mockito.verify(userVmDao).update(vmId, userVmVoMock);
}
+
+ @Test
+ public void getCurrentVmPasswordOrDefineNewPasswordTestTemplateIsNotPasswordEnabledReturnPreDefinedString() {
+ String expected = "saved_password";
+
+ Mockito.doReturn(false).when(vmTemplateVoMock).isEnablePassword();
+
+ String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getCurrentVmPasswordOrDefineNewPasswordTestVmHasPasswordReturnCurrentPassword() {
+ String expected = "current_password";
+
+ Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword();
+ Mockito.doReturn(expected).when(userVmVoMock).getDetail("password");
+
+ String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock);
+
+ Assert.assertEquals(expected, result);
+ }
+
+ @Test
+ public void getCurrentVmPasswordOrDefineNewPasswordTestUserDefinedPasswordReturnNewPasswordAndSetVmPassword() {
+ String expected = "new_password";
+
+ Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword();
+ Mockito.doReturn(null).when(userVmVoMock).getDetail("password");
+ Mockito.doCallRealMethod().when(userVmVoMock).setPassword(Mockito.any());
+ Mockito.doCallRealMethod().when(userVmVoMock).getPassword();
+
+ String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword(expected, userVmVoMock, vmTemplateVoMock);
+
+ Assert.assertEquals(expected, result);
+ Assert.assertEquals(expected, userVmVoMock.getPassword());
+ }
+
+ @Test
+ public void getCurrentVmPasswordOrDefineNewPasswordTestUserDefinedPasswordReturnRandomPasswordAndSetVmPassword() {
+ String expected = "random_password";
+
+ Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword();
+ Mockito.doReturn(null).when(userVmVoMock).getDetail("password");
+ Mockito.doReturn(expected).when(managementServiceMock).generateRandomPassword();
+ Mockito.doCallRealMethod().when(userVmVoMock).setPassword(Mockito.any());
+ Mockito.doCallRealMethod().when(userVmVoMock).getPassword();
+
+ String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock);
+
+ Assert.assertEquals(expected, result);
+ Assert.assertEquals(expected, userVmVoMock.getPassword());
+ }
}