In addition to executing Python script as regular script NSClient++ can also execute them via the PythonScript module inside NSClient++. The benefit of this is that you can call APIs of NSClient++ as well as maintain state.
To add a python script as an internal script you need to load the PythonScript module as well as configure it to run your script:
There is a command line interface to do this:
nscp py add --script my_script.py
This will create the following configuration for you:
[/modules]
PythonScript = enabled
[/settings/python/scripts]
my_script = my_script.py
The skeleton of a script is the following three functions:
- __main__
- init
- shutdown
The mainfunction is used when the script is run from the command line. For instance when you want to provide an install or helper function to the administrator much like we did when we installed the script:
nscp py execute --script my_script.py install --root /tmp
The main function takes as input argument a list of command line options:
from NSCP import log
def __main__(args):
log("My arguments are: %s"%args)
Running this script:
nscp py execute --script my_script.py install --root /tmp
L python My arguments are: ['install', '--root', '/tmp']
The init function called when the script is loaded and provides important variables for talking to NSClient++. The function takes tree arguments:
- plugin_id
- plugin_alias
- script_alias
The plugin id is an internal id of the plugin instance which is used in many API calls. The plugin and script aliases can be used to differentiate between which instance is loaded. The idea is that you can load a script multiple times in multiple modules and they should then have seprate config.
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))
The result of running this script from NSClient++:
...
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
...
Consider the following example:
[/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 will load the same script four times with the following arguments:
plugin_id | plugin_alias | script_alias |
---|---|---|
1 | ps1 | ms1 |
1 | ps1 | ms2 |
2 | ps2 | ms1 |
2 | ps2 | ms2 |
Thus when loading configuration it is recommended to place it under the following pseudo key /settings/:script_name/:plugin_alias/:script_alias/
to allow for users loading a script multiple times.
At the same time it is discouraged from using the alias concept to load a script multiple times which is why the command line interfaces does not provide commands for doing so. But in complicated scenarios it can be very useful but it causes a lot of complexity.
The shutdown function is called whenever NSClient++ is shutting down or the PythonScript module is unloaded. The function takes no arguments.
from NSCP import log
def shutdown(args):
log("Good bye")
NSClient++ provides a rich API where you can do just about anything that can be done with NSClient++. This includes loading modules, accessing settings, providing command line interfaces etc etc. While most of it has a simple wrappers which you can use directly some things might require you to use the underlying protobuf Api.
The API is split three modules.
- Settings
- Registry
- Core
There is also an enum: status
as well as a some direct functions:
- log
- log_error
- log_debug
- sleep
log(message)
Used to send log messages to NSClient++.
These log messages will be visible on the command line as well as the any configure log file.
The log message will be logged on the info
level.
from NSCP import log
log("This is a log message")
log_error(message)
Used to send log messages to NSClient++.
These log messages will be visible on the command line as well as the any configure log file.
The log message will be logged on the error
level.
from NSCP import log_error
log_error("This is an error message")
log_debug(message)
Used to send log messages to NSClient++.
These log messages will be visible on the command line as well as the any configure log file.
The log message will be logged on the debug
level.
from NSCP import log_debug
log_debug("This is a debug message")
sleep(milli_seconds)
Used to sleep for a given number of milliseconds. The reason this function exists is that python is inherently single threaded and whenever you are executing python code your are essentially locking any other code from executing in python. This means that sleeping or delaying inside python will prevent any other script to run. Thus if you need to wait please use this function as it will wait outside the locks and allow other script to run.
from NSCP import sleep, log_debug
log_debug("Waiting for 1 second")
sleep(1000)
log_debug("It is now one second later")
Create an instance of the registry object.
Registry.simple_function(query_name, query_function, description)
Bind a function to a check query. This is similar to the Registry.simple_function
function but the bound
function uses a more powerful syntax which requires you to parse the request/response using the protobuf API.
Option | Description |
---|---|
query_name | The name of the query (i.e. the check command name) |
query_function | The function to call when the query is executed |
description | The description of the query. |
The bound function should look like this:
def my_function(request):
# ...
return response_message.SerializeToString()
Example:
from NSCP import log, Registry, status
import plugin_pb2
def my_function(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 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(query_name, query_function, description)
Bind a function to a check query. This is similar to the Registry.function
function but the bound
function has a simpler syntax so you wont have to deal with the complexity of the protobuf API.
Option | Description |
---|---|
query_name | The name of the query (i.e. the check command name) |
query_function | The function to call when the query is executed |
description | The description of th query. |
The bound function should look like this:
def my_function(args):
return (status.OK, "This is the messge", "'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 messge", "'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(command_name, function)
Bind a function to a check query. This is similar to the Registry.simple_cmdline
function but the bound
function uses a more powerfull syntax which requires you to parse the reques/response using the protobuf API.
Option | Description |
---|---|
command_name | The name of the command to expose |
function | The function to call when the command is executed |
The bound function should look like this:
def my_function(request):
# ...
return response_message.SerializeToString()
Example:
from NSCP import log, Registry, status
import plugin_pb2
def my_function(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 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(command_name, function)
Bind a function to a check query. This is similar to the Registry.function
function but the bound
function has a simpler syntax so you wont have to deal with the complexity of the protobuf API.
Option | Description |
---|---|
command_name | The name of the command to expose |
function | The function to call when the command is executed |
The bound function should look like this:
def my_function(args):
return (0, "This is the messge")
Example:
from NSCP import log, Registry, status
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(channel, function)
Bind a function to a notification (think passive checks results). This is similar to the Registry.simple_subscription
function but the bound
function has a more powerful syntax with protobuf messages.
Option | Description |
---|---|
channel | The channel name to subscribe to |
function | The function to call when the command is executed |
The bound function should look like this:
def my_function(channel, message):
return True
Example:
from NSCP import Registry, Core, log_error
import plugin_pb2
def on_message(channel, message):
message = plugin_pb2.SubmitRequestMessage()
message.ParseFromString(request)
if len(message.payload) != 1:
log_error("Got invalid message on channel: %s"%channel)
return False
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.subscription('test-channel', on_message)
Registry.simple_subscription(channel, function)
Bind a function to a notification (think passive checks results). This is similar to the Registry.subscription
function but the bound
function has a simpler syntax so you wont have to deal with the complexity of the protobuf API.
Option | Description |
---|---|
channel | The channel name to subscribe to |
function | The function to call when the command is executed |
The bound function should look like this:
def my_function(channel, source, command, status, message, perf):
return True
Example: Filter out repeated NSCA events
from NSCP import Registry, Core, status, log
g_last_message = ''
g_plugin_id = 0
def filter_nsca(channel, source, command, status, message, perf):
global g_last_message, g_plugin_id
if not 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)
else:
log("Supressing duplicte message")
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)
To use the above specify filter_nsca
as your target instead of NSCA like so:
[/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(event_name, event_function)
Register a function which listens for a given event.
Option | Description |
---|---|
event_name | The name of the event to subscribe to. |
event_function | The function to call when the event fires |
The bound function should look like this:
def my_function(event_name, data):
log("This is from the data: %s"%data['key'])
Example:
from NSCP import log, Registry
def on_event(event, data):
log('Got event: %s'%event)
def init(plugin_id, plugin_alias, script_alias):
reg = Registry.get(plugin_id)
reg.event('eventlog:login', on_event)
The core is a representation of NSClient++ and exposes functions to interact with NSClient++ it self such as running queries or reloading modules.
Core.get()
Get an instance of the core module.
Example:
from NSCP import Core
core = Core.get()
(code, message, perf) = core.simpler_query("check_cpu")
(status, message, perf) = Core.simple_query(query, arguments)
Execute a check query.
Option | Description |
---|---|
query | The name of the query to execute |
arguments | Arguments for the query |
status | The nagios status code of the result |
message | The resulting message |
perf | The resulting performance data |
Example:
from NSCP import Core, log
core = Core.get()
(code, message, perf) = core.simpler_query("check_cpu")
log(message)
reply = Core.simple_query(request)
Execute a check query.
Option | Description |
---|---|
request | A probuf QueryRequestMessage |
reply | A protobuf QueryResponseMessage |
Example:
from NSCP import Core, log
import plugin_pb2
request_message = plugin_pb2.QueryRequestMessage()
core = Core.get()
response = core.query(request_message.SerializeToString())
response_message = plugin_pb2.QueryResponseMessage()
response_message.ParseFromString(response)
log('Got command: %s'%request_message.payload[0].command)
Core.load_module(module, alias)
Load a module into NSClient++.
Option | Description |
---|---|
module | The name of the module to load |
alias | A alias (if loading a module twice) |
Example:
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
core.load("CheckEventLog", "")
Core.load_module(module_or_alias)
Unload a module from NSClient++.
Option | Description |
---|---|
module_or_alias | The name or the alias of the module to unload |
Example:
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
core.unload("CheckEventLog", "")
path = Core.expand_path(path_expression)
Parse a path expression into a path.
Option | Description |
---|---|
path_expression | A path expression which can contain ${..} keywords such as ${modules}. |
path | The resulting path |
Example:
from NSCP import Core, log
def init(plugin_id, plugin_alias, script_alias):
core = Core.get(plugin_id)
path = core.expand_path("${scripts}/ptython/myscript.py")
log('The script path is: %s'%path)
The Settings
object wraps the Settings API which allows you to access and modify the NSClient++ configuration file.
Settings.get(plugin_id)
Option | Description |
---|---|
plugin_id | The plugin id as supplied at module ini (i.e. the init function). |
Get an instance of the settings module.
Example:
from NSCP import Settings
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
(code, message, perf) = config.get_string("/modules", "PythonScript", "disabled")
keys = Settings.get_section(path)
Fetch all keys under a given section in the settings file.
Option | Description |
---|---|
path | The settings path to query all keys for |
keys | A list of keys under a the section |
Example:
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)
value = Settings.get_string(path, key, default_value)
Fetch a string from the settings store given a path and a key.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
default_value | The value to return if the key is not found. |
value | The resulting value. |
Example:
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))
Settings.set_string(path, key, value)
Set a string in the settings store given a path and a key.
please note changing the settings will not save so unless you call Settings.save()
afterwards the settings will never be written to your settings file.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
value | The value to set. |
Example:
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.get_string("/modules", "PythonScript", "enabled")
value = Settings.get_string(path, key, default_value)
Fetch a boolean from the settings store given a path and a key.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
default_value | The value to return if the key is not found. |
value | The resulting value. |
Example:
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, False)
log("THe module %s is %s"%(key, value))
Settings.set_bool(path, key, value)
Set a boolean in the settings store given a path and a key.
please note changing the settings will not save so unless you call Settings.save()
afterwards the settings will never be written to your settings file.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
value | The value to set. |
Example: Enable a module
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.set_bool("/modules", "PythonScript", True)
value = Settings.get_int(path, key, default_value)
Fetch a number from the settings store given a path and a key.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
default_value | The value to return if the key is not found. |
value | The resulting value. |
Example: Get the NRPE port
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)
Settings.set_int(path, key, value)
Set a boolean in the settings store given a path and a key.
please note changing the settings will not save so unless you call Settings.save()
afterwards the settings will never be written to your settings file.
Option | Description |
---|---|
path | The settings path to query all keys for |
key | The key to lookup |
value | The value to set. |
Example: Change the NRPE port.
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.set_int("/settings/NRPE/server", "port", 1234)
Settings.save()
Save the settings file to disk (or registry depending on where it is stored).
Example: Save changed settings
from NSCP import Settings, log
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
config.set_int("/settings/NRPE/server", "port", 1234)
config.save()
Used to register a path with the settings handler. The idea with registering keys is that this provides documentation inside the settings file and the WEB UI.
Used to register a key with the settings handler. The idea with registering keys is that this provides documentation inside the settings file and the WEB UI.
Settings.query()
Invoke the RAW settings API with a protobuf message.
Example:
from NSCP import Settings, log
import plugin_pb2
def init(plugin_id, plugin_alias, script_alias):
config = Settings.get(plugin_id)
request_message = plugin_pb2.SettingsRequestMessage()
response = config.query(request_message.SerializeToString())
response_message = plugin_pb2.SettingsResponseMessage()
response_message.ParseFromString(response)
Key | Value | Description |
---|---|---|
UNKNOWN | 3 | Unknown nagios status code |
CRITICAL | 2 | Critical nagios status code |
WARNING | 1 | Warning nagios status code |
OK | 0 | Ok nagios status code |