Skip to content

Commit 33d5090

Browse files
authored
Merge pull request #1785 from jamesoff/feature/nut
Add NUT UPS monitoring
2 parents daabc59 + d581619 commit 33d5090

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
lines changed

.github/workflows/publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Build
2424
run: poetry build
2525
- name: Store distribution packages
26-
uses: actions/upload-artifact@v3
26+
uses: actions/upload-artifact@v4
2727
with:
2828
name: python-package-distributions
2929
path: dist/
@@ -63,7 +63,7 @@ jobs:
6363
name: python-package-distributions
6464
path: dist/
6565
- name: Sign the dists with Sigstore
66-
uses: sigstore/gh-action-sigstore-python@v2.1.1
66+
uses: sigstore/gh-action-sigstore-python@v3.0.0
6767
with:
6868
inputs: >-
6969
./dist/*.tar.gz

simplemonitor/Monitors/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
MonitorFileStat,
1515
MonitorLoadAvg,
1616
MonitorMemory,
17+
MonitorNUT,
1718
MonitorPkgAudit,
1819
MonitorPortAudit,
1920
MonitorSwap,
@@ -55,6 +56,7 @@
5556
"MonitorHost",
5657
"MonitorLoadAvg",
5758
"MonitorMemory",
59+
"MonitorNUT",
5860
"MonitorPing",
5961
"MonitorPkgAudit",
6062
"MonitorPortAudit",

simplemonitor/Monitors/host.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,70 @@ def get_params(self) -> Tuple:
224224
return (self.path,)
225225

226226

227+
@register
228+
class MonitorNUT(Monitor):
229+
"""Check a NUT UPS"""
230+
231+
monitor_type = "nut"
232+
233+
def __init__(self, name: str, config_options: dict) -> None:
234+
super().__init__(name, config_options)
235+
self.ups = cast(
236+
str, self.get_config_option("ups", required=True, allow_empty=False)
237+
)
238+
239+
def run_test(self) -> bool:
240+
info = {}
241+
try:
242+
_output = subprocess.check_output(["upsc", self.ups]) # nosec
243+
output = _output.decode("utf-8") # type: str
244+
except subprocess.CalledProcessError as error:
245+
output = error.output
246+
except OSError as error:
247+
return self.record_fail(f"Could not run upsc: {error}")
248+
except Exception as error:
249+
return self.record_fail(f"Error while getting UPS info: {error}")
250+
251+
for line in output.splitlines():
252+
if line.find(":") > -1:
253+
bits = line.split(":")
254+
info[bits[0].strip()] = bits[1].strip()
255+
256+
try:
257+
ups_status = cast(str, info["ups.status"])
258+
except KeyError:
259+
return self.record_fail("Could not get UPS status")
260+
261+
status_tokens = ups_status.split()
262+
ok = True
263+
message: list[str] = []
264+
if "OB" in status_tokens:
265+
message.append("UPS is on battery")
266+
ok = False
267+
if "OL" in status_tokens:
268+
message.append("online")
269+
if "RB" in status_tokens:
270+
message.append("battery needs replacing!")
271+
ok = False
272+
if "ups.load" in info:
273+
message.append(f"load: {info['ups.load']}%")
274+
if "battery.charge" in info:
275+
message.append(f"charge: {info['battery.charge']}")
276+
if "battery.runtime" in info:
277+
message.append(f"runtime: {info['battery.runtime']}")
278+
279+
message_str = "; ".join(message)
280+
if ok:
281+
return self.record_success(message_str)
282+
return self.record_fail(message_str)
283+
284+
def describe(self) -> str:
285+
return f"Monitoring UPS {self.ups} to make sure it's online"
286+
287+
def get_params(self) -> Tuple:
288+
return (self.ups,)
289+
290+
227291
@register
228292
class MonitorPortAudit(Monitor):
229293
"""Check a host doesn't have outstanding security issues."""

0 commit comments

Comments
 (0)