diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 953727f9b347..563721d1e888 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -20,11 +20,11 @@ import java.util.List; import java.util.Map; -import com.cloud.dc.DataCenter; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; +import com.cloud.dc.DataCenter; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; @@ -50,8 +50,8 @@ import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.user.User; -import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.Pair; +import com.cloud.utils.fsm.NoTransitionException; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; @@ -348,4 +348,6 @@ void implementNetworkElementsAndResources(DeployDestination dest, ReservationCon Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter datacenter, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException; void unmanageNics(VirtualMachineProfile vm); + + boolean isNetworkImplemented(final Network network); } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 26b63d2d728b..a45c832f2c4c 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.engine.orchestration; +import static com.cloud.configuration.ConfigurationManager.MESSAGE_DELETE_VLAN_IP_RANGE_EVENT; + import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -241,8 +243,8 @@ import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; @@ -255,8 +257,6 @@ import com.cloud.vm.dao.VMInstanceDao; import com.googlecode.ipv6.IPv6Address; -import static com.cloud.configuration.ConfigurationManager.MESSAGE_DELETE_VLAN_IP_RANGE_EVENT; - /** * NetworkManagerImpl implements NetworkManager. */ @@ -1339,20 +1339,6 @@ protected NicTO toNicTO(final NicVO nic, final NicProfile profile, final Network return to; } - boolean isNetworkImplemented(final NetworkVO network) { - final Network.State state = network.getState(); - final NetworkOfferingVO offeringVO = _networkOfferingDao.findById(network.getNetworkOfferingId()); - if (state == Network.State.Implemented) { - return true; - } else if (state == Network.State.Setup) { - final DataCenterVO zone = _dcDao.findById(network.getDataCenterId()); - if ((!isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) && !offeringVO.isPersistent()) || zone.getNetworkType() == NetworkType.Basic) { - return true; - } - } - return false; - } - Pair implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context, final boolean isRouter) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { Pair implemented = null; @@ -1450,6 +1436,20 @@ private boolean networkMeetsPersistenceCriteria(NetworkVO network, NetworkOfferi } } + @Override + public boolean isNetworkImplemented(final Network network) { + final Network.State state = network.getState(); + final NetworkOfferingVO offeringVO = _networkOfferingDao.findById(network.getNetworkOfferingId()); + if (state == Network.State.Implemented) { + return true; + } else if (state == Network.State.Setup) { + final DataCenterVO zone = _dcDao.findById(network.getDataCenterId()); + return (!isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) && !offeringVO.isPersistent()) + || zone.getNetworkType() == NetworkType.Basic; + } + return false; + } + @Override @DB public Pair implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException, diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java index 15238dc1c3b4..b2549811bacd 100644 --- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java @@ -74,7 +74,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateListener; @@ -83,6 +82,7 @@ import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; @@ -137,6 +137,8 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle ImageStoreDao imgstore; @Inject private HypervisorGuruManager _hvGuruMgr; + @Inject + UserVmManager userVmManager; private final static Integer CONFIGDRIVEDISKSEQ = 4; @@ -291,13 +293,7 @@ public boolean saveUserData(final Network network, final NicProfile nic, final V */ private void storePasswordInVmDetails(VirtualMachineProfile vm) { final String password = (String) vm.getParameter(VirtualMachineProfile.Param.VmPassword); - final String password_encrypted = DBEncryptionUtil.encrypt(password); - final UserVmVO userVmVO = _userVmDao.findById(vm.getId()); - - _userVmDetailsDao.addDetail(vm.getId(), VmDetailConstants.PASSWORD, password_encrypted, false); - - userVmVO.setUpdateParameters(true); - _userVmDao.update(userVmVO.getId(), userVmVO); + userVmManager.storePasswordInVmDetails(vm.getId(), password); } @Override diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index 334cfb59073f..ab1b28ac95e3 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -24,17 +24,6 @@ import javax.inject.Inject; -import org.apache.commons.collections.CollectionUtils; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -import com.cloud.storage.dao.VMTemplateDao; -import com.cloud.vm.VirtualMachineProfileImpl; -import com.cloud.vm.VmDetailConstants; -import com.cloud.vm.dao.NicDao; -import com.google.gson.Gson; - import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd; import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd; import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd; @@ -45,6 +34,10 @@ import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinitionBuilder; import org.apache.cloudstack.network.topology.NetworkTopology; import org.apache.cloudstack.network.topology.NetworkTopologyContext; +import org.apache.commons.collections.CollectionUtils; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.configuration.ConfigurationManager; @@ -99,10 +92,10 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.exception.CloudRuntimeException; @@ -115,8 +108,11 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfileImpl; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; +import com.google.gson.Gson; public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, @@ -729,15 +725,7 @@ public boolean savePassword(final Network network, final NicProfile nic, final V } final String password = (String) uservm.getParameter(VirtualMachineProfile.Param.VmPassword); - final String password_encrypted = DBEncryptionUtil.encrypt(password); - final UserVmVO userVmVO = _userVmDao.findById(vm.getId()); - - _userVmDao.loadDetails(userVmVO); - userVmVO.setDetail(VmDetailConstants.PASSWORD, password_encrypted); - _userVmDao.saveDetails(userVmVO); - - userVmVO.setUpdateParameters(true); - _userVmDao.update(userVmVO.getId(), userVmVO); + _userVmMgr.storePasswordInVmDetails(uservm.getId(), password); return true; } diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index 7cd1a98fd743..efb440d2a22d 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -131,4 +131,6 @@ UserVm updateVirtualMachine(long id, String displayName, String group, Boolean h Boolean getDestroyRootVolumeOnVmDestruction(Long domainId); + void storePasswordInVmDetails(long vmId, String password); + } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 173cec83d71a..1b94bbc3c6ca 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -129,8 +129,8 @@ import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.ParserUtils; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; import org.apache.cloudstack.vm.UnmanagedVMsManager; +import org.apache.cloudstack.vm.schedule.VMScheduleManager; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.math.NumberUtils; @@ -874,67 +874,108 @@ public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws Re return userVm; } - private boolean resetVMPasswordInternal(Long vmId, String password) throws ResourceUnavailableException, InsufficientCapacityException { - Long userId = CallContext.current().getCallingUserId(); - VMInstanceVO vmInstance = _vmDao.findById(vmId); - - if (password == null || password.equals("")) { + protected boolean updateVMPasswordInNetworkElement(UserVmVO vm, VMTemplateVO template, String password) throws ResourceUnavailableException { + Nic defaultNic = _networkModel.getDefaultNic(vm.getId()); + if (defaultNic == null) { + s_logger.error(String.format("Unable to update password for %s in network element as the instance doesn't have default nic", + vm)); return false; } + Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); + NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, + null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), + _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork)); + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); + vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); - if (template.isEnablePassword()) { - Nic defaultNic = _networkModel.getDefaultNic(vmId); - if (defaultNic == null) { - s_logger.error("Unable to reset password for vm " + vmInstance + " as the instance doesn't have default nic"); - return false; - } - - Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); - NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), - _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork)); - VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); - vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); + UserDataServiceProvider element = _networkMgr.getPasswordResetProvider(defaultNetwork); + if (element == null) { + throw new CloudRuntimeException(String.format("Can't find network element for %s provider needed for password update", Service.UserData.getName())); + } + if (State.Stopped.equals(vm.getState()) && + Arrays.asList(GuestType.Shared, GuestType.Isolated).contains(defaultNetwork.getGuestType()) && + !_networkMgr.isNetworkImplemented(defaultNetwork)) { + s_logger.debug(String.format("%s is not ready therefore storing in VM details for now", defaultNetwork)); + storePasswordInVmDetails(vm, password); + return true; + } + return element.savePassword(defaultNetwork, defaultNicProfile, vmProfile); + } - UserDataServiceProvider element = _networkMgr.getPasswordResetProvider(defaultNetwork); - if (element == null) { - throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for password reset"); + /** + * Sets or resets the password for a virtual machine (VM) based on the template's password configuration. + * + *

+ * If the template does not have password support enabled, the operation is aborted. If the VM is running, + * the method ensures that the password is updated in the network element and then reboots the VM to + * redownload and apply the new password. If the VM is stopped, it avoids rebooting. The new password + * is also securely encrypted and stored. + *

+ * + * @param vm the virtual machine whose password is to be set or reset + * @param template the VM template associated with the virtual machine + * @param password the new password to be set for the virtual machine + * @return {@code true} if the password was successfully set and the VM rebooted if necessary; + * {@code false} if the operation failed or the template does not support password reset + * @throws ResourceUnavailableException if a required network resource is unavailable + * @throws InsufficientCapacityException if there is insufficient capacity to perform the operation + */ + protected boolean setOrResetVMPassword(UserVmVO vm, VMTemplateVO template, String password) + throws ResourceUnavailableException, InsufficientCapacityException { + if (!template.isEnablePassword()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Reset password called for a vm that is not using a password enabled template"); } - - boolean result = element.savePassword(defaultNetwork, defaultNicProfile, vmProfile); - - // Need to reboot the virtual machine so that the password gets - // redownloaded from the DomR, and reset on the VM - if (!result) { - s_logger.debug("Failed to reset password for the virtual machine; no need to reboot the vm"); + return false; + } + boolean result = updateVMPasswordInNetworkElement(vm, template, password); + if (!result) { + s_logger.debug("Failed to update password for the virtual machine in the network element"); + return false; + } else { + _vmDao.loadDetails(vm); + encryptAndStorePassword(vm, password); + if (vm.getState() == State.Stopped) { + s_logger.debug(vm + " is stopped, not rebooting it as a part of password reset"); + return true; + } + long userId = CallContext.current().getCallingUserId(); + if (rebootVirtualMachine(userId, vm.getId(), false, false) == null) { + s_logger.warn("Failed to reboot the vm " + vm); return false; } else { - final UserVmVO userVm = _vmDao.findById(vmId); - _vmDao.loadDetails(userVm); - // update the password in vm_details table too - // Check if an SSH key pair was selected for the instance and if so - // use it to encrypt & save the vm password - encryptAndStorePassword(userVm, password); - - if (vmInstance.getState() == State.Stopped) { - s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of password reset"); - return true; - } - - if (rebootVirtualMachine(userId, vmId, false, false) == null) { - s_logger.warn("Failed to reboot the vm " + vmInstance); - return false; - } else { - s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of password reset"); - return true; - } - } - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Reset password called for a vm that is not using a password enabled template"); + s_logger.debug(vm + " is rebooted successfully as a part of password reset"); + return true; } + } + } + + protected void setStoppedVMPasswordDuringFinalizingCreate(UserVmVO userVm) { + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId()); + if (template == null || !template.isEnablePassword()) { + return; + } + s_logger.debug(String.format("Generating a random password for %s", userVm)); + String password = _mgr.generateRandomPassword(); + boolean result = false; + try { + result = setOrResetVMPassword(userVm, template, password); + } catch (ResourceUnavailableException | InsufficientCapacityException | CloudRuntimeException e) { + s_logger.error(String.format("Setting password during VM creation failed due to: %s", e.getMessage()), e); + } + if (!result) { + throw new CloudRuntimeException("Unable to set password for the VM"); + } + userVm.setPassword(password); + } + + private boolean resetVMPasswordInternal(Long vmId, String password) throws ResourceUnavailableException, InsufficientCapacityException { + UserVmVO vmInstance = _vmDao.findById(vmId); + if (StringUtils.isEmpty(password)) { return false; } + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + return setOrResetVMPassword(vmInstance, template, password); } @Override @@ -3805,11 +3846,12 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm") public UserVm finalizeCreateVirtualMachine(long vmId) { s_logger.info("Loading UserVm " + vmId + " from DB"); - UserVm userVm = getUserVm(vmId); + UserVmVO userVm = _vmDao.findById(vmId); if (userVm == null) { - s_logger.info("Loaded UserVm " + vmId + " (" + userVm.getUuid() + ") from DB"); - } else { s_logger.warn("UserVm " + vmId + " does not exist in DB"); + } else { + s_logger.info("Loaded UserVm " + vmId + " (" + userVm.getUuid() + ") from DB"); + setStoppedVMPasswordDuringFinalizingCreate(userVm); } return userVm; } @@ -8579,4 +8621,16 @@ private void setVncPasswordForKvmIfAvailable(Map customParameter vm.setVncPassword(customParameters.get(VmDetailConstants.KVM_VNC_PASSWORD)); } } + + protected void storePasswordInVmDetails(UserVmVO userVmVO, String password) { + final String password_encrypted = DBEncryptionUtil.encrypt(password); + userVmDetailsDao.addDetail(userVmVO.getId(), VmDetailConstants.PASSWORD, password_encrypted, false); + userVmVO.setUpdateParameters(true); + _vmDao.update(userVmVO.getId(), userVmVO); + } + + @Override + public void storePasswordInVmDetails(long vmId, String password) { + storePasswordInVmDetails(_vmDao.findById(vmId), password); + } } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index ba9e5ba69225..3c9280c53e78 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -16,6 +16,53 @@ // under the License. package com.cloud.vm; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.BaseCmd.HTTPMethod; +import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; +import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd; +import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd; +import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.template.VnfTemplateManager; +import org.apache.cloudstack.userdata.UserDataManager; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + import com.cloud.api.query.dao.ServiceOfferingJoinDao; import com.cloud.api.query.vo.ServiceOfferingJoinVO; import com.cloud.configuration.Resource; @@ -37,9 +84,11 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.Network; import com.cloud.network.NetworkModel; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.security.SecurityGroupVO; import com.cloud.offering.ServiceOffering; import com.cloud.server.ManagementService; @@ -72,6 +121,7 @@ import com.cloud.user.dao.UserDataDao; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.dao.NicDao; @@ -80,52 +130,6 @@ import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; -import org.apache.cloudstack.api.BaseCmd.HTTPMethod; -import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; -import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd; -import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd; -import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; -import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; -import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.template.VnfTemplateManager; -import org.apache.cloudstack.userdata.UserDataManager; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.when; - @RunWith(MockitoJUnitRunner.class) public class UserVmManagerImplTest { @@ -1339,4 +1343,197 @@ public void testRestoreVirtualMachineWithVMSnapshots() throws ResourceUnavailabl userVmManagerImpl.restoreVirtualMachine(accountMock, vmId, newTemplateId, null, false, null); } + + @Test + public void testUpdateVMPasswordInNetworkElement_noDefaultNic() { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Mockito.when(networkModel.getDefaultNic(Mockito.anyLong())).thenReturn(null); + boolean result = false; + try { + result = userVmManagerImpl.updateVMPasswordInNetworkElement(vm, Mockito.mock(VMTemplateVO.class), "pass"); + } catch (ResourceUnavailableException e) { + Assert.fail(e.getMessage()); + } + Assert.assertFalse(result); + } + + @Test + public void testUpdateVMPasswordInNetworkElement_shouldThrowException() { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Nic defaultNic = Mockito.mock(Nic.class); + Mockito.when(vm.getId()).thenReturn(1L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + NetworkVO defaultNetwork = Mockito.mock(NetworkVO.class); + Mockito.when(networkModel.getDefaultNic(vm.getId())).thenReturn(defaultNic); + Mockito.when(defaultNic.getNetworkId()).thenReturn(1L); + Mockito.when(_networkDao.findById(1L)).thenReturn(defaultNetwork); + Mockito.when(template.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)).thenReturn(false); + Mockito.when(networkModel.getNetworkTag(Mockito.any(), Mockito.any())).thenReturn("tag"); + Mockito.when(_networkMgr.getPasswordResetProvider(defaultNetwork)).thenReturn(null); + Assert.assertThrows(CloudRuntimeException.class, () -> + userVmManagerImpl.updateVMPasswordInNetworkElement(vm, template, "pass") + ); + } + + @Test + public void testUpdateVMPasswordInNetworkElement_shouldStorePasswordAndReturnTrue() throws Exception { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Nic defaultNic = Mockito.mock(Nic.class); + Mockito.when(vm.getId()).thenReturn(1L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + NetworkVO defaultNetwork = Mockito.mock(NetworkVO.class); + Mockito.when(networkModel.getDefaultNic(vm.getId())).thenReturn(defaultNic); + Mockito.when(defaultNic.getNetworkId()).thenReturn(1L); + Mockito.when(_networkDao.findById(1L)).thenReturn(defaultNetwork); + Mockito.when(template.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)).thenReturn(false); + Mockito.when(networkModel.getNetworkTag(Mockito.any(), Mockito.any())).thenReturn("tag"); + UserDataServiceProvider element = Mockito.mock(UserDataServiceProvider.class); + Mockito.when(_networkMgr.getPasswordResetProvider(defaultNetwork)).thenReturn(element); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(defaultNetwork.getGuestType()).thenReturn(Network.GuestType.Isolated); + Mockito.when(_networkMgr.isNetworkImplemented(defaultNetwork)).thenReturn(false); + boolean result = userVmManagerImpl.updateVMPasswordInNetworkElement(vm, template, "secret"); + Mockito.verify(userVmManagerImpl, Mockito.times(1)).storePasswordInVmDetails(vm, "secret"); + Assert.assertTrue(result); + } + + @Test + public void testUpdateVMPasswordInNetworkElement_shouldReturnTrue() throws Exception { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Nic defaultNic = Mockito.mock(Nic.class); + Mockito.when(vm.getId()).thenReturn(1L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + NetworkVO defaultNetwork = Mockito.mock(NetworkVO.class); + Mockito.when(networkModel.getDefaultNic(vm.getId())).thenReturn(defaultNic); + Mockito.when(defaultNic.getNetworkId()).thenReturn(1L); + Mockito.when(_networkDao.findById(1L)).thenReturn(defaultNetwork); + Mockito.when(template.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)).thenReturn(false); + Mockito.when(networkModel.getNetworkTag(Mockito.any(), Mockito.any())).thenReturn("tag"); + UserDataServiceProvider element = Mockito.mock(UserDataServiceProvider.class); + Mockito.when(_networkMgr.getPasswordResetProvider(defaultNetwork)).thenReturn(element); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(defaultNetwork.getGuestType()).thenReturn(Network.GuestType.Isolated); + Mockito.when(_networkMgr.isNetworkImplemented(defaultNetwork)).thenReturn(true); + Mockito.when(element.savePassword(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true); + boolean result = userVmManagerImpl.updateVMPasswordInNetworkElement(vm, template, "secret"); + Assert.assertTrue(result); + } + + public void runTestUpdateVMPasswordInNetworkElementResult(boolean result) throws Exception { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Nic defaultNic = Mockito.mock(Nic.class); + Mockito.when(vm.getId()).thenReturn(1L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + NetworkVO defaultNetwork = Mockito.mock(NetworkVO.class); + Mockito.when(networkModel.getDefaultNic(vm.getId())).thenReturn(defaultNic); + Mockito.when(defaultNic.getNetworkId()).thenReturn(1L); + Mockito.when(_networkDao.findById(1L)).thenReturn(defaultNetwork); + Mockito.when(template.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)).thenReturn(false); + Mockito.when(networkModel.getNetworkTag(Mockito.any(), Mockito.any())).thenReturn("tag"); + UserDataServiceProvider element = Mockito.mock(UserDataServiceProvider.class); + Mockito.when(_networkMgr.getPasswordResetProvider(defaultNetwork)).thenReturn(element); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Running); + Mockito.when(element.savePassword(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(result); + Assert.assertEquals(result, userVmManagerImpl.updateVMPasswordInNetworkElement(vm, template, "secret")); + } + + @Test + public void testUpdateVMPasswordInNetworkElement_runningVmShouldReturnFalse() throws Exception { + runTestUpdateVMPasswordInNetworkElementResult(false); + } + + @Test + public void testUpdateVMPasswordInNetworkElement_runningVmShouldReturnTrue() throws Exception { + runTestUpdateVMPasswordInNetworkElementResult(true); + } + + @Test + public void testStorePasswordInVmDetails() { + String vmPassword = "test-password"; + long vmId = 42L; + String encrypted = "encrypted-password"; + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(vmId); + try(MockedStatic mocked = Mockito.mockStatic(DBEncryptionUtil.class)) { + mocked.when(() -> DBEncryptionUtil.encrypt(vmPassword)).thenReturn(encrypted); + userVmManagerImpl.storePasswordInVmDetails(vm, vmPassword); + Mockito.verify(userVmDetailsDao).addDetail(vmId, VmDetailConstants.PASSWORD, encrypted, false); + Mockito.verify(vm).setUpdateParameters(true); + Mockito.verify(userVmDao).update(vmId, vm); + } + } + + @Test + public void testSetStoppedVMPasswordDuringFinalizingCreate_templateNull() { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getTemplateId()).thenReturn(123L); + Mockito.when(templateDao.findByIdIncludingRemoved(123L)).thenReturn(null); + userVmManagerImpl.setStoppedVMPasswordDuringFinalizingCreate(vm); + Mockito.verifyNoInteractions(managementServiceMock); + } + + @Test + public void testSetStoppedVMPasswordDuringFinalizingCreate_templateNotPasswordEnabled() { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getTemplateId()).thenReturn(123L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(templateDao.findByIdIncludingRemoved(123L)).thenReturn(template); + Mockito.when(template.isEnablePassword()).thenReturn(false); + userVmManagerImpl.setStoppedVMPasswordDuringFinalizingCreate(vm); + Mockito.verifyNoInteractions(managementServiceMock); + } + + @Test + public void testSetPasswordSuccess_shouldSetPassword() throws Exception { + String randomPassword = "random123"; + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getTemplateId()).thenReturn(123L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(templateDao.findByIdIncludingRemoved(123L)).thenReturn(template); + Mockito.when(template.isEnablePassword()).thenReturn(true); + Mockito.when(managementServiceMock.generateRandomPassword()).thenReturn(randomPassword); + Mockito.doReturn(true).when(userVmManagerImpl).setOrResetVMPassword(vm, template, randomPassword); + userVmManagerImpl.setStoppedVMPasswordDuringFinalizingCreate(vm); + Mockito.verify(vm).setPassword(randomPassword); + } + + @Test + public void testSetPasswordFails_shouldThrowException() throws Exception { + String randomPassword = "random123"; + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getTemplateId()).thenReturn(123L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(templateDao.findByIdIncludingRemoved(123L)).thenReturn(template); + Mockito.when(template.isEnablePassword()).thenReturn(true); + Mockito.when(managementServiceMock.generateRandomPassword()).thenReturn(randomPassword); + Mockito.doReturn(false).when(userVmManagerImpl).setOrResetVMPassword(vm, template, randomPassword); + Assert.assertThrows(CloudRuntimeException.class, () -> + userVmManagerImpl.setStoppedVMPasswordDuringFinalizingCreate(vm) + ); + } + + @Test + public void testSetPasswordThrowsException_shouldThrowRuntime() throws Exception { + String randomPassword = "random123"; + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getTemplateId()).thenReturn(123L); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(templateDao.findByIdIncludingRemoved(123L)).thenReturn(template); + Mockito.when(template.isEnablePassword()).thenReturn(true); + Mockito.when(managementServiceMock.generateRandomPassword()).thenReturn(randomPassword); + Mockito.doThrow(new ResourceUnavailableException("unavailable", Network.class, 1L)) + .when(userVmManagerImpl).setOrResetVMPassword(vm, template, randomPassword); + Assert.assertThrows(CloudRuntimeException.class, () -> + userVmManagerImpl.setStoppedVMPasswordDuringFinalizingCreate(vm) + ); + } } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index c6762b2a2a15..3815a545f172 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -1090,4 +1090,9 @@ public PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpC public void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd) { } + + @Override + public boolean isNetworkImplemented(Network network) { + return false; + } }