/* * 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.measurement; import static java.util.Arrays.asList; import static org.joda.time.DateTime.now; 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 static org.rhq.test.AssertUtils.assertCollectionEqualsNoOrder; import java.util.ArrayList; import java.util.List; import javax.ejb.EJB; import javax.inject.Inject; import javax.persistence.EntityManager; import org.joda.time.DateTime; 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.storage.StorageClientManager; 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; import org.rhq.server.metrics.domain.AggregateNumericMetric; import org.rhq.server.metrics.domain.Bucket; /** * @author John Sanda */ public class MeasurementOOBManagerBeanTest 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 MeasurementOOBManagerLocal oobManager; @EJB private MeasurementBaselineManagerLocal baselineManager; @EJB private StorageClientManager storageClientManager; @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(); } /** * Verifies that OOBs are calculated when there are both upper and lower bound * violations. It also verifies that no OOB is generated a schedule whose values stay * in bounds. */ @Test public void calculateOOBs() { final MeasurementSchedule schedule1 = createSchedule(); final MeasurementSchedule schedule2 = createSchedule(); MeasurementSchedule schedule3 = createSchedule(); DateTime currentHour = now().hourOfDay().roundFloorCopy(); final DateTime lastHour = currentHour.minusHours(1); insertBaselines(asList( // baseline(schedule1, 4.34, 3.9, 5.2), // baseline(schedule2, 7.43, 7.38, 7.49), // baseline(schedule3, 3.2, 2.95, 3.6) // )); List<AggregateNumericMetric> metrics = asList( // new AggregateNumericMetric(schedule1.getId(), Bucket.ONE_HOUR, 3.8, 2.11, 4.6, lastHour.getMillis()), // new AggregateNumericMetric(schedule2.getId(), Bucket.ONE_HOUR, 9.492, 9.481, 9.53, lastHour.getMillis()), // new AggregateNumericMetric(schedule3.getId(), Bucket.ONE_HOUR, 3.15, 2.96, 3.59, lastHour.getMillis()) // ); oobManager.computeOOBsForLastHour(overlord, metrics); executeInTransaction(new TransactionCallback() { @Override @SuppressWarnings("unchecked") public void execute() throws Exception { EntityManager em = getEntityManager(); List<MeasurementOOB> oobs = em.createQuery("select oob from MeasurementOOB oob").getResultList(); List<TestMeasurementOOB> actual = new ArrayList<TestMeasurementOOB>(); for (MeasurementOOB oob : oobs) { actual.add(new TestMeasurementOOB(oob)); } List<TestMeasurementOOB> expected = asList( // new TestMeasurementOOB(schedule1.getId(), lastHour.getMillis(), 138), // new TestMeasurementOOB(schedule2.getId(), lastHour.getMillis(), 1855) // ); assertCollectionEqualsNoOrder(expected, actual, "The OOBs do not match"); } }); } 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 void insertBaselines(final List<MeasurementBaseline> baselines) { executeInTransaction(false, new TransactionCallback() { @Override public void execute() throws Exception { EntityManager em = getEntityManager(); for (MeasurementBaseline baseline : baselines) { 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 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; } }); } }