/*
* 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";
}
}