Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
CommanderStorm authored Jul 15, 2024
2 parents e791c70 + 4aef678 commit 1822b55
Show file tree
Hide file tree
Showing 35 changed files with 365 additions and 67 deletions.
16 changes: 16 additions & 0 deletions db/knex_migrations/2024-04-26-0000-snmp-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("snmp_oid").defaultTo(null);
table.enum("snmp_version", [ "1", "2c", "3" ]).defaultTo("2c");
table.string("json_path_operator").defaultTo(null);
});
};

exports.down = function (knex) {
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("snmp_oid");
table.dropColumn("snmp_version");
table.dropColumn("json_path_operator");
});
};
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
"mssql": "~11.0.0",
"mysql2": "~3.9.6",
"nanoid": "~3.3.4",
"net-snmp": "^3.11.2",
"node-cloudflared-tunnel": "~1.0.9",
"node-radius-client": "~1.0.0",
"nodemailer": "~6.9.13",
Expand Down
26 changes: 9 additions & 17 deletions server/model/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const dayjs = require("dayjs");
const axios = require("axios");
const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
SQL_DATETIME_FORMAT
SQL_DATETIME_FORMAT, evaluateJsonQuery
} = require("../../src/util");
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
Expand All @@ -17,7 +17,6 @@ const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server");
const { DockerHost } = require("../docker");
const Gamedig = require("gamedig");
const jsonata = require("jsonata");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const { UptimeCalculator } = require("../uptime-calculator");
Expand Down Expand Up @@ -161,6 +160,9 @@ class Monitor extends BeanModel {
kafkaProducerMessage: this.kafkaProducerMessage,
screenshot,
remote_browser: this.remote_browser,
snmpOid: this.snmpOid,
jsonPathOperator: this.jsonPathOperator,
snmpVersion: this.snmpVersion,
};

