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 <[email protected]>
AuthorDate: Thu Oct 12 10:16:37 2023 -0300

    Allow password definition during VM deploy (#6947)
    
    Co-authored-by: Stephan Krug <[email protected]>
    Co-authored-by: dahn <[email protected]>
    Co-authored-by: GaOrtiga <[email protected]>
    Co-authored-by: Gabriel Pordeus Santos <[email protected]>
    Co-authored-by: Gabriel <[email protected]>
---
 .../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());
+    }
 }

Reply via email to