/* * RHQ Management Platform * Copyright (C) 2005-2014 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.purge; import static java.lang.Math.random; import static org.rhq.core.domain.measurement.DataType.MEASUREMENT; import static org.rhq.core.domain.measurement.NumericType.DYNAMIC; import static org.rhq.core.domain.resource.ResourceCategory.SERVER; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import javax.ejb.EJB; import javax.inject.Inject; import javax.persistence.EntityManager; import org.testng.annotations.Test; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.criteria.ResourceCriteria; import org.rhq.core.domain.measurement.MeasurementBaseline; import org.rhq.core.domain.measurement.MeasurementDefinition; import org.rhq.core.domain.measurement.MeasurementOOB; import org.rhq.core.domain.measurement.MeasurementSchedule; import org.rhq.core.domain.resource.Agent; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceType; import org.rhq.enterprise.server.drift.DriftServerPluginService; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.test.AbstractEJB3Test; import org.rhq.enterprise.server.test.TransactionCallback; import org.rhq.enterprise.server.test.TransactionCallbackReturnable; import org.rhq.enterprise.server.util.Overlord; import org.rhq.enterprise.server.util.ResourceTreeHelper; public class PurgeOOBTest extends AbstractEJB3Test { private final String RESOURCE_TYPE = getClass().getName() + "_TYPE"; private final String PLUGIN = getClass().getName() + "_PLUGIN"; private final String AGENT_NAME = getClass().getName() + "_AGENT"; private final String DYNAMIC_DEF_NAME = getClass().getName() + "_DYNAMIC"; private final String RESOURCE_KEY = getClass().getName() + "_RESOURCE_KEY"; private final String RESOURCE_NAME = getClass().getName() + "_NAME"; private final String RESOURCE_UUID = getClass().getSimpleName() + "_UUID"; private ResourceType resourceType; private Agent agent; private Resource resource; private List<MeasurementDefinition> measurementDefs; private List<MeasurementSchedule> schedules; @Inject @Overlord private Subject overlord; @EJB private ResourceManagerLocal resourceManager; @EJB private PurgeManagerLocal purgeManager; @Override protected void beforeMethod() throws Exception { // MeasurementDataManagerUtility looks up config settings from SystemManagerBean. // SystemManagerBean.getDriftServerPluginManager method requires drift server plugin. DriftServerPluginService driftServerPluginService = new DriftServerPluginService(getTempDir()); prepareCustomServerPluginService(driftServerPluginService); driftServerPluginService.masterConfig.getPluginDirectory().mkdirs(); measurementDefs = new ArrayList<MeasurementDefinition>(); schedules = new ArrayList<MeasurementSchedule>(); createInventory(); } @Override protected void afterMethod() throws Exception { purgeDB(); } @Test public void testRemoveOutdatedOOBs() throws Exception { long now = System.currentTimeMillis(); long anHourAgo = now - TimeUnit.HOURS.toMillis(1); long halfAnHourAgo = now - TimeUnit.MINUTES.toMillis(30); for (int i = 0; i < 100; i++) { MeasurementSchedule schedule = createSchedule(); insertBaseline(baseline(schedule, random(), random(), random())); if (i % 2 == 0) { moveBaselineBackInPast(schedule, anHourAgo); } insertOOB(oob(schedule, (int) (1000 * random()), now)); } purgeManager.removeOutdatedOOBs(halfAnHourAgo); for (int i = 0; i < 100; i++) { MeasurementSchedule schedule = schedules.get(i); long oobCount = countOOBsFor(schedule).longValue(); if (i % 2 == 0) { assertEquals(1, oobCount); } else { assertEquals(0, oobCount); } } } private void moveBaselineBackInPast(final MeasurementSchedule schedule, final long anHourAgo) { executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { String query = "update MeasurementBaseline set computeTime = :computeTime " // + "where scheduleId = :scheduleId"; getEntityManager().createQuery(query) // .setParameter("scheduleId", schedule.getId()) // .setParameter("computeTime", anHourAgo) // .executeUpdate(); } }); } private Long countOOBsFor(final MeasurementSchedule schedule) { return executeInTransaction(new TransactionCallbackReturnable<Long>() { @Override public Long execute() throws Exception { String query = "select count(oob) from MeasurementOOB oob " // + "where oob.id = :scheduleId"; return getEntityManager().createQuery(query, Long.class) // .setParameter("scheduleId", schedule.getId()) // .getSingleResult(); } }); } private void insertOOB(final MeasurementOOB oob) { executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { EntityManager em = getEntityManager(); em.persist(oob); } }); } private MeasurementOOB oob(MeasurementSchedule schedule, int factor, long timestamp) { MeasurementOOB oob = new MeasurementOOB(); oob.setScheduleId(schedule.getId()); oob.setOobFactor(factor); oob.setTimestamp(timestamp); return oob; } private void insertBaseline(final MeasurementBaseline baseline) { executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { EntityManager em = getEntityManager(); em.persist(baseline); } }); } private MeasurementBaseline baseline(MeasurementSchedule schedule, double avg, double min, double max) { MeasurementBaseline baseline = new MeasurementBaseline(); baseline.setSchedule(schedule); baseline.setMean(avg); baseline.setMax(max); baseline.setMin(min); return baseline; } private void createInventory() throws Exception { purgeDB(); executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { resourceType = new ResourceType(RESOURCE_TYPE, PLUGIN, SERVER, null); em.persist(resourceType); agent = new Agent(AGENT_NAME, "localhost", 9999, "", "randomToken"); em.persist(agent); resource = new Resource(RESOURCE_KEY, RESOURCE_NAME, resourceType); resource.setUuid(RESOURCE_UUID); resource.setAgent(agent); em.persist(resource); } }); } private void purgeDB() { purgeBaselines(); purgeOOBs(); executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { ResourceCriteria c = new ResourceCriteria(); c.addFilterInventoryStatus(null); c.addFilterResourceKey(RESOURCE_KEY); c.fetchSchedules(true); List<Resource> r = resourceManager.findResourcesByCriteria(overlord, c); // Note that the order of deletes is important due to FK // constraints. if (!r.isEmpty()) { assertTrue("Should be only 1 resource", r.size() == 1); Resource doomedResource = r.get(0); deleteMeasurementSchedules(); deleteResource(doomedResource); } deleteAgent(); deleteDynamicMeasurementDef(); deleteResourceType(); } }); } private void deleteDynamicMeasurementDef() { if (!measurementDefs.isEmpty()) { em.createQuery("delete from MeasurementDefinition d where d in :defs") .setParameter("defs", measurementDefs).executeUpdate(); } } private void deleteAgent() { em.createQuery("delete from Agent where name = :name").setParameter("name", AGENT_NAME).executeUpdate(); } private void deleteResourceType() { em.createQuery("delete from ResourceType where name = :name and plugin = :plugin") .setParameter("name", RESOURCE_TYPE).setParameter("plugin", PLUGIN).executeUpdate(); } private void deleteResource(Resource doomedResource) { ResourceTreeHelper.deleteResource(em, doomedResource); em.flush(); } private void deleteMeasurementSchedules() { for (MeasurementSchedule schedule : schedules) { em.createQuery("delete from MeasurementSchedule where id = :id").setParameter("id", schedule.getId()) .executeUpdate(); } em.flush(); } private void purgeBaselines() { purgeTables("rhq_measurement_bline"); } private void purgeOOBs() { purgeTables("rhq_measurement_oob"); } private void purgeTables(final String... tables) { executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { for (String table : tables) { getEntityManager().createNativeQuery("delete from " + table).executeUpdate(); } } }); } private MeasurementSchedule createSchedule() { return executeInTransaction(false, new TransactionCallbackReturnable<MeasurementSchedule>() { @Override public MeasurementSchedule execute() throws Exception { EntityManager em = getEntityManager(); MeasurementDefinition definition = new MeasurementDefinition(resourceType, DYNAMIC_DEF_NAME + measurementDefs.size()); definition.setDefaultOn(true); definition.setDataType(MEASUREMENT); definition.setMeasurementType(DYNAMIC); em.persist(definition); MeasurementSchedule schedule = new MeasurementSchedule(definition, resource); schedule.setEnabled(true); resource.addSchedule(schedule); em.persist(schedule); schedules.add(schedule); measurementDefs.add(definition); return schedule; } }); } }