/*
* NOTE: This copyright does *not* cover user programs that use Hyperic
* 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-2010], VMware, Inc.
* This file is part of Hyperic.
*
* Hyperic 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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.ObjectNotFoundException;
import org.hyperic.hq.agent.AgentConnectionException;
import org.hyperic.hq.agent.AgentRemoteException;
import org.hyperic.hq.appdef.Agent;
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.AppdefUtil;
import org.hyperic.hq.authz.server.session.Resource;
import org.hyperic.hq.authz.server.session.ResourceEdge;
import org.hyperic.hq.authz.shared.PermissionException;
import org.hyperic.hq.authz.shared.ResourceManager;
import org.hyperic.hq.measurement.MeasurementUnscheduleException;
import org.hyperic.hq.measurement.agent.client.AgentMonitor;
import org.hyperic.hq.measurement.agent.client.MeasurementCommandsClient;
import org.hyperic.hq.measurement.agent.client.MeasurementCommandsClientFactory;
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.MeasurementProcessor;
import org.hyperic.hq.measurement.shared.SRNManager;
import org.hyperic.hq.stats.ConcurrentStatsCollector;
import org.hyperic.hq.zevents.ZeventEnqueuer;
import org.hyperic.util.timer.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
*/
@Service
@Transactional
public class MeasurementProcessorImpl implements MeasurementProcessor {
private final Log log = LogFactory.getLog(MeasurementProcessorImpl.class);
@Autowired
private AgentManager agentManager;
@Autowired
private AvailabilityManager availManager;
@Autowired
private MeasurementManager measurementManager;
@Autowired
private SRNManager srnManager;
@Autowired
private AgentMonitor agentMonitor;
@Autowired
private MeasurementCommandsClientFactory measurementCommandsClientFactory;
@Autowired
private ResourceManager resourceManager;
@Autowired
private ZeventEnqueuer zEventManager;
@Autowired
private ConcurrentStatsCollector concurrentStatsCollector;
public MeasurementProcessorImpl() {}
@PostConstruct
public void initStatsCollector() {
concurrentStatsCollector.register(ConcurrentStatsCollector.MEASUREMENT_SCHEDULE_TIME);
}
/**
* Ping the agent to make sure it's up
*/
public boolean ping(Agent a) throws PermissionException {
return agentMonitor.ping(a);
}
/**
* Schedules enabled measurements for the entire ResourceEdge hierarchy
* based on the "containment" relationship. These metrics are scheduled
* after the transaction is committed.
*
*/
public void scheduleHierarchyAfterCommit(Collection<Resource> resources) {
if (resources.isEmpty()) {
return;
}
final Set<AppdefEntityID> aeids = new LinkedHashSet<AppdefEntityID>(resources.size()*2);
for (final Resource resource : resources ) {
if (resource == null || resource.isInAsyncDeleteState()) {
continue;
}
final Collection<ResourceEdge> edges =
resourceManager.findResourceEdges(resourceManager.getContainmentRelation(), resource);
aeids.add(AppdefUtil.newAppdefEntityId(resource));
for (final ResourceEdge e : edges ) {
final Resource r = e.getTo();
if (r == null || r.isInAsyncDeleteState()) {
continue;
}
aeids.add(AppdefUtil.newAppdefEntityId(e.getTo()));
}
}
if (!aeids.isEmpty()) {
final AgentScheduleSyncZevent event = new AgentScheduleSyncZevent(aeids);
zEventManager.enqueueEventAfterCommit(event);
}
}
/**
* Schedules enabled measurements for the entire ResourceEdge hierarchy
* based on the "containment" relationship. These metrics are scheduled
* after the transaction is committed.
*
*/
public void scheduleHierarchyAfterCommit(Resource resource) {
scheduleHierarchyAfterCommit(Collections.singletonList(resource));
}
/**
* @param aeids {@link List} of {@link AppdefEntityID}
*/
public void scheduleSynchronous(Collection<AppdefEntityID> aeids) {
try {
Map<Integer, Collection<AppdefEntityID>> agents = getAgentMap(aeids);
for (Map.Entry<Integer, Collection<AppdefEntityID>> entry : agents.entrySet()) {
Agent agent = agentManager.findAgent(entry.getKey());
Collection<AppdefEntityID> entityIds = entry.getValue();
scheduleEnabled(agent, entityIds);
}
} catch (Exception e) {
log.error("Exception scheduling [" + aeids + "]: " + e.getMessage(), e);
}
}
/**
* @return Map of {@link Agent} to {@link List<AppdefEntityID>}
*/
private Map<Integer, Collection<AppdefEntityID>> getAgentMap(Collection<AppdefEntityID> aeids) {
return agentManager.getAgentMap(aeids);
}
/**
* @param eids List<AppdefEntityID>
*/
@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)
public void scheduleEnabled(Agent agent, Collection<AppdefEntityID> eids) throws MonitorAgentException {
final StopWatch watch = new StopWatch();
final boolean debug = log.isDebugEnabled();
if (debug) watch.markTimeBegin("findEnabledMeasurements");
Map<Integer,List<Measurement>> measMap = measurementManager.findEnabledMeasurements(eids);
if (debug) watch.markTimeEnd("findEnabledMeasurements");
// availability measurements in scheduled downtime are disabled, but not unscheduled.
// need to schedule these measurements for "new" agents.
if (debug) watch.markTimeBegin("getAvailMeasurementsInDowntime");
Map<Integer, Measurement> downtimeMeasMap = availManager.getAvailMeasurementsInDowntime(eids);
if (debug) watch.markTimeEnd("getAvailMeasurementsInDowntime");
for (Map.Entry<Integer, Measurement> entry : downtimeMeasMap.entrySet()) {
Integer resourceId = entry.getKey();
List<Measurement> measurements = measMap.get(resourceId);
if (measurements == null) {
measurements = new ArrayList<Measurement>();
measMap.put(resourceId, measurements);
}
measurements.add(entry.getValue());
}
final int batchSize = 10000;
final List<AppdefEntityID> aeids = new ArrayList<AppdefEntityID>(eids);
for (int i=0; i<aeids.size(); i+=batchSize) {
final int end = Math.min(i+batchSize, aeids.size());
if (debug) watch.markTimeBegin("scheduleMeasurements[" + end + "]");
scheduleMeasurements(agent, measMap, aeids.subList(i, end));
if (debug) watch.markTimeEnd("scheduleMeasurements[" + end + "]");
}
if (debug) {
log.debug("scheduleEnabled: " + watch
+ ", { Size: [appdefEntity=" + eids.size()
+ "] [availMeasurementsInDowntime=" + downtimeMeasMap.size()
+ "] }");
}
}
private void scheduleMeasurements(Agent agent, Map<Integer,List<Measurement>> measMap,
Collection<AppdefEntityID> eids)
throws MonitorAgentException {
final boolean debug = log.isDebugEnabled();
final Map<SRN,List<Measurement>> schedMap = new HashMap<SRN,List<Measurement>>();
MeasurementCommandsClient client = null;
final StringBuilder debugBuf = new StringBuilder();
try {
client = measurementCommandsClientFactory.getClient(agent);
for (AppdefEntityID eid : eids ) {
final long begin = now();
ScheduleRevNum srnObj = srnManager.get(eid);
SRN srn = new SRN(eid, srnObj == null ? -1 : srnObj.getSrn());
Resource r = resourceManager.findResource(eid);
if (r == null || r.isInAsyncDeleteState()) {
continue;
}
List<Measurement> measurements = measMap.get(r.getId());
if (measurements == null) {
continue;
}
schedMap.put(srn, measurements);
try {
if (debug) {
debugBuf.append("scheduling mids=")
.append(measurements)
.append(" for aeid=")
.append(eid)
.append("\n");
}
Measurement[] array = measurements.toArray(new Measurement[0]);
agentMonitor.schedule(client, srn, array);
concurrentStatsCollector.addStat((now()-begin), ConcurrentStatsCollector.MEASUREMENT_SCHEDULE_TIME);
} catch (AgentConnectionException e) {
final String emsg = "Error reported by agent @ "
+ agent.connectionString()
+ ": " + e.getMessage();
log.warn(emsg);
throw new MonitorAgentException(e.getMessage(), e);
} catch (AgentRemoteException e) {
final String emsg = "Error reported by agent @ "
+ agent.connectionString()
+ ": " + e.getMessage();
log.warn(emsg);
throw new MonitorAgentException(emsg, e);
}
}
} finally {
if (debug) log.debug(debugBuf);
}
}
private final long now() {
return System.currentTimeMillis();
}
private void unschedule(Agent a, Collection<AppdefEntityID> entIds) throws MeasurementUnscheduleException,
MonitorAgentException {
if (log.isDebugEnabled()) {
log.debug("unschedule agentId=" + a.getId() + ", numOfResources=" + entIds.size());
}
for (AppdefEntityID entId: entIds) {
try {
srnManager.removeSrn(entId);
} catch (ObjectNotFoundException e) {
// Ok to ignore, this is the first time scheduling metrics
// for this resource.
}
}
List<AppdefEntityID> tmp = new ArrayList<AppdefEntityID>(entIds);
agentMonitor.unschedule(a, tmp.toArray(new AppdefEntityID[0]));
}
/**
* Unschedule metrics of multiple appdef entities
* @param agentToken the entity whose agent will be contacted for the
* unschedule
* @param entIds the entity IDs whose metrics should be unscheduled
* @throws MeasurementUnscheduleException if an error occurs
*/
@Transactional(readOnly=true)
public void unschedule(String agentToken, Collection<AppdefEntityID> entIds) {
try {
// Get the agent from agent token
Agent a = agentManager.getAgent(agentToken);
unschedule(a, entIds);
} catch (MonitorAgentException e) {
log.error("Error unscheduling metrics: " + e);
log.debug(e,e);
} catch (AgentNotFoundException e) {
log.warn("Error unscheduling metrics: " + e);
log.debug(e,e);
} catch (MeasurementUnscheduleException e) {
log.error("Error unscheduling metrics: " + e, e);
}
}
/**
* Unschedule metrics of multiple appdef entities
* @param agentEnt the entity whose agent will be contacted for the
* unschedule
* @param entIds the entity IDs whose metrics should be unscheduled
* @throws MeasurementUnscheduleException if an error occurs
*/
@Transactional(readOnly=true)
public void unschedule(AppdefEntityID agentEnt, AppdefEntityID[] entIds){
try {
// Get the agent IP and Port from server ID
Agent a = agentManager.getAgent(agentEnt);
unschedule(a, Arrays.asList(entIds));
} catch (MonitorAgentException e) {
log.warn("Error unscheduling metrics: " + e);
log.debug(e,e);
} catch (AgentNotFoundException e) {
log.warn("Error unscheduling metrics: " + e);
log.debug(e,e);
} catch (MeasurementUnscheduleException e) {
log.warn("Error unscheduling metrics: " + e);
log.debug(e,e);
}
}
/**
* Unschedule measurements
* @param aeids List of {@link AppdefEntityID}
* @throws MeasurementUnscheduleException if an error occurs
*/
@Transactional(readOnly=true)
public void unschedule(Collection<AppdefEntityID> aeids) {
Map<Integer, Collection<AppdefEntityID>> agents = getAgentMap(aeids);
for (Map.Entry<Integer, Collection<AppdefEntityID>> entry : agents.entrySet()) {
Agent agent = agentManager.findAgent(entry.getKey());
Collection<AppdefEntityID> eids = entry.getValue();
unschedule(agent.getAgentToken(), eids);
}
}
}