/* * RHQ Management Platform * Copyright (C) 2005-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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.resource.metadata; import static org.rhq.core.domain.criteria.Criteria.Restriction.COLLECTION_ONLY; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.domain.criteria.MeasurementDefinitionCriteria; import org.rhq.core.domain.measurement.DataType; import org.rhq.core.domain.measurement.DisplayType; import org.rhq.core.domain.measurement.MeasurementCategory; import org.rhq.core.domain.measurement.MeasurementDefinition; import org.rhq.core.domain.measurement.MeasurementSchedule; import org.rhq.core.domain.measurement.MeasurementUnits; import org.rhq.core.domain.measurement.NumericType; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceType; import org.rhq.enterprise.server.RHQConstants; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerLocal; import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerLocal; @Stateless public class MeasurementMetadataManagerBean implements MeasurementMetadataManagerLocal { private static final Log LOG = LogFactory.getLog(MeasurementMetadataManagerBean.class); @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME) private EntityManager entityMgr; @EJB private MeasurementScheduleManagerLocal scheduleMgr; @EJB private MeasurementDefinitionManagerLocal measurementDefinitionMgr; @EJB private SubjectManagerLocal subjectMgr; @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void updateMetadata(ResourceType existingType, ResourceType newType) { if (LOG.isDebugEnabled()) { LOG.debug("Updating metric definitions for " + existingType); } existingType = entityMgr.find(ResourceType.class, existingType.getId()); Set<MeasurementDefinition> existingDefinitions = existingType.getMetricDefinitions(); // if necessary insert the mandatory AvailabilityType metric Set<MeasurementDefinition> newTypeMetricDefinitions = getMetricDefinitions(newType); if (existingDefinitions.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug(existingType + " currently does not define any metric definitions. " + "New metric definitions to be added: " + newType.getMetricDefinitions()); } // They're all new. for (MeasurementDefinition newDefinition : newType.getMetricDefinitions()) { if (newDefinition.getDefaultInterval() < MeasurementSchedule.MINIMUM_INTERVAL) { newDefinition.setDefaultInterval(MeasurementSchedule.MINIMUM_INTERVAL); LOG.info("Definition [" + newDefinition + "] has too short of a default interval, setting to minimum"); } existingType.addMetricDefinition(newDefinition); entityMgr.persist(newDefinition); // Now create schedules for already existing resources scheduleMgr.createSchedulesForExistingResources(existingType, newDefinition); } } else { // Update existing or add new metrics for (MeasurementDefinition newDefinition : newType.getMetricDefinitions()) { boolean found = false; for (MeasurementDefinition existingDefinition : existingDefinitions) { if (existingDefinition.getName().equals(newDefinition.getName()) && (existingDefinition.isPerMinute() == newDefinition.isPerMinute())) { found = true; if (LOG.isDebugEnabled()) { LOG.debug("Updating existing metric definition: " + existingDefinition); } // We normally protect the user's interval settings. But the Availability metric is // a bit special. It's built-in and we know the resource category default values. If the // existing setting is the default, we'll allow it to be changed by the plugin. It's possible // the user wants it the old way, and can set it back, but given that avail collection is // critical to agent perf, we'll assume plugin knows best in this case. This only happens // if the interval is at the default, a non-default value set by an earlier rev of the // plugin will not be updated. boolean isAvail = MeasurementDefinition.AVAILABILITY_NAME.equals(newDefinition.getName()); long defaultInterval = (ResourceCategory.SERVER == existingDefinition.getResourceType() .getCategory()) ? MeasurementDefinition.AVAILABILITY_DEFAULT_PERIOD_SERVER : MeasurementDefinition.AVAILABILITY_DEFAULT_PERIOD_SERVICE; boolean updateInterval = (isAvail && (defaultInterval == existingDefinition .getDefaultInterval())); existingDefinition.update(newDefinition, updateInterval); // we normally do not want to touch interval in case a user changed it, // but we cannot allow too-short of an interval, so override it if necessary if (existingDefinition.getDefaultInterval() < MeasurementSchedule.MINIMUM_INTERVAL) { existingDefinition.setDefaultInterval(MeasurementSchedule.MINIMUM_INTERVAL); LOG.info("Definition [" + existingDefinition + "] has too short of a default interval, setting to minimum"); } entityMgr.merge(existingDefinition); // There is nothing in the schedules that need to be updated. // We do not want to change schedules (such as collection interval) // because the user might have customized them. So leave them be. break; } } if (!found) { // It's new - create it LOG.info("Metadata update: Adding new " + newDefinition.getDataType().name().toLowerCase() + " definition [" + newDefinition.getDisplayName() + "] to type " + existingType + "..."); existingType.addMetricDefinition(newDefinition); entityMgr.persist(newDefinition); // Now create schedules for already existing resources scheduleMgr.createSchedulesForExistingResources(existingType, newDefinition); } } /* * Now delete outdated measurement definitions. First find them ... */ List<MeasurementDefinition> definitionsToDelete = new ArrayList<MeasurementDefinition>(); for (MeasurementDefinition existingDefinition : existingDefinitions) { if (!newType.getMetricDefinitions().contains(existingDefinition)) { definitionsToDelete.add(existingDefinition); } } // ... and remove them existingDefinitions.removeAll(definitionsToDelete); for (MeasurementDefinition definitionToDelete : definitionsToDelete) { LOG.info("Metadata update: Removing " + definitionToDelete.getDataType().name().toLowerCase() + " definition [" + definitionToDelete.getDisplayName() + "] from type " + existingType + "..."); measurementDefinitionMgr.removeMeasurementDefinition(definitionToDelete); } entityMgr.flush(); } // TODO what if they are null? --> delete everything from existingType // not needed see JBNADM-1639 } public static Set<MeasurementDefinition> getMetricDefinitions(ResourceType newType) { Set<MeasurementDefinition> result = newType.getMetricDefinitions(); result = (null == result) ? new HashSet<MeasurementDefinition>(1) : result; long period; switch (newType.getCategory()) { case PLATFORM: return result; case SERVER: period = MeasurementDefinition.AVAILABILITY_DEFAULT_PERIOD_SERVER; break; default: period = MeasurementDefinition.AVAILABILITY_DEFAULT_PERIOD_SERVICE; } MeasurementDefinition rhqAvailability = new MeasurementDefinition(newType, MeasurementDefinition.AVAILABILITY_NAME); rhqAvailability.setDefaultInterval(period); rhqAvailability.setDefaultOn(true); rhqAvailability.setCategory(MeasurementCategory.AVAILABILITY); rhqAvailability.setDisplayName(MeasurementDefinition.AVAILABILITY_DISPLAY_NAME); rhqAvailability.setDescription(MeasurementDefinition.AVAILABILITY_DESCRIPTION); rhqAvailability.setDataType(DataType.AVAILABILITY); rhqAvailability.setUnits(MeasurementUnits.NONE); // n/a protects against non-null rhqAvailability.setNumericType(NumericType.DYNAMIC); // n/a protects against non-null rhqAvailability.setDisplayType(DisplayType.DETAIL); // n/a protects against non-null // Add the built in metric if it is not defined. Otherwise, override only allowed fields if (!result.contains(rhqAvailability)) { result.add(rhqAvailability); } else { MeasurementDefinition override = null; for (MeasurementDefinition aResult : result) { override = aResult; if (override.equals(rhqAvailability)) { break; } } // don't let the override muck with fixed field values, only defaultOn and defaultInterval override.setCategory(MeasurementCategory.AVAILABILITY); override.setDisplayName(MeasurementDefinition.AVAILABILITY_DISPLAY_NAME); override.setDescription(MeasurementDefinition.AVAILABILITY_DESCRIPTION); override.setDataType(DataType.AVAILABILITY); override.setUnits(MeasurementUnits.NONE); // n/a protects against non-null override.setNumericType(NumericType.DYNAMIC); // n/a protects against non-null override.setDisplayType(DisplayType.DETAIL); // n/a protects against non-null } return result; } @Override public void deleteMetadata(ResourceType existingType) { if (LOG.isDebugEnabled()) { LOG.debug("Deleting metric definitions for " + existingType); } MeasurementDefinitionCriteria criteria = new MeasurementDefinitionCriteria(); criteria.addFilterResourceTypeId(existingType.getId()); criteria.setRestriction(COLLECTION_ONLY); // Remove the type's metric definitions. We do this separately, rather than just relying on cascade // upon deletion of the ResourceType, because the removeMeasurementDefinition() will also take care // of removing any associated schedules and those schedules' OOBs. List<MeasurementDefinition> definitions = Collections.emptyList(); do { for (MeasurementDefinition definition : definitions) { measurementDefinitionMgr.removeMeasurementDefinition(definition); } definitions = measurementDefinitionMgr.findMeasurementDefinitionsByCriteria(subjectMgr.getOverlord(), criteria); } while (!definitions.isEmpty()); } }