Naemon Event Broker Modules (NEB)
Everything related to Naemon event broker modules (NEB).
Philosophy
The idea behind NEB modules is to provide a way to extend Naemon at a very low level. NEB modules are shared objects (mostly written in C) which are loaded into memory on runtime. These modules can then hook callbacks into certain events. Arguments and return codes depend on the event type.
Example
This is the most basic NEB module, it needs a nebmodule_init and a nebmodule_deinit function. It simply logs a welcome message and does nothing else.
Usually callbacks would be registered during the init function.
// module.c
#include <naemon/naemon.h>
NEB_API_VERSION(CURRENT_NEB_API_VERSION);
static void *neb_handle = NULL;
int nebmodule_init(int flags, char *arg, nebmodule *handle) {
neb_handle = (void *)handle;
nm_log(NSLOG_INFO_MESSAGE, "module loaded");
return OK;
}
int nebmodule_deinit(int flags, int reason) {
return OK;
}compile with:
%> gcc $(pkg-config --cflags naemon) -shared -fPIC module.c -o module.soAnd load the module from your naemon.cfg with:
broker_module=/path/to/module.soIf everything worked, you should see something like this in your naemon.log
[1636297273] module loaded
[1636297273] Event broker module '/path/to/module.so' initialized successfully.Writing a module
API version. NEB_API_VERSION(CURRENT_NEB_API_VERSION) records the version your module was built against. Naemon only loads a module whose version matches its own exactly, so a module has to be recompiled whenever CURRENT_NEB_API_VERSION changes.
Arguments. Everything after the path in the broker_module line is passed to nebmodule_init() as arg:
broker_module=/path/to/module.so config=/etc/mymodule.cfg debug=1Registering callbacks. Call neb_register_callback(type, handle, priority, func) from nebmodule_init(). Callbacks of the same type run in ascending order of priority. Naemon deregisters all of a module's callbacks when it is unloaded.
Return values. A callback normally returns OK. Some events, such as an upcoming check, notification, event handler or external command, also accept NEBERROR_CALLBACKOVERRIDE or NEBERROR_CALLBACKCANCEL to take over or stop the action; Event Types says which, and what each does. Both also skip the callbacks of modules that come after yours.
Which events are sent. event_broker_options in naemon.cfg decides which kinds of events Naemon sends to modules at all. The default -1 sends everything; a narrower setting is the usual reason a callback never fires. The bits are the BROKER_* flags in naemon/broker.h.
Callbacks run in the event loop. Naemon is single threaded and waits for every callback to return, so keep them short and never block on I/O. The data a callback receives is only valid during the call: copy what you want to keep. If your module runs threads of its own, they must not call into Naemon.
Real World Examples
Also have a look at real world examples:
- https://github.com/naemon/naemon-livestatus (Naemon Livestatus API)
- https://github.com/naemon/naemon-vimcrypt-vault-broker (Naemon vim vault macros)
- https://github.com/sni/mod_gearman (Distributed checks with Gearman)
- https://github.com/ITRS-Group/monitor-merlin (Loadbalancing in Naemon)
- https://github.com/statusengine/broker (Export status information as JSON, C++)
- https://github.com/statusengine/module (End-of-life C broker that also exports status information as JSON. Although unmaintained, its straightforward codebase makes it a useful reference for developers.)
- https://github.com/ConSol/go-neb-wrapper (Go framework to write neb modules in Golang)
Example: Vault Macros
A NEBCALLBACK_VAULT_MACRO_DATA callback can set macro values dynamically. The module registers a single callback which sets the value of the supplied data structure. Naemon only calls it when event_broker_options includes BROKER_VAULT_MACROS, which the default -1 does.
// module.c
#include <naemon/naemon.h>
NEB_API_VERSION(CURRENT_NEB_API_VERSION);
static void *neb_handle = NULL;
static int handle_vault_macro(int cb, void *_ds) {
nebstruct_vault_macro_data *ds = (nebstruct_vault_macro_data *)_ds;
nm_free(ds->value);
ds->value = strdup("example macro value");
return OK;
}
int nebmodule_init(__attribute__((unused)) int flags, char *arg, nebmodule *handle) {
neb_handle = (void *)handle;
neb_register_callback(NEBCALLBACK_VAULT_MACRO_DATA, neb_handle, 0, handle_vault_macro);
return OK;
}
int nebmodule_deinit(__attribute__((unused)) int flags, __attribute__((unused)) int reason) {
return OK;
}compile with:
%> gcc $(pkg-config --cflags naemon) -shared -fPIC module.c -o module.soAnd load the module from your naemon.cfg with:
broker_module=/path/to/module.soEvent Types
Every callback receives a struct whose type field holds one of the NEBTYPE_* constants from naemon/broker.h. Types marked not sent exist for compatibility, but Naemon never emits them.
Not tied to a callback
NEBTYPE_NONE,_HELLO,_GOODBYE,_INFO(0–3): not sent
NEBCALLBACK_PROCESS_DATA
NEBTYPE_PROCESS_START(100): the configuration is loaded andobjects.cachewrittenNEBTYPE_PROCESS_DAEMONIZE(101): Naemon has daemonizedNEBTYPE_PROCESS_RESTART(102): the event loop ended for a restartNEBTYPE_PROCESS_SHUTDOWN(103): Naemon shuts down, normally or after a startup errorNEBTYPE_PROCESS_PRELAUNCH(104): modules are loaded, before the configuration is readNEBTYPE_PROCESS_EVENTLOOPSTART(105): the event loop is about to startNEBTYPE_PROCESS_EVENTLOOPEND(106): the event loop has ended
NEBCALLBACK_TIMED_EVENT_DATA
NEBTYPE_TIMEDEVENT_*(200–205): not sent
NEBCALLBACK_LOG_DATA
NEBTYPE_LOG_DATA(300): a line is written to the logNEBTYPE_LOG_ROTATION(301): not sent
NEBCALLBACK_SYSTEM_COMMAND_DATA
NEBTYPE_SYSTEM_COMMAND_START,_END(400, 401): not sent
NEBCALLBACK_EVENT_HANDLER_DATA
NEBTYPE_EVENTHANDLER_START(500): an event handler is about to run;NEBERROR_CALLBACKOVERRIDEskips itNEBTYPE_EVENTHANDLER_END(501): an event handler was handed to a worker
NEBCALLBACK_NOTIFICATION_DATA
NEBTYPE_NOTIFICATION_START(600): a host or service notification begins;NEBERROR_CALLBACKOVERRIDEorNEBERROR_CALLBACKCANCELstops itNEBTYPE_NOTIFICATION_END(601): a host or service notification is done
NEBCALLBACK_CONTACT_NOTIFICATION_DATA
NEBTYPE_CONTACTNOTIFICATION_START(602): a contact is about to be notified;NEBERROR_CALLBACKOVERRIDEorNEBERROR_CALLBACKCANCELskips this contactNEBTYPE_CONTACTNOTIFICATION_END(603): a contact has been notified
NEBCALLBACK_CONTACT_NOTIFICATION_METHOD_DATA
NEBTYPE_CONTACTNOTIFICATIONMETHOD_START(604): a contact's notification command is about to run;NEBERROR_CALLBACKOVERRIDEskips this command,NEBERROR_CALLBACKCANCELall remaining ones for this contactNEBTYPE_CONTACTNOTIFICATIONMETHOD_END(605): a contact's notification command was handed to a worker
NEBCALLBACK_SERVICE_CHECK_DATA
NEBTYPE_SERVICECHECK_INITIATE(700): a service check is about to go to a worker;NEBERROR_CALLBACKOVERRIDEtakes it over,NEBERROR_CALLBACKCANCELpostpones itNEBTYPE_SERVICECHECK_PROCESSED(701): a service check result has been processedNEBTYPE_SERVICECHECK_RAW_START,_RAW_END(702, 703): not sentNEBTYPE_SERVICECHECK_ASYNC_PRECHECK(704): before a scheduled service check is prepared; same return codes asINITIATE
NEBCALLBACK_HOST_CHECK_DATA
NEBTYPE_HOSTCHECK_INITIATE(800): a host check is about to go to a worker;NEBERROR_CALLBACKOVERRIDEtakes it over,NEBERROR_CALLBACKCANCELpostpones itNEBTYPE_HOSTCHECK_PROCESSED(801): a host check result has been processedNEBTYPE_HOSTCHECK_RAW_START,_RAW_END(802, 803): not sentNEBTYPE_HOSTCHECK_ASYNC_PRECHECK(804): before a scheduled host check is prepared; same return codes asINITIATENEBTYPE_HOSTCHECK_SYNC_PRECHECK(805): not sent
NEBCALLBACK_COMMENT_DATA
NEBTYPE_COMMENT_ADD(900): a comment was addedNEBTYPE_COMMENT_DELETE(901): a comment was deleted, or dropped while reading retention dataNEBTYPE_COMMENT_LOAD(902): a comment is put into memory: when read from retention data, and right beforeCOMMENT_ADD
NEBCALLBACK_FLAPPING_DATA
NEBTYPE_FLAPPING_START(1000): a host or service started flappingNEBTYPE_FLAPPING_STOP(1001): it stopped flapping, or flap detection was disabled for it
NEBCALLBACK_DOWNTIME_DATA
NEBTYPE_DOWNTIME_ADD(1100): a downtime was scheduledNEBTYPE_DOWNTIME_DELETE(1101): a downtime was removedNEBTYPE_DOWNTIME_LOAD(1102): a downtime is put into memory: when read from retention data, and right beforeDOWNTIME_ADDNEBTYPE_DOWNTIME_START(1103): a downtime beganNEBTYPE_DOWNTIME_STOP(1104): a downtime ended or was cancelled
NEBCALLBACK_PROGRAM_STATUS_DATA
NEBTYPE_PROGRAMSTATUS_UPDATE(1200): program-wide status changed
NEBCALLBACK_HOST_STATUS_DATA
NEBTYPE_HOSTSTATUS_UPDATE(1201): host status changed, see belowNEBTYPE_HOSTSTATUS_SCHEDULE(1204): only the host's next check was (re)scheduled, see below
NEBCALLBACK_SERVICE_STATUS_DATA
NEBTYPE_SERVICESTATUS_UPDATE(1202): service status changed, see belowNEBTYPE_SERVICESTATUS_SCHEDULE(1205): only the service's next check was (re)scheduled, see below
NEBCALLBACK_CONTACT_STATUS_DATA
NEBTYPE_CONTACTSTATUS_UPDATE(1203): contact status changed
NEBCALLBACK_ADAPTIVE_PROGRAM_DATA
NEBTYPE_ADAPTIVEPROGRAM_UPDATE(1300): a program-wide setting was changed at runtime
NEBCALLBACK_ADAPTIVE_HOST_DATA
NEBTYPE_ADAPTIVEHOST_UPDATE(1301): a host attribute was changed at runtime
NEBCALLBACK_ADAPTIVE_SERVICE_DATA
NEBTYPE_ADAPTIVESERVICE_UPDATE(1302): a service attribute was changed at runtime
NEBCALLBACK_ADAPTIVE_CONTACT_DATA
NEBTYPE_ADAPTIVECONTACT_UPDATE(1303): a contact attribute was changed at runtime
NEBCALLBACK_EXTERNAL_COMMAND_DATA
NEBTYPE_EXTERNALCOMMAND_START(1400): an external command is about to be processed;NEBERROR_CALLBACKOVERRIDEorNEBERROR_CALLBACKCANCELdrops itNEBTYPE_EXTERNALCOMMAND_END(1401): an external command has been processed
NEBCALLBACK_AGGREGATED_STATUS_DATA
NEBTYPE_AGGREGATEDSTATUS_STARTDUMP(1500): the periodicstatus.datdump startsNEBTYPE_AGGREGATEDSTATUS_ENDDUMP(1501): the periodicstatus.datdump is done
NEBCALLBACK_RETENTION_DATA
NEBTYPE_RETENTIONDATA_STARTLOAD(1600): retention data is about to be readNEBTYPE_RETENTIONDATA_ENDLOAD(1601): retention data has been readNEBTYPE_RETENTIONDATA_STARTSAVE(1602): retention data is about to be writtenNEBTYPE_RETENTIONDATA_ENDSAVE(1603): retention data has been written
NEBCALLBACK_ACKNOWLEDGEMENT_DATA
NEBTYPE_ACKNOWLEDGEMENT_ADD(1700): a host or service problem was acknowledgedNEBTYPE_ACKNOWLEDGEMENT_REMOVE,_LOAD(1701, 1702): not sent
NEBCALLBACK_STATE_CHANGE_DATA
NEBTYPE_STATECHANGE_START(1800): not sentNEBTYPE_STATECHANGE_END(1801): a host or service changed state
Status updates and schedule events
Since NEB API version 9, host and service status events come in two types. NEBTYPE_*STATUS_UPDATE means something about the object changed: a check result, a downtime, an acknowledgement, flapping, an external command or a notification. NEBTYPE_*STATUS_SCHEDULE is sent when the object only got a new next_check. Each scheduled check used to send two identical-looking updates, see #162.
Both carry a complete snapshot of the object. A schedule event usually differs only in next_check, but can also carry a fresh check result.
A module that stores status can skip schedule events: an update follows every check result. The exception is a check that is scheduled but then does not run (host down, outside the check period, failed dependencies, checks disabled). For those objects only the schedule event fires, so skipping it leaves their next_check stale. Modules that ignore the type see both events as before.