NXSL Cookbook
Practical recipes for common NetXMS scripting tasks. Each recipe states a goal, the script context it applies to, and a complete solution.
Data Collection
Format a UNIX timestamp as human-readable text
- Goal
-
Convert a UNIX timestamp to a formatted date/time string.
- Context
-
Transformation script — receives the raw collected value as
$1. - Solution
-
return DateTime($1).format("%d.%m.%Y %H:%M:%S");Creates a
DateTimeobject from the timestamp and formats it using standard format specifiers. See DateTime::format() for all available format codes.
Extract a single value from a Table DCI
- Goal
-
Create a scalar DCI from a column value in an existing Table DCI on the same node.
- Context
-
Script DCI — configure with two script arguments:
$1is the table DCI description,$2is the column name. - Solution
-
table = GetDCIValueByDescription($node, $1); if (table != NULL) { col = table.getColumnIndex($2); if (col >= 0) { return table.get(0, col); } } return 0;Looks up a Table DCI by description, finds the requested column, and returns the value from the first row.
| This script only works when the Table DCI is on the same node as the scalar DCI. |
Calculate 95th percentile average for ICMP packet loss
- Goal
-
Aggregate ICMP packet loss values from child nodes, discard the top 5% outliers, and return the average of the remaining values.
- Context
-
Script DCI — runs on a node that is a parent of a container named "all voice".
- Solution
-
trace(1, "Global Ping Loss 95"); pValue = []; arrayI = 0; for (parent : $node.parents) { trace(3, "Parent object: name='" .. parent.name .. "' id=" .. parent.id); if (parent.name == "all voice") { for (vNode : parent.children) { dciName = "ICMP: Packet loss to " .. vNode.name; dciId = FindDCIByDescription(vNode, dciName); if (dciId > 0) { tmpValue = GetDCIValue(vNode, dciId); if (tmpValue != null) { pValue[arrayI++] = tmpValue; } } } } } // Sort the array (arrays are copied when passed as arguments, so use the returned array) pValue = bubbleSort(pValue); // Apply the 95 percent rule: keep only the bottom 95% of values upTo = arrayI * 0.95; pLoss = 0; for (ia = 0; ia < upTo; ia++) { pLoss += pValue[ia]; } if (upTo <= 0) return 0; p95AvgLoss = pLoss / upTo; trace(1, "Global Ping Loss 95 Summary: arrayI=" .. arrayI .. " upTo=" .. upTo .. " p95AvgLoss=" .. p95AvgLoss); return p95AvgLoss; function bubbleSort(arr) { swapped = true; while (swapped == true) { swapped = false; for (ia = 1; arr[ia] != null; ia++) { ib = ia - 1; if (arr[ib] > arr[ia]) { t = arr[ib]; arr[ib] = arr[ia]; arr[ia] = t; swapped = true; } } } return arr; }The script walks the parent container’s children, collects packet loss DCI values, sorts them, then averages only the bottom 95%. This filters out spike outliers, giving a more representative loss metric.
Note that NXSL arrays are copied when passed as function arguments, so the sort function operates on a copy and must return the sorted array — the caller reassigns it with
pValue = bubbleSort(pValue);.
Node and Object Information
Find the primary MAC address of a node
- Goal
-
Print the MAC address of the interface that holds the node’s primary IP address.
- Context
-
Script DCI or Script Library (run from debug console).
- Solution
-
for (i : $node.interfaces) { for (a : i.ipAddressList) { if (a.address == $node.ipAddress) println(i.macAddr); } }Iterates all interfaces and their IP addresses, matching against the node’s primary IP.
Check if a node belongs to a cluster or container
- Goal
-
Return
TRUEif the current node is a child of at least one container or cluster. - Context
- Solution
-
for (p : $node.parents) { if (p.type == 5 or p.type == 14) { return true; } } return false;Object type 5 is Container, type 14 is Cluster. See Constants for all object type values.
Enumerate all nodes in the object tree
- Goal
-
Walk the entire object tree and print each object with its type.
- Context
-
Script Library — run from the server debug console or as an EPP action.
Requires the Objects.Security.CheckTrustedObjects server configuration variable set to 0 (default) to access all nodes.
|
- Solution 1 — recursive tree walk
-
function EnumerateNodes(obj, level) { for (o : obj.children) { for (i = 0; i < level; i++) { print(" "); } println("[" .. o.type .. " / " .. classof(o) .. "] " .. o.name); EnumerateNodes(o, level + 1); } } EnumerateNodes(FindObject("Entire Network"), 0); - Solution 2 — flat node list
-
When you only need nodes (not the full tree structure), use GetAllNodes():
for (n : GetAllNodes()) { println(n.name); }
List all custom attributes on a node
- Goal
-
Print all custom attribute key/value pairs for the current node.
- Context
-
Script Library or Script DCI.
- Solution
-
attributes = $node.customAttributes; for (a : attributes.keys) { println(a .. "=" .. attributes[a]); }
Recursively collect custom attribute values from parent objects
- Goal
-
Gather
contactscustom attribute values from all parent objects (recursively), concatenate them into a semicolon-separated string, and skip duplicates. - Context
-
Script Library or EPP action script.
- Solution
-
global contacts = ""; global presence = %{ }; for (o : $node.parents) { add_contacts(o); } println("Contacts: " .. contacts); function add_contacts(curr) { c = curr.getCustomAttribute("contacts"); if ((c != null) && (presence[c] == null)) { if (contacts.length > 0) contacts = contacts .. ";" .. c; else contacts = c; presence[c] = true; } for (o : curr.parents) { add_contacts(o); } }Uses
globalvariables so the recursive function can accumulate results across calls. Thepresencehash map ensures each value is added only once.
Query asset properties for all nodes
- Goal
-
List all nodes linked to an asset with their serial number, vendor, and model.
- Context
-
Object query (saved query).
- Solution using
withsyntax -
with objName (order = "asc", name = "Object name") = { return $node.name; }, serial (name = "Serial") = { return $node.assetProperties.serial; }, vendorVar (name = "Vendor") = { return $node.assetProperties.vendor; }, model (name = "Model") = { return $node.assetProperties.model; } (type == NODE) && $node.asset != null - Equivalent solution without
withsyntax -
if (($object.type == NODE) && $node.asset != null) { global objName (order = "asc", name = "Object name") = $node.name; global serial (name = "Serial") = $node.assetProperties.serial; global vendorVar (name = "Vendor") = $node.assetProperties.vendor; global model (name = "Model") = $node.assetProperties.model; } return ($object.type == NODE) && $node.asset != null;Both forms produce the same tabular output. The
withsyntax is more concise for object queries.
Interface Management
Set all interfaces to "ignore" expected state
- Goal
-
Change the expected state of every interface on every node to "IGNORE", preventing unwanted interface-down alerts.
- Context
-
Script Library — run from the server debug console.
- Solution
-
for (n : GetAllNodes()) { println(n.name .. "(" .. n.id .. ")"); for (i : n.interfaces) { println("\t" .. i.name); i.setExpectedState("IGNORE"); } }
Filter interfaces during instance discovery
- Goal
-
Create DCIs only for
eth*andbond*interfaces when usingNet.InterfaceNamesas an instance discovery source. - Context
-
Instance discovery filter —
$1contains the instance value. - Prerequisites
-
Set the instance discovery method to "Agent List" with
Net.InterfaceNamesas the list name. - Solution
-
name = $1; if (name ~= "eth|bond") { return [name]; } return false;Returns the instance name wrapped in an array on match, or
falseto filter it out.
Filter out interfaces from being created
- Goal
-
Prevent NetXMS from creating interface objects for
isatap*interfaces. - Context
-
Hook::CreateInterface — receives the interface prototype as
$1. - Solution
-
if ($1.name ~= "^isatap") return false; return true;Returning
falsetells the server to skip creating the interface object.
SNMP
Read an SNMP value from a node
- Goal
-
Query a single SNMP OID from a node specified by name or ID.
- Context
-
Script Library — run from the server debug console with the node name/ID as
$1. - Solution
-
if ($1 == null) { println("Please specify node name as parameter"); return 3; } node = FindObject($1); if (node == null) { println("Node not found: " .. $1); return 3; } transport = node.createSNMPTransport(); if (transport == null) { println("Failed to create SNMP transport, exit"); return 1; } value = transport.getValue("1.3.6.1.2.1.1.1.0"); if (value == null) { println("Failed to issue SNMP GET request"); return 2; } else { println("System description: " .. value); return 0; }Creates an SNMP transport for the target node and retrieves
sysDescription. Replace the OID with the value you need.
Read an SNMP octet string as raw bytes
- Goal
-
Retrieve an SNMP value and read it byte-by-byte as a binary stream.
- Context
-
Script DCI or Script Library.
- Solution
-
transport = $node.createSNMPTransport(); if (transport == null) exit; varbind = transport.get("1.3.6.1.2.1.25.3.5.1.2.1"); if (varbind == null) exit; bytestream = varbind.getValueAsByteStream(); println(bytestream.pos); // position starts at 0 println("0x" .. d2x(bytestream.readByte(), 2)); // hex value of first byteThe
getValueAsByteStream()method returns a ByteStream object for processing binary SNMP data.
Set node geolocation from SNMP values
- Goal
-
Read latitude and longitude from SNMP OIDs and set the node’s geolocation.
- Context
- Solution
-
transport = $node.createSNMPTransport(); if (transport == null) { return null; } lat = transport.getValue("1.2.3.4.1"); lon = transport.getValue("1.2.3.4.2"); if (lat == null || lon == null) { return null; } geoLoc = new GeoLocation(lat, lon); $node.setGeoLocation(geoLoc); return 0;Replace the OIDs with the actual OIDs for your device’s GPS coordinates.
Event Processing
Add connected device info to interface events
- Goal
-
Enrich interface up/down event notifications with the name, IP address, and MAC address of the connected device.
- Context
-
EPP filter script — runs for each event matching the rule.
- Solution
-
// only for interface up and down events if (($event.name != "SYS_IF_DOWN") && ($event.name != "SYS_IF_UP")) return true; // get interface object from interface index iface = $node.getInterface($5); if (iface == null) return true; // get peer node (node connected to this interface) object peer = iface.peerNode; if (peer == null) return true; // get peer interface object (needed to obtain MAC address) peerIface = iface.peerInterface; if (peerIface != null) { macAddr = peerIface.macAddr; } else { macAddr = "<MAC unknown>"; } // set event's named parameter $event.setNamedParameter("additionalInfo", "Peer: " .. peer.name .. " " .. peer.ipAddress .. " " .. macAddr); return true;After this script runs, use
%<additionalInfo>in your notification message template to include the peer device details.
Agent Tables
Read and display a table from an agent
- Goal
-
Retrieve a table from an agent and print it in a formatted layout.
- Context
-
Script Library — run from the server debug console with node name/ID as
$1and table name as$2. - Solution
-
node = FindObject($1); if (node == null) { println("ERROR: Node not found"); return; } table = node.readAgentTable($2); if (table == null) { println("ERROR: Cannot read table from agent"); return; } // Print column headers for (i = 0; i < table.columnCount; i++) print("| " .. table.getColumnName(i).left(20)); println("|"); for (i = 0; i < table.columnCount; i++) print("+" .. "-".left(21, "-")); println("+"); // Print data rows for (i = 0; i < table.rowCount; i++) { for (j = 0; j < table.columnCount; j++) { v = table.get(i, j); print("| " .. ((v != null) ? v.left(20) : "".left(20))); } println("|"); }
Sort an array of values
- Goal
-
Sort an array for use in aggregation or reporting scripts.
- Context
-
Any script context. Useful as a helper function within larger scripts.
- Alphabetical sort
-
function BubbleSort(a) { n = a.maxIndex + 1; do { newn = 0; for (i = 1; i < n; i++) { if (a[i - 1].compareTo(a[i]) > 0) { t = a[i - 1]; a[i - 1] = a[i]; a[i] = t; newn = i; } } n = newn; } while(n > 1); return a; } - Numeric sort
-
function BubbleSort(a) { n = a.maxIndex + 1; do { newn = 0; for (i = 1; i < n; i++) { if (a[i - 1] > a[i]) { t = a[i - 1]; a[i - 1] = a[i]; a[i] = t; newn = i; } } n = newn; } while(n > 1); return a; }Uses the bubble sort algorithm. For small arrays this is sufficient; for large datasets consider using built-in array methods.
NXSL arrays are copied when passed as function arguments, so the function sorts a copy and returns it. Always use the returned array:
a = BubbleSort(a);