Meridian 2026 ships a new set of Karaf shell commands for working with provisioning requisitions, foreign source definitions, and detectors. Until recently, managing a requisition meant clicking through the web UI, driving the ReST API with curl, or reaching for provision.pl. All of those still work, but there was no way to do it from the same shell where you already run opennms:filter, check daemon health, or poke at the event bus.

Now, there is. The new commands are:

Command What it does
opennms:add-detector-to-foreignsource Add a detector to a foreign source definition, creating the definition if needed
opennms:delete-detector-from-foreignsource Remove one or more detectors from a foreign source definition
opennms:show-foreignsource-definition List all definitions, or show one as a table or as XML
opennms:delete-foreignsource Delete a foreign source definition
opennms:add-requisitioned-node Add a node to a requisition, creating the requisition if needed
opennms:delete-requisitioned-node Remove one or more nodes from a requisition by foreign ID
opennms:show-requisition List all requisitions, or show one as a table or as XML
opennms:synchronize-requisition Import a named requisition into the database, optionally waiting for completion
opennms:import-requisition Trigger an import from any requisition URL handler (file, http, dns, vmware, and so on)
opennms:delete-requisition Empty or delete a requisition

Everything below was run against a live Meridian 2026 instance. Access the Karaf shell with ssh -p 8101 admin@localhost and you can follow along at home!

Building a foreign source definition

Start with the detectors. The first add-detector-to-foreignsource call for a new name creates the definition for you. Detector parameters are plain key=value pairs, and you can pass -p as many times as you need. The --class option tab-completes against the detector classes loaded in the running system, which saves a lot of typing.



admin@opennms()> opennms:add-detector-to-foreignsource --foreignsource Branch-Office --name ICMP --class org.opennms.netmgt.provision.detector.icmp.IcmpDetector -p timeout=1500 -p retries=1
Creating foreign source definition 'Branch-Office'.
Successfully added detector "ICMP" to "Branch-Office"!
admin@opennms()> opennms:add-detector-to-foreignsource -f Branch-Office -n SNMP -c org.opennms.netmgt.provision.detector.snmp.SnmpDetector -p timeout=3000
Successfully added detector "SNMP" to "Branch-Office"!
admin@opennms()> opennms:add-detector-to-foreignsource -f Branch-Office -n HTTP -c org.opennms.netmgt.provision.detector.web.WebDetector -p port=8980 -p path=/opennms -v

<plugin xmlns="http://xmlns.opennms.org/xsd/config/foreign-source" name="HTTP" class="org.opennms.netmgt.provision.detector.web.WebDetector">
    <parameter key="port" value="8980"/>
    <parameter key="path" value="/opennms"/>
</plugin>

Successfully added detector "HTTP" to "Branch-Office"!

show-foreignsource-definition with a name gives you a table. Add --xml to see exactly what was written to disk, which is handy when you want to copy a definition somewhere else.


admin@opennms()> opennms:show-foreignsource-definition Branch-Office
Name │ Class                                                   │ Parameters
─────┼─────────────────────────────────────────────────────────┼──────────────
ICMP │ org.opennms.netmgt.provision.detector.icmp.IcmpDetector │ timeout=1500
     │                                                         │ retries=1
SNMP │ org.opennms.netmgt.provision.detector.snmp.SnmpDetector │ timeout=3000
HTTP │ org.opennms.netmgt.provision.detector.web.WebDetector   │ port=8980
     │                                                         │ path=/opennms
admin@opennms()> opennms:show-foreignsource-definition --xml Branch-Office

<foreign-source xmlns="http://xmlns.opennms.org/xsd/config/foreign-source" name="Branch-Office" date-stamp="2026-10-05T15:26:16.502Z">
    <scan-interval>1d</scan-interval>
    <detectors>
        <detector name="ICMP" class="org.opennms.netmgt.provision.detector.icmp.IcmpDetector">
            <parameter key="timeout" value="1500"/>
            <parameter key="retries" value="1"/>
        </detector>
        <detector name="SNMP" class="org.opennms.netmgt.provision.detector.snmp.SnmpDetector">
            <parameter key="timeout" value="3000"/>
        </detector>
        <detector name="HTTP" class="org.opennms.netmgt.provision.detector.web.WebDetector">
            <parameter key="port" value="8980"/>
            <parameter key="path" value="/opennms"/>
        </detector>
    </detectors>
    <policies/>
