/*****************************************************************************
*
* Copyright (C) Zenoss, Inc. 2011, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
****************************************************************************/
package org.zenoss.zep.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zenoss.protobufs.model.Model.ModelElementType;
import org.zenoss.protobufs.zep.Zep.DaemonHeartbeat;
import org.zenoss.protobufs.zep.Zep.Event;
import org.zenoss.protobufs.zep.Zep.EventActor;
import org.zenoss.protobufs.zep.Zep.EventSeverity;
import org.zenoss.protobufs.zep.Zep.EventSummary;
import org.zenoss.protobufs.zep.Zep.EventSummaryRequest;
import org.zenoss.protobufs.zep.Zep.EventSummaryResult;
import org.zenoss.zep.EventPublisher;
import org.zenoss.zep.HeartbeatProcessor;
import org.zenoss.zep.UUIDGenerator;
import org.zenoss.zep.ZepConstants;
import org.zenoss.zep.ZepException;
import org.zenoss.zep.dao.HeartbeatDao;
import org.zenoss.zep.index.EventIndexDao;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Heartbeat processing logic.
*/
public class HeartbeatProcessorImpl implements HeartbeatProcessor {
private static final Logger logger = LoggerFactory.getLogger(HeartbeatProcessorImpl.class);
private static final String STATUS_HEARTBEAT = "/Status/Heartbeat";
private HeartbeatDao heartbeatDao;
private EventPublisher eventPublisher;
private EventIndexDao eventIndexDao;
private final EventSummaryRequest heartbeatRequest;
private UUIDGenerator uuidGenerator;
public HeartbeatProcessorImpl() {
EventSummaryRequest.Builder builder = EventSummaryRequest.newBuilder();
builder.getEventFilterBuilder().addEventClass(STATUS_HEARTBEAT);
builder.getEventFilterBuilder().addAllStatus(ZepConstants.OPEN_STATUSES);
heartbeatRequest = builder.build();
}
public void setHeartbeatDao(HeartbeatDao heartbeatDao) {
this.heartbeatDao = heartbeatDao;
}
public void setEventPublisher(EventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setEventIndexDao(EventIndexDao eventIndexDao) {
this.eventIndexDao = eventIndexDao;
}
public void setUuidGenerator(UUIDGenerator uuidGenerator) {
this.uuidGenerator = uuidGenerator;
}
@Override
public void sendHeartbeatEvents() throws ZepException {
final Set<Entry<String,String>> currentHeartbeatEvents = getCurrentHeartbeatEvents();
final long now = System.currentTimeMillis();
final List<DaemonHeartbeat> heartbeats = this.heartbeatDao.findAll();
for (DaemonHeartbeat heartbeat : heartbeats) {
final long expireTime = heartbeat.getLastTime() + TimeUnit.SECONDS.toMillis(heartbeat.getTimeoutSeconds());
final boolean isClear = (expireTime > now);
final Entry<String,String> entry = new SimpleImmutableEntry<String, String>(heartbeat.getMonitor(),
heartbeat.getDaemon());
final boolean hasCurrentHeartbeat = currentHeartbeatEvents.remove(entry);
if (!isClear || hasCurrentHeartbeat) {
final Event event = createHeartbeatEvent(heartbeat.getMonitor(), heartbeat.getDaemon(), now,
isClear);
logger.debug("Publishing heartbeat event: {}", event);
eventPublisher.publishEvent(event);
}
}
// We didn't find heartbeat records for events with outstanding warnings - send clears for them
for (Entry<String,String> entry : currentHeartbeatEvents) {
final Event event = createHeartbeatEvent(entry.getKey(), entry.getValue(), now, true);
eventPublisher.publishEvent(event);
}
}
/**
* Retrieves all of the current heartbeat events in OPEN status. This is used
* to optimize how often clear events are sent to only send a clear event if
* it has a possibility of clearing a heartbeat event.
*
* @return The current open heartbeat events.
* @throws ZepException If an exception occurs.
*/
private Set<Entry<String,String>> getCurrentHeartbeatEvents() throws ZepException {
final Set<Entry<String,String>> events = new HashSet<Entry<String,String>>();
final EventSummaryResult result = this.eventIndexDao.list(this.heartbeatRequest);
for (EventSummary summary : result.getEventsList()) {
final Event occurrence = summary.getOccurrence(0);
events.add(new SimpleImmutableEntry<String,String>(occurrence.getMonitor(), occurrence.getAgent()));
}
logger.debug("Current heartbeat events: {}", events);
return events;
}
private Event createHeartbeatEvent(String monitor, String daemon, long createdTime, boolean isClear) {
final Event.Builder event = Event.newBuilder();
event.setUuid(this.uuidGenerator.generate().toString());
event.setCreatedTime(createdTime);
final EventActor.Builder actor = event.getActorBuilder();
actor.setElementIdentifier(monitor).setElementTypeId(ModelElementType.DEVICE);
actor.setElementSubIdentifier(daemon).setElementSubTypeId(ModelElementType.COMPONENT);
event.setMonitor(monitor);
event.setAgent(daemon);
// Per old behavior - alerting rules typically are configured to only fire
// for devices in production. These events don't have a true "device" with
// a production state a lot of the time, and so we have to set this manually.
event.addDetailsBuilder().setName(ZepConstants.DETAIL_DEVICE_PRODUCTION_STATE)
.addValue(Integer.toString(ZepConstants.PRODUCTION_STATE_PRODUCTION));
if (isClear) {
event.setSeverity(EventSeverity.SEVERITY_CLEAR);
event.setSummary(monitor + " " + daemon + " heartbeat clear");
}
else {
event.setSeverity(EventSeverity.SEVERITY_ERROR);
event.setSummary(monitor + " " + daemon + " heartbeat failure");
}
event.setEventClass(STATUS_HEARTBEAT);
return event.build();
}
}