Unified Python client for the NGX Storage Manager API v2. One client owns auth, TLS, bounded 725-busy retry, controller failover, and work-mode selection; every driver (Cinder FC, Cinder iSCSI, Cinder NFS) imports this SDK instead of writing its own NGX HTTP client.
Mirrors the Go SDK (ngxstorage-sdk-go) with the same architecture and
canonical backend fields.
- API-key auth (Bearer) — never logged
- Controller failover with work-mode resolution (
single-master,master-ready,cluster) - Bounded 725-busy retry (6 attempts, 10–30s exponential backoff)
- Transport failover replays safe methods (GET/DELETE) only — POST never replayed
- Optional TLS verification (skipped by default — NGX Storage Arrays are self-signed)
- Pluggable
BaseMiddlewarechain - Per-resource services covering every API v2 group
pip install -e .from ngxstorage import NGXClient, ClientConfig
client = NGXClient(ClientConfig(
controllers=["192.168.1.201", "192.168.1.202"],
api_key="your-api-key",
pool_name="pool1",
# NGX Storage Arrays are self-signed; verification is skipped by default.
# Provide ca_bundle to enforce a customer trust chain.
insecure_skip_verify=True,
))
# Resolve the serving controller + pool once before serving traffic.
client.refresh_controller()
# LUN (block)
lun = client.luns.create("vol1", 100)
client.luns.expand(lun["id"], 200)
client.luns.delete(lun["id"])
# NFS share (created with export disabled)
share = client.shares.create("share1", 100 * 1024**3)
client.shares.set_export_enabled(share["id"], True)
# Snapshot + clone
snap = client.snapshots.create(lun["id"], "snap1")
clone_id = client.snapshots.clone(snap["id"], "clone1")
# iSCSI target + CHAP
client.iscsi_targets.add_lun(target_id, lun["id"], owner)
client.auth_groups.add_chap(group_id, "user", "pass")
# Pool capacity / cluster status
available, reserved = client.pools.get_configured_capacity()
cluster = client.status.cluster()ClientConfig fields:
| Field | Description |
|---|---|
controllers |
1–2 controller IPs/hostnames (required) |
api_key |
Bearer token (required, never logged) |
pool_name |
Canonical pool name; empty skips pool ownership checks |
insecure_skip_verify |
Skip TLS verification (default True for self-signed NGX) |
ca_bundle |
Optional path to a customer CA bundle |
middlewares |
BaseMiddleware chain, outermost-first |
timeout |
Per-request timeout (default 60s) |
max_retries / base_delay / max_delay |
725-busy retry bounds |
ClientConfig.__repr__ redacts the API key.
| Service | Accessor | Operations |
|---|---|---|
| LUN | client.luns |
create, get, list, delete, modify, expand |
| Share (NFS) | client.shares |
create, get, list, list_names, delete, modify, expand, set_export_enabled, set_read_only |
| Snapshot | client.snapshots |
create, get, list, list_detail, delete, clone, restore |
| FC target | client.fc_targets |
list, get, add_lun, remove_lun |
| iSCSI target | client.iscsi_targets |
list, get, create, delete, add_lun, remove_lun |
| Auth group | client.auth_groups |
create, get, list, delete, add_chap, delete_chap |
| Portal group | client.portal_groups |
list, get, create, delete |
| Pool | client.pools |
list, get, overview, get_configured_capacity |
| Status | client.status |
cluster, services, capacity, iops, bandwidth |
The SDK raises typed exceptions; classify without string matching:
from ngxstorage import APIError
from ngxstorage.errors import is_not_found, is_busy, is_already_exists
try:
client.luns.delete(lun_id)
except APIError as exc:
if is_not_found(exc):
pass # idempotent success
elif is_busy(exc):
pass # 725, already retriedAPIError carries status_code, code, message, method, endpoint.
Sentinels: PoolNotFound, ClusterNotReady, TransportError.
- The API key, CHAP passwords, and S3 secret keys are never logged.
- TLS verification is optional: skipped by default because NGX Storage Arrays use
self-signed certificates and most customers have no private CA/DNS. Pass
ca_bundleto enforce a customer trust chain. - POST mutations are never replayed after an ambiguous transport failure.
Reads use the live backend JSON contract (nested capacity.soft_quota,
exports.nfs.enabled/read_only); writes use the canonical flat fields
(soft_quota, nfs_export, nfs_read_only). The SDK does not probe
alternate names.
pip install -e ".[test]"
PYTHONPATH=src pytestApache-2.0