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 .fromngxstorageimportNGXClient, ClientConfigclient=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 + clonesnap=client.snapshots.create(lun["id"], "snap1")
clone_id=client.snapshots.clone(snap["id"], "clone1")
# iSCSI target + CHAPclient.iscsi_targets.add_lun(target_id, lun["id"], owner)
client.auth_groups.add_chap(group_id, "user", "pass")
# Pool capacity / cluster statusavailable, 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:
fromngxstorageimportAPIErrorfromngxstorage.errorsimportis_not_found, is_busy, is_already_existstry:
client.luns.delete(lun_id)
exceptAPIErrorasexc:
ifis_not_found(exc):
pass# idempotent successelifis_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