</foreign-source>

Changed your mind about that detector? Delete it by name. --name accepts multiple values if you want to remove several at once.

admin@opennms()> opennms:delete-detector-from-foreignsource --foreignsource Branch-Office --name HTTP
Deleting HTTP...
Deleted 1 detector(s).

With no arguments, show-foreignsource-definition lists every definition on the system with detector and policy counts.


admin@opennms()> opennms:show-foreignsource-definition
Name          │ Datestamp                    │ Detectors │ Policies
──────────────┼──────────────────────────────┼───────────┼─────────
Branch-Office │ Mon Oct 05 15:26:16 GMT 2026 │ 3         │ 0
Minions       │ Thu Sep 03 19:38:28 GMT 2026 │ 1         │ 1
selfmonitor   │ Mon Oct 05 15:26:17 GMT 2026 │ 5         │ 0

Adding nodes to a requisition

add-requisitioned-node is the workhorse. Like the detector command, it creates the requisition on first use. The interesting option is --interface, which takes a comma-separated ipAddr,snmpPrimary,SERVICE,SERVICE,... list and can be repeated for nodes with multiple interfaces. --services adds services to every interface on the node. Categories, assets, metadata, Minion location, city, building, and path-outage parents all have flags, too.





admin@opennms()> opennms:add-requisitioned-node --requisition Branch-Office --node-label core-rtr-01 --foreign-id core-rtr-01 --interface 127.0.1.1,P,SNMP --services ICMP --category Routers --category Branch-Office --asset vendor=Ubiquiti --asset region=Southeast --meta-data site=ATL-01 --city Atlanta
Created requisition 'Branch-Office'.
Successfully added node "core-rtr-01" to requisition "Branch-Office".
admin@opennms()> opennms:add-requisitioned-node -r Branch-Office -n branch-sw-01 -f branch-sw-01 -i 127.0.1.2,P,SNMP -i 127.0.1.3,N -s ICMP -c Switches -c Branch-Office -m site=ATL-01 -v
Node XML:
<node xmlns="http://xmlns.opennms.org/xsd/config/model-import" foreign-id="branch-sw-01" node-label="branch-sw-01">
    <interface ip-addr="127.0.1.2" managed="true" status="1" snmp-primary="P">
        <monitored-service service-name="ICMP"/>
        <monitored-service service-name="SNMP"/>
    </interface>
    <interface ip-addr="127.0.1.3" managed="true" status="1" snmp-primary="N">
        <monitored-service service-name="ICMP"/>
    </interface>
    <category name="Switches"/>
    <category name="Branch-Office"/>
    <meta-data context="requisition" key="site" value="ATL-01"/>
</node>

Successfully added node "branch-sw-01" to requisition "Branch-Office".

The --foreign-id argument is optional and defaults to a timestamp, but defining the value it yourself is worth the extra keystrokes: re-running the command with the same foreign ID replaces the node instead of adding a duplicate, so your scripts become idempotent. Metadata goes into the requisition context, where it is available to expressions in poller and collector configuration.

show-requisition renders the whole thing as a table, including a summary line with import and update timestamps and node count.





admin@opennms()> opennms:show-requisition Branch-Office
Requisition Name │ Last Import Date │ Last Update Date             │ Node Count
─────────────────┼──────────────────┼──────────────────────────────┼───────────
Branch-Office    │                  │ Mon Oct 05 15:26:34 GMT 2026 │ 2

Node Label   │ Foreign ID   │ Minion Location │ Building │ City    │ Categories    │ Meta Data     │ Assets             │ Interfaces     │ parentNodeLabel │ parentForeignSource │ parentForeignID
─────────────┼──────────────┼─────────────────┼──────────┼─────────┼───────────────┼───────────────┼────────────────────┼────────────────┼─────────────────┼─────────────────────┼────────────────
branch-sw-01 │ branch-sw-01 │                 │          │         │ Switches      │ site = ATL-01 │                    │ /127.0.1.2 : P │                 │                     │
             │              │                 │          │         │ Branch-Office │               │                    │ ICMP           │                 │                     │
             │              │                 │          │         │               │               │                    │ SNMP           │                 │                     │
             │              │                 │          │         │               │               │                    │                │                 │                     │
             │              │                 │          │         │               │               │                    │ /127.0.1.3 : N │                 │                     │
             │              │                 │          │         │               │               │                    │ ICMP           │                 │                     │
