AIOps

AIOps

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.


#ITAutomation
#AIOps
#CloudPakforAIOps
#AIOps

 View Only

Stop Writing ObjectServer Boilerplate — Use netcool_objectserver_client.py

By Ludan Zhao posted 07/27/26 03:13 AM

  

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.

0 comments
7 views

Permalink