// 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.ha; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContext; import org.apache.log4j.Logger; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import com.cloud.agent.AgentManager; import com.cloud.alert.AlertManager; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.ha.HighAvailabilityManager.Step; import com.cloud.ha.HighAvailabilityManager.WorkType; import com.cloud.ha.dao.HighAvailabilityDao; 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.HypervisorType; import com.cloud.resource.ResourceManager; import com.cloud.server.ManagementServer; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.StorageManager; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.user.AccountManager; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.VMInstanceDao; @RunWith(MockitoJUnitRunner.class) public class HighAvailabilityManagerImplTest { private static final Logger s_logger = Logger.getLogger(HighAvailabilityManagerImplTest.class); @Mock HighAvailabilityDao _haDao; @Mock VMInstanceDao _instanceDao; @Mock HostDao _hostDao; @Mock DataCenterDao _dcDao; @Mock HostPodDao _podDao; @Mock ClusterDetailsDao _clusterDetailsDao; @Mock ServiceOfferingDao _serviceOfferingDao; @Mock ManagedContext _managedContext; @Mock AgentManager _agentMgr; @Mock AlertManager _alertMgr; @Mock StorageManager _storageMgr; @Mock GuestOSDao _guestOSDao; @Mock GuestOSCategoryDao _guestOSCategoryDao; @Mock VirtualMachineManager _itMgr; @Mock AccountManager _accountMgr; @Mock ResourceManager _resourceMgr; @Mock ManagementServer _msServer; @Mock ConfigurationDao _configDao; @Mock VolumeOrchestrationService volumeMgr; @Mock HostVO hostVO; HighAvailabilityManagerImpl highAvailabilityManager; HighAvailabilityManagerImpl highAvailabilityManagerSpy; static Method processWorkMethod = null; @BeforeClass public static void initOnce() { try { processWorkMethod = HighAvailabilityManagerImpl.class.getDeclaredMethod("processWork", HaWorkVO.class); processWorkMethod.setAccessible(true); } catch (NoSuchMethodException e) { s_logger.info("[ignored] expected NoSuchMethodException caught: " + e.getLocalizedMessage()); } } @Before public void setup() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { highAvailabilityManager = new HighAvailabilityManagerImpl(); for (Field injectField : HighAvailabilityManagerImpl.class.getDeclaredFields()) { if (injectField.isAnnotationPresent(Inject.class)) { injectField.setAccessible(true); injectField.set(highAvailabilityManager, this.getClass().getDeclaredField(injectField.getName()).get(this)); } else if (injectField.getName().equals("_workers")) { injectField.setAccessible(true); for (Class<?> clz : HighAvailabilityManagerImpl.class.getDeclaredClasses()) { if (clz.getName().equals("com.cloud.ha.HighAvailabilityManagerImpl$WorkerThread")) { Object obj = Array.newInstance(clz, 0); injectField.set(highAvailabilityManager, obj); } } } else if (injectField.getName().equals("_maxRetries")) { injectField.setAccessible(true); injectField.set(highAvailabilityManager, 5); } } highAvailabilityManagerSpy = Mockito.spy(highAvailabilityManager); } @Test public void scheduleRestartForVmsOnHost() { Mockito.when(hostVO.getType()).thenReturn(Host.Type.Routing); Mockito.when(hostVO.getHypervisorType()).thenReturn(HypervisorType.KVM); Mockito.when(_instanceDao.listByHostId(42l)).thenReturn(Arrays.asList(Mockito.mock(VMInstanceVO.class))); Mockito.when(_podDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostPodVO.class)); Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); highAvailabilityManager.scheduleRestartForVmsOnHost(hostVO, true); } @Test public void scheduleRestartForVmsOnHostNotSupported() { Mockito.when(hostVO.getType()).thenReturn(Host.Type.Routing); Mockito.when(hostVO.getHypervisorType()).thenReturn(HypervisorType.VMware); highAvailabilityManager.scheduleRestartForVmsOnHost(hostVO, true); } @Test public void scheduleRestartForVmsOnHostNonEmptyVMList() { Mockito.when(hostVO.getId()).thenReturn(1l); Mockito.when(hostVO.getType()).thenReturn(Host.Type.Routing); Mockito.when(hostVO.getHypervisorType()).thenReturn(HypervisorType.XenServer); List<VMInstanceVO> vms = new ArrayList<VMInstanceVO>(); VMInstanceVO vm1 = Mockito.mock(VMInstanceVO.class); Mockito.when(vm1.getHostId()).thenReturn(1l); Mockito.when(vm1.getInstanceName()).thenReturn("i-2-3-VM"); Mockito.when(vm1.getType()).thenReturn(VirtualMachine.Type.User); Mockito.when(vm1.isHaEnabled()).thenReturn(true); vms.add(vm1); VMInstanceVO vm2 = Mockito.mock(VMInstanceVO.class); Mockito.when(vm2.getHostId()).thenReturn(1l); Mockito.when(vm2.getInstanceName()).thenReturn("r-2-VM"); Mockito.when(vm2.getType()).thenReturn(VirtualMachine.Type.DomainRouter); Mockito.when(vm2.isHaEnabled()).thenReturn(true); vms.add(vm2); Mockito.when(_instanceDao.listByHostId(Mockito.anyLong())).thenReturn(vms); Mockito.when(_instanceDao.findByUuid(vm1.getUuid())).thenReturn(vm1); Mockito.when(_instanceDao.findByUuid(vm2.getUuid())).thenReturn(vm2); Mockito.when(_podDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostPodVO.class)); Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); Mockito.when(_haDao.findPreviousHA(Mockito.anyLong())).thenReturn(Arrays.asList(Mockito.mock(HaWorkVO.class))); Mockito.when(_haDao.persist((HaWorkVO)Mockito.anyObject())).thenReturn(Mockito.mock(HaWorkVO.class)); Mockito.when(_serviceOfferingDao.findById(vm1.getServiceOfferingId())).thenReturn(Mockito.mock(ServiceOfferingVO.class)); highAvailabilityManager.scheduleRestartForVmsOnHost(hostVO, true); } @Test public void investigateHostStatusSuccess() { Mockito.when(_hostDao.findById(Mockito.anyLong())).thenReturn(hostVO); // Set the list of investigators, CheckOnAgentInvestigator suffices for now Investigator investigator = Mockito.mock(CheckOnAgentInvestigator.class); List<Investigator> investigators = new ArrayList<Investigator>(); investigators.add(investigator); highAvailabilityManager.setInvestigators(investigators); // Mock isAgentAlive to return host status as Down Mockito.when(investigator.isAgentAlive(hostVO)).thenReturn(Status.Down); assertTrue(highAvailabilityManager.investigate(1l) == Status.Down); } @Test public void investigateHostStatusFailure() { Mockito.when(_hostDao.findById(Mockito.anyLong())).thenReturn(hostVO); // Set the list of investigators, CheckOnAgentInvestigator suffices for now // Also no need to mock isAgentAlive() as actual implementation returns null Investigator investigator = Mockito.mock(CheckOnAgentInvestigator.class); List<Investigator> investigators = new ArrayList<Investigator>(); investigators.add(investigator); highAvailabilityManager.setInvestigators(investigators); assertNull(highAvailabilityManager.investigate(1l)); } private void processWorkWithRetryCount(int count, Step expectedStep) { assertNotNull(processWorkMethod); HaWorkVO work = new HaWorkVO(1l, VirtualMachine.Type.User, WorkType.Migration, Step.Scheduled, 1l, VirtualMachine.State.Running, count, 12345678l); Mockito.doReturn(12345678l).when(highAvailabilityManagerSpy).migrate(work); try { processWorkMethod.invoke(highAvailabilityManagerSpy, work); } catch (IllegalAccessException e) { s_logger.info("[ignored] expected IllegalAccessException caught: " + e.getLocalizedMessage()); } catch (IllegalArgumentException e) { s_logger.info("[ignored] expected IllegalArgumentException caught: " + e.getLocalizedMessage()); } catch (InvocationTargetException e) { s_logger.info("[ignored] expected InvocationTargetException caught: " + e.getLocalizedMessage()); } assertTrue(work.getStep() == expectedStep); } @Test public void processWorkWithRetryCountExceeded() { processWorkWithRetryCount(5, Step.Done); // max retry count is 5 } @Test public void processWorkWithRetryCountNotExceeded() { processWorkWithRetryCount(3, Step.Scheduled); } }