/* * This file is part of the Cerebro distribution. * (https://github.com/voyages-sncf-technologies/cerebro) * Copyright (C) 2017 VSCT. * * Cerebro is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, version 3 of the License. * * Cerebro 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.vsct.supervision.notification.service; import java.math.BigDecimal; import java.net.URI; import java.util.Collection; import java.util.Objects; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.vsct.supervision.notification.ErrorCode; import com.vsct.supervision.notification.email.NotificationHandler; import com.vsct.supervision.notification.exception.CerebroException; import com.vsct.supervision.notification.exception.DuplicateSubscriptionException; import com.vsct.supervision.notification.repository.SeyrenRepository; import com.vsct.supervision.seyren.api.Alarm; import com.vsct.supervision.seyren.api.Subscription; @Service public class AlarmService { private static final Logger LOGGER = LoggerFactory.getLogger(AlarmService.class); @Autowired private SeyrenRepository seyrenRepository; @Autowired private SubscriptionService subscriptionService; @Autowired private NotificationHandler notificationHandler; /** * Get a alarm by this ID. * * <i>This method do nothing more than calling {@link SeyrenRepository#getAlarm(String)}</i> * * @param id Alarm ID to recover * @return Alarm recovered * @see SeyrenRepository#getAlarm(String) */ public Alarm getAlarm(String id) { return seyrenRepository.getAlarm(id); } /** * Get all alarms from Seyren. * * <i>This method do nothing more than calling {@link SeyrenRepository#getAllAlarms()}</i> * * @return All alarm in Seyren */ public Collection<Alarm> getAllAlarms() { return seyrenRepository.getAllAlarms().getValues(); } /** * Add a subscription to a alarm. * * If alarm not exist, create it. Otherwise, add subscription to existing alarm. * If subscription already exist, do nothing. * * @param alarm Alarm to create or add subscription * @return The alarm created/updated * @throws CerebroException */ public Alarm subscribeToAAlarm(final Alarm alarm) { LOGGER.debug("Search identical alarm to: " + alarm); Alarm identicalAlarm = this.searchAlarm(alarm); if (identicalAlarm != null) { // Alarm already exist, enable it if needed and manage subscription LOGGER.debug("Identical alarm found ({}).", identicalAlarm.getId()); // Enable alarm if it's not activateAlarm(identicalAlarm); Subscription subscriptionToAdd = alarm.getSubscriptions().iterator().next(); // Adding subscription try { subscriptionService.addSubscription(subscriptionToAdd, identicalAlarm.getId()); return identicalAlarm; } catch (DuplicateSubscriptionException exception) { LOGGER.debug("Adding subscription not possible, identical subscription found for alarm {}.", identicalAlarm.getId()); return identicalAlarm; } } else { // Error if alarm name already exists Alarm alarmByName = this.searchAlarmByName(alarm.getName()); if (alarmByName != null) { throw new CerebroException(ErrorCode.ALARM_DUPLICATE_NAME, "A alarm with name '" + alarm.getName() + "' already exist"); } seyrenRepository.addAlarm(alarm); return this.searchAlarmByName(alarm.getName()); } } /** * If a alarm is disable, enable it. * * @param alarm The alarm to enable */ private void activateAlarm(final Alarm alarm) { if (alarm != null && !alarm.isEnabled()) { LOGGER.debug("Activating alarm {}", alarm.getId()); alarm.setEnabled(true); seyrenRepository.updateAlarm(alarm); } } /** * Search a alarm by is name. * * <i>Seyren can't store multiple alarms with the same name.</i> * * @param name Name of the alarm to find * @return The alarm found */ private Alarm searchAlarmByName(final String name) { Alarm.Alarms alarms = seyrenRepository.getAllAlarms(); return alarms.getValues().stream().filter(c -> c.getName().equals(name)).findAny().orElse(null); } private Collection<Alarm> searchAlarmsBySourceTargetAndThresholds(final URI source, final String target, final BigDecimal warn, final BigDecimal error) { return getAllAlarms().stream() .filter(alarm -> Objects.equals(alarm.getGraphiteBaseUrl(), source) && Objects.equals(alarm.getTarget(), target) && Objects.equals(alarm.getWarn(), warn) && Objects.equals(alarm.getError(), error)) .collect(Collectors.toList()); } /** * Search a alarm with same Graphite source, Graphite target, warn and error threshold. * * Actually, take each alarms with same properties, and return the first. * * @param alarm The alarm to search * @return Alarm found */ public Alarm searchAlarm(final Alarm alarm) { Collection<Alarm> identicalAlarms = searchAlarmsBySourceTargetAndThresholds(alarm.getGraphiteBaseUrl(), alarm.getTarget(), alarm.getWarn(), alarm.getError()); if (!identicalAlarms.isEmpty()) { return identicalAlarms.iterator().next(); } return null; } /** * Search all the alarms who have least once subscription with needed target * * @param subTarget * @return */ public Collection<Alarm> searchAlarmsBySubscriptionTarget(String subTarget) { return getAllAlarms().stream().filter(alarm -> alarm.getSubscriptions().stream().anyMatch(sub -> subTarget.equals(sub.getTarget()))) .collect(Collectors.toList()); } /** * Update a alarm. * * If alarm not exist, throw a CerebroException. * * @param alarm Alarm to update * @return The alarm updated */ public Alarm updateAlarm(final Alarm alarm) { LOGGER.debug("Update alarm to: " + alarm); validateUpdatable(alarm); try { seyrenRepository.updateAlarm(alarm); if(alarm.isEnabled()) { notificationHandler.sendAlarmHasBeenModified(alarm); } else{ notificationHandler.sendAlarmHasBeenDeactivated(alarm); } return this.searchAlarmByName(alarm.getName()); } catch (RuntimeException exception) { LOGGER.error("Error updating alarm", exception); throw new CerebroException(ErrorCode.CEREBRO_UNKNOWN_ERROR, "Error updating alarm", exception); } } private void validateUpdatable(Alarm alarm) { Alarm identicalAlarm = getAlarm(alarm.getId()); // Error if alarm name already exists Alarm alarmByName = this.searchAlarmByName(alarm.getName()); if (alarmByName != null && identicalAlarm != null && !alarmByName.getId().equals(alarm.getId())) { throw new CerebroException(ErrorCode.ALARM_DUPLICATE_NAME, "A alarm with name '" + alarm.getName() + "' already exist"); } Collection<Alarm> identicalAlarms = searchAlarmsBySourceTargetAndThresholds(alarm.getGraphiteBaseUrl(), alarm.getTarget(), alarm.getWarn(), alarm.getError()); for (Alarm alarm2 : identicalAlarms) { if (!alarm.getId().equals(alarm2.getId())) { throw new CerebroException(ErrorCode.ALARM_DUPLICATE_DATAS, "A alarm with same data already exist: " + alarm2.getId()); } } } }