// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.resource; import com.cloud.agent.AgentManager; import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; import com.cloud.capacity.dao.CapacityDao; import com.cloud.event.ActionEventUtils; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.StorageManager; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.ssh.SSHCmdHelper; import com.cloud.utils.ssh.SshException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import com.trilead.ssh2.Connection; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.cloud.resource.ResourceState.Event.InternalEnterMaintenance; import static com.cloud.resource.ResourceState.Event.UnableToMigrate; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest({ActionEventUtils.class, ResourceManagerImpl.class, SSHCmdHelper.class}) public class ResourceManagerImplTest { @Mock private CapacityDao capacityDao; @Mock private StorageManager storageManager; @Mock private HighAvailabilityManager haManager; @Mock private UserVmDetailsDao userVmDetailsDao; @Mock private AgentManager agentManager; @Mock private HostDao hostDao; @Mock private VMInstanceDao vmInstanceDao; @Mock private ConfigurationDao configurationDao; @Spy @InjectMocks private ResourceManagerImpl resourceManager = new ResourceManagerImpl(); @Mock private HostVO host; @Mock private VMInstanceVO vm1; @Mock private VMInstanceVO vm2; @Mock private GetVncPortAnswer getVncPortAnswerVm1; @Mock private GetVncPortAnswer getVncPortAnswerVm2; @Mock private GetVncPortCommand getVncPortCommandVm1; @Mock private GetVncPortCommand getVncPortCommandVm2; @Mock private Connection sshConnection; private static long hostId = 1L; private static final String hostUsername = "user"; private static final String hostPassword = "password"; private static final String hostPrivateIp = "192.168.1.10"; private static long vm1Id = 1L; private static String vm1InstanceName = "i-1-VM"; private static long vm2Id = 2L; private static String vm2InstanceName = "i-2-VM"; private static String vm1VncAddress = "10.2.2.2"; private static int vm1VncPort = 5900; private static String vm2VncAddress = "10.2.2.2"; private static int vm2VncPort = 5901; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); when(host.getType()).thenReturn(Host.Type.Routing); when(host.getId()).thenReturn(hostId); when(host.getResourceState()).thenReturn(ResourceState.Enabled); when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); when(host.getClusterId()).thenReturn(1L); when(hostDao.findById(hostId)).thenReturn(host); when(host.getDetail("username")).thenReturn(hostUsername); when(host.getDetail("password")).thenReturn(hostPassword); when(host.getStatus()).thenReturn(Status.Up); when(host.getPrivateIpAddress()).thenReturn(hostPrivateIp); when(vm1.getId()).thenReturn(vm1Id); when(vm2.getId()).thenReturn(vm2Id); when(vm1.getInstanceName()).thenReturn(vm1InstanceName); when(vm2.getInstanceName()).thenReturn(vm2InstanceName); when(vmInstanceDao.listByHostId(hostId)).thenReturn(new ArrayList<>()); when(vmInstanceDao.listVmsMigratingFromHost(hostId)).thenReturn(new ArrayList<>()); when(vmInstanceDao.listNonMigratingVmsByHostEqualsLastHost(hostId)).thenReturn(new ArrayList<>()); PowerMockito.mockStatic(ActionEventUtils.class); BDDMockito.given(ActionEventUtils.onCompletedActionEvent(anyLong(), anyLong(), anyString(), anyString(), anyString(), anyLong())) .willReturn(1L); when(getVncPortAnswerVm1.getAddress()).thenReturn(vm1VncAddress); when(getVncPortAnswerVm1.getPort()).thenReturn(vm1VncPort); when(getVncPortAnswerVm2.getAddress()).thenReturn(vm2VncAddress); when(getVncPortAnswerVm2.getPort()).thenReturn(vm2VncPort); PowerMockito.whenNew(GetVncPortCommand.class).withArguments(vm1Id, vm1InstanceName).thenReturn(getVncPortCommandVm1); PowerMockito.whenNew(GetVncPortCommand.class).withArguments(vm2Id, vm2InstanceName).thenReturn(getVncPortCommandVm2); when(agentManager.easySend(eq(hostId), eq(getVncPortCommandVm1))).thenReturn(getVncPortAnswerVm1); when(agentManager.easySend(eq(hostId), eq(getVncPortCommandVm2))).thenReturn(getVncPortAnswerVm2); PowerMockito.mockStatic(SSHCmdHelper.class); BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22), eq(hostUsername), eq(hostPassword))).willReturn(sshConnection); BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection), eq("service cloudstack-agent restart"))). willReturn(new SSHCmdHelper.SSHCmdResult(0,"","")); when(configurationDao.getValue(ResourceManager.KvmSshToAgentEnabled.key())).thenReturn("true"); } @Test public void testCheckAndMaintainEnterMaintenanceMode() throws NoTransitionException { boolean enterMaintenanceMode = resourceManager.checkAndMaintain(hostId); verify(resourceManager).isHostInMaintenance(host, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); verify(resourceManager).setHostIntoMaintenance(host); verify(resourceManager).resourceStateTransitTo(eq(host), eq(InternalEnterMaintenance), anyLong()); Assert.assertTrue(enterMaintenanceMode); } @Test public void testCheckAndMaintainErrorInMaintenanceRunningVms() throws NoTransitionException { when(vmInstanceDao.listByHostId(hostId)).thenReturn(Arrays.asList(vm1, vm2)); boolean enterMaintenanceMode = resourceManager.checkAndMaintain(hostId); verify(resourceManager).isHostInMaintenance(host, Arrays.asList(vm1, vm2), new ArrayList<>(), new ArrayList<>()); Assert.assertFalse(enterMaintenanceMode); } @Test public void testCheckAndMaintainErrorInMaintenanceMigratingVms() throws NoTransitionException { when(vmInstanceDao.listVmsMigratingFromHost(hostId)).thenReturn(Arrays.asList(vm1, vm2)); boolean enterMaintenanceMode = resourceManager.checkAndMaintain(hostId); verify(resourceManager).isHostInMaintenance(host, new ArrayList<>(), Arrays.asList(vm1, vm2), new ArrayList<>()); Assert.assertFalse(enterMaintenanceMode); } @Test public void testCheckAndMaintainErrorInMaintenanceFailedMigrations() throws NoTransitionException { when(vmInstanceDao.listNonMigratingVmsByHostEqualsLastHost(hostId)).thenReturn(Arrays.asList(vm1, vm2)); boolean enterMaintenanceMode = resourceManager.checkAndMaintain(hostId); verify(resourceManager).isHostInMaintenance(host, new ArrayList<>(), new ArrayList<>(), Arrays.asList(vm1, vm2)); verify(resourceManager).setHostIntoErrorInMaintenance(host, Arrays.asList(vm1, vm2)); verify(resourceManager).resourceStateTransitTo(eq(host), eq(UnableToMigrate), anyLong()); Assert.assertFalse(enterMaintenanceMode); } @Test public void testConfigureVncAccessForKVMHostFailedMigrations() { when(host.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); List vms = Arrays.asList(vm1, vm2); resourceManager.configureVncAccessForKVMHostFailedMigrations(host, vms); verify(agentManager).pullAgentOutMaintenance(hostId); verify(resourceManager).setKVMVncAccess(hostId, vms); verify(agentManager, times(vms.size())).easySend(eq(hostId), any(GetVncPortCommand.class)); verify(userVmDetailsDao).addDetail(eq(vm1Id), eq("kvm.vnc.address"), eq(vm1VncAddress), anyBoolean()); verify(userVmDetailsDao).addDetail(eq(vm1Id), eq("kvm.vnc.port"), eq(String.valueOf(vm1VncPort)), anyBoolean()); verify(userVmDetailsDao).addDetail(eq(vm2Id), eq("kvm.vnc.address"), eq(vm2VncAddress), anyBoolean()); verify(userVmDetailsDao).addDetail(eq(vm2Id), eq("kvm.vnc.port"), eq(String.valueOf(vm2VncPort)), anyBoolean()); verify(agentManager).pullAgentToMaintenance(hostId); } @Test public void testCheckAndMaintainErrorInMaintenanceRetries() throws NoTransitionException { resourceManager.setHostMaintenanceRetries(host); List failedMigrations = Arrays.asList(vm1, vm2); when(vmInstanceDao.listByHostId(host.getId())).thenReturn(failedMigrations); when(vmInstanceDao.listNonMigratingVmsByHostEqualsLastHost(host.getId())).thenReturn(failedMigrations); Integer retries = ResourceManager.HostMaintenanceRetries.valueIn(host.getClusterId()); for (int i = 0; i <= retries; i++) { resourceManager.checkAndMaintain(host.getId()); } verify(resourceManager, times(retries + 1)).isHostInMaintenance(host, failedMigrations, new ArrayList<>(), failedMigrations); verify(resourceManager).setHostIntoErrorInMaintenance(host, failedMigrations); } @Test(expected = CloudRuntimeException.class) public void testGetHostCredentialsMissingParameter() { when(host.getDetail("password")).thenReturn(null); resourceManager.getHostCredentials(host); } @Test public void testGetHostCredentials() { Pair credentials = resourceManager.getHostCredentials(host); Assert.assertNotNull(credentials); Assert.assertEquals(hostUsername, credentials.first()); Assert.assertEquals(hostPassword, credentials.second()); } @Test(expected = CloudRuntimeException.class) public void testConnectAndRestartAgentOnHostCannotConnect() { BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22), eq(hostUsername), eq(hostPassword))).willReturn(null); resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); } @Test(expected = CloudRuntimeException.class) public void testConnectAndRestartAgentOnHostCannotRestart() throws Exception { BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection), eq("service cloudstack-agent restart"))).willThrow(new SshException("exception")); resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); } @Test public void testConnectAndRestartAgentOnHost() { resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); } @Test public void testHandleAgentSSHEnabledNotConnectedAgent() { when(host.getStatus()).thenReturn(Status.Disconnected); resourceManager.handleAgentIfNotConnected(host, false); verify(resourceManager).getHostCredentials(eq(host)); verify(resourceManager).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); } @Test public void testHandleAgentSSHEnabledConnectedAgent() { when(host.getStatus()).thenReturn(Status.Up); resourceManager.handleAgentIfNotConnected(host, false); verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); } @Test(expected = CloudRuntimeException.class) public void testHandleAgentSSHDisabledNotConnectedAgent() { when(host.getStatus()).thenReturn(Status.Disconnected); when(configurationDao.getValue(ResourceManager.KvmSshToAgentEnabled.key())).thenReturn("false"); resourceManager.handleAgentIfNotConnected(host, false); } @Test public void testHandleAgentSSHDisabledConnectedAgent() { when(host.getStatus()).thenReturn(Status.Up); when(configurationDao.getValue(ResourceManager.KvmSshToAgentEnabled.key())).thenReturn("false"); resourceManager.handleAgentIfNotConnected(host, false); verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); } @Test public void testHandleAgentVMsMigrating() { resourceManager.handleAgentIfNotConnected(host, true); verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); } }