core-rtr-01  │ core-rtr-01  │                 │          │ Atlanta │ Routers       │ site = ATL-01 │ vendor = Ubiquiti  │ /127.0.1.1 : P │                 │                     │
             │              │                 │          │         │ Branch-Office │               │ region = Southeast │ ICMP           │                 │                     │
             │              │                 │          │         │               │               │                    │ SNMP           │                 │                     │

show-requisition --xml Branch-Office prints the requisition XML, and show-requisition with no arguments lists every requisition on the system. Note the empty Last Import Date above: nothing has been synchronized yet, which brings us to the fun part.

A fun trick: source a file full of commands

The Karaf shell has a source command that reads a file and executes each line as if you typed it. Combine that with add-requisitioned-node and you have a bulk loader that needs no XML, no ReST client, and no Perl.

Put something like this in $OPENNMS_HOME/etc/branch-nodes.karaf. The file can be generated however you like: a spreadsheet export, a template in your configuration management tool, a query against your CMDB, or a one-liner in awk.




opennms:add-requisitioned-node -r Branch-Office -n branch-ap-01 -f branch-ap-01 -i 127.0.1.20,P,SNMP -s ICMP -c AccessPoints -c Branch-Office -m site=ATL-01
opennms:add-requisitioned-node -r Branch-Office -n branch-ap-02 -f branch-ap-02 -i 127.0.1.21,P,SNMP -s ICMP -c AccessPoints -c Branch-Office -m site=ATL-01
opennms:add-requisitioned-node -r Branch-Office -n branch-ups-01 -f branch-ups-01 -i 127.0.1.30,P,SNMP -s ICMP -c Power -c Branch-Office -m site=ATL-01 -a vendor=APC
opennms:add-requisitioned-node -r Branch-Office -n branch-printer-01 -f branch-printer-01 -i 127.0.1.40,N -s ICMP -c Printers -c Branch-Office -m site=ATL-01
opennms:show-requisition Branch-Office

Then source it:






admin@opennms()> shell:source /opt/opennms/etc/branch-nodes.karaf
Successfully added node "branch-ap-01" to requisition "Branch-Office".
Successfully added node "branch-ap-02" to requisition "Branch-Office".
Successfully added node "branch-ups-01" to requisition "Branch-Office".
Successfully added node "branch-printer-01" to requisition "Branch-Office".
Requisition Name │ Last Import Date │ Last Update Date             │ Node Count
─────────────────┼──────────────────┼──────────────────────────────┼───────────
Branch-Office    │                  │ Mon Oct 05 15:26:34 GMT 2026 │ 6
...

Any Karaf command can go in the file, so the last line shows the result. You could just as easily end the file with a synchronize-requisition and have the whole thing provisioned in the database in one shot.

Synchronizing

Adding nodes to a requisition only edits the requisition. To get them into the database, synchronize it. --wait blocks until the import completes and prints a dot per second while you wait, which makes it script-friendly. --rescan takes the same true, false, or dbonly values as the ReST API.


admin@opennms()> opennms:filter "categoryName == 'Branch-Office'"
No matching nodes/interfaces for this rule.
admin@opennms()> opennms:synchronize-requisition --wait Branch-Office
Requisition import triggered asynchronously for URL:
file:/opt/opennms/etc/imports/Branch-Office.xml
...........
Import succeeded.
admin@opennms()> opennms:show-requisition
Requisition Name │ Last Import Date             │ Last Update Date             │ Node Count
─────────────────┼──────────────────────────────┼──────────────────────────────┼───────────
Branch-Office    │ Mon Oct 05 15:30:48 GMT 2026 │ Mon Oct 05 15:26:34 GMT 2026 │ 6
Minions          │ Thu Sep 03 19:38:28 GMT 2026 │ Thu Sep 03 19:38:28 GMT 2026 │ 1
selfmonitor      │                              │ Wed Sep 02 20:02:27 GMT 2026 │ 1
admin@opennms()> opennms:filter "categoryName == 'Branch-Office'"

nodeId=5 nodeLabel=branch-printer-01 location=Default
categories:
Branch-Office Printers IpAddresses:
127.0.1.40

nodeId=6 nodeLabel=branch-sw-01 location=Default
categories:
Switches Branch-Office IpAddresses:
127.0.1.3
127.0.1.2
...

