/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2012], VMWare, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.measurement.server.session; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PreDestroy; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.ObjectNotFoundException; import org.hibernate.UnresolvableObjectException; import org.hyperic.hq.agent.server.session.AgentDataTransferJob; import org.hyperic.hq.agent.server.session.AgentSynchronizer; import org.hyperic.hq.appdef.Agent; import org.hyperic.hq.appdef.AppService; import org.hyperic.hq.appdef.server.session.AppdefResource; import org.hyperic.hq.appdef.server.session.Application; import org.hyperic.hq.appdef.server.session.ApplicationDAO; import org.hyperic.hq.appdef.server.session.NewResourceVerifiedZevent; import org.hyperic.hq.appdef.server.session.Platform; import org.hyperic.hq.appdef.server.session.ResourceCreatedZevent; import org.hyperic.hq.appdef.server.session.ResourceRefreshZevent; import org.hyperic.hq.appdef.server.session.ResourceUpdatedZevent; import org.hyperic.hq.appdef.server.session.ResourceZevent; import org.hyperic.hq.appdef.server.session.Server; import org.hyperic.hq.appdef.server.session.Service; import org.hyperic.hq.appdef.shared.AgentManager; import org.hyperic.hq.appdef.shared.AgentNotFoundException; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.appdef.shared.AppdefEntityNotFoundException; import org.hyperic.hq.appdef.shared.AppdefEntityValue; import org.hyperic.hq.appdef.shared.AppdefResourceValue; import org.hyperic.hq.appdef.shared.AppdefUtil; import org.hyperic.hq.appdef.shared.ApplicationNotFoundException; import org.hyperic.hq.appdef.shared.ConfigFetchException; import org.hyperic.hq.appdef.shared.ConfigManager; import org.hyperic.hq.appdef.shared.InvalidConfigException; import org.hyperic.hq.authz.server.session.AuthzSubject; import org.hyperic.hq.authz.server.session.Resource; import org.hyperic.hq.authz.server.session.ResourceDeleteRequestedEvent; import org.hyperic.hq.authz.server.session.ResourceGroup; import org.hyperic.hq.authz.server.session.ResourceType; import org.hyperic.hq.authz.shared.AuthzConstants; import org.hyperic.hq.authz.shared.AuthzSubjectManager; import org.hyperic.hq.authz.shared.PermissionException; import org.hyperic.hq.authz.shared.PermissionManager; import org.hyperic.hq.authz.shared.ResourceGroupManager; import org.hyperic.hq.authz.shared.ResourceManager; import org.hyperic.hq.common.SystemException; import org.hyperic.hq.events.MaintenanceEvent; import org.hyperic.hq.management.shared.MeasurementInstruction; import org.hyperic.hq.measurement.MeasurementConstants; import org.hyperic.hq.measurement.MeasurementCreateException; import org.hyperic.hq.measurement.MeasurementNotFoundException; import org.hyperic.hq.measurement.TemplateNotFoundException; import org.hyperic.hq.measurement.agent.client.AgentMonitor; import org.hyperic.hq.measurement.ext.MeasurementEvent; import org.hyperic.hq.measurement.monitor.LiveMeasurementException; import org.hyperic.hq.measurement.monitor.MonitorAgentException; import org.hyperic.hq.measurement.shared.AvailabilityManager; import org.hyperic.hq.measurement.shared.MeasurementManager; import org.hyperic.hq.measurement.shared.SRNManager; import org.hyperic.hq.measurement.shared.TrackerManager; import org.hyperic.hq.product.MeasurementPluginManager; import org.hyperic.hq.product.Metric; import org.hyperic.hq.product.MetricValue; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.ProductPlugin; import org.hyperic.hq.product.shared.ProductManager; import org.hyperic.hq.util.Reference; import org.hyperic.hq.zevents.ZeventManager; import org.hyperic.util.Transformer; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.EncodingException; import org.hyperic.util.pager.PageControl; import org.hyperic.util.timer.StopWatch; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.transaction.annotation.Transactional; /** * The MeasurementManager provides APIs to deal with Measurement objects. */ @org.springframework.stereotype.Service @Transactional public class MeasurementManagerImpl implements MeasurementManager, ApplicationContextAware, ApplicationListener<ResourceDeleteRequestedEvent> { private final Log log = LogFactory.getLog(MeasurementManagerImpl.class); // XXX scottmf, need to re-evalutate why SAMPLE_SIZE is used private static final int SAMPLE_SIZE = 10; @Autowired private ResourceManager resourceManager; @Autowired private ResourceGroupManager resourceGroupManager; @Autowired private ApplicationDAO applicationDAO; @Autowired private PermissionManager permissionManager; @Autowired private AuthzSubjectManager authzSubjectManager; @Autowired private ConfigManager configManager; @Autowired private MetricDataCache metricDataCache; @Autowired private MeasurementDAO measurementDAO; @Autowired private MeasurementTemplateDAO measurementTemplateDAO; @Autowired private AgentManager agentManager; @Autowired private AgentMonitor agentMonitor; @Autowired private ApplicationContext applicationContext; @Autowired private ZeventManager zeventManager; @Autowired private SRNManager srnManager; @Autowired private AvailabilityManager availabilityManager; @Autowired private MeasurementInserterHolder measurementInserterHolder; @Autowired private AgentSynchronizer agentSynchronizer; // TODO: Resolve circular dependency with ProductManager private MeasurementPluginManager getMeasurementPluginManager() throws Exception { return (MeasurementPluginManager) applicationContext.getBean(ProductManager.class).getPluginManager( ProductPlugin.TYPE_MEASUREMENT); } /** * Translate a template string into a DSN */ private String translate(String tmpl, ConfigResponse config) { try { return getMeasurementPluginManager().translate(tmpl, config); } catch (Exception e) { return tmpl; } } /** * Enqueue a {@link MeasurementScheduleZevent} on the zevent queue * corresponding to the change in schedule for the measurement. * * @param dm The Measurement * @param interval The new collection interval. */ private void enqueueZeventForMeasScheduleChange(Measurement dm, long interval) { MeasurementScheduleZevent event = new MeasurementScheduleZevent(dm.getId().intValue(), interval); zeventManager.enqueueEventAfterCommit(event); } /** * Enqueue a {@link MeasurementScheduleZevent} on the zevent queue * corresponding to collection disabled for the measurements. * * @param mids The measurement ids. */ private void enqueueZeventsForMeasScheduleCollectionDisabled(Integer[] mids) { List<MeasurementScheduleZevent> events = new ArrayList<MeasurementScheduleZevent>( mids.length); for (Integer mid : mids) { if (mid != null) { events.add(new MeasurementScheduleZevent(mid.intValue(), 0)); } } zeventManager.enqueueEventsAfterCommit(events); } private Measurement createMeasurement(Resource instanceId, MeasurementTemplate mt, ConfigResponse props, long interval) throws MeasurementCreateException { String dsn = translate(mt.getTemplate(), props); return measurementDAO.create(instanceId, mt, dsn, interval); } /** * Remove Measurements that have been deleted from the DataCache * @param mids */ private void removeMeasurementsFromCache(Integer[] mids) { for (Integer mid : mids) { metricDataCache.remove(mid); } } public List<Measurement> createOrUpdateMeasurements(AppdefEntityID id, Integer[] templates, long[] intervals, ConfigResponse props, Reference<Boolean> updated) throws MeasurementCreateException, TemplateNotFoundException { if (log.isDebugEnabled()) { log.debug("createOrUpdateMeasurements called for aeid=" + id + ", config=" + props); } Resource resource = resourceManager.findResource(id); if (resource == null || resource.isInAsyncDeleteState()) { return Collections.emptyList(); } if (intervals.length != templates.length) { throw new IllegalArgumentException("The templates and intervals lists must be the same size"); } MeasurementTemplateDAO tDao = measurementTemplateDAO; MeasurementDAO dao = measurementDAO; List<Measurement> metrics = dao.findByTemplatesForInstance(templates, resource); // Put the metrics in a map for lookup Map<Integer, Measurement> lookup = new HashMap<Integer, Measurement>(metrics.size()); for (Measurement m : metrics) { lookup.put(m.getTemplate().getId(), m); } boolean anyMeasurementUpdated = false; ArrayList<Measurement> dmList = new ArrayList<Measurement>(); for (int i = 0; i < templates.length; i++) { MeasurementTemplate t = tDao.get(templates[i]); if (t == null) { continue; } Measurement m = lookup.get(templates[i]); if (m == null) { // No measurement, create it anyMeasurementUpdated = true; m = createMeasurement(resource, t, props, intervals[i]); } else { String dsn = translate(m.getTemplate().getTemplate(), props); boolean measurementUpdated = (m.isEnabled() != (intervals[i] != 0)); measurementUpdated = measurementUpdated || ( m.getInterval() != intervals[i]); measurementUpdated = measurementUpdated || (!m.getDsn().equals(dsn)); if (measurementUpdated) { m.setEnabled(intervals[i] != 0); m.setInterval(intervals[i]); m.setDsn(dsn); enqueueZeventForMeasScheduleChange(m, intervals[i]); anyMeasurementUpdated = anyMeasurementUpdated || measurementUpdated; } } dmList.add(m); } if (anyMeasurementUpdated) { ManualMeasurementScheduleZevent event = new ManualMeasurementScheduleZevent(Collections.singletonList(resource.getId())); zeventManager.enqueueEventAfterCommit(event); } if (updated != null) { updated.set(anyMeasurementUpdated || (null == updated.get() ? false : updated.get())); } return dmList; } public List<Measurement> createOrUpdateOrDeleteMeasurements(AuthzSubject subject, Resource resource, AppdefEntityID aeid, Collection<MeasurementInstruction> measurementInstructions, ConfigResponse props) throws MeasurementCreateException, PermissionException { if(log.isDebugEnabled()) { log.debug("createOrUpdateMeasurements called for resource=" + resource.getInstanceId() + ", config=" + props); } if(resource == null || resource.isInAsyncDeleteState()) { return Collections.emptyList(); } if((null == measurementInstructions) || measurementInstructions.isEmpty()) { log.debug("No measurement instructions provided, hence not changing any measurements."); return Collections.emptyList(); } permissionManager.checkModifyPermission(subject.getId(), aeid); final Integer[] templatesInMeasurementInstructions = getTemplateIds(measurementInstructions); ArrayList<Measurement> dmList = new ArrayList<Measurement>(); final Map<Integer, Collection<Measurement>> measurementsByTemplateId = measurementDAO.getMeasurementsForInstanceByTemplateIds(templatesInMeasurementInstructions, resource); boolean updated = false; for(MeasurementInstruction mi:measurementInstructions) { updated |= addOrUpdateMeasurement(resource, props, dmList, measurementsByTemplateId, mi); } List<Integer> measurementsToRemove = measurementDAO.getMeasurementsNotInTemplateIds(templatesInMeasurementInstructions, resource); if (!measurementsToRemove.isEmpty()) { updated = true; disableMeasurements(aeid, measurementsToRemove.toArray(new Integer[] {})); } if (log.isDebugEnabled()) { log.debug("scheduling measurements for aeid=" + aeid + ", config=" + props); } srnManager.scheduleInBackground(Collections.singletonList(aeid), true, updated); return dmList; } private boolean addOrUpdateMeasurement(Resource resource, ConfigResponse props, ArrayList<Measurement> resultingMeasurements, final Map<Integer, Collection<Measurement>> measurementsByTemplateId, MeasurementInstruction mi) throws MeasurementCreateException { MeasurementTemplate template = mi.getMeasurementTemplate(); final Collection<Measurement> measurements = measurementsByTemplateId.get(template.getId()); boolean updated = false; if(measurements == null) { // No measurement, create it updated = true; Measurement m = createMeasurement(resource, mi, props); resultingMeasurements.add(m); }else { for(Measurement measurement:measurements) { boolean measurementUpdated = (measurement.isEnabled() != mi.isDefaultOn()); measurementUpdated = measurementUpdated || (measurement.getInterval() != mi.getInterval()); updated = updated || measurementUpdated; if(measurementUpdated) { measurement.setEnabled(mi.isDefaultOn()); measurement.setInterval(mi.getInterval()); enqueueZeventForMeasScheduleChange(measurement, mi.getInterval()); resultingMeasurements.add(measurement); } } } return updated; } private Integer[] getTemplateIds(Collection<MeasurementInstruction> measurementInstructions) { final Transformer<MeasurementInstruction, Integer> t = new Transformer<MeasurementInstruction, Integer>() { @Override public Integer transform(MeasurementInstruction measurementInstruction) { final MeasurementTemplate measurementTemplate = measurementInstruction.getMeasurementTemplate(); return (null != measurementTemplate ? measurementTemplate.getId() : null); } }; final Integer[] templatesInMeasurementInstructions = t.transformToList(measurementInstructions).toArray(new Integer[] {}); return templatesInMeasurementInstructions; } private Measurement createMeasurement(Resource resource, MeasurementInstruction mi, ConfigResponse props) throws MeasurementCreateException { String dsn = translate(mi.getMeasurementTemplate().getTemplate(), props); return measurementDAO.create(resource, mi.getMeasurementTemplate(), dsn, mi.getInterval()); } /** * Create Measurements and enqueue for scheduling after commit */ public List<Measurement> createMeasurements(AuthzSubject subject, AppdefEntityID aeid, Integer[] templates, long[] intervals, ConfigResponse config) throws PermissionException, MeasurementCreateException, TemplateNotFoundException { // Authz check permissionManager.checkModifyPermission(subject.getId(), aeid); Reference<Boolean> updated = new Reference<Boolean>(false); List<Measurement> dmList = createOrUpdateMeasurements(aeid, templates, intervals, config, updated); if (log.isDebugEnabled()) { log.debug("scheduling measurements for aeid=" + aeid + ", config=" + config); } srnManager.scheduleInBackground(Collections.singletonList(aeid), true, updated.get()); return dmList; } /** * Create Measurement objects based their templates and default intervals * * @param templates List of Integer template IDs to add * @param id instance ID (appdef resource) the templates are for * @param props Configuration data for the instance * * @return a List of the associated Measurement objects */ public List<Measurement> createMeasurements(AuthzSubject subject, AppdefEntityID id, Integer[] templates, ConfigResponse props) throws PermissionException, MeasurementCreateException, TemplateNotFoundException { long[] intervals = new long[templates.length]; for (int i = 0; i < templates.length; i++) { MeasurementTemplate tmpl = measurementTemplateDAO.findById(templates[i]); intervals[i] = tmpl.getDefaultInterval(); } return createMeasurements(subject, id, templates, intervals, props); } /** */ @Transactional(readOnly = true) public Measurement findMeasurementById(Integer mid) { return measurementDAO.findById(mid); } @Transactional(readOnly=true) public Collection<MeasurementTemplate> getTemplatesByPrototype(Resource proto) { if (proto == null) { return Collections.emptyList(); } return measurementTemplateDAO.findTemplatesByMonitorableType(proto.getName()); } @Transactional(readOnly = true) public Map<Integer,Measurement> findMeasurementsByIds(final List<Integer> mids) { Integer[] midsArr = mids.toArray(new Integer[mids.size()]); List<Measurement> msmts = measurementDAO.findByIds(midsArr); Map<Integer,Measurement> midToMsmt = new HashMap<Integer,Measurement>(); for(Measurement msmt:msmts) { midToMsmt.put(msmt.getId(), msmt); } return midToMsmt; } /** * Create Measurement objects for an appdef entity based on default * templates. This method will only create them if there currently no * metrics enabled for the appdef entity. * * @param subject Spider subject * @param id appdef entity ID of the resource * @param mtype The string name of the plugin type * @param props Configuration data for the instance * * @return a List of the associated Measurement objects */ public List<Measurement> createDefaultMeasurements(AuthzSubject subject, AppdefEntityID id, String mtype, ConfigResponse props) throws TemplateNotFoundException, PermissionException, MeasurementCreateException { // We're going to make sure there aren't metrics already List<Measurement> dms = findMeasurements(subject, id, null, PageControl.PAGE_ALL); // Find the templates Collection<MeasurementTemplate> mts = measurementTemplateDAO .findTemplatesByMonitorableType(mtype); if (mts.size() == 0 || (dms.size() != 0 && dms.size() == mts.size())) { return dms; } Integer[] tids = new Integer[mts.size()]; long[] intervals = new long[mts.size()]; Iterator<MeasurementTemplate> it = mts.iterator(); for (int i = 0; it.hasNext(); i++) { MeasurementTemplate tmpl = it.next(); tids[i] = tmpl.getId(); if (tmpl.isDefaultOn()) intervals[i] = tmpl.getDefaultInterval(); else intervals[i] = 0; } return createMeasurements(subject, id, tids, intervals, props); } /** * Update the Measurements of a resource * */ private void updateMeasurements(AuthzSubject subject, AppdefEntityID id, ConfigResponse props) throws PermissionException, MeasurementCreateException { try { List<Measurement> all = measurementDAO.findByResource(resourceManager.findResource(id)); List<Measurement> mcol = new ArrayList<Measurement>(); for (Measurement dm : all) { // Translate all dsns dm.setDsn(translate(dm.getTemplate().getTemplate(), props)); // Now see which Measurements need to be rescheduled if (dm.isEnabled()) { mcol.add(dm); } } Integer[] templates = new Integer[mcol.size()]; long[] intervals = new long[mcol.size()]; int idx = 0; for (Iterator<Measurement> it = mcol.iterator(); it.hasNext(); idx++) { Measurement dm = it.next(); templates[idx] = dm.getTemplate().getId(); intervals[idx] = dm.getInterval(); } createMeasurements(subject, id, templates, intervals, props); } catch (TemplateNotFoundException e) { // Would not happen since we're creating measurements with the // template that we just looked up log.error(e); } } /** * Remove all measurements no longer associated with a resource. * @return The number of Measurement objects removed. */ public int removeOrphanedMeasurements(int batchSize) { StopWatch watch = new StopWatch(); MeasurementDAO dao = measurementDAO; List<Integer> mids = dao.findOrphanedMeasurements(batchSize); // Shrink the list down to MAX_MIDS so that we spread out the work over // successive data purges if (mids.size() > batchSize) { mids = mids.subList(0, batchSize); } if (mids.size() > 0) { applicationContext.publishEvent(new MetricsDeleteRequestedEvent(mids)); dao.deleteByIds(mids); } if (log.isDebugEnabled()) { log.debug("MeasurementManager.removeOrphanedMeasurements() " + watch); } return mids.size(); } /** * Look up a Measurement for a Resource and Measurement alias * @return a The Measurement for the Resource of the given alias. */ @Transactional(readOnly = true) public Measurement getMeasurement(AuthzSubject s, Resource r, String alias) throws MeasurementNotFoundException { Measurement m = measurementDAO.findByAliasAndID(alias, r); if (m == null) { throw new MeasurementNotFoundException(alias + " for " + r.getName() + " not found"); } return m; } /** * Get a Measurement by Id. */ @Transactional(readOnly = true) public Measurement getMeasurement(Integer mid) { return measurementDAO.get(mid); } /** * Get the live measurement values for a given resource. * @param id The id of the resource */ @Transactional(readOnly = true) public void getLiveMeasurementValues(AuthzSubject subject, AppdefEntityID id) throws PermissionException, LiveMeasurementException, MeasurementNotFoundException { List<Measurement> mcol = measurementDAO.findEnabledByResource(resourceManager.findResource(id), false); String[] dsns = new String[mcol.size()]; Integer availMeasurement = null; // For insert of AVAIL down Iterator<Measurement> it = mcol.iterator(); for (int i = 0; it.hasNext(); i++) { Measurement dm = it.next(); dsns[i] = dm.getDsn(); MeasurementTemplate template = dm.getTemplate(); if (template.getAlias().equals(Metric.ATTR_AVAIL)) { availMeasurement = dm.getId(); } } log.info("Getting live measurements for " + dsns.length + " measurements"); try { getLiveMeasurementValues(id, dsns, true); } catch (LiveMeasurementException e) { log.info("Resource " + id + " reports it is unavailable, setting " + "measurement ID " + availMeasurement + " to DOWN: " + e); // Only print the full stack trace in debug mode if (log.isDebugEnabled()) { log.error("Exception details: ", e); } if (availMeasurement != null) { MetricValue val = new MetricValue(MeasurementConstants.AVAIL_DOWN); List<DataPoint> l = new ArrayList<DataPoint>(1); l.add(new DataPoint(availMeasurement, val)); DataInserter<DataPoint> inserter = measurementInserterHolder.getAvailDataInserter(); synchronized (inserter.getLock()) { try { inserter.insertData(l); } catch (Exception exp) { log.warn("Problem inserting new availability data for measurement '" + availMeasurement + "'", exp); } } } } } /** * Count of metrics enabled for a particular entity * * @return a The number of metrics enabled for the given entity */ @Transactional(readOnly = true) public int getEnabledMetricsCount(AuthzSubject subject, AppdefEntityID id) { final Resource res = resourceManager.findResource(id); if (res == null || res.isInAsyncDeleteState()) { return 0; } final List<Measurement> mcol = measurementDAO.findEnabledByResource(res, false); return mcol.size(); } /** * @param subject {@link AuthzSubject} * @param resIdsToTemplIds {@link Map} of {@link Integer} of resourceIds to * {@link List} of templateIds * @return {@link Map} of {@link Resource} to {@link List} of * {@link Measurement}s * @throws PermissionException */ @Transactional(readOnly = true) public Map<Resource, List<Measurement>> findMeasurements( AuthzSubject subject, Map<Integer, List<Integer>> resIdsToTemplIds) throws PermissionException { Map<Resource, List<Measurement>> rtn = new HashMap<Resource, List<Measurement>>(); for (Map.Entry<Integer, List<Integer>> entry : resIdsToTemplIds.entrySet()) { Integer resId = entry.getKey(); List<Integer> templs = entry.getValue(); Integer[] tids = templs.toArray(new Integer[0]); Resource resource = resourceManager.findResourceById(resId); // checkModifyPermission(subject.getId(), appId); Integer resTypeId = resource.getResourceType().getId(); if (resTypeId.equals(AuthzConstants.authzGroup)) { ResourceGroup grp = resourceGroupManager.findResourceGroupById(subject, resource .getInstanceId()); Collection<Resource> mems = resourceGroupManager.getMembers(grp); for (Resource res : mems) { rtn.put(res, measurementDAO.findByTemplatesForInstance(tids, res)); } } else { rtn.put(resource, measurementDAO.findByTemplatesForInstance(tids, resource)); } } return rtn; } private List<Measurement> findTemplatesForInstance(final Resource rsc, final Integer[] tids, Map<Integer, Exception> failedResources) { List<Measurement> msmts = null; try { msmts = measurementDAO.findByTemplatesForInstance(tids, rsc); if (msmts==null || msmts.isEmpty()) { log.error("no measurement templates with the following ids can be assigned to resource " + rsc.getName() + ":\n" + tids); failedResources.put(rsc.getId(), null); } } catch (UnresolvableObjectException e) { // don't fail the whole group for a wrong resource IDs log.error(e); if (failedResources!=null) { failedResources.put(rsc.getId(),e); } } return msmts; } /** * similar to findMeasurements with the same parameters, with the difference that this one wont fail the whole process on resources which does not exists. * It would just not return them. The same goes for measurement names which does not exist. */ @Transactional(readOnly = true) public Map<Resource, List<Measurement>> findBulkMeasurements(AuthzSubject subject, Map<Integer, List<Integer>> resIdsToTemplIds, Map<Integer, Exception> failedResources) throws PermissionException { Map<Resource, List<Measurement>> rtn = new HashMap<Resource, List<Measurement>>(); Resource resource = null; for (Map.Entry<Integer, List<Integer>> entry : resIdsToTemplIds.entrySet()) { Integer rscId = null; try { rscId = entry.getKey(); resource = resourceManager.findResourceById(rscId); List<Integer> templs = entry.getValue(); if (templs==null || templs.isEmpty()) { continue; } Integer[] tids = new Integer[templs.size()]; templs.toArray(tids); Integer resTypeId = resource.getResourceType().getId(); if (resTypeId.equals(AuthzConstants.authzGroup)) { ResourceGroup grp = resourceGroupManager.findResourceGroupById(subject, resource.getInstanceId()); Collection<Resource> mems = resourceGroupManager.getMembers(grp); List<Measurement> msmts = null; for (Resource member : mems) { msmts = findTemplatesForInstance(member, tids, failedResources); if (msmts!=null && !msmts.isEmpty()) { rtn.put(member, msmts); } } } else { List<Measurement> msmts = findTemplatesForInstance(resource, tids, failedResources); if (msmts!=null && !msmts.isEmpty()) { rtn.put(resource, msmts); } } } catch (UnresolvableObjectException e) { // don't fail the whole request for wrong resource IDs log.error(e); if (failedResources!=null) { failedResources.put(rscId,e); } resource = null; continue; } } return rtn; } /** * Find the Measurement corresponding to the given MeasurementTemplate id * and instance id. * * @param tid The MeasurementTemplate id * @param aeid The entity id. * @return a Measurement value */ @Transactional(readOnly = true) public Measurement findMeasurement(AuthzSubject subject, Integer tid, AppdefEntityID aeid) throws MeasurementNotFoundException { List<Measurement> metrics = measurementDAO.findByTemplatesForInstance( new Integer[] { tid }, resourceManager.findResource(aeid)); if (metrics.size() == 0) { throw new MeasurementNotFoundException("No measurement found " + "for " + aeid + " with " + "template " + tid); } return metrics.get(0); } /** * Look up a Measurement, allowing for the query to return a stale copy of * the Measurement (for efficiency reasons). * * @param subject The subject. * @param tid The template Id. * @param iid The instance Id. * @param allowStale <code>true</code> to allow stale copies of an alert * definition in the query results; <code>false</code> to never allow * stale copies, potentially always forcing a sync with the database. * @return The Measurement */ @Transactional(readOnly = true) public Measurement findMeasurement(AuthzSubject subject, Integer tid, Integer iid, boolean allowStale) throws MeasurementNotFoundException { Measurement dm = measurementDAO.findByTemplateForInstance(tid, iid, allowStale); if (dm == null) { throw new MeasurementNotFoundException("No measurement found " + "for " + iid + " with " + "template " + tid); } return dm; } /** * Look up a list of Measurements for a template and instances * * @return a list of Measurement's */ @Transactional(readOnly = true) public List<Measurement> findMeasurements(AuthzSubject subject, Integer tid, AppdefEntityID[] aeids) { ArrayList<Measurement> results = new ArrayList<Measurement>(); for (AppdefEntityID aeid : aeids) { results.addAll(measurementDAO.findByTemplatesForInstance(new Integer[] { tid }, resourceManager.findResource(aeid))); } return results; } /** * Look up a list of Measurements for a template and instances * * @return An array of Measurement ids. */ @Transactional(readOnly = true) public Integer[] findMeasurementIds(AuthzSubject subject, Integer tid, Integer[] ids) { List<Integer> results = measurementDAO.findIdsByTemplateForInstances(tid, ids); return results.toArray(new Integer[results.size()]); } /** * Look up a list of Measurements for a category XXX: Why is this method * called findMeasurements() but only returns enabled measurements if cat == * null?? * * @return a List of Measurement objects. */ @Transactional(readOnly = true) public List<Measurement> findMeasurements(AuthzSubject subject, AppdefEntityID id, String cat, PageControl pc) { List<Measurement> meas; // See if category is valid boolean sorted = (pc.getSortorder() != PageControl.SORT_UNSORTED) ? true : false; if (cat == null || Arrays.binarySearch(MeasurementConstants.VALID_CATEGORIES, cat) < 0) { meas = measurementDAO.findEnabledByResource(resourceManager.findResource(id), sorted); } else { meas = measurementDAO.findByResourceForCategory(resourceManager.findResource(id), cat); } return meas; } /** * @param aeids {@link List} of {@link Resource}s * @return {@link Map} of {@link Integer} representing resourceId to * {@link List} of {@link Measurement}s */ public Map<Integer,List<Measurement>> getEnabledNonAvailMeasurements(List<Resource> resources) { return measurementDAO.findEnabledByResources(resources, false); } /** * @param aeids {@link List} of {@link Resource}s * @return {@link Map} of {@link Integer} representing resourceId to * {@link List} of {@link Measurement}s */ public Map<Integer,List<Measurement>> getEnabledMeasurements(List<Resource> resources) { return measurementDAO.findEnabledByResources(resources, true); } /** * Look up a list of enabled Measurements for a category * * @return a list of {@link Measurement} */ @Transactional(readOnly = true) public List<Measurement> findEnabledMeasurements(AuthzSubject subject, AppdefEntityID id, String cat) { List<Measurement> mcol; // See if category is valid if (cat == null || Arrays.binarySearch(MeasurementConstants.VALID_CATEGORIES, cat) < 0) { mcol = measurementDAO.findEnabledByResource(resourceManager.findResource(id), true); } else { mcol = measurementDAO.findByResourceForCategory(resourceManager.findResource(id), cat); } return mcol; } /** * Look up a List of designated Measurements for an entity * * @return A List of Measurements */ @Transactional(readOnly = true) public List<Measurement> findDesignatedMeasurements(AppdefEntityID id) { return measurementDAO.findDesignatedByResource(resourceManager.findResource(id)); } /** * Look up a list of designated Measurements for an entity for a category * * @return A List of Measurements */ @Transactional(readOnly = true) public List<Measurement> findDesignatedMeasurements(AuthzSubject subject, AppdefEntityID id, String cat) { return measurementDAO.findDesignatedByResourceForCategory(resourceManager.findResource(id), cat); } /** * Look up a list of designated Measurements for an group for a category * * @return A List of Measurements */ @Transactional(readOnly = true) public List<Measurement> findDesignatedMeasurements(AuthzSubject subject, ResourceGroup g, String cat) { return measurementDAO.findDesignatedByCategoryForGroup(g, cat); } @Transactional(readOnly=true) public long getMaxCollectionInterval(ResourceGroup g, Integer templateId) { Long max = measurementDAO.getMaxCollectionInterval(g, templateId); if (max == null) { throw new IllegalArgumentException("Invalid template id =" + templateId + " for resource " + "group " + g.getId()); } return max.longValue(); } @Transactional(readOnly=true) public List<Measurement> getMetricsCollecting(ResourceGroup g, Integer templateId) { return measurementDAO.getMetricsCollecting(g, templateId); } /** * @param aeids {@link List} of {@link AppdefEntityID}s * @return {@link Map} of {@link Integer} representing resourceId to * {@link List} of {@link Measurement}s * */ public Map<Integer,List<Measurement>> findEnabledMeasurements(Collection<AppdefEntityID> aeids) { final List<Resource> resources = new ArrayList<Resource>(aeids.size()); for (AppdefEntityID aeid : aeids) { resources.add(resourceManager.findResource(aeid)); } return measurementDAO.findEnabledByResources(resources, true); } /** * Get an Availabilty Measurement by AppdefEntityId * @deprecated Use getAvailabilityMeasurement(Resource) instead. * */ @Deprecated @Transactional(readOnly = true) public Measurement getAvailabilityMeasurement(AuthzSubject subject, AppdefEntityID id) { return getAvailabilityMeasurement(resourceManager.findResource(id)); } /** * Get an Availability Measurement by Resource. May return null. */ @Transactional(readOnly = true) public Measurement getAvailabilityMeasurement(Resource r) { return measurementDAO.findAvailMeasurement(r); } /** * Look up a list of Measurement objects by category * */ @Transactional(readOnly = true) public List<Measurement> findMeasurementsByCategory(String cat) { return measurementDAO.findByCategory(cat); } /** * Look up a Map of Measurements for a Category * * XXX: This method needs to be re-thought. It only returns a single * designated metric per category even though HQ supports multiple * designates per category. * * @return A List of designated Measurements keyed by AppdefEntityID * */ @Transactional(readOnly = true) public Map<AppdefEntityID, Measurement> findDesignatedMeasurements(AuthzSubject subject, AppdefEntityID[] ids, String cat) throws MeasurementNotFoundException { if (ids.length == 0) { return new HashMap<AppdefEntityID, Measurement>(0,1); } Map<AppdefEntityID, Measurement> midMap = new HashMap<AppdefEntityID, Measurement>(ids.length); ArrayList<Resource> resources = new ArrayList<Resource>(ids.length); for (AppdefEntityID id : ids) { resources.add(resourceManager.findResource(id)); } List<Measurement> list = measurementDAO.findDesignatedByResourcesForCategory(resources, cat); for (Measurement m:list) { midMap.put(AppdefUtil.newAppdefEntityId(m.getResource()), m); } return midMap; } /** * @return {@link Map} of {@link Integer} to {@link List} of * {@link Measurement}s, Integer => Resource.getId(), */ @Transactional(readOnly = true) public Map<Integer, List<Measurement>> getAvailMeasurements(Collection<?> resources) { final Map<Resource, List<Measurement>> map = new HashMap<Resource, List<Measurement>>(); Collection<Measurement> measurements = getAvailMeasurements(resources, map); if (measurements == null) { measurements = Collections.emptyList(); } final Map<Integer, List<Measurement>> rtn = new HashMap<Integer, List<Measurement>>(); for (final Map.Entry<Resource, List<Measurement>> entry : map.entrySet()) { final Resource r = entry.getKey(); final List<Measurement> list = entry.getValue(); rtn.put(r.getId(), list); } for (final Measurement m : measurements) { rtn.put(m.getResource().getId(), Collections.singletonList(m)); } return rtn; } /** * @return {@link Map} of {@link Resource} to {@link List} of {@link Measurement}s */ @Transactional(readOnly = true) public Map<Resource, List<Measurement>> getAvailMeasurementsByResource(Collection<?> resources) { final Map<Resource, List<Measurement>> rtn = new HashMap<Resource, List<Measurement>>(); final Collection<Measurement> measurements = getAvailMeasurements(resources, rtn); // may be null if measurements have not been configured if (measurements != null && !measurements.isEmpty()) { for (final Measurement m : measurements) { rtn.put(m.getResource(), Collections.singletonList(m)); } } return rtn; } private Collection<Measurement> getAvailMeasurements(Collection<?> objects, Map<Resource, List<Measurement>> map) { final List<Resource> resources = new ArrayList<Resource>(objects.size()); for (final Object o : objects) { Resource resource = null; try { resource = getResourceFromObject(o); if (resource == null || resource.isInAsyncDeleteState()) { continue; } } catch (ObjectNotFoundException e) { log.debug(e,e); continue; } final ResourceType type = resource.getResourceType(); if (type.getId().equals(AuthzConstants.authzGroup)) { final ResourceGroup grp = resourceGroupManager.getResourceGroupByResource(resource); map.put(resource, measurementDAO.findAvailMeasurements(grp)); } else if (type.getId().equals(AuthzConstants.authzApplication)) { map.putAll(getAvailMeas(resource)); } else { map.put(resource, Collections.<Measurement> emptyList()); resources.add(resource); } } return measurementDAO.findAvailMeasurements(resources); } /** * TODO: scottmf, need to do some more work to handle other authz resource * types other than platform, server, service, and group */ private Resource getResourceFromObject(Object o) { if (o == null) { return null; } else if (o instanceof AppdefEntityValue) { AppdefEntityValue rv = (AppdefEntityValue) o; AppdefEntityID aeid = rv.getID(); return resourceManager.findResource(aeid); } else if (o instanceof AppdefEntityID) { AppdefEntityID aeid = (AppdefEntityID) o; return resourceManager.findResource(aeid); } else if (o instanceof AppdefResource) { AppdefResource r = (AppdefResource) o; return resourceManager.findResource(r.getEntityId()); } else if (o instanceof Resource) { return (Resource) o; } else if (o instanceof ResourceGroup) { ResourceGroup grp = (ResourceGroup) o; return grp.getResource(); } else if (o instanceof AppdefResourceValue) { AppdefResourceValue r = (AppdefResourceValue) o; AppdefEntityID aeid = r.getEntityId(); return resourceManager.findResource(aeid); } else { return resourceManager.findResourceById((Integer) o); } } private Application findApplicationById(AuthzSubject subject, Integer id) throws ApplicationNotFoundException, PermissionException { try { Application app = applicationDAO.findById(id); permissionManager.checkViewPermission(subject, app.getEntityId()); return app; } catch (ObjectNotFoundException e) { throw new ApplicationNotFoundException(id, e); } } private final Map<Resource, List<Measurement>> getAvailMeas(Resource application) { final Integer typeId = application.getResourceType().getId(); if (!typeId.equals(AuthzConstants.authzApplication)) { return Collections.emptyMap(); } final AuthzSubject overlord = authzSubjectManager.getOverlordPojo(); try { final Application app = findApplicationById(overlord, application.getInstanceId()); final Collection<AppService> appServices = app.getAppServices(); final List<Resource> resources = new ArrayList<Resource>(appServices.size()); for (AppService appService : appServices) { resources.addAll(getAppResources(appService)); } return getAvailMeasurementsByResource(resources); } catch (ApplicationNotFoundException e) { log.warn("cannot find Application by id = " + application.getInstanceId()); } catch (PermissionException e) { log.error("error finding application using overlord", e); } return Collections.emptyMap(); } private final List<Resource> getAppResources(AppService appService) { if (!appService.isIsGroup()) { final Service service = appService.getService(); if (service == null || service.getResource() == null || service.getResource().isInAsyncDeleteState()) { return Collections.emptyList(); } return Collections.singletonList(service.getResource()); } final ResourceGroup group = appService.getResourceGroup(); final Resource resource = group.getResource(); if (resource == null || resource.isInAsyncDeleteState()) { return Collections.emptyList(); } return new ArrayList<Resource>(resourceGroupManager.getMembers(group)); } /** * Look up a list of Measurement intervals for template IDs. * * @return a map keyed by template ID and values of metric intervals There * is no entry if a metric is disabled or does not exist for the * given entity or entities. However, if there are multiple * entities, and the intervals differ or some enabled/not enabled, * then the value will be "0" to denote varying intervals. */ @Transactional(readOnly = true) public Map<Integer, Long> findMetricIntervals(AuthzSubject subject, AppdefEntityID[] aeids, Integer[] tids) { final Long disabled = new Long(-1); MeasurementDAO ddao = measurementDAO; Map<Integer, Long> intervals = new HashMap<Integer, Long>(tids.length); for (AppdefEntityID aeid : aeids) { Resource res = resourceManager.findResource(aeid); List<Measurement> metrics = ddao.findByTemplatesForInstance(tids, res); for (Measurement dm : metrics) { Long interval = new Long(dm.getInterval()); if (!dm.isEnabled()) { interval = disabled; } Integer templateId = dm.getTemplate().getId(); Long previous = intervals.get(templateId); if (previous == null) { intervals.put(templateId, interval); } else { if (!previous.equals(interval)) { intervals.put(templateId, new Long(0)); } } } } // Filter by template IDs, since we only pay attention to what was // passed, but may have more than that in our map. for (Integer tid : tids) { if (!intervals.containsKey(tid)) { intervals.put(tid, null); } } // Copy the keys, since we are going to be modifying the interval map Set<Integer> keys = new HashSet<Integer>(intervals.keySet()); for (Integer templateId : keys) { if (disabled.equals(intervals.get(templateId))) { // Disabled // so don't return it intervals.remove(templateId); } } return intervals; } private AtomicBoolean loaded = new AtomicBoolean(false); @Transactional(readOnly = true) public void findAllEnabledMeasurementsAndTemplates() { if (loaded.get()) { return; } log.info("Commencing Measurement cache preload sequence") ; final StopWatch watch = new StopWatch(); final List<Object[]> list = measurementDAO.findAllEnabledMeasurementsAndTemplates(); log.info("Finished Measurement cache preload equence of " + list.size() + " entries in " + watch + " seconds") ; loaded.set(true); } /** * Set the interval of Measurements based their template ID's Enable * Measurements and enqueue for scheduling after commit * */ @Transactional(readOnly = true) public void enableMeasurements(AuthzSubject subject, AppdefEntityID[] aeids, Integer[] mtids, long interval) throws MeasurementNotFoundException, MeasurementCreateException, TemplateNotFoundException, PermissionException { // Create a list of IDs Integer[] iids = new Integer[aeids.length]; for (int i = 0; i < aeids.length; i++) { permissionManager.checkModifyPermission(subject.getId(), aeids[i]); iids[i] = aeids[i].getId(); } List<Integer> mids = new ArrayList<Integer>(aeids.length * mtids.length); for (Integer mtid : mtids) { mids.addAll(measurementDAO.findIdsByTemplateForInstances(mtid, iids)); } for (Integer mid : mids) { final Measurement m = measurementDAO.findById(mid); m.setEnabled(true); m.setInterval(interval); } // Update the agent schedule List<AppdefEntityID> toSchedule = Arrays.asList(aeids); srnManager.scheduleInBackground(toSchedule, true, true); } /** * Enable a collection of metrics, enqueue for scheduling after commit */ public void enableMeasurements(AuthzSubject subject, Integer[] mids) throws PermissionException { final StopWatch watch = new StopWatch(); final List<AppdefEntityID> appIdList = new ArrayList<AppdefEntityID>(); final List<Integer> midsList = Arrays.asList(mids); final boolean debug = log.isDebugEnabled(); if (debug) watch.markTimeBegin("setEnabled"); for (Integer mid : midsList) { final Measurement meas = measurementDAO.get(mid); if (!meas.isEnabled()) { final Resource resource = meas.getResource(); final AppdefEntityID appId = AppdefUtil.newAppdefEntityId(resource); permissionManager.checkModifyPermission(subject.getId(), appId); appIdList.add(appId); meas.setEnabled(true); } } if (debug) watch.markTimeEnd("setEnabled"); srnManager.scheduleInBackground(appIdList, true, true); if (debug) log.debug("enableMeasurements: total=" + appIdList.size() + ", time=" + watch); } /** * Enable the Measurement and enqueue for scheduling after commit */ public void enableMeasurement(AuthzSubject subject, Integer mId, long interval) throws PermissionException { final List<Integer> mids = Collections.singletonList(mId); Measurement meas = measurementDAO.get(mId); if (meas.isEnabled()) { return; } Resource resource = meas.getResource(); AppdefEntityID appId = AppdefUtil.newAppdefEntityId(resource); permissionManager.checkModifyPermission(subject.getId(), appId); MeasurementDAO dao = measurementDAO; for (Integer mid : mids) { final Measurement m = dao.findById(mid); m.setEnabled(true); m.setInterval(interval); } final List<AppdefEntityID> aeids = Collections.singletonList(appId); srnManager.scheduleInBackground(aeids, true, true); } /** * Enable the default on metrics for a given resource, enqueue for * scheduling after commit */ public void enableDefaultMeasurements(AuthzSubject subj, Resource r) throws PermissionException { AppdefEntityID appId = AppdefUtil.newAppdefEntityId(r); permissionManager.checkModifyPermission(subj.getId(), appId); boolean sendToAgent = false; List<Measurement> metrics = measurementDAO.findDefaultsByResource(r); for (Measurement dm : metrics) { if (!dm.isEnabled()) { dm.setEnabled(true); sendToAgent = true; } } if (sendToAgent) { List<AppdefEntityID> aeids = Collections.singletonList(appId); srnManager.scheduleInBackground(aeids, true, true); } } /** * @throws PermissionException */ public void updateMeasurementInterval(AuthzSubject subject, Integer mId, long interval) throws PermissionException { Measurement meas = measurementDAO.get(mId); meas.setEnabled((interval != 0)); meas.setInterval(interval); Resource resource = meas.getResource(); AppdefEntityID appId = AppdefUtil.newAppdefEntityId(resource); permissionManager.checkModifyPermission(subject.getId(), appId); enqueueZeventForMeasScheduleChange(meas, interval); } /** * Disable all measurements for the given resources. * * @param agentId The entity id to use to look up the agent connection * @param ids The list of entitys to unschedule * * NOTE: This method requires all entity ids to be monitored by the * same agent as specified by the agentId */ public void disableMeasurements(AuthzSubject subject, AppdefEntityID agentId, AppdefEntityID[] ids) throws PermissionException, AgentNotFoundException { Agent agent = agentManager.getAgent(agentId); disableMeasurements(subject, agent, ids, false); } public void disableMeasurementsForDeletion(AuthzSubject subject, Agent agent, AppdefEntityID[] ids) throws PermissionException { if (agent == null || ids == null) { return; } List<Resource> resources = new ArrayList<Resource>(); for (int i = 0; i < ids.length; i++) { permissionManager.checkModifyPermission(subject.getId(), ids[i]); resources.add(resourceManager.findResource(ids[i])); } List<Measurement> mcol = measurementDAO.findByResources(resources); Integer[] mids = new Integer[mcol.size()]; Iterator<Measurement> it = mcol.iterator(); for (int j = 0; it.hasNext(); j++) { Measurement dm = it.next(); dm.setEnabled(false); mids[j] = dm.getId(); } removeMeasurementsFromCache(mids); enqueueZeventsForMeasScheduleCollectionDisabled(mids); zeventManager.enqueueEventAfterCommit(new AgentUnscheduleZevent(Arrays.asList(ids), agent.getAgentToken())); } /** * Disable all measurements for the given resources. * * @param agent The agent for the given resources * @param ids The list of entitys to unschedule * @param isAsyncDelete Indicates whether it is for async delete * @ejb:interface-method * * NOTE: This method requires all entity ids to be monitored by the same * agent as specified by the agent */ public void disableMeasurements(AuthzSubject subject, Agent agent, AppdefEntityID[] ids, boolean isAsyncDelete) throws PermissionException { final boolean debug = log.isDebugEnabled(); for (int i = 0; i < ids.length; i++) { permissionManager.checkModifyPermission(subject.getId(), ids[i]); List<Measurement> mcol = null; Resource res = resourceManager.findResource(ids[i]); if (isAsyncDelete) { // For asynchronous deletes, we need to get all measurements // because some disabled measurements are not unscheduled // from the agent (like during the maintenance window) and // we need to unschedule these measurements mcol = findMeasurements(subject, res); } else { mcol = measurementDAO.findEnabledByResource(res, false); } if (mcol.isEmpty()) { if (debug) { log.debug("No measurements to disable for resource[" + ids[i] + "], isAsyncDelete=" + isAsyncDelete); } continue; } Integer[] mids = new Integer[mcol.size()]; Iterator<Measurement> it = mcol.iterator(); for (int j = 0; it.hasNext(); j++) { Measurement dm = it.next(); dm.setEnabled(false); mids[j] = dm.getId(); } removeMeasurementsFromCache(mids); enqueueZeventsForMeasScheduleCollectionDisabled(mids); } zeventManager.enqueueEventAfterCommit(new AgentUnscheduleZevent(Arrays.asList(ids), agent.getAgentToken())); } /** * Disable all Measurements for a resource * */ public void disableMeasurements(AuthzSubject subject, AppdefEntityID id) throws PermissionException { // Authz check permissionManager.checkModifyPermission(subject.getId(), id); disableMeasurements(subject, resourceManager.findResource(id)); } /** * Disable all Measurements for a resource * */ public void disableMeasurements(AuthzSubject subject, Resource res) throws PermissionException { List<Measurement> mcol = measurementDAO.findEnabledByResource(res, false); if (mcol.size() == 0) { return; } Integer[] mids = new Integer[mcol.size()]; Iterator<Measurement> it = mcol.iterator(); for (int i = 0; it.hasNext(); i++) { Measurement dm = it.next(); dm.setEnabled(false); mids[i] = dm.getId(); } removeMeasurementsFromCache(mids); enqueueZeventsForMeasScheduleCollectionDisabled(mids); try { final AppdefEntityID aeid = AppdefUtil.newAppdefEntityId(res); final Agent agent = agentManager.getAgent(aeid); final AgentUnscheduleZevent zevent = new AgentUnscheduleZevent(Collections.singletonList(aeid), agent.getAgentToken()); zeventManager.enqueueEventAfterCommit(zevent); } catch (AgentNotFoundException e) { log.error(e,e); } } /** * XXX: not sure why all the findMeasurements require an authz if they do * not check the viewPermissions?? */ @Transactional(readOnly = true) public List<Measurement> findMeasurements(AuthzSubject subject, Resource res) { return measurementDAO.findByResource(res); } /** * Disable measurements for an instance Enqueues reschedule events after * commit * */ public void disableMeasurements(AuthzSubject subject, AppdefEntityID id, Integer[] tids) throws PermissionException { // Authz check permissionManager.checkModifyPermission(subject.getId(), id); Resource resource = resourceManager.findResource(id); List<Measurement> mcol = measurementDAO.findByResource(resource); HashSet<Integer> tidSet = null; if (tids != null) { tidSet = new HashSet<Integer>(Arrays.asList(tids)); } List<Integer> toUnschedule = new ArrayList<Integer>(); for (Measurement dm : mcol) { // Check to see if we need to remove this one if (tidSet != null && !tidSet.contains(dm.getTemplate().getId())) { continue; } dm.setEnabled(false); toUnschedule.add(dm.getId()); } Integer[] mids = toUnschedule.toArray(new Integer[toUnschedule.size()]); disableMeasurements(id, mids); ManualMeasurementScheduleZevent event = new ManualMeasurementScheduleZevent(Collections.singletonList(resource.getId())); zeventManager.enqueueEventAfterCommit(event); } private void disableMeasurements(AppdefEntityID id, Integer[] mids) { removeMeasurementsFromCache(mids); enqueueZeventsForMeasScheduleCollectionDisabled(mids); final List<AppdefEntityID> aeids = Collections.singletonList(id); srnManager.scheduleInBackground(aeids, true, true); } /** * Disable or enable measurements for a collection of resources * during a maintenance window */ public List<DataPoint> enableMeasurements(AuthzSubject admin, MaintenanceEvent event, Collection<Resource> resources) { final List<DataPoint> rtn = new ArrayList<DataPoint>(resources.size()); for (Resource resource : resources) { // HQ-1653: Only disable/enable availability measurements // TODO: G (when AvailabilityManager is convered) List<Measurement> measurements = availabilityManager.getAvailMeasurementChildren(resource, AuthzConstants.ResourceEdgeContainmentRelation); measurements.add(availabilityManager.getAvailMeasurement(resource)); rtn.addAll(manageAvailabilityMeasurements(event, measurements)); } return rtn; } /** * @return {@link List} of {@link DataPoint}s to insert into db * Disable or enable availability measurements */ private List<DataPoint> manageAvailabilityMeasurements(MaintenanceEvent event, Collection<Measurement> measurements) { if (measurements == null || measurements.isEmpty()) { return Collections.emptyList(); } Integer key = null; final List<DataPoint> availDataPoints = new ArrayList<DataPoint>(measurements.size()); final boolean debug = log.isDebugEnabled(); final long eventStart = event.getStartTime(); for (Measurement m : measurements) { if (m == null) { continue; } key = m.getId(); if (!event.getMeasurements().contains(key)) { if (event.activate() && !m.isEnabled()) { m.setEnabled(true); if (debug) { log.debug("enabling mid=" + m.getId() + " for maintenance window end"); } } else if (!event.activate() && m.isEnabled()) { m.setEnabled(false); if (debug) { log.debug("disabling mid=" + m.getId() + " for maintenance window begin"); } // [HQ-1837] only create "pause" datapoint to mark the // beginning // of the downtime window availDataPoints.add(getPausedDataPoint(m, eventStart)); } event.getMeasurements().add(key); } else { if (debug) { log.debug("Availability measurement already processed. " + "Skipping measurement [id=" + key + "] for " + event); } } } return availDataPoints; } private final DataPoint getPausedDataPoint(Measurement m, long time) { return new DataPoint(m.getId().intValue(), MeasurementConstants.AVAIL_PAUSED, time); } /* public void syncPluginMetrics(String plugin) { List<java.lang.Number[]> entities = measurementDAO.findMetricsCountMismatch(plugin); AuthzSubject overlord = authzSubjectManager.getOverlordPojo(); for (java.lang.Number[] vals : entities) { java.lang.Number type = vals[0]; java.lang.Number id = vals[1]; AppdefEntityID aeid = new AppdefEntityID(type.intValue(), id.intValue()); try { log.info("syncPluginMetrics sync'ing metrics for " + aeid); ConfigResponse c = configManager.getMergedConfigResponse(overlord, ProductPlugin.TYPE_MEASUREMENT, aeid, true); enableDefaultMetrics(overlord, aeid, c, false); } catch (AppdefEntityNotFoundException e) { log.debug(e,e); } catch (PermissionException e) { // Quite impossible log.error(e,e); assert (false); } catch (ConfigFetchException e) { log.error(e,e); } catch (EncodingException e) { log.error(e,e); } } } */ /** * Gets a summary of the metrics which are scheduled for collection, across * all resource types and metrics. * * @return a list of {@link CollectionSummary} beans */ @Transactional(readOnly = true) public List<CollectionSummary> findMetricCountSummaries() { return measurementDAO.findMetricCountSummaries(); } /** * Find a list of tuples (of size 4) consisting of the {@link Agent} the * {@link Platform} it manages the {@link Server} representing the Agent the * {@link Measurement} that contains the Server Offset value * */ @Transactional(readOnly = true) public List<Object[]> findAgentOffsetTuples() { return measurementDAO.findAgentOffsetTuples(); } /** * Get the # of metrics that each agent is collecting. * * @return a map of {@link Agent} onto Longs indicating how many metrics * that agent is collecting. */ @Transactional(readOnly = true) public Map<Agent, Long> findNumMetricsPerAgent() { return measurementDAO.findNumMetricsPerAgent(); } /** * Handle events from the {@link MeasurementEnabler}. This method is * required to place the operation within a transaction (and session) * */ public void handleCreateRefreshEvents(List<ResourceZevent> events) { List<AppdefEntityID> eids = new ArrayList<AppdefEntityID>(); final boolean debug = log.isDebugEnabled(); for (ResourceZevent z : events) { if (debug) log.debug("handling event: " + z); AuthzSubject subject = authzSubjectManager.findSubjectById(z.getAuthzSubjectId()); AppdefEntityID aeid = z.getAppdefEntityID(); final Resource r = resourceManager.findResource(aeid); if (r == null || r.isInAsyncDeleteState()) { continue; } /** * Create - create new measurements and schedule them to the agent * Refresh - Simple resend = schedule current measurement to the agent with no update. * Most commonly will happen if agent is reinitialized (data dir erased). * Update - Config changed - update measurements with new config and schedule to agent. */ boolean isCreate = z instanceof ResourceCreatedZevent; boolean isRefresh = z instanceof ResourceRefreshZevent; boolean isUpdate = z instanceof ResourceUpdatedZevent; boolean isVerified = z instanceof NewResourceVerifiedZevent; try { ConfigResponse config = configManager.getMergedConfigResponse(subject, ProductPlugin.TYPE_MEASUREMENT, aeid, true); if (debug) log.debug("for event " + z + "\ngot config response:\n" + config); boolean verifyConfig = true; if (isCreate) { if (config == null || config.size() == 0) { //If this is the creation of a new measurement we will wait for 2 seconds //so that when we will call the getMergedConfigResponse() method for this resource all //the information will be there. Fix for Jira bug [HQ-3876] if (debug) log.debug("for event " + z + "config response was null, retrying"); try{ Thread.sleep(2000); } catch (InterruptedException e) { } config = configManager.getMergedConfigResponse(subject, ProductPlugin.TYPE_MEASUREMENT, aeid, true); if (debug) log.debug("for event " + z + "\n (2nd try) got config response:\n" + config); } } else if (isUpdate) { verifyConfig = ((ResourceUpdatedZevent) z).verifyConfig(); if (debug) log.debug("Updated metric schedule for [" + aeid + "]"); eids.add(aeid); } else if (isRefresh) { if(debug) log.debug("Refreshing metric schedule for [" + aeid + "]"); eids.add(aeid); continue; } else if (isVerified) { NewResourceVerifiedZevent verifiedEvent = ((NewResourceVerifiedZevent) z); if (!verifiedEvent.isSuccess()) { configManager.setValidationError(subject, aeid, verifiedEvent.getFailureEx().getMessage()); continue; } verifyConfig = false; if (debug) log.debug("Config verified for [" + aeid + "]"); eids.add(aeid); } // For either create or update events, schedule the default // metrics if (getEnabledMetricsCount(subject, aeid) == 0) { if (debug) log.debug("Enabling default metrics for [" + aeid + "]"); List<Measurement> metrics = asyncEnableDefaultMetrics(subject, aeid, config, verifyConfig); if (!metrics.isEmpty()) { eids.add(aeid); } } else { // Update the configuration updateMeasurements(subject, aeid, config); } if (isCreate) { // On initial creation of the service check if log or config // tracking is enabled. If so, enable it. We don't auto // enable log or config tracking for update events since // in the callback we don't know if that flag has changed. // TODO break circular dep preventing DI of TrackerManager applicationContext.getBean(TrackerManager.class).enableTrackers(subject, aeid, config); } // don't wrap everything in exception, certain exceptions should be allowed to propagate up the stack } catch (ConfigFetchException e) { log.warn("Config for resource=[" + r + "] is invalid (this is usually ok): " + e); } catch (MeasurementCreateException e) { log.error("Unable to create measurements for [" + aeid + "]: " + e, e); } catch (PermissionException e) { log.error(e,e); } catch (BeansException e) { log.error(e,e); } catch (PluginException e) { log.error(e,e); } catch (AppdefEntityNotFoundException e) { log.error(e,e); } catch (EncodingException e) { log.error(e,e); }catch(AsyncValidationInProgressException e) { log.debug("Async validation in progress"); } } srnManager.scheduleInBackground(eids, true, false); } public void setSrnManager(SRNManager srnManager) { this.srnManager = srnManager; } public void setMeasurementDao(MeasurementDAO dao) { this.measurementDAO = dao; } public void setResourceManager(ResourceManager resourceManager){ this.resourceManager = resourceManager; } public void setMeasurementTemplateDao(MeasurementTemplateDAO mTemplateDao){ this.measurementTemplateDAO = mTemplateDao; } private String[] getTemplatesToCheck(AuthzSubject s, AppdefEntityID id) throws AppdefEntityNotFoundException, PermissionException { String mType = (new AppdefEntityValue(id, s)).getMonitorableType(); List<MeasurementTemplate> templates = measurementTemplateDAO.findDefaultsByMonitorableType( mType, id.getType()); List<String> dsnList = new ArrayList<String>(SAMPLE_SIZE); int idx = 0; int availIdx = -1; for (MeasurementTemplate template : templates) { if (template.isAvailability() && template.isDesignate()) { availIdx = idx; } if (idx == availIdx || (availIdx == -1 && idx < (SAMPLE_SIZE - 1)) || (availIdx != -1 && idx < SAMPLE_SIZE)) { dsnList.add(template.getTemplate()); // Increment only after we have successfully added DSN idx++; if (idx >= SAMPLE_SIZE) break; } } return dsnList.toArray(new String[dsnList.size()]); } /** * Check a configuration to see if it returns DSNs which the agent can use * to successfully monitor an entity. This routine will attempt to get live * DSN values from the entity. * * @param entity Entity to check the configuration for * @param config Configuration to check * */ @Transactional(readOnly = true) public void checkConfiguration(AuthzSubject subject, AppdefEntityID entity, ConfigResponse config, boolean priority) throws PermissionException, InvalidConfigException, AppdefEntityNotFoundException { String[] templates = getTemplatesToCheck(subject, entity); // there are no metric templates, just return if (templates.length == 0) { log.debug("No metrics to checkConfiguration for " + entity); return; } else { log.debug("Using " + templates.length + " metrics to checkConfiguration for " + entity); } String[] dsns = new String[templates.length]; for (int i = 0; i < dsns.length; i++) { dsns[i] = translate(templates[i], config); } try { getLiveMeasurementValues(entity, dsns, priority); } catch (LiveMeasurementException exc) { throw new InvalidConfigException("Invalid configuration: " + exc.getMessage(), exc); } } @Transactional(readOnly = true) public void asyncCheckConfiguration(AuthzSubject subject, AppdefEntityID entity, ConfigResponse config, boolean priority) throws PermissionException, InvalidConfigException, AppdefEntityNotFoundException { String[] templates = getTemplatesToCheck(subject, entity); // there are no metric templates, just return if (templates.length == 0) { log.debug("No metrics to checkConfiguration for " + entity); return; } else { log.debug("Using " + templates.length + " metrics to checkConfiguration for " + entity); } String[] dsns = new String[templates.length]; for (int i = 0; i < dsns.length; i++) { dsns[i] = translate(templates[i], config); } try { asyncVerifyLiveMeasurementValues(subject, entity, dsns, priority); } catch (LiveMeasurementException exc) { throw new InvalidConfigException("Invalid configuration: " + exc.getMessage(), exc); } } /** * @return List {@link Measurement} of MeasurementIds */ @Transactional(readOnly = true) public List<Measurement> getEnabledMeasurements(Integer[] tids, Integer[] aeids) { return measurementDAO.findMeasurements(tids, aeids, true); } /** * @return List {@link Measurement} of MeasurementIds */ @Transactional(readOnly = true) public List<Measurement> getMeasurements(Integer[] tids, Integer[] aeids) { return measurementDAO.findMeasurements(tids, aeids); } /** * Get live measurement values for a series of DSNs * * NOTE: Since this routine allows callers to pass in arbitrary DSNs, the * caller must do all the appropriate translation, etc. * * @param entity Entity to get the measurement values from * @param dsns Translated DSNs to fetch from the entity * @param priority tells the {@link AgentSynchronizer} to execute this task immediately rather than queuing it. * Typically set priority = false for background tasks * * @return A list of MetricValue objects for each DSN passed */ private MetricValue[] getLiveMeasurementValues(AppdefEntityID entity, String[] dsns, boolean priority) throws LiveMeasurementException, PermissionException { try { final boolean debug = log.isDebugEnabled(); Agent a = agentManager.getAgent(entity); LiveValuesAgentDataJob job = new LiveValuesAgentDataJob(a.getId(), dsns); if (debug) log.debug("Getting live measurement values: adding job. Agent: " + a + ", Entity: " + entity); agentSynchronizer.addAgentJob(job, priority); if (debug) log.debug("Getting live measurement values: waiting for job. Agent: " + a + ", Entity: " + entity); job.waitForJob(); if (job.values != null) { if (debug) { log.debug("Live measurement values job has returned with values. Values: " + job.values + ", Agent: " + a + ", Entity: " + entity); } return job.values; } else { if (debug) { log.debug("Live measurement values job has returned an error. Agent: " + a + ", Entity: " + entity + ", Exception: " + job.failureEx); } throw new LiveMeasurementException(job.failureEx); } } catch (AgentNotFoundException e) { throw new LiveMeasurementException(e.getMessage(), e); } } private void asyncVerifyLiveMeasurementValues(AuthzSubject subject, AppdefEntityID entity, String[] dsns, boolean priority) throws LiveMeasurementException, PermissionException { try { final boolean debug = log.isDebugEnabled(); Agent a = agentManager.getAgent(entity); LiveValuesAgentDataJobWithEvent job = new LiveValuesAgentDataJobWithEvent(a.getId(), dsns, subject, entity); if (debug) log.debug("Getting live measurement values: adding job. Agent: " + a + ", Entity: " + entity); agentSynchronizer.addAgentJob(job, priority); } catch (AgentNotFoundException e) { throw new LiveMeasurementException(e.getMessage(), e); } } private class LiveValuesAgentDataJob implements AgentDataTransferJob { private final String[] dsns; // Not keeping Agent itself since job can move to a different hibernate session. private final int agentId; private final AtomicBoolean success = new AtomicBoolean(false); private final AtomicBoolean hasExecuted = new AtomicBoolean(false); private MetricValue[] values; private final Object obj = new Object(); protected Exception failureEx; private LiveValuesAgentDataJob(final int agentId, final String[] dsns) { this.agentId = agentId; this.dsns = dsns; } public boolean wasSuccessful() { return success.get(); } public void onFailure(String reason) { log.warn("failed to getLiveValues from agent=" + agentId + ": " + reason + ", dsns=" + Arrays.asList(dsns)); hasExecuted.set(true); if (failureEx == null) { failureEx = new SystemException(reason); } } public String getJobDescription() { return "getLiveMeasurementValues"; } public int getAgentId() { return agentId; } public void execute() { try { if (log.isDebugEnabled()) { log.debug("executing getLiveValues to agent=" + agentId + ", dsns=" + Arrays.asList(dsns)); } values = agentMonitor.getLiveValues(agentId, dsns); success.set(true); } catch (MonitorAgentException e) { failureEx = e; } catch (LiveMeasurementException e) { failureEx = e; } finally { hasExecuted.set(true); // always set success true since we don't want to retry synchronized (obj) { obj.notify(); } } } private void waitForJob() { try { synchronized (obj) { while (!hasExecuted.get()) { obj.wait(1000); } } } catch (InterruptedException e) { } } } private class LiveValuesAgentDataJobWithEvent extends LiveValuesAgentDataJob { private Integer subjectID; private AppdefEntityID id; private LiveValuesAgentDataJobWithEvent(int agentId, final String[] dsns, AuthzSubject subject, AppdefEntityID id) { super(agentId, dsns); this.subjectID = subject.getId(); this.id = id; } @Transactional(readOnly = true) public void execute() { boolean executeSucceeded = false; try { super.execute(); executeSucceeded = wasSuccessful(); } catch (RuntimeException e) { log.debug("LiveValuesAgentDataJob threw an exception. subjectID=" + subjectID + " and id=" + id, e); executeSucceeded = false; } // Send event when job completes NewResourceVerifiedZevent event = new NewResourceVerifiedZevent(subjectID, id, executeSucceeded, failureEx); log.debug("Sending event: " + event); try { zeventManager.enqueueEvent(event); } catch (InterruptedException e) { log.warn("Interrupted while enqueueing resource verified event: ",e); } catch (Exception e) { log.warn("Error while enqueueing resource verified event: ",e); } } } public void onApplicationEvent(ResourceDeleteRequestedEvent event) { measurementDAO.clearResource(event.getResource()); } private String getMonitorableTypeIfExists(AuthzSubject subj, AppdefEntityID id) { try { if(id.isPlatform() || id.isServer() || id.isService()) { AppdefEntityValue av = new AppdefEntityValue(id, subj); try { return av.getMonitorableType(); }catch(AppdefEntityNotFoundException e) { // Non existent resource, we'll clean it up in // removeOrphanedMeasurements() return ""; } } }catch(Exception e) { log.error("Unable to enable default metrics for [" + id + "]", e); return ""; } return ""; } private class AsyncValidationInProgressException extends Exception { private static final long serialVersionUID = 1L; public AsyncValidationInProgressException() { super(); } } /** * Enable the default metrics for a resource. This should only be called by * the {@link MeasurementEnabler}. If you want the behavior of this method, * use the {@link MeasurementEnabler} * @return {@link List} of {@link Measurement}s */ private List<Measurement> asyncEnableDefaultMetrics(AuthzSubject subj, AppdefEntityID id, ConfigResponse config, boolean verifyConfig) throws AppdefEntityNotFoundException, PermissionException, AsyncValidationInProgressException { List<Measurement> rtn = new ArrayList<Measurement>(0); String mtype = getMonitorableTypeIfExists(subj, id); if (mtype.length() == 0) { return rtn; } // Check the configuration if(verifyConfig) { try { asyncCheckConfiguration(subj, id, config, true); }catch(InvalidConfigException e) { log.warn("Error turning on default metrics, configuration (" + config + ") " + "couldn't be validated", e); configManager.setValidationError(subj, id, e.getMessage()); return rtn; }catch(Exception e) { log.warn("Error turning on default metrics, " + "error in validation", e); configManager.setValidationError(subj, id, e.getMessage()); return rtn; } throw new AsyncValidationInProgressException(); } // Enable the metrics try { rtn = createDefaultMeasurements(subj, id, mtype, config); configManager.clearValidationError(subj, id); // Publish the event so other people can do things when the // metrics have been created (like create type-based alerts) applicationContext.publishEvent(new MetricsEnabledEvent(id)); }catch(Exception e) { log.warn("Unable to enable default metrics for id=" + id + ": " + e.getMessage(), e); } return rtn; } /** * Initializes the units and resource properties of a measurement event */ public void buildMeasurementEvent(MeasurementEvent event) { Measurement dm = null; try { dm = measurementDAO.get(event.getInstanceId()); int resourceType = dm.getTemplate().getMonitorableType().getAppdefType(); event.setResource(new AppdefEntityID(resourceType, dm.getInstanceId())); event.setUnits(dm.getTemplate().getUnits()); } catch (Exception e) { if (event == null) { log.warn("Measurement event is null"); } else if (dm == null) { log.warn("Measurement is null for measurement event with metric id=" + event.getInstanceId()); } else if (event.getResource() == null) { log.error("Unable to set resource for measurement event with metric id=" + event.getInstanceId(), e); } else if (event.getUnits() == null) { log.error("Unable to set units for measurement event with metric id=" + event.getInstanceId(), e); } else { log.error("Unable to build measurement event with metric id=" + event.getInstanceId(), e); } } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public MeasurementTemplate getTemplatesByMeasId(Integer measId) { if (measId == null) { return null; } return measurementTemplateDAO.get(measId); } @PreDestroy public final void destroy() { this.agentManager = null ; this.agentMonitor = null ; this.applicationContext = null ; this.applicationDAO = null ; this.authzSubjectManager = null ; this.availabilityManager = null ; this.configManager = null ; this.measurementDAO = null ; this.measurementTemplateDAO = null ; this.metricDataCache = null ; this.permissionManager = null ; this.srnManager = null ; this.zeventManager = null ; } }