-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Add nut server monitor #3967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add nut server monitor #3967
Changes from all commits
fb58ec7
f9fd532
f01d6fe
779eb76
f62cace
2fb468b
4f605a7
4f76058
ddf0d4c
fecaec6
ee464c6
9bba305
2d98cbe
09a6c19
74a4fe2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| exports.up = function (knex) { | ||
| // Add new column | ||
| return knex.schema | ||
| .alterTable("monitor", function (table) { | ||
| table.text("ups_name"); | ||
| table.text("nut_username"); | ||
| table.text("nut_password"); | ||
| }); | ||
|
|
||
| }; | ||
|
|
||
| exports.down = function (knex) { | ||
| // Drop nut variable column | ||
| return knex.schema | ||
| .alterTable("monitor", function (table) { | ||
| table.dropColumn("ups_name"); | ||
| table.dropColumn("nut_username"); | ||
| table.dropColumn("nut_password"); | ||
| }); | ||
| }; | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -166,6 +166,7 @@ | |
| "favico.js": "~0.3.10", | ||
| "jest": "~29.6.1", | ||
| "marked": "~4.2.5", | ||
| "node-nut": "^1.0.3", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure if this library is a good idea… |
||
| "node-ssh": "~13.1.0", | ||
| "postcss-html": "~1.5.0", | ||
| "postcss-rtlcss": "~3.7.2", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| const { MonitorType } = require("./monitor-type"); | ||
| const { UP, DOWN } = require("../../src/util"); | ||
| const dayjs = require("dayjs"); | ||
| const jsonata = require("jsonata"); | ||
| const Nut = require("node-nut"); | ||
|
|
||
| const { log } = require("../../src/util"); | ||
|
|
||
| class NutMonitorType extends MonitorType { | ||
|
|
||
| name = "nut"; | ||
|
|
||
| /** | ||
| * @inheritdoc | ||
| */ | ||
| async check(monitor, heartbeat, _server) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| let startTime = dayjs().valueOf(); | ||
| let expression = jsonata(monitor.jsonPath); | ||
|
|
||
| const nut = new Nut(monitor.port, monitor.hostname); | ||
|
|
||
| nut.on("ready", () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be me not knowing js enough, but I think this function is racable (have not debugged this, just a hunch)
I think this might be why you are seeing I think the following code looks more promissing: |
||
| nut.GetUPSList((upslist, err) => { | ||
| if (err) { | ||
| nut.close(); | ||
| log.error("NUT Error: " + err); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is not returning here intentional? |
||
| } | ||
|
|
||
| let upsname = upslist[monitor.upsName] && monitor.upsName || Object.keys(upslist)[0]; | ||
|
|
||
| nut.GetUPSVars(upsname, async (vars, err) => { | ||
| nut.close(); | ||
| if (err) { | ||
| throw new Error("Error getting UPS variables"); | ||
| } else { | ||
| // convert data to object | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you clarify that |
||
| if (typeof vars === "string") { | ||
| vars = JSON.parse(vars); | ||
| } | ||
| const data = ({ ...vars }); // copy vars | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you clarify the comment why this is nessesary? |
||
|
|
||
| // Check device status | ||
| let result = await expression.evaluate(data); | ||
|
|
||
| if (result.toString() === monitor.expectedValue) { | ||
| heartbeat.status = UP; | ||
| heartbeat.msg = ""; | ||
| heartbeat.ping = dayjs().valueOf() - startTime; | ||
| } else { | ||
| heartbeat.status = DOWN; | ||
| heartbeat.msg = `Value not expected, value was: [${result}]`; | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| nut.start(); | ||
| } | ||
| } | ||
|
|
||
| module.exports = { | ||
| NutMonitorType, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,7 @@ | |
| <span v-if="monitor.type === 'redis'">{{ filterPassword(monitor.databaseConnectionString) }}</span> | ||
| <span v-if="monitor.type === 'sqlserver'">SQL Server: {{ filterPassword(monitor.databaseConnectionString) }}</span> | ||
| <span v-if="monitor.type === 'steam'">Steam Game Server: {{ monitor.hostname }}:{{ monitor.port }}</span> | ||
| <span v-if="monitor.type === 'nut'">NUT: {{monitor.upsName}} on {{ monitor.hostname }}:{{ monitor.port }}</span> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a translation for
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are some linting warnings associated with this line |
||
| </p> | ||
|
|
||
| <div class="functions"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,9 @@ | |
| <option value="mqtt"> | ||
| MQTT | ||
| </option> | ||
| <option value="nut"> | ||
| Network UPS Tools | ||
| </option> | ||
| <option value="kafka-producer"> | ||
| Kafka Producer | ||
| </option> | ||
|
|
@@ -144,8 +147,26 @@ | |
| </div> | ||
| </div> | ||
|
|
||
| <!-- Json Query --> | ||
| <div v-if="monitor.type === 'json-query'" class="my-3"> | ||
| <!-- NUT --> | ||
| <!-- For NUT Type --> | ||
| <template v-if="monitor.type === 'nut'"> | ||
| <div class="my-3"> | ||
| <label for="upsName" class="form-label">UPS {{ $t("Name") }}</label> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think adding a description on where to obtain the correct ups name might be worth it to prevent other users spending excessive time on this field
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am unshure if a longer name like |
||
| <input id="upsName" v-model="monitor.upsName" type="text" class="form-control" required> | ||
| </div> | ||
| <div class="my-3"> | ||
| <label for="nutUsername" class="form-label">{{ $t("Username") }}</label> | ||
| <input id="nutUsername" v-model="monitor.nutUsername" type="text" class="form-control"> | ||
| </div> | ||
|
|
||
| <div class="my-3"> | ||
| <label for="nutPassword" class="form-label">{{ $t("Password") }}</label> | ||
| <input id="nutPassword" v-model="monitor.nutPassword" type="password" class="form-control"> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you utilise the
CommanderStorm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </div> | ||
| </template> | ||
|
|
||
| <!-- Json Query and NUT--> | ||
| <div v-if="monitor.type === 'json-query' || monitor.type === 'nut'" class="my-3"> | ||
| <label for="jsonPath" class="form-label">{{ $t("Json Query") }}</label> | ||
| <input id="jsonPath" v-model="monitor.jsonPath" type="text" class="form-control" required> | ||
|
|
||
|
|
@@ -221,15 +242,15 @@ | |
| </template> | ||
|
|
||
| <!-- Hostname --> | ||
| <!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping only --> | ||
| <div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' ||monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping'" class="my-3"> | ||
| <!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / NUT only --> | ||
| <div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' ||monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'nut'" class="my-3"> | ||
| <label for="hostname" class="form-label">{{ $t("Hostname") }}</label> | ||
| <input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern : ipOrHostnameRegexPattern}`" required> | ||
| </div> | ||
|
|
||
| <!-- Port --> | ||
| <!-- For TCP Port / Steam / MQTT / Radius Type --> | ||
| <div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius'" class="my-3"> | ||
| <!-- For TCP Port / Steam / MQTT / Radius Type / NUT only --> | ||
| <div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'nut'" class="my-3"> | ||
| <label for="port" class="form-label">{{ $t("Port") }}</label> | ||
| <input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1"> | ||
| </div> | ||
|
|
@@ -1156,12 +1177,14 @@ message HealthCheckResponse { | |
| } | ||
| } | ||
|
|
||
| // Set default port for DNS if not already defined | ||
| if (! this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812") { | ||
| // Set default port for DNS, RADIUS, NUT if not already defined | ||
| if (! this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812" || this.monitor.port === "3493") { | ||
| if (this.monitor.type === "dns") { | ||
| this.monitor.port = "53"; | ||
| } else if (this.monitor.type === "radius") { | ||
| this.monitor.port = "1812"; | ||
| } else if (this.monitor.type === "nut") { | ||
| this.monitor.port = "3493"; | ||
| } else { | ||
| this.monitor.port = undefined; | ||
| } | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the description you mentioned the following, what are your questions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to know if these column names are OK and in keeping with the conventions, I think I did the right thing based on what else I saw.