Skip to main content

Maintenance

Plan a maintenance window: for a period, stop OpenNMS polling, notifications, and threshold (and optionally collection) evaluation on chosen devices. Maps to an OpenNMS scheduled outage (/rest/sched-outages); named and multi-instance (one document per window), reconciled like requisitions. There is no version gate: the API is present in every supported Horizon/Meridian.

YAML​

apiVersion: maintenance.opennms.org/v1
kind: Maintenance
metadata: { name: weekend-patching }
spec:
schedule:
type: specific # specific | daily | weekly | monthly
times:
- { begins: "20-Jun-2026 22:00:00", ends: "21-Jun-2026 04:00:00" }
devices: # selectors are a UNION, deduped to nodeIds
interfaces: [192.168.8.8] # an IP, or the single literal `match-any`
nodes: [ { foreignSource: hq, foreignId: web01 } ] # resolved to a nodeId at apply
categories: [Routers] # every node in ANY listed category
locations: [Berlin] # every node at ANY listed Minion location
asset: { field: city, value: Berlin }
suppress:
polling: { packages: [production] } # explicit packages required (no default)
notifications: true # global (no package)

Explicit packages; server timezone; foreignId nodes. polling/thresholds/collection require an explicit packages list. Times are the server's timezone. Nodes are named by {foreignSource, foreignId} and resolved at apply: an un-imported node fails that window, which is why Maintenance applies after Requisition. Prefer interfaces/match-any when nodes may be un-imported.

Dynamic selectors (categories/locations/asset) are a union, not an intersection, re-resolved on every apply (a snapshot). A selector matching nothing warns; a window covering nothing fails.

Apply​

onmsctl apply -f examples/maintenance.yaml --dry-run --diff

Apply writes the outage definition (a true Created/Updated/Unchanged), then attaches it per daemon (polling to pollerd, thresholds to threshd, collection to collectd per package; notifications to notifd global).

Attachments are ensure-present. The API can't read which daemons an outage is attached to, so onmsctl re-issues the idempotent attach every apply and cannot detach; removing a suppress entry does not detach it. To reduce suppression, maintenance delete <name> and re-apply.

Inspect and delete​

onmsctl maintenance list # or: onmsctl maint list
onmsctl maintenance status 192.168.8.8 12 # IP or nodeId → in a window now?
onmsctl maintenance delete weekend-patching # full teardown

Example​

examples/maintenance.yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/no42-org/onmsctl/main/schemas/maintenance.schema.json
#
# kind: Maintenance — a maintenance window (OpenNMS scheduled outage). For the
# window, OpenNMS stops the chosen daemons (polling / thresholds / collection /
# notifications) for the chosen devices. NAMED + multi-instance: one document per
# window, metadata.name = the outage name. `onmsctl apply -f` reconciles it
# against /rest/sched-outages:
#
# onmsctl apply -f examples/maintenance.yaml --dry-run --diff
# onmsctl maintenance list
# onmsctl maintenance status 192.168.8.8
# onmsctl maintenance delete weekend-patching
#
# Notes:
# - Times are interpreted in the OpenNMS SERVER's timezone.
# - `suppress` requires EXPLICIT packages for polling/thresholds/collection
# (there is no default package); `notifications` is a global boolean.
# - Attachments are ensure-present: apply never detaches. To reduce suppression,
# `onmsctl maintenance delete <name>` then re-apply.
#
apiVersion: maintenance.opennms.org/v1
kind: Maintenance
metadata:
name: weekend-patching
spec:
schedule:
type: specific # specific | daily | weekly | monthly
times:
# specific → full datetimes "dd-MMM-yyyy HH:mm:ss"
- begins: "20-Jun-2026 22:00:00"
ends: "21-Jun-2026 04:00:00"
# daily → { begins: "HH:mm:ss", ends: "HH:mm:ss" }
# weekly → { day: monday, begins: "HH:mm:ss", ends: "HH:mm:ss" }
# monthly → { day: "15", begins: "HH:mm:ss", ends: "HH:mm:ss" }
devices:
# All selectors below are ADDITIVE (union), deduped to nodeIds at apply.
# An IP, or the single literal `match-any` (= every interface).
interfaces: [192.168.8.8]
# Nodes by foreign reference (resolved to the server nodeId at apply).
nodes:
- { foreignSource: labmonkeys-hq, foreignId: web01 }
# Every node in ANY of these OpenNMS categories (resolved at apply via the
# v2 nodes search; snapshot — re-apply to refresh as membership changes).
categories: [Routers, Core]
# Every node at ANY of these monitoring (Minion) locations. Selects whole
# nodes by id — the outage model has no location field, so an interface IP
# cannot be scoped to a location.
locations: [Berlin]
# Nodes whose OpenNMS asset-record field matches (the searchable key/value).
# NOTE: node meta-data (context:key=value) is NOT searchable by the node API,
# so `asset` is the supported key/value selector.
asset: { field: city, value: Berlin }
suppress:
polling: { packages: [production] } # → pollerd package(s)
thresholds: { packages: [production] } # → threshd package(s)
# collection: { packages: [production] } # → collectd package(s)
notifications: true # global (no package)