package org.ovirt.engine.core.notifier.dao; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import org.ovirt.engine.core.common.AuditLogSeverity; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.notifier.NotificationServiceException; import org.ovirt.engine.core.notifier.filter.AuditLogEvent; import org.ovirt.engine.core.notifier.filter.AuditLogEventType; import org.ovirt.engine.core.notifier.filter.FirstMatchSimpleFilter; import org.ovirt.engine.core.notifier.transport.Observable; import org.ovirt.engine.core.notifier.transport.Observer; import org.ovirt.engine.core.utils.db.StandaloneDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class EventsManager implements Observer { public static final String DATABASE_UNREACHABLE = "DATABASE_UNREACHABLE"; private static final Logger log = LoggerFactory.getLogger(EventsManager.class); private DataSource ds; private Map<String, String> eventMap; public EventsManager() throws NotificationServiceException { try { ds = new StandaloneDataSource(); eventMap = populateEventMap(); } catch (SQLException e) { throw new NotificationServiceException("Failed to obtain database connectivity", e); } } private Map<String, String> populateEventMap() throws SQLException { Map<String, String> eventMap = new HashMap<>(); try (Connection connection = ds.getConnection(); PreparedStatement ps = connection.prepareStatement( "SELECT em.event_up_name, em.event_down_name " + "FROM event_map em;"); ResultSet rs = ps.executeQuery()) { while (rs.next()) { eventMap.put(rs.getString("event_up_name"), rs.getString("event_down_name")); } } return eventMap; } public List<FirstMatchSimpleFilter.FilterEntry> getAuditLogEventSubscribers() throws SQLException { List<FirstMatchSimpleFilter.FilterEntry> eventSubscribers = new ArrayList<>(); try (Connection connection = ds.getConnection(); PreparedStatement ps = connection.prepareStatement( "SELECT event_subscriber.event_up_name, " + " event_subscriber.method_address, " + " event_subscriber.notification_method " + "FROM event_subscriber "); ResultSet rs = ps.executeQuery()) { while (rs.next()) { String eventUpName = rs.getString("event_up_name"); String eventDownName = eventMap.get(eventUpName); eventSubscribers.add( new FirstMatchSimpleFilter.FilterEntry(eventUpName, null, false, rs.getString("notification_method"), rs.getString("method_address"))); if (eventDownName != null) { eventSubscribers.add( new FirstMatchSimpleFilter.FilterEntry(eventDownName, null, false, rs.getString("notification_method"), rs.getString("method_address"))); } } } return eventSubscribers; } public List<AuditLogEvent> getAuditLogEvents() throws SQLException { HashSet<String> downEvents = new HashSet<>(eventMap.values()); List<AuditLogEvent> auditLogEvents = new ArrayList<>(); try (Connection connection = ds.getConnection(); PreparedStatement ps = connection.prepareStatement( "SELECT al.audit_log_id, al.log_type_name, " + " al.user_id, al.user_name, " + " al.vm_id, al.vm_name, al.vm_template_id, al.vm_template_name, " + " al.vds_id, al.vds_name, al.storage_pool_id, al.storage_pool_name, " + " al.storage_domain_id, al.storage_domain_name, " + " al.log_time, al.severity, al.message " + "FROM audit_log al " + "WHERE al.processed = FALSE ;"); ResultSet rs = ps.executeQuery()) { while (rs.next()) { auditLogEvents.add(extractAuditLogEvent(rs, downEvents)); } } if (log.isDebugEnabled()) { log.debug("{} unprocessed events read from audit_log database table.", auditLogEvents.size()); for (int i = 0; i < auditLogEvents.size(); i++) { log.debug("event {} => {}", i, auditLogEvents.get(i).toString()); } } return auditLogEvents; } private AuditLogEvent extractAuditLogEvent(ResultSet rs, Set<String> downEvents) throws SQLException { AuditLogEvent auditLogEvent = new AuditLogEvent(); auditLogEvent.setId(rs.getLong("audit_log_id")); auditLogEvent.setLogTypeName(rs.getString("log_type_name")); if (downEvents.contains(auditLogEvent.getLogTypeName())) { auditLogEvent.setType(AuditLogEventType.resolveMessage); } else { auditLogEvent.setType(AuditLogEventType.alertMessage); } auditLogEvent.setUserId(Guid.createGuidFromString(rs.getString("user_id"))); auditLogEvent.setUserName(rs.getString("user_name")); auditLogEvent.setVmId(Guid.createGuidFromString(rs.getString("vm_id"))); auditLogEvent.setVmName(rs.getString("vm_name")); auditLogEvent.setVmTemplateId(Guid.createGuidFromString(rs.getString("vm_template_id"))); auditLogEvent.setVmTemplateName(rs.getString("vm_template_name")); auditLogEvent.setVdsId(Guid.createGuidFromString(rs.getString("vds_id"))); auditLogEvent.setVdsName(rs.getString("vds_name")); auditLogEvent.setStoragePoolId(Guid.createGuidFromStringDefaultEmpty(rs.getString("storage_pool_id"))); auditLogEvent.setStoragePoolName(rs.getString("storage_pool_name")); auditLogEvent.setStorageDomainId(Guid.createGuidFromStringDefaultEmpty(rs.getString("storage_domain_id"))); auditLogEvent.setStorageDomainName(rs.getString("storage_domain_name")); auditLogEvent.setLogTime(rs.getTimestamp("log_time")); auditLogEvent.setSeverity(AuditLogSeverity.forValue(rs.getInt("severity"))); auditLogEvent.setMessage(rs.getString("message")); return auditLogEvent; } public AuditLogEvent createDBDownEvent() { final AuditLogEvent dbDownEvent = new AuditLogEvent(); dbDownEvent.setLogTime(new Date()); dbDownEvent.setType(AuditLogEventType.alertMessage); dbDownEvent.setLogTypeName(DATABASE_UNREACHABLE); dbDownEvent.setMessage("Failed to query for notifications. Database Connection refused."); dbDownEvent.setSeverity(AuditLogSeverity.ERROR); return dbDownEvent; } public void markOldEventsAsProcessed(int daysToSendOnStartup) { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE, -daysToSendOnStartup); Timestamp ts = new Timestamp(calendar.getTimeInMillis()); int updatedRecords; try (Connection connection = ds.getConnection(); PreparedStatement statement = connection.prepareStatement( "UPDATE audit_log " + "SET processed = 'true' " + "WHERE processed = 'false' AND log_time < ? ;")) { statement.setTimestamp(1, ts); updatedRecords = statement.executeUpdate(); if (updatedRecords > 0) { log.debug("{} old records were marked as processed in the \"audit_log\" table.", updatedRecords); } } catch (SQLException e) { throw new NotificationServiceException("Failed mark old events as processed.", e); } } public void deleteObsoleteHistoryData(int daysToKeepHistory) throws SQLException { if (daysToKeepHistory > 0) { Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.DATE, -daysToKeepHistory); Timestamp startDeleteFrom = new Timestamp(cal.getTimeInMillis()); int deletedRecords; try (Connection connection = ds.getConnection(); PreparedStatement deleteStmt = connection.prepareStatement( "DELETE " + "FROM event_notification_hist " + "WHERE sent_at < ? ;")) { deleteStmt.setTimestamp(1, startDeleteFrom); deletedRecords = deleteStmt.executeUpdate(); if (deletedRecords > 0) { log.debug("{} records were deleted from \"event_notification_hist\" table.", deletedRecords); } } } } public void updateAuditLogEventProcessed(long auditLogId) throws SQLException { try (Connection connection = ds.getConnection(); PreparedStatement ps = connection.prepareStatement( "UPDATE audit_log " + "SET processed = 'true' " + "WHERE audit_log_id = ? ;")) { ps.setLong(1, auditLogId); int updated = ps.executeUpdate(); if (updated != 1) { log.error("Failed to mark audit_log entry as processed for audit_log_id: {}", auditLogId); } } } @Override public void update(Observable o, DispatchResult dispatchResult) { AuditLogEvent event = dispatchResult.getEvent(); try (Connection connection = ds.getConnection(); CallableStatement cs = connection.prepareCall("{call Insertevent_notification_hist(?,?,?,?,?,?)}")) { cs.setLong(1, event.getId()); cs.setString(2, event.getLogTypeName()); cs.setString(3, dispatchResult.getNotificationMethod().name()); cs.setString(4, dispatchResult.getErrorMessage()); cs.setTimestamp(5, new java.sql.Timestamp(new Date().getTime())); cs.setBoolean(6, dispatchResult.isSuccess()); cs.executeUpdate(); } catch (SQLException e) { log.error("Could not insert event notification history event", e); } } }