Python Scripts¶
In addition to executing Python scripts as regular external scripts, NSClient++ can also execute them in-process via the PythonScript module. The benefit is that scripts can call NSClient++’s APIs directly (run checks, listen on channels, register handlers, read/write settings) and keep state between invocations.
To add a Python script as an internal script you need to load the PythonScript module and register the script:
nscp py add --script my_script.py
This produces:
[/modules]
PythonScript = enabled
[/settings/python/scripts]
my_script.py = my_script.py
The key is the script’s alias, and add uses the file name unless you pass --alias my_script.
Managing scripts¶
| Command | What it does |
|---|---|
nscp py list [--json] [--include-lib] |
List the files under ${scripts}/python; helpers in a lib folder only with --include-lib |
nscp py add --script <file> [--alias <name>] |
Configure a script and enable the module; --no-config loads it without saving |
nscp py add --script <file> --import <path> [--replace] |
Copy a script into ${scripts}/python first; an existing file is only overwritten with --replace |
nscp py show --script <file> |
Print a script |
nscp py delete --script <file> |
Delete a script and every [/settings/python/scripts] entry that loads it |
nscp py install --add <file> / --remove <file> |
Add or remove a configured script |
nscp py execute --script <file> [args...] |
Run a script’s __main__ (see below) |
show and delete only reach ${scripts}/python; a name that resolves anywhere else is refused, and
so is a path through a symlinked folder that leads out of it. show reads a symlink only when its
target is inside the folder too. delete removes the entry in the folder - a file, or a symlink
itself, whatever it points at, dangling or not - and never what a link points to. A deleted script stays loaded until the module reloads. The same operations are available
over REST under /api/v2/scripts/py.
Lifecycle functions¶
A script may define any of three lifecycle functions:
__main__— invoked when the script is run from the command lineinit— invoked when the script is loaded by the moduleshutdown— invoked when the module unloads or NSClient++ stops
All three are optional. If a function is missing it is simply skipped.
__main__¶
__main__ is invoked when the script is run from the command line. Use it for installation,
configuration helpers, or anything else an administrator might want to run interactively:
nscp py execute --script my_script.py install --root /tmp
The function receives the command-line arguments as a list of strings:
from NSCP import log
def __main__(args):
log("My arguments are: %s" % args)
Output:
nscp py execute --script my_script.py install --root /tmp
L python My arguments are: ['install', '--root', '/tmp']
nscp py execute loads the script as a fresh instance of its own: it calls init(plugin_id, "", "")
(no aliases, since nothing configured it), then __main__(args), then shutdown(). The scripts in
[/settings/python/scripts] are loaded alongside it as usual.
init¶
init is invoked when the script is loaded and is where you obtain the values needed to talk back
to NSClient++. It receives three arguments:
plugin_id— internal identifier of the plugin instance; required byRegistry.get(),Core.get(), andSettings.get()plugin_alias— the alias the PythonScript module is loaded underscript_alias— the key the script is registered under in[/settings/<alias>/scripts]
from NSCP import log
def init(plugin_id, plugin_alias, script_alias):
log("*** plugin_id: %d, plugin_alias: %s, script_alias: %s"
% (plugin_id, plugin_alias, script_alias))
A typical startup log looks like:
D python boot python
D python Prepare python
D python init python
D python Adding script: osmc (c:\source\build\x64\dev\scripts\python\osmc.py)
D python Loading python script: c:\source\build\x64\dev\scripts\python\osmc.py
D python Lib path: c:\source\build\x64\dev\scripts\python\lib
L python *** plugin_id: 0, plugin_alias: python, script_alias: my_script
plugin_alias versus script_alias¶
Consider:
[/modules]
ps1 = PythonScript
ps2 = PythonScript
[/settings/ps1/scripts]
ms1 = my_script.py
ms2 = my_script.py
[/settings/ps2/scripts]
ms1 = my_script.py
ms2 = my_script.py
This loads the same script four times with the following arguments:
| plugin_id | plugin_alias | script_alias |
|---|---|---|
| 0 | ps1 | ms1 |
| 0 | ps1 | ms2 |
| 1 | ps2 | ms1 |
| 1 | ps2 | ms2 |
The plugin_id values are assigned by the agent as it loads modules; only rely on them being the
same for every script of one module instance. A module loaded without an alias
(PythonScript = enabled) passes python as plugin_alias.
If a script may legitimately be loaded multiple times, store its configuration under a path that
includes both aliases, e.g. /settings/<script_name>/<plugin_alias>/<script_alias>/.
Warning
Loading the same script multiple times is discouraged — the CLI does not provide shortcuts for it — but it is supported for complex scenarios.
shutdown¶
shutdown is invoked when NSClient++ stops, when the PythonScript module is unloaded, and when it is
reloaded - a reload calls shutdown on every script and then loads them again, calling init anew. It
takes no arguments:
from NSCP import log
def shutdown():
log("Good bye")
API¶
The module is imported as NSCP and exposes:
- Three wrapper classes:
Settings,Registry,Core— each obtained via.get(plugin_id) - An enum:
status— Nagios-style return codes - Free functions:
log,log_error,log_debug,sleep
Most operations have a “simple” variant that uses plain Python types, and a “raw” variant that takes serialized protobuf messages for full access to the underlying API. Use the simple form unless you need fields the simple form doesn’t expose.
Free functions¶
log / log_error / log_debug¶
log(message)
log_error(message)
log_debug(message)
Write a message to the NSClient++ log at the corresponding level (info, error, debug).
Messages appear on the command line and in any configured log file.
from NSCP import log, log_error, log_debug
log("Informational message")
log_error("Something went wrong")
log_debug("Internal state: %s" % state)
sleep¶
sleep(milliseconds)
Sleep for the given number of milliseconds. Use this instead of time.sleep(): Python is
single-threaded with respect to the GIL, so a blocking time.sleep() inside an NSClient++ script
prevents every other Python callback from running. sleep() releases the GIL while it waits.
from NSCP import sleep, log_debug
log_debug("Waiting for 1 second")
sleep(1000) # Other taska can no execute while we wait
log_debug("It is now one second later")
status enum¶
| Key | Value | Description |
|---|---|---|
| OK | 0 | Nagios OK |
| WARNING | 1 | Nagios warning |
| CRITICAL | 2 | Nagios critical |
| UNKNOWN | 3 | Nagios unknown |
Errors in handlers¶
A handler that raises does not take the agent down. The traceback goes to the NSClient++ log, and the caller gets an answer it can act on:
| Handler | Raises, or returns the wrong shape | Returns None |
|---|---|---|
simple_function (a check) |
UNKNOWN with the message Exception in: <command> |
UNKNOWN with the message None |
simple_cmdline |
the message Exception in: <command>, non-zero exit |
the message None, non-zero exit |
simple_subscription |
the submission fails | the submission succeeds |
event, event_pb, metrics |
logged; the other scripts’ handlers still run | ignored |
A tuple shorter than documented is accepted: (status.WARNING,) is a warning with an empty message.
A script that does not parse, or whose init raises, is logged and skipped; the other scripts load
regardless, and whatever init registered before it raised stays registered.
Registry¶
Registry lets a script publish itself into NSClient++ — registering check commands, command-line
commands, channel subscriptions, event handlers, and metrics providers.
Registry.get¶
Registry.get(plugin_id)
Obtain an instance of Registry bound to the calling plugin. Pass the plugin_id you received in
init.
Registry.function¶
Registry.function(query_name, query_function, description)
Register a check query with a raw, protobuf-based callback. Use simple_function instead unless
you need fields it doesn’t expose.
| Option | Description |
|---|---|
query_name |
The check command name (e.g. check_py_test) |
query_function |
Callback invoked when the query runs |
description |
Help text shown by <command> help and in the WEB UI |
The callback receives the command name and a serialized QueryRequestMessage, and must return a
2-tuple (return_code, response_bytes) where response_bytes is a serialized
QueryResponseMessage:
def my_function(command, request):
request_message = plugin_pb2.QueryRequestMessage()
request_message.ParseFromString(request)
# ... build response ...
response_message = plugin_pb2.QueryResponseMessage()
return (status.OK, response_message.SerializeToString())
Example:
from NSCP import log, Registry, status
import plugin_pb2
def my_function(command, request):
request_message = plugin_pb2.QueryRequestMessage()
request_message.ParseFromString(request)
log('Got command: %s' % request_message.payload[0].command)
response_message = plugin_pb2.QueryResponseMessage()
return (status.OK, response_message.SerializeToString())
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.function('check_py_test', my_function, 'This is a sample python function')
Registry.simple_function¶
Registry.simple_function(query_name, query_function, description)
Register a check query with a simple Python callback — no protobuf required.
| Option | Description |
|---|---|
query_name |
The check command name |
query_function |
Callback invoked when the query runs |
description |
Help text shown by <command> help |
The callback receives the arguments as a list of strings and must return a 3-tuple
(status, message, perfdata):
def my_function(args):
return (status.OK, "This is the message", "'count'=123;200;600")
Example:
from NSCP import log, Registry, status
def my_function(args):
log('Got arguments: %s' % args)
return (status.OK, "This is the message", "'count'=123;200;600")
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.simple_function('check_py_test', my_function, 'This is a sample python function')
Registry.cmdline¶
Registry.cmdline(command_name, function)
Register a command-line command with a protobuf-based callback. Use simple_cmdline unless you
need raw access. A command-line command is run with nscp client --module PythonScript --exec <command>
[arguments...], or from a script with Core.simple_exec. The module’s own verbs
come first, so a command registered under one of their names is never reached: help, execute,
python-script, add, install, list, show and delete.
| Option | Description |
|---|---|
command_name |
The command name to expose |
function |
Callback invoked when the command runs |
Callback signature: (command, request) -> (return_code, response_bytes), where request is a
serialized ExecuteRequestMessage and response_bytes is a serialized ExecuteResponseMessage.
from NSCP import log, Registry, status
import plugin_pb2
def my_function(command, request):
request_message = plugin_pb2.ExecuteRequestMessage()
request_message.ParseFromString(request)
log('Got command: %s' % request_message.payload[0].command)
response_message = plugin_pb2.ExecuteResponseMessage()
return (0, response_message.SerializeToString())
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.cmdline('do_foo', my_function)
Registry.simple_cmdline¶
Registry.simple_cmdline(command_name, function)
Register a command-line command with a simple callback. The callback receives the arguments as a
list of strings and returns a 2-tuple (exit_code, message). Run it with:
nscp client --module PythonScript --exec do_something first second
def my_function(args):
return (0, "This is the message")
Example:
from NSCP import log, Registry
def my_function(args):
log('Got arguments: %s' % args)
return (1, "Everything is awesome")
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.simple_cmdline('do_something', my_function)
Registry.subscription¶
Registry.subscription(channel, function)
Subscribe to a channel (think passive check submissions) with a protobuf-based handler. Use
simple_subscription unless you need raw access.
| Option | Description |
|---|---|
channel |
The channel name to subscribe to |
function |
Callback invoked when a message arrives |
Callback signature: (channel, message) -> (success_bool, response_bytes). message is a memoryview
over a serialized SubmitRequestMessage.
def my_function(channel, message):
request = plugin_pb2.SubmitRequestMessage()
request.ParseFromString(bytes(message))
# ... process ...
return (True, b"")
Registry.simple_subscription¶
Registry.simple_subscription(channel, function)
Subscribe to a channel with a simple per-payload callback.
| Option | Description |
|---|---|
channel |
The channel name to subscribe to |
function |
Callback invoked per submitted check |
Callback signature: (channel, source, command, status, message, perf) -> bool. Return True (or
nothing) to accept the submission; False fails it, and the submitter sees the error. source is the
system the result is about: the result’s own source when its producer set one, otherwise the source
the submission names (for example check_and_forward’s source=). It is empty when neither does.
Example — suppress repeated NSCA submissions:
from NSCP import Registry, Core, status, log
g_last_message = ''
g_plugin_id = 0
def filter_nsca(channel, source, command, code, message, perf):
global g_last_message, g_plugin_id
if message != g_last_message:
g_last_message = message
log("Sending: %s" % message)
core = Core.get(g_plugin_id)
core.simple_submit('NSCA', command, status.CRITICAL,
message.encode('utf-8'), perf)
return True
else:
log("Suppressing duplicate message")
return True
def init(plugin_id, plugin_alias, script_alias):
global g_plugin_id
g_plugin_id = plugin_id
reg = Registry.get(plugin_id)
reg.simple_subscription('filter_nsca', filter_nsca)
Then point a real-time filter at the synthetic filter_nsca channel instead of NSCA:
[/modules]
PythonScript=enabled
CheckEventLog=enabled
NSCAClient=enabled
[/settings/python/scripts]
filter_nsca=filter_nsca.py
[/settings/eventlog/real-time]
enabled = true
[/settings/eventlog/real-time/filters/login]
log=Security
filter=id=4624
target=filter_nsca
detail syntax=%(id)
Registry.event / Registry.event_pb¶
Registry.event(event_name, event_function)
Registry.event_pb(event_name, event_function)
Register a handler for a named event. The two variants differ in how the event payload is delivered to the callback:
event— payload is delivered as a Pythondict. Callback signature:(event_name, data) -> None.event_pb— payload is delivered asbytes: a serializedEventMessage. Callback signature:(event_name, request_bytes). The return value is ignored.
One message can carry several records - a real-time CPU filter sends one per core. An event
handler is called once per record, with that record’s data; an event_pb handler once per message,
with all of them.
An event is named <source>:<alias>; a real-time CPU filter called high_cpu raises
system.cpu:high_cpu.
Example (event):
from NSCP import log, Registry
def on_event(event, data):
log('Got event: %s with key=%s' % (event, data.get('key')))
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.event('eventlog:login', on_event)
Registry.submit_metrics¶
Registry.submit_metrics(callable)
Register a function that receives metrics each time NSClient++ publishes a metrics snapshot. Useful for forwarding metrics to a custom backend.
Callback signature: (metrics, "") -> None, where metrics is a flat dict of dotted-name keys to
stringified values.
from NSCP import Registry, log
def on_metrics(metrics, _):
for key, value in metrics.items():
log("metric %s = %s" % (key, value))
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.submit_metrics(on_metrics)
Registry.fetch_metrics¶
Registry.fetch_metrics(callable)
Register a function that produces metrics on demand. The callback takes no arguments and returns
a dict whose values may be strings, ints, or floats; numeric values are emitted as gauges.
from NSCP import Registry
def my_metrics():
return {
"my_script.requests": 42,
"my_script.latency_ms": 17.3,
"my_script.status": "ok",
}
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.fetch_metrics(my_metrics)
Describing a metric¶
A bare number says nothing about what it is, so it scrapes as an anonymous
gauge on /api/v2/openmetrics. Return a
dict in place of the value to say more:
def my_metrics():
return {
"my_script.requests": {
"value": 42,
"help": "Requests this script has handled since it started",
"type": "counter",
},
"my_script.latency": {
"value": 17.3,
"help": "Time the last request took",
"unit": "milliseconds",
},
"my_script.status": "ok",
}
| Key | Required | Meaning |
|---|---|---|
value |
yes | The reading, as a string, int or float — exactly what you would have returned on its own. A dict without it is skipped. |
help |
no | One line describing what the value is. Becomes the # HELP line. |
unit |
no | What the value is measured in (bytes, seconds, percent, …). Becomes the # UNIT line, and the metric name is made to end in it, which OpenMetrics requires of a metric that declares one. Leave it off for a plain count and for a per-second rate. |
type |
no | gauge (the default), counter for a value that only grows while the script runs, or unknown. Anything else is reported as a gauge and logged once, so a typo is visible. |
labels |
no | A dict of strings that becomes the metric’s labels, the same way the built-in per-core and per-NIC metrics are labelled. |
labels is how a script says what one reading is of, rather than what it
means:
def my_metrics():
return {
"my_script.queue_depth": {
"value": 42,
"help": "Messages waiting on the queue",
"labels": {"queue": "inbound", "region": "eu-west"},
},
}
which scrapes as my_script_queue_depth{queue="inbound",region="eu-west"} 42.
A label whose name or value is not a string is skipped rather than guessed at,
and an empty value is skipped too, since x="" and an absent x are the same
series to a scraper. On a string value the labels decide which series of the
bundle’s _info family the string lands on.
A unit becomes part of the metric name, so it has to be spelled in
[a-zA-Z0-9_]; anything else is rewritten the way a name is (bytes/sec
becomes bytes_sec) or dropped if nothing usable is left, and the agent logs
it. A counter whose key already ends in _total does not get the suffix
twice — the family is named without it and the sample carries it, as the
specification requires.
Both forms may be mixed in one dict, and a plain value keeps meaning exactly
what it always did: a gauge with no description. A string value, in either
form, becomes a label of the bundle’s _info family rather than a sample.
The metadata and the labels are only read by the OpenMetrics endpoint. The
JSON endpoints, Graphite, collectd and submit_metrics see the same keys and
values as before.
Registry.query¶
(return_code, response_bytes) = Registry.query(request_bytes)
Issue a raw query against the registry — used to enumerate modules, queries, channels, etc. The
request_bytes must be a serialized RegistryRequestMessage; the response is a serialized
RegistryResponseMessage.
Core¶
The Core object exposes operations that interact with the running NSClient++ instance itself —
running queries, submitting passive results, loading and reloading modules, expanding paths.
Core.get¶
Core.get(plugin_id)
Obtain a Core instance bound to the calling plugin.
Core.simple_query¶
(code, message, perf) = Core.simple_query(query, arguments)
Run a check command by name with a list of argument strings.
| Option | Description |
|---|---|
query |
Name of the query to run |
arguments |
List of keyword=value argument strings |
code |
Nagios status code |
message |
Resulting message |
perf |
Resulting performance data |
A query nobody has registered answers UNKNOWN with the message Unknown command(s): <query>.
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
(code, message, perf) = core.simple_query("check_cpu", [])
log(message)
Core.query¶
(return_code, response_bytes) = Core.query(command, request_bytes)
Raw protobuf variant of simple_query. request_bytes is a serialized QueryRequestMessage;
response_bytes is a serialized QueryResponseMessage.
from NSCP import Core, log
import plugin_pb2
def init(plugin_id, plugin_alias, script_alias):
request_message = plugin_pb2.QueryRequestMessage()
# ... populate request_message ...
core = Core.get(plugin_id)
(ret, response) = core.query("check_cpu", request_message.SerializeToString())
response_message = plugin_pb2.QueryResponseMessage()
response_message.ParseFromString(response)
Core.simple_exec¶
(return_code, results) = Core.simple_exec(target, command, arguments)
Execute a command-line command (one a script registered via Registry.cmdline /
Registry.simple_cmdline, or one a module provides). target picks the module to run it in:
target |
Runs the command in |
|---|---|
| a module name or alias | that module ("PythonScript", "CheckSystem") |
"any" |
the first module that has the command |
"all" or "*" |
every module that has the command |
The name is matched as a substring of each module’s name, so "Python" also reaches
PythonScript. Avoid "": it matches every module as though each had been named, and a module asked
by name for a command it does not have may answer with an error rather than staying silent. results is a
list of strings, one per module that answered.
When nothing could run it - no such module, or no such command in it - return_code is
status.UNKNOWN and results holds the reason, Failed to execute <command> on <target>.
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
(code, lines) = core.simple_exec("PythonScript", "do_something", ["first"])
for line in lines:
log(line)
Core.exec¶
(return_code, response_bytes) = Core.exec(target, request_bytes)
Raw protobuf variant of simple_exec.
Core.simple_submit¶
(success, response) = Core.simple_submit(channel, command, code, message, perf)
Submit a passive check result on a channel. When nothing listens on the channel, success is
False and response says why (Failed to submit message: <channel>).
| Option | Description |
|---|---|
channel |
Channel to submit to (e.g. "NSCA", "NRDP") |
command |
Check command name being reported |
code |
status.OK / status.WARNING / status.CRITICAL / status.UNKNOWN |
message |
Message text (bytes or str) |
perf |
Performance data string |
from NSCP import Core, status
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
core.simple_submit("NSCA", "check_py", status.OK,
"All good".encode("utf-8"), "")
Core.submit¶
(success, error_message) = Core.submit(channel, request_bytes)
Raw protobuf variant of simple_submit. request_bytes is a serialized SubmitRequestMessage.
Core.reload¶
Core.reload(module)
Reload the given module by name (e.g. "CheckEventLog"). Pass "service" to reload the entire
service.
Called from inside a handler - a check, a subscription, an event - the reload cannot run on that
thread, so it is queued and runs shortly after: reload returns True as soon as it is queued, before
the module has reloaded, and before anyone knows whether the module exists. A script may reload its
own module this way; its shutdown runs, then its init again.
Core.load_module¶
Core.load_module(module, alias)
Load a module into the running NSClient++ instance. alias lets you load the same module multiple
times under different names; pass "" for the default. Returns True once the module is loaded,
False if it could not be (for example, no module by that name).
from NSCP import Core
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
core.load_module("CheckEventLog", "")
Core.unload_module¶
Core.unload_module(module)
Unload a previously loaded module by its module name ("CheckEventLog"); an alias it was loaded under
is not looked up. Returns True once it is unloaded, False if no module by that name is loaded. A
script cannot unload the module it runs in; that returns False and is logged.
from NSCP import Core
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
core.unload_module("CheckEventLog")
Core.expand_path¶
path = Core.expand_path(path_expression)
Expand a path containing ${...} placeholders (e.g. ${scripts}, ${modules}, ${base-path}).
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
path = core.expand_path("${scripts}/python/myscript.py")
log('The script path is: %s' % path)
Settings¶
Settings wraps the configuration store. Read and write are supported. A written value is seen by
the next read at once, but is in-memory only until save() is called.
Settings.get¶
Settings.get(plugin_id)
Obtain a Settings instance bound to the calling plugin.
from NSCP import Settings
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
value = config.get_string("/modules", "PythonScript", "disabled")
Settings.get_section¶
keys = Settings.get_section(path)
Return the list of keys under a given section.
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
for key in config.get_section("/modules"):
log("Module: %s" % key)
Settings.get_string / Settings.set_string¶
value = Settings.get_string(path, key, default_value)
Settings.set_string(path, key, value)
Read or write a string. Writes are not persisted until save() is called.
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
for key in config.get_section("/modules"):
value = config.get_string("/modules", key, "unknown")
log("The module %s is %s" % (key, value))
from NSCP import Settings
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.set_string("/modules", "PythonScript", "enabled")
config.save()
Settings.get_bool / Settings.set_bool¶
value = Settings.get_bool(path, key, default_value)
Settings.set_bool(path, key, value)
Read or write a boolean. true, 1 and yes (in any case) read as True and any other value as
False; default_value is returned only when the key is not set.
from NSCP import Settings
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.set_bool("/modules", "PythonScript", True)
config.save()
Settings.get_int / Settings.set_int¶
value = Settings.get_int(path, key, default_value)
Settings.set_int(path, key, value)
Read or write an integer. A value that is not a number reads as default_value.
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
port = config.get_int("/settings/NRPE/server", "port", -1)
log("NRPE port is: %d" % port)
config.set_int("/settings/NRPE/server", "port", 1234)
config.save()
Settings.save¶
Settings.save()
Persist any in-memory changes back to the settings store (file or registry, depending on configuration).
Settings.register_path¶
Settings.register_path(path, title, description)
Register a settings section so it is documented in the generated settings file and visible in the WEB UI.
Settings.register_key¶
Settings.register_key(path, key, type, title, description, default_value)
Register an individual settings key for documentation and UI purposes. The type argument is
accepted for forward-compatibility, but the underlying store currently treats all script-registered
keys as strings.
Settings.query¶
(return_code, response_bytes) = Settings.query(request_bytes)
Issue a raw settings query. request_bytes is a serialized SettingsRequestMessage; the response is
a SettingsResponseMessage. Use this when you need operations that the typed helpers above don’t
cover (bulk reads, diffs, deletions, etc.).
from NSCP import Settings
import plugin_pb2
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
request_message = plugin_pb2.SettingsRequestMessage()
# ... populate request_message ...
(ret, response) = config.query(request_message.SerializeToString())
response_message = plugin_pb2.SettingsResponseMessage()
response_message.ParseFromString(response)