Skip to main content

EventSource

Keep event configuration in git as YAML and push it to Horizon declaratively. The loop has three steps: event-source convert brings existing XML in, onmsctl apply -f ships edits out, and event-source export snapshots the server back to YAML.

YAML​

metadata.name becomes the server's stored source name verbatim. Horizon derives vendor as the prefix before the first . in that name: cisco.foo becomes vendor cisco. See Field semantics below for the full per-field schema and Example for a complete document.

Apply​

onmsctl apply -f cisco.foo.yaml --diff # push (diff to stderr)

apply is idempotent: Horizon's upsert replaces events under an existing basename.

Inspect and delete​

onmsctl event-source list # filter / sort / page
onmsctl event-source get <id>
onmsctl event-source download <id|name> -O out.xml # raw XML for one source
onmsctl event list --source <id> # events for a source

download and export accept a selector that is either a numeric id or an exact source name.

Snapshot server sources back to git-managed YAML, the reverse of apply:

onmsctl event-source export cisco.foo # one source → stdout (YAML)
onmsctl event-source export cisco.foo --out ./s/ # one source → ./s/cisco.foo.yaml
onmsctl event-source export --out ./sources/ # every source → one file each

Export routes through the convert migrator, so it carries the same EC### findings, on stderr. In bulk it is continue-on-error: warnings are written, blocking and download-failed sources are reported and counted, and the exit code reflects the highest severity seen. --out validates every target filename before writing anything: it is all-or-nothing.

Read/raw verbs stay imperative: event-source list|get|names|names-and-ids, event list (including cross-source --uei/--vendor), and the raw-XML event-source upload|download round trip. Use the raw-XML round trip for full fidelity when a source carries fields the YAML model doesn't represent yet.

Convert​

# Single file to stdout
onmsctl event-source convert mevents.xml > mevents.yaml

# Read from stdin (single input; --name required)
cat mevents.xml | onmsctl event-source convert - --name my-source > my-source.yaml

