Stop Writing ObjectServer Boilerplate — Use netcool_objectserver_client.py
A ready-made Python client for Netcool/OMNIbus ObjectServer that handles JDBC connection, alert CRUD, and failover. Copy it, configure it, start querying in minutes.
TL;DR
The file python/netcool_objectserver_client.py in katamari/test-automation/test-automation-netcool-connector is a drop-in Python client for Netcool ObjectServer. It gives you a clean API for connecting, querying, inserting, updating, acknowledging, clearing and deleting alerts — without touching raw SQL or JDBC setup. It also ships with 11 ready-to-run examples in the main() function covering every common operation. This post walks you through getting it running and what each example does.
1. Why I Built This
Every team that integrates with Netcool/OMNIbus ObjectServer eventually writes the same thing: a chunk of Python that wrangles JayDeBeApi, figures out the right Sybase driver class name, constructs JDBC URLs, handles primary/backup failover, and translates raw cursor rows into usable dictionaries. I needed all of that for the Netcool Connector test automation framework, so I built it once, properly, as a reusable module.
If your team tests any connector that reads from or writes to an ObjectServer — or if you need to script alert management tasks — this client saves you that work entirely.
2. How It Works
The client wraps JDBC connectivity using JayDeBeApi, a Python library that bridges Python code to Java JDBC drivers via JPype. Under the hood, it speaks the Sybase TDS protocol to ObjectServer on port 4100 (the default ObjectServer AGG_P port). You give it a config dict and paths to the two required JARs; it handles the rest.
The class is one file, zero project-specific dependencies. It only imports from the Python standard library plus jaydebeapi. You can copy it directly into any project.
3. Prerequisites
1
Java Runtime (JRE or JDK 8+) — JayDeBeApi needs a JVM.
java -version # verify
brew install openjdk # macOS
sudo yum install java-11-openjdk # RHEL/CentOS
2
Two Sybase JDBC driver JARs
| JAR |
Size |
What it provides |
nco_g_java.jar |
202 KB |
Netcool Java gateway library — entry point for ObjectServer JDBC connections |
jconn3.jar |
977 KB |
Sybase jConnect 3 JDBC driver — provides com.sybase.jdbc3.jdbc.SybDriver |
Option A — from any host where Netcool is installed:
cp /opt/IBM/tivoli/netcool/omnibus/java/jars/nco_g_java.jar python/jars/
cp /opt/IBM/tivoli/netcool/omnibus/java/jars/jconn3.jar python/jars/
Option B — already included in the repo:
git clone git@github.ibm.com:katamari/test-automation/test-automation-netcool-connector.git
ls test-automation-netcool-connector/python/jars/
# nco_g_java.jar jconn3.jar ...
3
Python dependencies
pip install jaydebeapi JPype1
# or: pip install -r python/requirements.txt
4
Network access to ObjectServer on port 4100:
nc -zv <objectserver-host> 4100
4. Installation
Clone the repo — the client and both JARs are already in place:
git clone git@github.ibm.com:katamari/test-automation/test-automation-netcool-connector.git
cd test-automation-netcool-connector
pip install -r python/requirements.txt
Or drop just the client into an existing project:
cp python/netcool_objectserver_client.py /your/project/
cp python/jars/nco_g_java.jar /your/project/jars/
cp python/jars/jconn3.jar /your/project/jars/
5. Configuration
config = {
"username": "root",
"password": "netcool_password",
"primary_objectserver": {
"url": "objectserver.example.com",
"api_port": 4100
},
# Optional — client retries here if primary fails
"backup_objectserver": {
"url": "objectserver-backup.example.com",
"api_port": 4100
},
"jdbc_driver_path": [
"python/jars/nco_g_java.jar",
"python/jars/jconn3.jar"
],
"tls": False
}
Use environment variables for credentials
import os
config = {
"username": os.environ["NETCOOL_USER"],
"password": os.environ["NETCOOL_PASSWORD"],
"primary_objectserver": {
"url": os.environ["NETCOOL_HOST"],
"api_port": int(os.environ.get("NETCOOL_PORT", "4100"))
},
"jdbc_driver_path": ["python/jars/nco_g_java.jar", "python/jars/jconn3.jar"]
}
6. Connecting and Disconnecting
from netcool_objectserver_client import NetcoolObjectServerClient
client = NetcoolObjectServerClient(config)
try:
client.connect()
# ... work ...
finally:
client.disconnect() # always runs, even on exception
connect() tries primary first, then backup if configured. It probes three Sybase driver class variants (jConnect 2.x, 3.x, 4.x) automatically, so it works regardless of which version is in your JAR.
7. The 11 Built-In Examples
The main() function contains 11 commented-out examples covering every common operation. To run one, uncomment the relevant block, update the config with your ObjectServer details, then:
python python/netcool_objectserver_client.py
1
Get ALL alerts with all 28 required fields
Retrieves up to 1,000 alerts, prints every field on the first row to verify the schema, then prints a full summary table sorted by LastOccurrence. Good first smoke-test that connectivity and field mapping are correct.
2
Get test alerts only
Filters to AlertGroup = 'Test' OR Manager = 'PythonClient' — rows created by the client itself. Lets you inspect test data without noise from production alerts.
3
Get critical alerts (Severity 5)
Short list of the most severe active alerts, sorted by Serial. A quick health check for any ObjectServer environment.
4
Alert count by severity
Runs COUNT(*) GROUP BY Severity and prints a breakdown. Useful for dashboards or pre-test environment sanity checks.
5
Get a specific alert by Serial number
Fetches a single alert by its Serial primary key and prints the full JSON. Starting point for any targeted inspection or update workflow.
6
Insert a test alert
Calls insert_test_alert("TestNode", "Test alert from Python", severity=5) and prints all returned fields including the auto-generated Serial. Foundation for any outbound-flow test.
7
Acknowledge an alert
Sets Acknowledged = 1 and updates StateChange. Demonstrates the inbound-action path used by AIOps to write back into ObjectServer.
8
Inspect table structure
Calls get_table_info("alerts.status") and prints column names and types. Useful when connecting to an unfamiliar ObjectServer to understand what fields are available.
9
Delete the oldest test alert
Fetches all test alerts, sorts by Serial to find the oldest, and hard-deletes it. A clean-up pattern to leave the ObjectServer in a known state after a test run.
10
Custom SQL query — recent high-severity alerts
Uses execute_query() with a hand-written SELECT TOP 5 … WHERE Severity >= 3 ORDER BY LastOccurrence DESC. Shows how to drop below the convenience methods when you need full SQL control.
11
Update a random test alert's severity
Picks a random test alert, changes its severity to 1, 3, or 5, then re-fetches it to verify the change. A self-contained update-and-verify pattern directly portable to BDD step definitions.
How to enable an example
All examples except Example 1 are commented out. Find the block for that example number, remove the # characters, update the config at the top of main() with your ObjectServer details, then run python python/netcool_objectserver_client.py.
8. Alert Operations — Full API Reference
8.1 Query alerts with a filter
alerts = client.get_alerts("Severity = 5 AND Acknowledged = 0", limit=50)
for alert in alerts:
print(f"Serial={alert['Serial']} Node={alert['Node']} Summary={alert['Summary']}")
8.2 Look up by Serial
alert = client.get_alert_by_serial(12345) # returns Dict or None
8.3 Get all alerts for a node
node_alerts = client.get_alerts_by_node("prod-router-01.example.com")
8.4 Alert counts by severity
counts = client.get_alert_count_by_severity() # {5: 12, 4: 7, 3: 21, ...}
8.5 Insert a test alert
inserted = client.insert_test_alert(
node="test-node-01", summary="CPU high", severity=4, agent="PythonClient"
)
print(f"Serial={inserted['Serial']}") # auto-generated, returned immediately
8.6 – 8.9 Modify / remove
client.update_alert_severity(serial=12345, new_severity=5)
client.acknowledge_alert(serial=12345) # Acknowledged=1, StateChange updated
client.clear_alert(serial=12345) # Severity=0
client.delete_alert(serial=12345) # hard DELETE
8.10 Arbitrary SQL
# SELECT
rows = client.execute_query("""
SELECT TOP 10 Serial, Node, Summary, Severity
FROM alerts.status WHERE Manager = 'ProbeWatch'
ORDER BY LastOccurrence DESC
""")
# INSERT / UPDATE / DELETE
client.execute_update(
"UPDATE alerts.status SET Acknowledged = 1 WHERE AlertGroup = 'TestGroup'"
)
9. How the Client Powers the BDD Scenario: New Alert Inserted in ObjectServer
This is the first — and currently most complete — scenario in outbound_flow.feature. It validates the full outbound path from ObjectServer all the way to Kafka:
ObjectServer
4 alerts inserted
→
IDUC Gateway
detects INSERTs
→
Netcool Connector
insertAlert events
→
Kafka
ChangeRequest verified
The Gherkin scenario reads:
Scenario: New Alert Inserted in ObjectServer
When 4 new alerts are inserted in ObjectServer with:
| Field | Value |
| Node | TestNode |
| Summary | Test alert from Python |
| Severity | 5 |
| Agent | test |
Then all 4 alerts should be successfully inserted in ObjectServer
And each inserted alert should have a Serial number
And the gateway should detect the INSERT operations
And the connector should receive "insertAlert" events
And at least 1 ChangeRequest should be created with type "create"
And the ChangeRequest should contain:
| Field | Value |
| entityType | alert |
| entity.summary | Test alert from Python |
| entity.severity | 6 |
| entity.state | open |
| entity.resource.name | TestNode |
And at least 1 ChangeRequest should be published to Kafka as a CloudEvent
The step definition for "4 new alerts are inserted" calls insert_test_alert() four times, collecting each returned Serial into context.current_alerts. The Kafka verification step then builds a set of those Serial integers and filters the full topic history — up to 100,000 messages — looking for a ChangeRequest whose entity.sender.serverSerial is in that set:
# Built from the Serial numbers returned by insert_test_alert()
inserted_serials = {alert.get("Serial") for alert in context.current_alerts if alert.get("Serial")}
# O(1) per-message membership test — efficient even at 100k messages
matching_requests = [
msg for msg in messages
if (msg.get("type") == "create"
and msg.get("entityType") == "alert"
and msg.get("entity", {}).get("sender", {}).get("serverSerial") in inserted_serials)
]
The 4 alerts are not deleted at the end of this scenario
The scenario's purpose is to verify that alerts are detected and forwarded — it intentionally does not include a clean-up step. The after_scenario hook in environment.py only records pass/fail results; it does not delete anything from ObjectServer. The 4 test alerts remain in alerts.status after the scenario ends.
If you need to clean up test data between runs, use Example 9 in netcool_objectserver_client.py — it finds and deletes test alerts by AlertGroup = 'Test' OR Manager = 'PythonClient' — or call client.delete_alert(serial) directly in your own teardown logic.
10. API Quick Reference
| Method |
What it does |
Returns |
connect() |
Connect to primary (then backup) ObjectServer |
bool |
disconnect() |
Close cursor and connection |
— |
get_alerts(filter, limit) |
SELECT from alerts.status with optional WHERE clause |
List[Dict] |
get_alert_by_serial(serial) |
Fetch a single alert by Serial PK |
Dict | None |
get_alerts_by_node(node) |
All alerts for a node name |
List[Dict] |
get_alert_count_by_severity() |
COUNT(*) grouped by Severity |
Dict[int,int] |
insert_test_alert(node, summary, severity, agent) |
INSERT alert, return dict with auto-generated Serial |
Dict | None |
update_alert_severity(serial, new_severity) |
UPDATE Severity + LastOccurrence |
bool |
acknowledge_alert(serial) |
SET Acknowledged=1, update StateChange |
bool |
clear_alert(serial) |
SET Severity=0 |
bool |
delete_alert(serial) |
DELETE FROM alerts.status WHERE Serial=… |
bool |
execute_query(sql) |
Run any SELECT, return list of dicts |
List[Dict] |
execute_update(sql) |
Run any INSERT / UPDATE / DELETE |
int (rows) |
get_table_info(table_name) |
Inspect column metadata for any table |
List[Dict] |
11. Troubleshooting
| Error |
Cause |
Fix |
SybDriver is not found |
Wrong or missing JAR |
Ensure both nco_g_java.jar and jconn3.jar are present and paths in jdbc_driver_path are correct |
JDBC driver not found at: … |
Path in config does not exist on disk |
Use an absolute path, or ensure the path is relative to where you run the script |
No Java runtime present |
JVM not on PATH |
Install JDK 8+ and ensure java -version works |
Connection refused |
ObjectServer not reachable on port 4100 |
Check firewall rules; verify with nc -zv host 4100 |
Login failed for user |
Wrong credentials |
Confirm with the Netcool desktop client or your admin |
NODEFAULT constraint violated |
Custom INSERT missing a NOT NULL column |
Use insert_test_alert() which supplies all required fields, or add the missing column to your SQL |
12. How to Get It
git clone git@github.ibm.com:katamari/test-automation/test-automation-netcool-connector.git
python/
├── netcool_objectserver_client.py ← the client (with 11 examples in main())
├── jars/
│ ├── nco_g_java.jar ← Netcool gateway library
│ └── jconn3.jar ← Sybase JDBC driver
├── SOLUTION.md ← connectivity options incl. REST API alternative
├── PYTHON_CLIENT_README.md ← full usage reference
└── TROUBLESHOOTING.md ← detailed error diagnosis
If you run into something the troubleshooting table does not cover, or if you extend the client with new methods your team needs, open a PR or ping me on Slack.