/* * RHQ Management Platform * Copyright (C) 2011 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.enterprise.server.drift; import static java.util.Arrays.asList; import static org.rhq.core.domain.drift.DriftCategory.FILE_ADDED; import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE; import static org.rhq.core.domain.drift.DriftConfigurationDefinition.BaseDirValueContext.fileSystem; import static org.rhq.core.domain.drift.DriftConfigurationDefinition.DriftHandlingMode.normal; import static org.rhq.core.domain.drift.DriftDefinitionComparator.CompareMode.BOTH_BASE_INFO_AND_DIRECTORY_SPECIFICATIONS; import static org.rhq.enterprise.server.util.LookupUtil.getDriftManager; import static org.rhq.enterprise.server.util.LookupUtil.getDriftTemplateManager; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.persistence.EntityManager; import org.rhq.core.domain.common.EntityContext; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.criteria.DriftDefinitionCriteria; import org.rhq.core.domain.criteria.JPADriftChangeSetCriteria; import org.rhq.core.domain.drift.Drift; import org.rhq.core.domain.drift.DriftChangeSet; import org.rhq.core.domain.drift.DriftComplianceStatus; import org.rhq.core.domain.drift.DriftConfigurationDefinition; import org.rhq.core.domain.drift.DriftDefinition; import org.rhq.core.domain.drift.DriftDefinitionComparator; import org.rhq.core.domain.drift.DriftDefinitionTemplate; import org.rhq.core.domain.drift.DriftSnapshot; import org.rhq.core.domain.drift.JPADrift; import org.rhq.core.domain.drift.JPADriftChangeSet; import org.rhq.core.domain.drift.JPADriftFile; import org.rhq.core.domain.drift.JPADriftSet; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility; import org.rhq.enterprise.server.safeinvoker.HibernateDetachUtility.SerializationType; import org.rhq.enterprise.server.test.TransactionCallback; import org.rhq.test.AssertUtils; import org.testng.annotations.Test; @Test public class ManageDriftDefinitionsTest extends AbstractDriftServerTest { private final String DRIFT_NOT_SUPPORTED_TYPE = NAME_PREFIX + "DRIFT_NOT_SUPPORTED_RESOURCE_TYPE"; private final String DRIFT_NOT_SUPPORTED_RESOURCE = NAME_PREFIX + "DRIFT_NOT_SUPPORTED_RESOURCE"; private DriftManagerLocal driftMgr; private DriftTemplateManagerLocal templateMgr; private ResourceType driftNotSupportedType; private Resource driftNotSupportedResource; @Override protected void beforeMethod(Method testMethod) throws Exception { super.beforeMethod(testMethod); driftMgr = getDriftManager(); templateMgr = getDriftTemplateManager(); } @Override protected void purgeDB() { super.purgeDB(); removeEntity(Resource.class, DRIFT_NOT_SUPPORTED_RESOURCE); removeEntity(ResourceType.class, DRIFT_NOT_SUPPORTED_TYPE); } public void createDefinitionFromUnpinnedTemplate() throws Exception { // first create a template final DriftDefinition templateDef = new DriftDefinition(new Configuration()); templateDef.setName(NAME_PREFIX + "createUnpinnedDefinition"); templateDef.setEnabled(true); templateDef.setDriftHandlingMode(normal); templateDef.setInterval(2400L); templateDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem, "/foo/bar/test")); // persist the template DriftDefinitionTemplate template = templateMgr.createTemplate(getOverlord(), resourceType.getId(), false, templateDef); // create and persist the definition DriftDefinition definition = template.createDefinition(); definition.setTemplate(template); driftMgr.updateDriftDefinition(getOverlord(), EntityContext.forResource(resource.getId()), definition); // verify that the definition was created DriftDefinition newDef = loadDefinition(definition.getName()); DriftDefinitionComparator comparator = new DriftDefinitionComparator( BOTH_BASE_INFO_AND_DIRECTORY_SPECIFICATIONS); HibernateDetachUtility.nullOutUninitializedFields(newDef, SerializationType.SERIALIZATION); assertEquals("The drift definition was not persisted correctly", 0, comparator.compare(definition, newDef)); assertEquals("The template association was not set on the definition", template, newDef.getTemplate()); } // The following two tests are commented out because when they are enabled // and all tests in the itests module are run, the @AfterClass method for // DriftTemplateManagerBeanTest does not run immediately after all of its // test methods have finished running. Instead, some of the tests in // ManageDriftDefinitionsTest start running. This leads to some database // constraint violations because of how agents are created in the parent // class, DriftServerTest. See http://groups.google.com/group/testng-users/browse_thread/thread/da2790679a430d51?pli=1 // more info on the order in which TestNG executes tests. // public void createEntitiesThatDoNotSupportDrift() { // // first create the resource type that does not support drift // driftNotSupportedType = new ResourceTypeBuilder() // .createResourceType() // .withId(0) // .withName(DRIFT_NOT_SUPPORTED_TYPE) // .withCategory(SERVER) // .withPlugin(DRIFT_NOT_SUPPORTED_TYPE.toLowerCase()) // .build(); // // // create a resource of the type that does not support drift // driftNotSupportedResource = new ResourceBuilder() // .createResource() // .withId(0) // .withName(DRIFT_NOT_SUPPORTED_RESOURCE) // .withResourceKey(DRIFT_NOT_SUPPORTED_RESOURCE) // .withRandomUuid() // .withResourceType(driftNotSupportedType) // .build(); // // executeInTransaction(new TransactionCallback() { // @Override // public void execute() throws Exception { // EntityManager em = getEntityManager(); // em.persist(driftNotSupportedType); // em.persist(driftNotSupportedResource); // } // }); // } // // @Test(dependsOnMethods = "createEntitiesThatDoNotSupportDrift", // expectedExceptions = EJBException.class, // expectedExceptionsMessageRegExp = ".*Cannot create drift definition.*type.*does not support drift management") // @InitDB(false) // public void doNotAllowDefinitionToBeCreatedForTypeThatDoesNotSupportDrift() { // DriftDefinition driftDef = new DriftDefinition(new Configuration()); // driftDef.setName("test_typeDoesNotSupportDrift"); // driftDef.setEnabled(true); // driftDef.setInterval(1800L); // driftDef.setDriftHandlingMode(normal); // driftDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem, "/foo/bar/test")); // // driftMgr.updateDriftDefinition(getOverlord(), EntityContext.forResource(driftNotSupportedResource.getId()), // driftDef); // } @SuppressWarnings("unchecked") public void createDefinitionFromPinnedTemplate() throws Exception { // We first need to create a pinned template. Users can only create a pinned // template from a snapshot of an existing resource-level drift definition. // We are going to take a bit of a short cut though by directly creating // and persisting the pinned change set. // first create the change set final JPADriftChangeSet changeSet0 = new JPADriftChangeSet(null, 0, COVERAGE, null); changeSet0.setDriftHandlingMode(DriftConfigurationDefinition.DriftHandlingMode.normal); final JPADriftFile driftFile1 = new JPADriftFile(NAME_PREFIX + "a1b2c3"); final JPADriftFile driftFile2 = new JPADriftFile(NAME_PREFIX + "1a2b3c"); JPADrift drift1 = new JPADrift(changeSet0, "drift.1", FILE_ADDED, null, driftFile1); JPADrift drift2 = new JPADrift(changeSet0, "drift.2", FILE_ADDED, null, driftFile2); final JPADriftSet driftSet = new JPADriftSet(); driftSet.addDrift(drift1); driftSet.addDrift(drift2); // create the template final DriftDefinition templateDef = new DriftDefinition(new Configuration()); templateDef.setName(NAME_PREFIX + "createUnpinnedDefinition"); templateDef.setEnabled(true); templateDef.setDriftHandlingMode(normal); templateDef.setInterval(2400L); templateDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem, "/foo/bar/test")); templateDef.setPinned(true); final DriftDefinitionTemplate template = templateMgr.createTemplate(getOverlord(), resourceType.getId(), true, templateDef); executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { EntityManager em = getEntityManager(); em.persist(driftFile1); em.persist(driftFile2); em.persist(changeSet0); em.persist(driftSet); changeSet0.setInitialDriftSet(driftSet); em.merge(changeSet0); // setting the change set id on the template is the last and the // most important step in making the template pinned template.setChangeSetId(changeSet0.getId()); em.merge(template); } }); // Create and persist a resource-level definition. final DriftDefinition definition = template.createDefinition(); definition.setTemplate(template); final AtomicBoolean agentInvoked = new AtomicBoolean(false); agentServiceContainer.driftService = new TestDefService() { @Override public void updateDriftDetection(int resourceId, DriftDefinition driftDef, DriftSnapshot snapshot) { try { HibernateDetachUtility.nullOutUninitializedFields(driftDef, HibernateDetachUtility.SerializationType.SERIALIZATION); HibernateDetachUtility.nullOutUninitializedFields(snapshot, HibernateDetachUtility.SerializationType.SERIALIZATION); agentInvoked.set(true); assertNotNull("Expected snapshot drift instances collection to be non-null", snapshot.getDriftInstances()); assertEquals("Expected snapshot to contain two drift entries", 2, snapshot.getDriftInstances() .size()); } catch (Exception e) { String msg = "Do not pass attached entites to agent since those entities are outside of " + "Hibernate's control. The persistence context should be flushed and cleared to ensure that " + "only detached objects are sent to the agent"; throw new RuntimeException(msg, e); } } }; driftMgr.updateDriftDefinition(getOverlord(), EntityContext.forResource(resource.getId()), definition); DriftDefinition newDef = loadDefinition(definition.getName()); // verify that the definition is marked as pinned assertTrue("The drift definition should be marked as pinned", newDef.isPinned()); // verify that the initial change set is generated for the definition JPADriftChangeSetCriteria criteria = new JPADriftChangeSetCriteria(); criteria.addFilterDriftDefinitionId(definition.getId()); criteria.addFilterCategory(COVERAGE); criteria.fetchDrifts(true); PageList<? extends DriftChangeSet<?>> changeSets = driftMgr.findDriftChangeSetsByCriteria(getOverlord(), criteria); assertEquals("Expected to find one change set", 1, changeSets.size()); JPADriftChangeSet expectedChangeSet = new JPADriftChangeSet(resource, 1, COVERAGE, null); List<? extends Drift> expectedDrifts = asList(new JPADrift(expectedChangeSet, drift1.getPath(), FILE_ADDED, null, driftFile1), new JPADrift(expectedChangeSet, drift2.getPath(), FILE_ADDED, null, driftFile2)); DriftChangeSet<?> actualChangeSet = changeSets.get(0); List<? extends Drift> actualDrifts = new ArrayList(actualChangeSet.getDrifts()); AssertUtils.assertCollectionMatchesNoOrder( "Expected to find drifts from change sets 1 and 2 in the template change set", (List<Drift>) expectedDrifts, (List<Drift>) actualDrifts, "id", "ctime", "changeSet", "newDriftFile"); // lastly verify that the agent is called assertTrue("Failed to send drift definition along with snapshot to agent", agentInvoked.get()); } public void unpinDefinition() { // First create the template final DriftDefinition templateDef = new DriftDefinition(new Configuration()); templateDef.setName(NAME_PREFIX + "unpin_def_template"); templateDef.setEnabled(true); templateDef.setDriftHandlingMode(normal); templateDef.setInterval(2400L); templateDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem, "/foo/bar/test")); final DriftDefinitionTemplate template = templateMgr.createTemplate(getOverlord(), resourceType.getId(), true, templateDef); // First create the definition DriftDefinition definition = template.createDefinition(); definition.setName(NAME_PREFIX + "unpin"); definition.setEnabled(true); definition.setBasedir(new DriftDefinition.BaseDirectory(fileSystem, "/foo/bar/test")); definition.setComplianceStatus(DriftComplianceStatus.OUT_OF_COMPLIANCE_DRIFT); definition.setInterval(1800L); definition.setDriftHandlingMode(normal); definition.setPinned(true); // persist the definition driftMgr.updateDriftDefinition(getOverlord(), EntityContext.forResource(resource.getId()), definition); // now update the definition DriftDefinition newDef = loadDefinition(definition.getName()); assertNotNull("Failed to load new definition, " + toString(definition)); newDef.setPinned(false); driftMgr.updateDriftDefinition(getOverlord(), EntityContext.forResource(resource.getId()), newDef); // now verify that the definition was updated DriftDefinition updatedDef = loadDefinition(definition.getName()); assertNotNull("Failed to load updated definition, " + toString(newDef)); assertFalse("The updated definition should be set to unpinned", updatedDef.isPinned()); assertEquals("The updated definition should be set to in compliance", DriftComplianceStatus.IN_COMPLIANCE, updatedDef.getComplianceStatus()); } private DriftDefinition loadDefinition(String name) { DriftDefinitionCriteria criteria = new DriftDefinitionCriteria(); criteria.addFilterResourceIds(resource.getId()); criteria.addFilterName(name); criteria.fetchConfiguration(true); criteria.fetchResource(true); criteria.fetchTemplate(true); PageList<DriftDefinition> driftDefs = driftMgr.findDriftDefinitionsByCriteria(getOverlord(), criteria); assertEquals("Expected to find one drift definition", 1, driftDefs.size()); return driftDefs.get(0); } }