# Batch a directory into per-input YAML files
onmsctl event-source convert ./xml/*.xml --output-dir ./yaml/

Conversion emits EC###-coded findings on stderr for anything the YAML model doesn't represent. Run onmsctl event-source convert --explain <code> for the rationale behind any finding. See Convert findings below for the full catalog.

Convert findings​

event-source convert parses each event against the local EventSource schema and emits findings on stderr. Example:

EC004 error event missing required field: uei
At: bad.events.xml:14:5 (event[3])
Fix: Add the required uei to the event in the source XML.
For the full rationale: onmsctl event-source convert --explain EC004

Finding codes (EC001-EC008) are stable across releases; --explain <code> prints the full rationale for any of them.

CodeSeverityMeaning
EC001warningUnmodeled direct-child element under <event> dropped on conversion.
EC002errorSource has zero events.
EC003errorReserved metadata.name.
EC004errorEvent missing a required field.
EC005warningSeverity case normalized (e.g. WARNING to Warning).
EC006errorPost-conversion validation failed (catch-all).
EC007erroralarm-type outside the accepted set {1, 2, 3}.
EC008errorInvalid metadata.name (disallowed characters).

Exit codes: 0 clean, 1 warnings (YAML written), 2 blocking findings (no YAML).

Flags:

FlagPurpose
--format jsonCI envelope with output path and yaml body.
--max-bytes 64MOverride the 16 MiB input cap.
--max-findings 0Disable the 1000-finding EC001 cap (set <n> for any other limit).
--forceOverwrite existing output.
--explain <code>Print the full rationale for a finding code and exit.

EC001 is the permanent forward-compatibility surface: any direct-child element under <event> that the YAML schema doesn't model fires EC001 rather than silently losing data. The early modeling gaps (<snmp>, <parameter>, <forward>, <script>, <filters>) are now first-class. Remaining unmodeled XSD elements (<priority>, <autoaction>, <operaction>, <loggroup>, vendor extensions) keep firing EC001 until modeled. For full fidelity today, keep the XML alongside the YAML and use event-source upload. EC001 is structural-only: it does not detect attribute extensions on modeled elements or enum-value drift on modeled fields.

Field semantics​

alarmType​

spec.events[].alarmData.alarmType accepts the three known states, in either symbolic (Web UI) form or the integer it maps to:

SymbolicInteger
raise1
resolution2
unresolvable3

Symbolic input is case-insensitive on parse; canonical YAML output is always lowercase. Anything else fails immediately: unknown symbolic strings ("problem", the alarmd Java alias) or integers outside {1, 2, 3}. YAML inputs reject at deserialize time; eventconf XML inputs produce an EC007 finding (error, exit 2).

snmp​

spec.events[].snmp mirrors the eventconf XSD's <snmp> element. Every sub-field is optional. Practical numeric ranges are documented but not enforced: out-of-range integers round-trip verbatim. String fields are rejected when empty or whitespace-only.

  • id: enterprise OID, a free string with no OID-format validation.
  • idtext: vendor-supplied textual label.
  • version: common values v1 / v2c / v3 (free string; variants like v3-auth-priv accepted verbatim).
  • generic: 0..=6 per RFC 1157.
  • specific: >= 0.
  • community: typically public.

parameters​

spec.events[].parameters mirrors <parameter name value expand/>: static per-event configuration eventd attaches to fired events. Each entry requires name and value; expand is optional and controls whether eventd substitutes %parm[#N]%-style placeholders at fire time. Document order is preserved.

This is distinct from parmCollection on a fired event instance, a runtime field on the JSON wire that is not modeled here. The names are similar but the domains differ; do not conflate them.

forwards and scripts​

spec.events[].forwards mirrors <forward state mechanism>target</forward>: eventd's forwarding directives, validated against the XSD-closed sets:

  • state is one of on, off.
  • mechanism is one of snmpudp, snmptcp, xmltcp, xmludp.

Values outside these sets are rejected locally (otherwise Horizon returns a 400). An empty forwards: [{}] entry is rejected; at least one of state/mechanism/target must be set.

spec.events[].scripts mirrors <script language>body</script>: embedded executable logic (typically BeanShell) that eventd runs on event arrival. language is required; body is optional and preserved byte-for-byte (use YAML's | literal block for multi-line bodies).

Security note

Shipping executable code via onmsctl apply lowers the friction for deploying server-side code execution on Horizon. The threat surface already exists at the raw eventconf-XML upload path; modeling <script> in YAML adds no new authority. Ensure RBAC on eventconf write access reflects it: anyone who can upload an event source can run code on the Horizon JVM.

filters​

spec.events[].filters mirrors <filters><filter eventparm pattern replacement/></filters>. Each entry is a regex-replacement rule eventd applies to a named event parameter at fire time:

Pattern.compile(pattern).matcher(parmValue).replaceAll(replacement)

All three fields are required. pattern is Java regex; replacement supports $1/$2 backreferences. The YAML is flat (filters: directly on the event); the <filters> wrapper materializes only on XML render.

<mask> versus <filters>: <mask> selects which events a source applies to (SNMP PDU shape matching on id / generic / specific / varbind values, each an OR-matched value list). <filters> operates after selection, rewriting parameter values on the fired event. These are two different layers.

Apply-time limitations​

Run onmsctl apply --help for the full text.

#LimitationWorkaround
1description not persisted server-side through apply.Carry intent in the YAML and git review; the field round-trips locally.
2Disabled-state apply has a bounded enabled-flap window.--verbose warns when this runs.
3vendor is filename-derived, not declared.Encode as the prefix before the first . in metadata.name.
4fileOrder is server-managed.Deferred to a future kind: EventConfMaster.

Example​

The examples directory also ships event-source-severities.yaml and event-source-disabled.yaml.

examples/event-source-minimal.yaml
# Minimal EventSource — the smallest document that `onmsctl apply`
# accepts. metadata.name MUST contain at least one '.' (the prefix before
# the first '.' becomes the server-derived vendor: here, "vendor-example").
#
# Apply against a configured context:
#
# onmsctl apply -f examples/minimal.yaml --diff
#
apiVersion: eventconf.opennms.org/v1
kind: EventSource
metadata:
name: vendor-example.cold-start
spec:
enabled: true
events:
- uei: uei.opennms.org/vendor-example/cold-start
label: "Vendor Example Cold Start"
severity: Warning
description: |
Demonstrates the minimum viable EventSource document.
examples/event-source-full.yaml
# Comprehensive EventSource — exercises every nested type the EventSource
# model supports. Use this as a reference when authoring your own
# EventSource documents.
#
# Style note: optional fields are OMITTED rather than set to `null`. The
# schema treats absence and `null` equivalently, but keeping the document
# minimal matches what `config view` would round-trip back.
apiVersion: eventconf.opennms.org/v1
kind: EventSource
metadata:
name: cisco.foo
spec:
enabled: true
events:
- uei: uei.opennms.org/vendor/cisco/foo/coldStart
label: "Cisco Foo Cold Start"
severity: Warning
description: |
Foo device performed a cold start (re-initialized configuration).
operinstruct: "Investigate device boot logs."
mouseovertext: "ifIndex=%parm[ifIndex]%, type=%parm[linkType]%"
logmsg:
# logndisplay | logonly | displayonly | suppress | donotpersist | discardtraps
dest: logndisplay
text: "%nodelabel% performed a cold start"
alarmData:
reductionKey: "%uei%:%dpname%:%nodeid%"
# alarmType accepts symbolic (`raise` / `resolution` / `unresolvable`)
# or integer (1 / 2 / 3) forms. Symbolic is recommended for readability.
alarmType: raise
autoClean: false
mask:
# The mask matches an INCOMING event (typically an SNMP trap) to this
# definition. Recognized element names: uei / source / nodeid / host /
# interface / snmphost / service / id (enterprise OID) / trapoid /
# generic / specific / community. A maskelement matches when the event
# value equals ANY listed value (OR within an element, AND across
# elements). `severity` is NOT a mask field — severity is assigned by
# this definition, not matched on.
elements:
- name: id
values: [".1.3.6.1.4.1.9.9.41.2"] # enterprise OID
- name: generic
values: ["6"] # enterprise-specific trap
- name: specific
values: ["1", "2"] # multi-value: specific 1 OR 2
# A varbind matches by position (vbnumber) or OID (vboid). Like
# maskelement values, `values` is a list matched with OR, and each
# value may be plain (exact), `~`-prefixed (regex), or `%`-suffixed
# (prefix / starts-with).
varbinds:
- vbnumber: 1 # positional match
values: ["3", "5"] # OR-matched: 3 or 5
- vboid: ".1.3.6.1.4.1.9.9.41.1.1.1.1.0" # OID-based match
values: ["~^crit.*"] # regex (leading `~`)
autoacknowledge:
state: "off"
tticket:
state: "off"
correlation:
state: "off"
# cmin / cmax are typed as Option<String> (event-correlation
# parameter strings, not raw integers) — quote to keep the
# fixture portable across YAML and JSON loaders.
cmin: "0"
cmax: "0"
cuei: []
varbindsdecode:
- parmid: "1"
decode:
- value: "0"
label: "off(0)"
- value: "1"
label: "on(1)"
snmp:
# Per RFC 1157: generic ∈ 0..=6, specific ≥ 0.
# `version` is a free string; common values are v1 / v2c / v3
# but `v3-auth-priv` and other Horizon-version-specific variants
# round-trip verbatim.
id: ".1.3.6.1.4.1.9.1.13"
idtext: "Cisco"
version: v2c
generic: 6
specific: 1
community: public
parameters:
# Static per-event configuration; distinct from runtime
# parmCollection (which is per-fired-event, not eventconf XML).
# eventd evaluates entries in document order. `expand` is
# optional — absent means the eventd default applies. The
# absent vs explicit `true`/`false` distinction round-trips.
- name: endpoint
value: "/var/log/foo"
- name: context
value: "%parm[#1]%"
expand: true
forwards:
# eventd forwarding directives. state is {on, off};
# mechanism is one of the XSD-closed set:
# snmpudp / snmptcp / xmltcp / xmludp
# Anything outside the closed sets is rejected at parse so
# Horizon doesn't reject the upload with a server-side 400.
- state: "on"
mechanism: snmpudp
target: "alarmcentral:162"
scripts:
# SECURITY NOTE: shipping executable code via `apply`
# requires RBAC on eventconf write access in Horizon. The
# threat surface already exists at the raw XML upload path;
# modeling here just lowers the friction.
- language: beanshell
body: |
// Multi-line BeanShell preserved byte-for-byte.
do_thing();
another_thing();
filters:
# eventd regex-replacement rules on named event parameters.
# `pattern` is Java regex; `replacement` is Matcher.replaceAll
# syntax (use $1 / $2 for capture-group backreferences).
# NOT a suppression filter — these rewrite parm values at
# event-fire time.
- eventparm: trapMsg
pattern: '\bWARN\b'
replacement: "WARNING"
- eventparm: ifAlias
pattern: '^old-(.*)$'
replacement: "new-$1"