Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions check-netbox-machine-state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,30 @@
import os
import sys
from functools import reduce
from typing import TYPE_CHECKING

import pynetbox
from sh import ErrorReturnCode, ping
import sh

if TYPE_CHECKING:
from pynetbox.models import dcim, ipam

ping_args = {"-c1", "-W1"}
_ping = sh.Command("ping")


def check_ping(destination: str | pynetbox.models.ipam.IpAddresses) -> bool:
def check_ping(destination: str | ipam.IpAddresses) -> bool:
"""Signal (True) that a machine is reachable when it should not be."""
destination = str(destination).split("/")[0]
try:
_ = ping(destination, *ping_args)
except ErrorReturnCode:
_ = _ping(destination, *ping_args)
except sh.ErrorReturnCode:
return False
log.warning("Ping to destination %s was successfull, this will fail the script!", destination)
return True


def check_machine(machine: pynetbox.models.dcim.Devices) -> bool:
def check_machine(machine: dcim.Devices) -> bool:
"""Detect reachability across all known addresses of a machine to avoid false negatives."""
attributes_to_check = {"oob_ip", "primary_ip", "primary_ip4", "primary_ip6"}
machine_attributes = [getattr(machine, attr) for attr in attributes_to_check if getattr(machine, attr) is not None]
Expand All @@ -58,8 +63,8 @@ def main(args: argparse.Namespace) -> int:
def loglevel_to_int(loglevel: str) -> int:
"""Allow LOGLEVEL env var to set the default verbosity as if the user had passed that many -v flags."""
loglevel_step = logging.CRITICAL - logging.ERROR
max_loglevel = logging.CRITICAL / loglevel_step
return max_loglevel - int(getattr(logging, loglevel.upper(), None) / loglevel_step)
max_loglevel = logging.CRITICAL // loglevel_step
return max_loglevel - int(getattr(logging, loglevel.upper(), logging.ERROR) // loglevel_step)


log = logging.getLogger(sys.argv[0] if __name__ == "__main__" else __name__)
Expand Down
9 changes: 5 additions & 4 deletions check-netbox-unused-machine-power.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import netsnmp
import pynetbox
from pynetbox.models import dcim

BACHMANN_RELAY_ON = 19 # Bachmann PDU relay status value for "on"

Expand Down Expand Up @@ -73,14 +74,14 @@ def green(s: str) -> str:
return f"\x1b[32m{s}\x1b[0m"


def print_device(device: pynetbox.models.dcim.Devices, dev_pdu_power: dict, watts: int) -> None:
def print_device(device: dcim.Devices, dev_pdu_power: dict, watts: int) -> None:
"""Report device power consumption per PDU outlet for human review."""
s = " " if verbose else ""
dev_pdu_power = " ".join([f"{h}:{green(p) if s else red(p)}={w}W" for (h, p), (w, s) in dev_pdu_power.items()])
print(f"{s}{device.name} status={device.status.value} {dev_pdu_power} ∑{watts}W")
pdu_power = " ".join([f"{h}:{green(p) if s else red(p)}={w}W" for (h, p), (w, s) in dev_pdu_power.items()])
print(f"{s}{device.name} status={device.status.value} {pdu_power} ∑{watts}W")


def print_no_connection(device: pynetbox.models.dcim.Devices) -> None:
def print_no_connection(device: dcim.Devices) -> None:
"""Warn that power consumption for this device cannot be verified."""
if verbose:
print(f"No connection for {device.name} ({device.display_url})", file=sys.stderr)
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ max-complexity = 9
# Enable reformatting of code snippets in docstrings.
docstring-code-format = true

[tool.ty.analysis]
allowed-unresolved-imports = ["netsnmp"]

[tool.ruff.lint.flake8-copyright]
author = "SUSE LLC"
notice-rgx = "Copyright"
Expand Down