2019-06-28 03:46:48 +02:00
2019-07-10 18:29:01 +02:00
/ *
* BSD 3 - Clause License
*
* Copyright ( c ) 2019 , NTT Ltd .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2019-06-28 03:46:48 +02:00
export default class Monitor {
2019-07-05 17:06:57 +02:00
constructor ( name , channel , params , env ) {
2019-07-03 01:41:05 +02:00
this . config = env . config ;
this . pubSub = env . pubSub ;
this . logger = env . logger ;
this . input = env . input ;
2020-01-09 18:31:03 +01:00
this . params = params || { } ;
this . maxDataSamples = this . params . maxDataSamples || 1000 ;
2019-06-28 03:46:48 +02:00
this . name = name ;
this . channel = channel ;
this . monitored = [ ] ;
2020-01-12 05:00:32 +01:00
this . alerts = { } ; // Dictionary containing the alerts <id, Array>. The id is the "group" key of the alert.
this . sent = { } ; // Dictionary containing the last sent unix timestamp of each group <id, int>
this . truncated = { } ; // Dictionary containing <id, boolean> if the alerts Array for "id" is truncated according to maxDataSamples
this . fadeOff = { } ; // Dictionary containing the last alert unix timestamp of each group <id, int> which contains alerts that have been triggered but are not ready yet to be sent (e.g. thresholdMinPeers not yet reached)
2019-10-27 01:35:10 +02:00
this . internalConfig = {
2020-01-18 18:48:08 +01:00
notificationInterval : ( this . config . notificationIntervalSeconds || 7200 ) * 1000 ,
checkFadeOffGroups : this . config . checkFadeOffGroupsSeconds || 30 * 1000 ,
fadeOff : this . config . fadeOffSeconds * 1000 || 60 * 6 * 1000
2019-10-27 01:35:10 +02:00
} ;
2020-01-09 18:31:03 +01:00
2020-01-12 05:00:32 +01:00
setInterval ( this . _publishFadeOffGroups , this . internalConfig . checkFadeOffGroups ) ;
2019-06-28 03:46:48 +02:00
} ;
2019-11-28 15:47:42 +01:00
updateMonitoredResources = ( ) => {
throw new Error ( 'The method updateMonitoredResources must be implemented in ' + this . name ) ;
2019-06-28 03:46:48 +02:00
} ;
monitor = ( message ) =>
new Promise ( ( resolve , reject ) => {
reject ( "You must implement a monitor method" ) ;
} ) ;
filter = ( message ) => {
throw new Error ( 'The method filter must be implemented in ' + this . name ) ;
} ;
squashAlerts = ( alerts ) => {
throw new Error ( 'The method squashAlerts must be implemented in ' + this . name ) ;
} ;
2020-01-09 18:31:03 +01:00
_squash = ( id ) => {
2019-06-28 03:46:48 +02:00
2020-01-09 18:31:03 +01:00
const alerts = this . alerts [ id ] ;
2019-07-09 02:46:08 +02:00
const message = this . squashAlerts ( alerts ) ;
2019-06-28 03:46:48 +02:00
2019-07-09 02:46:08 +02:00
if ( message ) {
const firstAlert = alerts [ 0 ] ;
let earliest = Infinity ;
let latest = - Infinity ;
2019-06-28 03:46:48 +02:00
2019-07-09 02:46:08 +02:00
for ( let alert of alerts ) {
earliest = Math . min ( alert . timestamp , earliest ) ;
latest = Math . max ( alert . timestamp , latest ) ;
2019-06-28 03:46:48 +02:00
}
2019-07-09 02:46:08 +02:00
return {
id ,
2020-01-18 13:31:11 +01:00
truncated : this . truncated [ id ] || false ,
2019-07-09 02:46:08 +02:00
origin : this . name ,
earliest ,
latest ,
affected : firstAlert . affected ,
message ,
2020-01-09 18:31:03 +01:00
data : alerts
2019-07-09 02:46:08 +02:00
}
2019-06-28 03:46:48 +02:00
}
} ;
2020-01-09 18:31:03 +01:00
publishAlert = ( id , affected , matchedRule , matchedMessage , extra ) => {
2020-01-12 05:00:32 +01:00
const now = new Date ( ) . getTime ( ) ;
2019-06-28 03:46:48 +02:00
const context = {
2020-01-12 05:00:32 +01:00
timestamp : now ,
2019-06-28 03:46:48 +02:00
affected ,
2019-07-05 17:06:57 +02:00
matchedRule ,
matchedMessage ,
extra
2019-06-28 03:46:48 +02:00
} ;
2020-01-12 05:00:32 +01:00
if ( ! this . sent [ id ] ||
( ! this . config . alertOnlyOnce && now > ( this . sent [ id ] + this . internalConfig . notificationInterval ) ) ) {
2020-01-09 18:31:03 +01:00
this . alerts [ id ] = this . alerts [ id ] || [ ] ;
this . alerts [ id ] . push ( context ) ;
// Check if for each alert group the maxDataSamples parameter is respected
if ( ! this . truncated [ id ] && this . alerts [ id ] . length > this . maxDataSamples ) {
this . truncated [ id ] = this . alerts [ id ] [ 0 ] . timestamp ; // Mark as truncated
this . alerts [ id ] = this . alerts [ id ] . slice ( - this . maxDataSamples ) ; // Truncate
}
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
this . _publishGroupId ( id , now ) ;
2019-06-28 03:46:48 +02:00
return true ;
}
return false ;
} ;
2020-01-12 05:00:32 +01:00
_publishFadeOffGroups = ( ) => {
const now = new Date ( ) . getTime ( ) ;
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
for ( let id in this . fadeOff ) {
this . _publishGroupId ( id , now ) ;
}
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
if ( ! this . config . alertOnlyOnce ) {
for ( let id in this . alerts ) {
if ( now > ( this . sent [ id ] + this . internalConfig . notificationInterval ) ) {
delete this . sent [ id ] ;
}
}
2019-06-28 03:46:48 +02:00
}
} ;
2020-01-12 05:00:32 +01:00
_publishGroupId = ( id , now ) => {
const group = this . _squash ( id ) ;
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
if ( group ) {
this . _publishOnChannel ( group ) ;
this . sent [ id ] = now ;
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
delete this . alerts [ id ] ;
delete this . fadeOff [ id ] ;
delete this . truncated [ id ] ;
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
} else if ( this . fadeOff [ id ] ) {
2020-01-09 18:31:03 +01:00
if ( now > this . fadeOff [ id ] + this . internalConfig . fadeOff ) {
delete this . fadeOff [ id ] ;
delete this . alerts [ id ] ;
delete this . truncated [ id ] ;
2019-07-09 02:46:08 +02:00
}
2019-06-28 03:46:48 +02:00
2020-01-12 05:00:32 +01:00
} else {
this . fadeOff [ id ] = this . fadeOff [ id ] || now ;
}
2019-06-28 03:46:48 +02:00
} ;
_publishOnChannel = ( alert ) => {
2019-06-29 03:35:53 +02:00
this . pubSub . publish ( this . channel , alert ) ;
2019-06-28 03:46:48 +02:00
return alert ;
2019-09-26 02:13:43 +02:00
} ;
getMoreSpecificMatch = ( prefix ) => {
const matched = this . input . getMoreSpecificMatch ( prefix ) ;
if ( matched ) {
if ( matched . includeMonitors . length > 0 && ! matched . includeMonitors . includes ( this . name ) ) {
return null ;
}
return ( matched . excludeMonitors . includes ( this . name ) ) ? null : matched ;
}
} ;
2019-06-28 03:46:48 +02:00
2019-06-14 18:04:20 +02:00
}