Store warning notifications externally
When deploying nAttrMon on ephemeral filesystems (such as a container filesystem) keeping track of the nAttrMon Warning (e.g. email, notifications, etc…) output notifications can become a challenge. For example, if channel persistance (NEED_CH_PERSISTENCE parameter) is set to true outputing warnings through email, after the notification email has been sent a record of that is kept on the container filesystem; if the container for some reason restarts thatn record is lost and a new email is sent. To avoid this the nAttrMon warning notifications (WNOTS) need to be persisted externally.
This can typically be achieved by using an OpenAF channel with external persistance (e.g. S3, ElasticSearch, MongoDB, etc.). Here is an example of nAttrMon configuration for S3 and ElasticSearch. The choice is ultimately driven by the each implementation details.
To change where nAttrMon keeps the warning notifications data you need to use the CHANNEL_WNOTS string parameter. It can be set as an environment variable, a nattrmon.yaml settings parameter or __NAM_CHANNEL_WNOTS in nattrmonInit for single mode)
The CHANNEL_WNOTS string parameter is a JSON based string mainly composed of two entries:
- type - the OpenAF channel type
- options - a map with the corresponding OpenAF channel type options
For the available options for each type do refer to the OpenAF channel type you plan to use’s documentation.
When a CHANNEL_WNOTS is used the notifications tracking data is no longer kept in the CHANNEL_WARNS. This can be helpful since CHANNEL_WNOTS gets less frequent updates (just when the notification happens or when it’s deleted for housekeeping) than CHANNEL_WARNS were the warning data is kept updated.
Example using ElasticSearch
If you are using a nAttrMon container with OpenAF’s sBuckets all you have to do is to set the ElasticSearch details on a sBucket (in this exmaple a file) and then set the container enviroment variable CHANNEL_WNOTS.
Example of setting a sBucket secrets file:
nattrmon:
elastic:
url : http://elasticdb.internal:9200
user: ...
pass: ...
Example of setting the CHANNEL_WNOTS enviroment variable in Kubernetes:
# ...
env:
- name : CHANNEL_WNOTS
value: "{ type: 'elasticsearch', options: { index: 'wnots', secKey: 'elastic', secBucket: 'nattrmon', secFile: 'elastic.yaml' } }"
Example using S3
On the latest S3 oPack (> 20240804) it’s possible to use a “s3” OpenAF channel type. This can be used, of course, for CHANNEL_WNOTS.
Here is a simple example using single mode:
init:
nattrmonInit: &INIT
__NAM_LOGCONSOLE : true
__NAM_NOPLUGFILES : true
__NAM_LIBS : s3.js
__NAM_CHANNEL_WNOTS : "{ type: 's3', options: { s3url: 'https://play.min.io:9000', s3bucket: 'nam-test', s3accessKey: 'Q3AM3UQ867SPQQA43P2F', s3secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG', s3prefix: "nattrmon/wnots", multifile: true, gzip: true } }"
# ...
nattrmon: &NATTRMON
# ------
# INPUTS
- input:
name: Input test
cron: "*/5 * * * * *"
exec: |
return { "Test 1": now() }
# -----------
# VALIDATIONS
- validation:
name : Validation generic
chSubscribe : nattrmon::cvals
waitForFinish: true
execFrom : nValidation_Generic
execArgs :
checks:
- attrPattern : "Test \\d+"
expr : |
{{value}} > 0
warnLevel : MEDIUM
warnTitleTemplate: "Problem with {{name}}"
warnDescTemplate : "{{name}} had value {{value}}"
# ------
# OUTPUT
- output:
name : Output Debug Inputs
chSubscribe: nattrmon::cvals
exec : |
_$(args.v, "args.v").$_()
print("VAL | " + ow.format.toCSLON(args.v, true))
- output:
name : Output Debug Warns
chSubscribe: nattrmon::warnings
exec : |
_$(args.v, "args.v").$_()
var warns = isArray(args.v) ? args.v : [args.v]
var id = sha1("Output Debug Warns")
warns.filter(w => Object.keys(w).length > 0).forEach(w => {
try {
if (!nattrmon.isNotified(w.title, w.level + id)) {
print("WARN | " + ow.format.toCSLON(args.v, true))
warns.forEach(w => nattrmon.setNotified(w.title, w.level + id) )
}
} catch(e) {
if (String(e).indexOf("Warning 'undefined' not found") >= 0) {
logWarn(`Output Debug Warns | Warning '${w.title}' not found`)
} else {
logErr("Output Debug Warns | " + e)
}
}
})
- output:
name : Output Warns HK
chSubscribe: nattrmon::warnings
execFrom : nOutput_HK
execArgs :
warningDeleteAfter: 1
# -------------------------
# -------------------------
todo:
- name: nAttrMon Prepare shutdown
- name: nAttrMon Init
args: *INIT
- name: Add plugs
#- name: nAttrMon Run Single
#args:
# onStart:
# - job 1
# - job 2
# onStop:
# - job 3
# - job 4
# Comment "Run Single" and uncomment "Start" for daemon mode
- nAttrMon Start
include:
- oJob_nAttrMon.yaml
ojob:
# Uncomment "daemon" for daemon mode
daemon : true
opacks :
- openaf: 20210412
- S3
catch : "logErr(job.name + ' | ' + exception + ' | ' + ow.format.toSLON(args));"
logToConsole: false # change to true for debugging
jobs:
# ---------------
- name: Add plugs
deps: nAttrMon Init
to : nAttrMon Add Plugs
args: *NATTRMON
Notice the use of the new LIBS parameter (since nAttrMon >= 20240804). This allows nAttrMon to preload the s3.js library from OpenAF’s S3 oPack before the definition of CHANNEL_WNOTS is evaluated.
For an environment variable implementation you would need to set the LIBS and the CHANNEL_WNOTS environment variables.
Using secrets
If necessary you can use SBuckets to store the S3 access information in a secret. For that just change the __NAM_CHANNEL_WNOTS value to:
__NAM_CHANNEL_WNOTS : "{ type: 's3', options: { secFile: '/secrets/secrets.yaml', secBucket: nattrmon, secKey: s3, multifile: true, gzip: true } }"
having a /secrets/secrets.yaml equivalent to:
nattrmon:
s3:
s3url : 'https://play.min.io:9000'
s3bucket : nam-test
s3accessKey: Q3AM3UQ867SPQQA43P2F
s3secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
s3prefix : nattrmon/wnots
Note on the S3 channel options
The choice between the values of the option multifile of the S3 OpenAF channel real depends on the specific implementation.
By default, in the absence of the multifile option, or setting it to false, only one JSON or YAML file will be kept at the specific S3 bucket. But everytime there is an update, or a need to check (nattrmon.isNotified function), that will mean the entire file will be retrieved and/or rewritten.
By using multifile=true
each notification entry becomes an individual object in the S3 bucket and it’s checked using the much smaller index file. The corresponding data object is only changed when a change exist for that specific warning. This might be an advantage if your S3 vendor only offers an eventual consistent S3 implementation and there might be multiple concorrent notification changes on the same nAttrmon or accross several containers.
On the hand, it might generate more small files and more operations per second which might also not be desirable tradeoff.