A complete maintenance planning system has been created

This commit is contained in:
Karel Krýda 2022-01-23 15:22:00 +01:00
parent c3c4db52ec
commit 0d3414c6d6
32 changed files with 1121 additions and 51 deletions

View file

@ -6,7 +6,7 @@ dayjs.extend(utc);
dayjs.extend(timezone);
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { debug, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger} = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog } = require("../util-server");
const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model");
@ -20,6 +20,7 @@ const apicache = require("../modules/apicache");
* 0 = DOWN
* 1 = UP
* 2 = PENDING
* 3 = MAINTENANCE
*/
class Monitor extends BeanModel {
@ -28,9 +29,12 @@ class Monitor extends BeanModel {
* Only show necessary data to public
*/
async toPublicJSON() {
const maintenance = await R.getAll("SELECT mm.*, maintenance.start_date, maintenance.end_date FROM monitor_maintenance mm JOIN maintenance ON mm.maintenance_id = maintenance.id WHERE mm.monitor_id = ? AND datetime(maintenance.start_date) <= datetime('now', 'localtime') AND datetime(maintenance.end_date) >= datetime('now', 'localtime')", [this.id]);
return {
id: this.id,
name: this.name,
maintenance: (maintenance.length !== 0),
};
}
@ -50,6 +54,7 @@ class Monitor extends BeanModel {
}
const tags = await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ?", [this.id]);
const maintenance = await R.getAll("SELECT mm.*, maintenance.start_date, maintenance.end_date FROM monitor_maintenance mm JOIN maintenance ON mm.maintenance_id = maintenance.id WHERE mm.monitor_id = ? AND datetime(maintenance.start_date) <= datetime('now', 'localtime') AND datetime(maintenance.end_date) >= datetime('now', 'localtime')", [this.id]);
return {
id: this.id,
@ -79,6 +84,7 @@ class Monitor extends BeanModel {
pushToken: this.pushToken,
notificationIDList,
tags: tags,
maintenance: (maintenance.length !== 0),
};
}
@ -136,6 +142,8 @@ class Monitor extends BeanModel {
bean.time = R.isoDateTime(dayjs.utc());
bean.status = DOWN;
const maintenance = await R.getAll("SELECT mm.*, maintenance.start_date, maintenance.end_date FROM monitor_maintenance mm JOIN maintenance ON mm.maintenance_id = maintenance.id WHERE mm.monitor_id = ? AND datetime(maintenance.start_date) <= datetime('now', 'localtime') AND datetime(maintenance.end_date) >= datetime('now', 'localtime')", [this.id]);
if (this.isUpsideDown()) {
bean.status = flipStatus(bean.status);
}
@ -148,7 +156,11 @@ class Monitor extends BeanModel {
}
try {
if (this.type === "http" || this.type === "keyword") {
if (maintenance.length !== 0) {
bean.msg = "Monitor under maintenance";
bean.status = MAINTENANCE;
}
else if (this.type === "http" || this.type === "keyword") {
// Do not do any queries/high loading things before the "bean.ping"
let startTime = dayjs().valueOf();
@ -387,8 +399,13 @@ class Monitor extends BeanModel {
if (isImportant) {
bean.important = true;
debug(`[${this.name}] sendNotification`);
await Monitor.sendNotification(isFirstBeat, this, bean);
if (Monitor.isImportantForNotification(isFirstBeat, previousBeat?.status, bean.status)) {
debug(`[${this.name}] sendNotification`);
await Monitor.sendNotification(isFirstBeat, this, bean);
}
else {
debug(`[${this.name}] will not sendNotification because it is (or was) under maintenance`);
}
// Clear Status Page Cache
debug(`[${this.name}] apicache clear`);
@ -405,6 +422,8 @@ class Monitor extends BeanModel {
beatInterval = this.retryInterval;
}
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`);
} else if (bean.status === MAINTENANCE) {
console.warn(`Monitor #${this.id} '${this.name}': Under Maintenance | Type: ${this.type}`);
} else {
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
}
@ -598,7 +617,7 @@ class Monitor extends BeanModel {
-- SUM all uptime duration, also trim off the beat out of time window
SUM(
CASE
WHEN (status = 1)
WHEN (status = 1 OR status = 3)
THEN
CASE
WHEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 < duration
@ -659,11 +678,42 @@ class Monitor extends BeanModel {
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
let isImportant = isFirstBeat ||
// MAINTENANCE -> MAINTENANCE = not important
// * MAINTENANCE -> UP = important
// * MAINTENANCE -> DOWN = important
// * DOWN -> MAINTENANCE = important
// * UP -> MAINTENANCE = important
return isFirstBeat ||
(previousBeatStatus === DOWN && currentBeatStatus === MAINTENANCE) ||
(previousBeatStatus === UP && currentBeatStatus === MAINTENANCE) ||
(previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN) ||
(previousBeatStatus === MAINTENANCE && currentBeatStatus === UP) ||
(previousBeatStatus === UP && currentBeatStatus === DOWN) ||
(previousBeatStatus === DOWN && currentBeatStatus === UP) ||
(previousBeatStatus === PENDING && currentBeatStatus === DOWN);
}
static isImportantForNotification(isFirstBeat, previousBeatStatus, currentBeatStatus) {
// * ? -> ANY STATUS = important [isFirstBeat]
// UP -> PENDING = not important
// * UP -> DOWN = important
// UP -> UP = not important
// PENDING -> PENDING = not important
// * PENDING -> DOWN = important
// PENDING -> UP = not important
// DOWN -> PENDING = this case not exists
// DOWN -> DOWN = not important
// * DOWN -> UP = important
// MAINTENANCE -> MAINTENANCE = not important
// MAINTENANCE -> UP = not important
// * MAINTENANCE -> DOWN = important
// DOWN -> MAINTENANCE = not important
// UP -> MAINTENANCE = not important
return isFirstBeat ||
(previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN) ||
(previousBeatStatus === UP && currentBeatStatus === DOWN) ||
(previousBeatStatus === DOWN && currentBeatStatus === UP) ||
(previousBeatStatus === PENDING && currentBeatStatus === DOWN);
return isImportant;
}
static async sendNotification(isFirstBeat, monitor, bean) {