if (includeSensitiveData) {
Expand Down Expand Up @@ -598,25 +600,15 @@ class Monitor extends BeanModel {
} else if (this.type === "json-query") {
let data = res.data;

// convert data to object
if (typeof data === "string" && res.headers["content-type"] !== "application/json") {
try {
data = JSON.parse(data);
} catch (_) {
// Failed to parse as JSON, just process it as a string
}
}
const { status, response } = await evaluateJsonQuery(data, this.jsonPath, this.jsonPathOperator, this.expectedValue);

let expression = jsonata(this.jsonPath);

let result = await expression.evaluate(data);

if (result.toString() === this.expectedValue) {
bean.msg += ", expected value is found";
if (status) {
bean.status = UP;
bean.msg = `JSON query passes (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})`;
} else {
throw new Error(bean.msg + ", but value is not equal to expected value, value was: [" + result + "]");
throw new Error(`JSON query does not pass (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})`);
}

}

} else if (this.type === "port") {
Expand Down
63 changes: 63 additions & 0 deletions server/monitor-types/snmp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const { MonitorType } = require("./monitor-type");
const { UP, log, evaluateJsonQuery } = require("../../src/util");
const snmp = require("net-snmp");

class SNMPMonitorType extends MonitorType {
name = "snmp";

/**
* @inheritdoc
*/
async check(monitor, heartbeat, _server) {
let session;
try {
const sessionOptions = {
port: monitor.port || "161",
retries: monitor.maxretries,
timeout: monitor.timeout * 1000,
version: snmp.Version[monitor.snmpVersion],
};
session = snmp.createSession(monitor.hostname, monitor.radiusPassword, sessionOptions);

// Handle errors during session creation
session.on("error", (error) => {
throw new Error(`Error creating SNMP session: ${error.message}`);
});

const varbinds = await new Promise((resolve, reject) => {
session.get([ monitor.snmpOid ], (error, varbinds) => {
error ? reject(error) : resolve(varbinds);
});
});
log.debug("monitor", `SNMP: Received varbinds (Type: ${snmp.ObjectType[varbinds[0].type]} Value: ${varbinds[0].value})`);

if (varbinds.length === 0) {
throw new Error(`No varbinds returned from SNMP session (OID: ${monitor.snmpOid})`);
}

if (varbinds[0].type === snmp.ObjectType.NoSuchInstance) {
throw new Error(`The SNMP query returned that no instance exists for OID ${monitor.snmpOid}`);
}

// We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in.
const value = varbinds[0].value;

const { status, response } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);

if (status) {
heartbeat.status = UP;
heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`;
} else {
throw new Error(`JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`);
}
} finally {
if (session) {
session.close();
}
}
}
}

module.exports = {
SNMPMonitorType,
};
4 changes: 4 additions & 0 deletions server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ let needSetup = false;
monitor.kafkaProducerAllowAutoTopicCreation;
bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly;
bean.remote_browser = monitor.remote_browser;
bean.snmpVersion = monitor.snmpVersion;
bean.snmpOid = monitor.snmpOid;
bean.jsonPathOperator = monitor.jsonPathOperator;
bean.timeout = monitor.timeout;

bean.validate();

Expand Down
2 changes: 2 additions & 0 deletions server/uptime-kuma-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class UptimeKumaServer {
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();

// Allow all CORS origins (polling) in development
Expand Down Expand Up @@ -517,4 +518,5 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor
const { TailscalePing } = require("./monitor-types/tailscale-ping");
const { DnsMonitorType } = require("./monitor-types/dns");
const { MqttMonitorType } = require("./monitor-types/mqtt");
const { SNMPMonitorType } = require("./monitor-types/snmp");
const { MongodbMonitorType } = require("./monitor-types/mongodb");
1 change: 0 additions & 1 deletion src/lang/bg-BG.json
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,6 @@
"twilioApiKey": "API ключ (по избор)",
"Expected Value": "Очаквана стойност",
"Json Query": "Заявка тип JSON",
"jsonQueryDescription": "Прави JSON заявка срещу отговора и проверява за очаквана стойност (Върнатата стойност ще бъде преобразувана в низ за сравнение). Разгледайте {0} за документация относно езика на заявката. Имате възможност да тествате {1}.",
"Badge Duration (in hours)": "Времетраене на баджа (в часове)",
"Badge Preview": "Преглед на баджа",
"Notify Channel": "Канал за известяване",
Expand Down
1 change: 0 additions & 1 deletion src/lang/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,6 @@
"Enable Kafka Producer Auto Topic Creation": "Povolit Kafka zprostředkovateli automatické vytváření vláken",
"Kafka Producer Message": "Zpráva Kafka zprostředkovatele",
"tailscalePingWarning": "Abyste mohli používat Tailscale Ping monitor, je nutné Uptime Kuma nainstalovat mimo Docker, a dále na váš server nainstalovat Tailscale klienta.",
"jsonQueryDescription": "Proveďte JSON dotaz vůči odpovědi a zkontrolujte očekávaný výstup (za účelem porovnání bude návratová hodnota převedena na řetězec). Dokumentaci k dotazovacímu jazyku naleznete na {0}, a využít můžete též {1}.",
"Select": "Vybrat",
"selectedMonitorCount": "Vybráno: {0}",
"Check/Uncheck": "Vybrat/Zrušit výběr",
Expand Down
1 change: 0 additions & 1 deletion src/lang/de-CH.json
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,6 @@
"Json Query": "Json-Abfrage",
"filterActive": "Aktiv",
"filterActivePaused": "Pausiert",
"jsonQueryDescription": "Führe eine JSON-Abfrage gegen die Antwort durch und prüfe den erwarteten Wert (der Rückgabewert wird zum Vergleich in eine Zeichenkette umgewandelt). Auf {0} findest du die Dokumentation zur Abfragesprache. {1} kannst du Abfragen üben.",
"Badge Duration (in hours)": "Abzeichen Dauer (in Stunden)",
"Badge Preview": "Abzeichen Vorschau",
"tailscalePingWarning": "Um den Tailscale Ping Monitor nutzen zu können, musst du Uptime Kuma ohne Docker installieren und den Tailscale Client auf dem Server installieren.",
Expand Down
1 change: 0 additions & 1 deletion src/lang/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,6 @@
"filterActivePaused": "Pausiert",
"Expected Value": "Erwarteter Wert",
"Json Query": "Json-Abfrage",
"jsonQueryDescription": "Führe eine JSON-Abfrage gegen die Antwort durch und prüfe den erwarteten Wert (der Rückgabewert wird zum Vergleich in eine Zeichenkette umgewandelt). Auf {0} findest du die Dokumentation zur Abfragesprache. {1} kannst du Abfragen üben.",
"tailscalePingWarning": "Um den Tailscale Ping Monitor nutzen zu können, musst du Uptime Kuma ohne Docker installieren und den Tailscale Client auf dem Server installieren.",
"Server URL should not contain the nfty topic": "Die Server-URL sollte das nfty-Thema nicht enthalten",
"pushDeerServerDescription": "Leer lassen um den offiziellen Server zu verwenden",
Expand Down
11 changes: 9 additions & 2 deletions src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"Keyword": "Keyword",
"Invert Keyword": "Invert Keyword",
"Expected Value": "Expected Value",
"Json Query": "Json Query",
"Json Query Expression": "Json Query Expression",
"Friendly Name": "Friendly Name",
"URL": "URL",
"Hostname": "Hostname",
Expand Down Expand Up @@ -588,7 +588,7 @@
"notificationDescription": "Notifications must be assigned to a monitor to function.",
"keywordDescription": "Search keyword in plain HTML or JSON response. The search is case-sensitive.",
"invertKeywordDescription": "Look for the keyword to be absent rather than present.",
"jsonQueryDescription": "Do a json Query against the response and check for expected value (Return value will get converted into string for comparison). Check out {0} for the documentation about the query language. A playground can be found {1}.",
"jsonQueryDescription": "Parse and extract specific data from the server's JSON response using JSON query or use \"$\" for the raw response, if not expecting JSON. The result is then compared to the expected value, as strings. See {0} for documentation and use {1} to experiment with queries.",
"backupDescription": "You can backup all monitors and notifications into a JSON file.",
"backupDescription2": "Note: history and event data is not included.",
"backupDescription3": "Sensitive data such as notification tokens are included in the export file; please store export securely.",
Expand Down Expand Up @@ -943,6 +943,13 @@
"cellsyntSplitLongMessages": "Split long messages into up to 6 parts. 153 x 6 = 918 characters.",
"max 15 digits": "max 15 digits",
"max 11 alphanumeric characters": "max 11 alphanumeric characters",
"Community String": "Community String",
"snmpCommunityStringHelptext": "This string functions as a password to authenticate and control access to SNMP-enabled devices. Match it with your SNMP device's configuration.",
"OID (Object Identifier)": "OID (Object Identifier)",
"snmpOIDHelptext": "Enter the OID for the sensor or status you want to monitor. Use network management tools like MIB browsers or SNMP software if you're unsure about the OID.",
"Condition": "Condition",
"SNMP Version": "SNMP Version",
"Please enter a valid OID.": "Please enter a valid OID.",
"wayToGetThreemaGateway": "You can register for Threema Gateway {0}.",
"threemaRecipient": "Recipient",
"threemaRecipientType": "Recipient Type",
Expand Down
1 change: 0 additions & 1 deletion src/lang/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,6 @@
"Json Query": "Consulta Json",
"invertKeywordDescription": "Comprobar si la palabra clave está ausente en vez de presente.",
"enableNSCD": "Habilitar NSCD (Demonio de Caché de Servicio de Nombres) para almacenar en caché todas las solicitudes DNS",
"jsonQueryDescription": "Realiza una consulta JSON contra la respuesta y verifica el valor esperado (el valor de retorno se convertirá a una cadena para la comparación). Consulta {0} para obtener documentación sobre el lenguaje de consulta. Puede encontrar un espacio de prueba {1}.",
"Request Timeout": "Tiempo de espera máximo de petición",
"timeoutAfter": "Expirar después de {0} segundos",
"chromeExecutableDescription": "Para usuarios de Docker, si Chromium no está instalado, puede que tarde unos minutos en ser instalado y mostrar el resultado de la prueba. Usa 1GB de espacio.",
Expand Down
1 change: 0 additions & 1 deletion src/lang/fa.json
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,6 @@
"filterActive": "فعال",
"webhookCustomBodyDesc": "یک بدنه HTTP سفارشی برای ریکوئست تعریف کنید. متغیر های قابل استفاده: {msg}, {heartbeat}, {monitor}.",
"tailscalePingWarning": "برای استفاده از Tailscale Ping monitor، شما باید آپتایم کوما را بدون استفاده از داکر و همچنین Tailscale client را نیز بر روی سرور خود نصب داشته باشید.",
"jsonQueryDescription": "یک کوئری json در برابر پاسخ انجام دهید و مقدار مورد انتظار را (مقدار برگشتی برای مقایسه به رشته تبدیل می شود). برای مستندات درباره زبان کوئری، {0} مشاهده کنید. همچنین محیط تست را میتوانید در {1} پیدا کنید.",
"Enter the list of brokers": "لیست بروکر هارا وارد کنید",
"Enable Kafka Producer Auto Topic Creation": "فعال سازی ایجاپ موضوع اتوماتیک تهیه کننده",
"Secret AccessKey": "کلید محرمانه AccessKey",
Expand Down
1 change: 0 additions & 1 deletion src/lang/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,6 @@
"emailTemplateLimitedToUpDownNotification": "saatavilla vain YLÖS/ALAS sydämensykkeille, muulloin null",
"Your User ID": "Käyttäjätunnuksesi",
"invertKeywordDescription": "Etsi puuttuvaa avainsanaa.",
"jsonQueryDescription": "Suorita JSON-kysely vastaukselle ja tarkista odotettu arvo (Paluuarvo muutetaan merkkijonoksi vertailua varten). Katso kyselykielen ohjeita osoitteesta {0}. Leikkikenttä löytyy osoitteesta {1}.",
"Bark API Version": "Bark API-versio",
"Notify Channel": "Ilmoitus kanavalle",
"aboutNotifyChannel": "Ilmoitus kanavalle antaa työpöytä- tai mobiili-ilmoituksen kaikille kanavan jäsenille; riippumatta ovatko he paikalla vai poissa.",
Expand Down
1 change: 0 additions & 1 deletion src/lang/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,6 @@
"twilioApiKey": "Clé API (facultatif)",
"Expected Value": "Valeur attendue",
"Json Query": "Requête Json",
"jsonQueryDescription": "Faites une requête json contre la réponse et vérifiez la valeur attendue (la valeur de retour sera convertie en chaîne pour comparaison). Consultez {0} pour la documentation sur le langage de requête. Une aire de jeux peut être trouvée {1}.",
"Badge Duration (in hours)": "Durée du badge (en heures)",
"Badge Preview": "Aperçu du badge",
"aboutNotifyChannel": "Notifier le canal déclenchera une notification de bureau ou mobile pour tous les membres du canal, que leur disponibilité soit active ou absente.",
Expand Down
1 change: 0 additions & 1 deletion src/lang/ga.json
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,6 @@
"confirmDisableTwoFAMsg": "An bhfuil tú cinnte gur mhaith leat 2FA a dhíchumasú?",
"affectedStatusPages": "Taispeáin an teachtaireacht cothabhála seo ar leathanaigh stádais roghnaithe",
"keywordDescription": "Cuardaigh eochairfhocal i ngnáthfhreagra HTML nó JSON. Tá an cuardach cás-íogair.",
"jsonQueryDescription": "Déan Iarratas json in aghaidh an fhreagra agus seiceáil an luach a bhfuiltear ag súil leis (Déanfar an luach fillte a thiontú ina theaghrán le haghaidh comparáide). Seiceáil {0} le haghaidh na gcáipéisí faoin teanga iarratais. Is féidir clós súgartha a aimsiú {1}.",
"backupDescription": "Is féidir leat gach monatóir agus fógra a chúltaca isteach i gcomhad JSON.",
"backupDescription2": "Nóta: níl sonraí staire agus imeachta san áireamh.",
"octopushAPIKey": "\"Eochair API\" ó dhintiúir API HTTP sa phainéal rialaithe",
Expand Down
Loading

0 comments on commit 1822b55

Please sign in to comment.