1
0
mirror of https://github.com/nttgin/BGPalerter.git synced 2024-05-19 06:50:08 +00:00

introduced persistence module

This commit is contained in:
Massimo Candela
2020-06-28 03:15:21 +02:00
parent 54ce4fd558
commit e9ff773086
9 changed files with 157 additions and 6 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ logs/
bgpalerter.pid
alertdata/
.npmrc
.cache/

View File

@@ -9,3 +9,4 @@ logs/
bgpalerter.pid
alertdata/
.npmrc
.cache/

View File

@@ -201,10 +201,12 @@ reports:
# - notificationIntervalSeconds
# Defines the amount of seconds after which an alert can be repeated. An alert is repeated only if the event that
# triggered it is not yet solved.
#
# - persistStatus
# Persist the status of BGPalerter. If the process is restarted, the list of alerts already sent is recovered
# and they are not repeated. The process must be able to write on disc, this option will create a file inside .cache/
notificationIntervalSeconds: 14400
persistStatus: true
logging:
directory: logs
@@ -216,7 +218,6 @@ logging:
checkForUpdatesAtBoot: true
############################
# Process monitoring settings:
# Uncomment or add classes under processMonitors if you want to monitor or send logs about the status of the BGPalerter process
@@ -246,6 +247,7 @@ checkForUpdatesAtBoot: true
monitoredPrefixesFiles:
- prefixes.yml
############################
# HTTP proxy setting:
# Allow to run BGPalerter behind an HTTP/HTTPS proxy.

View File

@@ -130,6 +130,7 @@ let config = {
notificationIntervalSeconds: 14400,
alarmOnlyOnce: false,
monitoredPrefixesFiles: ["prefixes.yml"],
persistStatus: true,
logging: {
directory: "logs",
logRotatePattern: "YYYY-MM-DD",
@@ -266,7 +267,15 @@ config.connectors = config.connectors
if (config.httpProxy) {
const HttpsProxyAgent = require("https-proxy-agent");
vector.agent = new HttpsProxyAgent(url.parse(config.httpProxy));
}
}
if (!!config.persistStatus) {
const Storage = require("./utils/storages/storageFile").default;
vector.storage = new Storage({
validity: 3600 * 4,
}, config);
}
const input = new Input(config);

View File

@@ -40,6 +40,7 @@ export default class Monitor {
this.pubSub = env.pubSub;
this.logger = env.logger;
this.input = env.input;
this.storage = env.storage;
this.params = params || {};
this.maxDataSamples = this.params.maxDataSamples || 1000;
this.name = name;
@@ -56,9 +57,10 @@ export default class Monitor {
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)
this._retrieveStatus();
this.internalConfig = {
notificationInterval: (this.config.notificationIntervalSeconds || 14400) * 1000,
checkFadeOffGroups: this.config.checkFadeOffGroupsSeconds || 30 * 1000,
checkFadeOffGroups: (this.config.checkFadeOffGroupsSeconds || 30) * 1000,
fadeOff: this.config.fadeOffSeconds * 1000 || 60 * 6 * 1000
};
@@ -158,6 +160,47 @@ export default class Monitor {
}
}
}
this._persistStatus();
};
_retrieveStatus = () => {
if (this.storage) {
this.storage.get("status")
.then(({ alerts={}, sent={}, truncated={}, fadeOff={} }) => {
this.alerts = alerts;
this.sent = sent;
this.truncated = truncated;
this.fadeOff = fadeOff;
})
.catch(error => {
this.logger.log({
level: 'error',
message: error
});
});
}
};
_persistStatus = () => {
if (this.storage) {
const status = {
alerts: this.alerts,
sent: this.sent,
truncated: this.truncated,
fadeOff: this.fadeOff
};
if (Object.values(status).some(i => Object.keys(i).length > 0)) { // If there is anything in the cache
this.storage.set("status", status)
.catch(error => {
this.logger.log({
level: 'error',
message: error
});
});
}
}
};
_publishGroupId = (id, now) => {

49
src/utils/storage.js Normal file
View File

@@ -0,0 +1,49 @@
export default class Storage {
constructor(params, config){
this.config = config;
this.params = params;
this.validity = this.params.validity || 3600 * 2;
};
set = (key, value) =>
new Promise((resolve, reject) => {
if (/^[a-z\-]+$/i.test(key)) {
const envelop = {
date: new Date().getTime(),
value
};
this._set(key, envelop)
.then(resolve);
resolve();
} else {
reject("Not a valid key. Use only chars and dashes.");
}
});
get = (key) =>
new Promise((resolve, reject) => {
if (/^[a-z\-]+$/i.test(key)) {
this._get(key)
.then(({ date, value }) => {
const now = new Date().getTime();
if (date + this.validity >= now) {
return value;
}
})
.then(resolve);
} else {
reject("Not a valid key. Use only chars and dashes.");
}
});
_set = (key, value) => {
throw new Error("The set method must be implemented");
};
_get = (key) => {
throw new Error("The get method must be implemented");
};
}

View File

@@ -0,0 +1,44 @@
import Storage from "../storage";
import fs from "fs";
export default class StorageFile extends Storage{
constructor(params, config){
super(params, config);
this.directory = this.config.volume + (this.params.directory || ".cache/");
if (!fs.existsSync(this.directory)) {
fs.mkdirSync(this.directory);
}
};
_set = (key, value) =>
new Promise((resolve, reject) => {
const file = this.directory + key + ".json";
fs.writeFile(file, JSON.stringify(value), error => {
if (error) {
reject(error);
} else {
resolve(true);
}
});
});
_get = (key) =>
new Promise((resolve, reject) => {
const file = this.directory + key + ".json";
if (fs.existsSync(file)) {
fs.readFile(file, (error, content) => {
if (error) {
reject(error);
} else {
try {
resolve(JSON.parse(content));
} catch (e) {
resolve({});
}
}
});
} else {
resolve({});
}
});
}

View File

@@ -78,7 +78,8 @@ describe("Composition", function() {
"maxMessagesPerSecond",
"fadeOffSeconds",
"checkFadeOffGroupsSeconds",
"volume"
"volume",
"persistStatus"
]);
expect(config.connectors[0]).to.have
.property('class')

View File

@@ -76,6 +76,7 @@ logging:
compressOnRotation: true
checkForUpdatesAtBoot: true
persistStatus: true
processMonitors:
- file: uptimeApi