/* * RHQ Management Platform * Copyright (C) 2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.core.pc.inventory; import static org.mockito.Matchers.any; import static org.mockito.Mockito.when; import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.TargetsContainer; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.arquillian.testng.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.rhq.core.clientapi.server.discovery.InventoryReport; import org.rhq.core.domain.discovery.MergeInventoryReportResults; import org.rhq.core.domain.discovery.PlatformSyncInfo; import org.rhq.core.domain.discovery.ResourceSyncInfo; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.pc.PluginContainer; import org.rhq.core.pc.PluginContainerConfiguration; import org.rhq.core.pc.inventory.testplugin.ManualAddDiscoveryComponent; import org.rhq.core.pc.inventory.testplugin.TestResourceComponent; import org.rhq.core.pc.inventory.testplugin.TestResourceDiscoveryComponent; import org.rhq.core.util.file.FileUtil; import org.rhq.test.arquillian.AfterDiscovery; import org.rhq.test.arquillian.BeforeDiscovery; import org.rhq.test.arquillian.MockingServerServices; import org.rhq.test.shrinkwrap.RhqAgentPluginArchive; /** * Base test class for testing ignoring resource types and notifying the {@link InventoryManager} about it. * * @author John Mazzitelli */ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquillian { @Deployment(name = "testIgnoreTypes", order = 1) @TargetsContainer("pc") public static RhqAgentPluginArchive getTestPlugin() { RhqAgentPluginArchive pluginJar = ShrinkWrap.create(RhqAgentPluginArchive.class, "test-plugin.jar"); return pluginJar.setPluginDescriptor("test-great-grandchild-discovery-plugin.xml").addClasses( TestResourceDiscoveryComponent.class, TestResourceComponent.class, ManualAddDiscoveryComponent.class); } @ArquillianResource private MockingServerServices serverServices; @ArquillianResource protected PluginContainerConfiguration pluginContainerConfiguration; // for subclasses to use this, this must be exposed as a protected field - can't use a getPluginContainer() method @ArquillianResource protected PluginContainer pluginContainer; protected Resource platform; protected HashMap<Integer, Resource> simulatedInventory; // key == resourceId == UUID.hashcode() protected HashSet<ResourceType> ignoredTypes; private CountDownLatch gotIgnoredTypeFromAgent; protected abstract void initializeIgnoredTypes(); @AfterMethod public void cleanUpPluginContainer() { File dataDir = pluginContainerConfiguration.getDataDirectory(); System.out.println("Purging data directory: " + dataDir); pluginContainer.getInventoryManager().shutdown(); FileUtil.purge(dataDir, true); } @BeforeDiscovery public void resetServerServices() throws Exception { platform = null; simulatedInventory = new HashMap<Integer, Resource>(); gotIgnoredTypeFromAgent = new CountDownLatch(1); // will be open once we know agent discovered types we want ignored initializeIgnoredTypes(); serverServices.resetMocks(); when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then( mergeInventoryReport()); when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then( getResourceSyncInfo()); } @AfterDiscovery public void afterDiscovery() throws Exception { // wait for agent discovery to start getting ignored types // actually, I don't think this latch will ever count down - if I did this right, // the first inventory report will tell the agent about the ignored types - before the agent // even attempted to discover resources of that type. Therefore, the agent will probably never // send up resources of the ignored types. But we have to wait anyway for the few inventory reports // that have to be received (which includes the resources of the unignored types). gotIgnoredTypeFromAgent.await(20L, TimeUnit.SECONDS); } protected void resetSimulatedServerSideInventory() { if (simulatedInventory != null) { simulatedInventory.clear(); } if (platform != null) { platform.getChildResources().clear(); if (simulatedInventory != null) { simulatedInventory.put(platform.getId(), platform); } } } protected void waitForInventory(int depth) throws Exception { long start = System.currentTimeMillis(); int inventoryDepth = getInventoryDepth(pluginContainer.getInventoryManager().getPlatform(), ""); while (inventoryDepth < depth) { if (System.currentTimeMillis() - start > 60000L) { assert false : "Failed to get proper depth, depth is currently at=" + inventoryDepth; } Thread.sleep(1000); inventoryDepth = getInventoryDepth(platform, ""); } return; } protected int getInventoryDepth(Resource root, String indent) { System.out.println("Inventory depth chart: " + indent + root); if (root == null) { return 0; } int maxDepth = 0; for (Resource c : root.getChildResources()) { int childDepth = getInventoryDepth(c, " " + indent); if (maxDepth < childDepth) { maxDepth = childDepth; } } return maxDepth + 1; } protected Answer<MergeInventoryReportResults> mergeInventoryReport() { return new Answer<MergeInventoryReportResults>() { @Override public MergeInventoryReportResults answer(InvocationOnMock invocation) throws Throwable { InventoryReport inventoryReport = (InventoryReport) invocation.getArguments()[0]; return simulateInventoryReportServerProcessing(inventoryReport); } }; } protected MergeInventoryReportResults simulateInventoryReportServerProcessing(InventoryReport inventoryReport) { PlatformSyncInfo syncInfo = null; if (inventoryReport.getAddedRoots() != null && !inventoryReport.getAddedRoots().isEmpty()) { for (Resource res : inventoryReport.getAddedRoots()) { persistInSimulatedInventory(res); } syncInfo = PlatformSyncInfo.buildPlatformSyncInfo(platform); } MergeInventoryReportResults result = new MergeInventoryReportResults(syncInfo, ignoredTypes); return result; } protected void persistInSimulatedInventory(Resource res) { if (!ignoredTypes.contains(res.getResourceType())) { if (!simulatedInventory.containsKey(res.getUuid().hashCode())) { Resource persisted = new Resource(res.getResourceKey(), res.getName(), res.getResourceType()); persisted.setId(res.getUuid().hashCode()); persisted.setUuid(res.getUuid()); persisted.setInventoryStatus(InventoryStatus.COMMITTED); // System.out.println("***** PERSISTED:\n" + persisted.getUuid() + ", " + persisted.getName() + "\n****"); simulatedInventory.put(persisted.getUuid().hashCode(), persisted); if (res.getParentResource() == Resource.ROOT) { platform = persisted; } else { Resource parent = simulatedInventory.get(res.getParentResource().getUuid().hashCode()); if (parent != null) { parent.addChildResource(persisted); } } } for (Resource child : res.getChildResources()) { persistInSimulatedInventory(child); } } else { gotIgnoredTypeFromAgent.countDown(); } return; } protected Answer<Collection<ResourceSyncInfo>> getResourceSyncInfo() { return new Answer<Collection<ResourceSyncInfo>>() { @Override public Collection<ResourceSyncInfo> answer(InvocationOnMock invocation) throws Throwable { Integer resourceId = (Integer) invocation.getArguments()[0]; Collection<ResourceSyncInfo> result = convert(simulatedInventory.get(resourceId)); return result; } }; } private static Collection<ResourceSyncInfo> convert(Resource root) { Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>(); convertInternal(root, result); return result; } private static void convertInternal(Resource root, Collection<ResourceSyncInfo> result) { ResourceSyncInfo rootSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(root); if (result.contains(rootSyncInfo)) { return; } try { result.add(rootSyncInfo); for (Resource child : root.getChildResources()) { convertInternal(child, result); } } catch (Exception e) { throw new IllegalStateException("Failed to convert resource " + root + " to a ResourceSyncInfo. This should not happen.", e); } } protected void validateFullInventory() { System.out.println("Validating full inventory..."); // get platform Resource platform = pluginContainer.getInventoryManager().getPlatform(); Assert.assertNotNull(platform); Assert.assertEquals(platform.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server Assert.assertEquals(platform.getChildResources().size(), 1, "plat children: " + platform.getChildResources()); Resource server = platform.getChildResources().iterator().next(); Assert.assertNotNull(server); Assert.assertEquals(server.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server's immediate child service Assert.assertEquals(server.getChildResources().size(), 1, "srv children: " + server.getChildResources()); Resource service = server.getChildResources().iterator().next(); Assert.assertNotNull(service); Assert.assertEquals(service.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server's grandchild service Assert.assertEquals(service.getChildResources().size(), 1, "svc children: " + service.getChildResources()); Resource grandchild = service.getChildResources().iterator().next(); Assert.assertNotNull(grandchild); Assert.assertEquals(grandchild.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server's great-grandchild service Assert.assertEquals(grandchild.getChildResources().size(), 1, "svc grandch: " + grandchild.getChildResources()); Resource greatgrandchild = grandchild.getChildResources().iterator().next(); Assert.assertNotNull(greatgrandchild); Assert.assertEquals(greatgrandchild.getInventoryStatus(), InventoryStatus.COMMITTED); assert greatgrandchild.getChildResources().isEmpty() : "great grandchild should have no children"; } protected void validatePartiallyIgnoredInventory() { System.out.println("Validating partially ignored inventory..."); // get platform Resource platform = pluginContainer.getInventoryManager().getPlatform(); Assert.assertNotNull(platform); Assert.assertEquals(platform.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server Assert.assertEquals(platform.getChildResources().size(), 1, "plat children: " + platform.getChildResources()); Resource server = platform.getChildResources().iterator().next(); Assert.assertNotNull(server); Assert.assertEquals(server.getInventoryStatus(), InventoryStatus.COMMITTED); // get top server's immediate child service Assert.assertEquals(server.getChildResources().size(), 1, "srv children: " + server.getChildResources()); Resource service = server.getChildResources().iterator().next(); Assert.assertNotNull(service); Assert.assertEquals(service.getInventoryStatus(), InventoryStatus.COMMITTED); // the grandchild service type is ignored, we should have no children from here on down assert service.getChildResources().isEmpty() : "grandchild should have been ignored"; } }