Six nodes scanned and in the database about eleven seconds after hitting enter!

import-requisition is the general-purpose sibling of synchronize-requisition for external requisitions. Instead of a requisition name it takes a handler type and key=value parameters, so it works with any of the requisition URL handlers: file, http, dns, vmware, and the rest.


admin@opennms()> opennms:import-requisition --wait --rescan dbonly file path=/opt/opennms/etc/imports/Branch-Office.xml
Requisition import triggered asynchronously for URL:
requisition://file?path=%2Fopt%2Fopennms%2Fetc%2Fimports%2FBranch-Office.xml
.
Import succeeded.

Removing things

delete-requisitioned-node takes one or more foreign IDs. As with adding, the node is gone from the database only after you synchronize.




admin@opennms()> opennms:delete-requisitioned-node --requisition Branch-Office --foreignid branch-printer-01
Deleting foreignid 'branch-printer-01'...
Deleted 1 node(s)
admin@opennms()> opennms:synchronize-requisition --wait --rescan dbonly Branch-Office
Requisition import triggered asynchronously for URL:
file:/opt/opennms/etc/imports/Branch-Office.xml
.
Import succeeded.

delete-requisition is deliberately a little stubborn, because deleting a requisition is how you delete every node in it. It refuses without --yes-i-really-mean-it, and it refuses to delete a requisition that still contains nodes. The intended sequence is: empty it, synchronize so the nodes leave the database, then delete it.



admin@opennms()> opennms:delete-requisition Branch-Office
You must confirm you really want to perform this action. see --help
admin@opennms()> opennms:delete-requisition --yes-i-really-mean-it Branch-Office
Requisition Branch-Office is not empty. Empty it with '--empty', and optionally send an appropriate 'uei.opennms.org/internal/importer/reloadImport' event or use 'opennms:synchronize-requisition' to synchronize it to the database.
admin@opennms()> opennms:delete-requisition --empty --yes-i-really-mean-it Branch-Office
Requisition contains 5 nodes.
Deleted 5 nodes.
admin@opennms()> opennms:synchronize-requisition --wait --rescan dbonly Branch-Office
Requisition import triggered asynchronously for URL:
file:/opt/opennms/etc/imports/Branch-Office.xml
.
Import succeeded.
admin@opennms()> opennms:delete-requisition --yes-i-really-mean-it Branch-Office
Deleted 'Branch-Office'.
admin@opennms()> opennms:delete-foreignsource --foreignsource Branch-Office
Deleted foreign source 'Branch-Office'.

Where this gets interesting

None of these commands offer anything you could not already do through the UI or the ReST API. What changes is where you can do it from and how easily it composes.

  • Bulk onboarding from any data source. Anything that can emit lines of text can emit a Karaf script. Export a sheet, template it, source it, synchronize.
  • Idempotent scripts. Set --foreign-id explicitly and the same file can be re-sourced after every change to your inventory. Nodes that already exist are updated, new ones are added.
  • Scripting over SSH. The Karaf shell accepts commands on the SSH command line, so ssh -p 8101 admin@opennms "opennms:synchronize-requisition --wait Branch-Office" is a one-line job you can put in cron, a CI pipeline, or a runbook.
  • Chaining with the rest of the shell. Semicolons work, so add-requisitioned-node ... ; synchronize-requisition --wait ... ; opennms:filter ... adds a node, imports it, and confirms it exists in a single line.
  • Inspecting without leaving the shell. show-requisition and show-foreignsource-definition are the fastest way to answer "what is actually in this requisition right now" when you are already in Karaf troubleshooting something else.

The full option list for each command is available with --help, and requisitionName and --class arguments tab-complete. The commands landed in OpenNMS PR #8417 and are documented in the Karaf shell reference section of the Meridian 2026 documentation.

If you are running Horizon or evaluating OpenNMS for the first time, the provisioning commands are one more reason to look at Meridian 2026. They turn the requisition workflow into something you can script, version, and run from the same place you already manage the platform. To see Meridian in your own environment, or to talk through how shell-based provisioning fits with your existing inventory sources, contact our team today.

Jump to section

About the Author: Dino Yancey

I'm the Lead Client Services Engineer at The OpenNMS Group, which is a way to use a lot of capital letters to say that I'm a huge nerd.
Published On: October 9th, 2026Last Updated: October 9th, 20261 min read