Clients and nodes¶
Niwaki and AsyncNiwaki own the session; NiwakiNode / AsyncNiwakiNode
are the DN-scoped handles they hand out. The clients observe — writing
goes through the design DSL (The design DSL).
Clients¶
- class niwaki.Niwaki(
- host=None,
- username=None,
- password=None,
- *,
- verify_ssl=True,
- timeout=30.0,
- refresh_threshold=60,
- retry=None,
- token_cache=None,
- private_key=None,
- cert_dn=None,
Bases:
objectSync entry point for the ACI SDK.
Use as a sync context manager — authentication happens on
__enter__and the session is closed on__exit__. For async code useAsyncNiwakiinstead.Use as a context manager (recommended):
with Niwaki("https://apic.example.com", "admin", "secret") as aci: bds = aci.query(fvBD).fetch()
Use
connect()for explicit lifecycle management:aci = Niwaki.connect("https://apic.example.com", "admin", "secret") try: bd = aci.root.tenant("prod").bd("web").read() finally: aci.close()
- Parameters:
host (str | None) – Base URL of the APIC (e.g.
"https://sandboxapicdc.cisco.com"). Falls back toAPIC_HOSTenvironment variable if omitted.username (str | None) – APIC username. Falls back to
APIC_USERNAMEif omitted.password (str | None) – APIC password. Falls back to
APIC_PASSWORDif omitted.verify_ssl (bool | str) – TLS verification —
True(system CA store), a path to a PEM CA bundle (private/enterprise CA), orFalse(lab only). Default:True.timeout (float) – HTTP request timeout in seconds. Default: 30.
refresh_threshold (int) – Seconds before token expiry at which a proactive refresh is triggered. Default: 60.
retry (RetryConfig | None) – Custom retry policy. Defaults to 3 attempts with exponential back-off. Construct with
RetryConfig.
- classmethod connect(
- host,
- username,
- password,
- *,
- verify_ssl=True,
- timeout=30.0,
- refresh_threshold=60,
- retry=None,
Create a Niwaki instance and authenticate immediately (sync).
Equivalent to entering
Niwaki(...)as a sync context manager but without thewithstatement — the same session construction and login path is used, so every constructor option (including retry) behaves identically.- Parameters:
host (str) – Base URL of the APIC.
username (str) – APIC username.
password (str) – APIC password.
verify_ssl (bool | str) – TLS verification —
True, a PEM CA bundle path, orFalse. Default:True.timeout (float) – HTTP timeout in seconds. Default: 30.
refresh_threshold (int) – Proactive refresh threshold in seconds. Default: 60.
retry (RetryConfig | None) – Custom retry policy. Defaults to 3 attempts with exponential back-off.
- Returns:
Authenticated
Niwakiinstance.- Raises:
LoginError – APIC rejected the credentials.
ConnectionError – APIC host is unreachable.
TimeoutError – Login request timed out.
TLSError – TLS certificate verification failed.
- Return type:
Example:
aci = Niwaki.connect( "https://sandboxapicdc.cisco.com", "admin", "ciscopsdt", verify_ssl=False, )
- close()[source]¶
Close the sync HTTP session and release network resources.
Called automatically on
__exit__. Safe to call explicitly when not usingNiwakias a context manager. Afterclose(), any further operation raisesAuthError(mirrorsAsyncNiwaki.close()).
- property retry: RetryConfig | None¶
Custom retry policy passed at construction time, or
Nonefor the default.- Returns:
The
RetryConfigoverride, orNonewhen the session default (3 attempts) is in use.
- classmethod with_client(client, host=None, username=None, password=None, **kwargs)[source]¶
Build a client that talks through an
httpxclient you configured.Everything this SDK does not model — an outbound proxy, mutual TLS, a pinned CA bundle, a custom transport, per-route timeouts — is already expressible in
httpx. Rather than mirror each as a parameter, this hands the whole client over; the returned object is identical in every other respect, and closing it closes the client you gave.- Parameters:
client (Client) – A configured
httpx.Client. Pass the same host you gave it, since cookies and WebSocket URLs are derived from it.host (str | None) – APIC base URL, or
Noneto readAPIC_HOST.username (str | None) – Login name, or
Noneto readAPIC_USERNAME.password (str | None) – Password, or
Noneto readAPIC_PASSWORD.**kwargs (Any) – Any other constructor argument, validated as usual.
- Returns:
A client using client for every request.
- Return type:
Example:
behind_proxy = httpx.Client(base_url=host, proxy="http://proxy:3128") aci = Niwaki.with_client(behind_proxy, host, user, password)
- property apic_version: str | None¶
The firmware the connected controller stated at login.
Compare it against
niwaki.catalog.schema_version()— the firmware this SDK’s models and vocabulary were generated from — to know whether you are inside the envelope it was built for. A mismatch is not a failure: reads stay tolerant, writes fail loudly. It is a reason to pilot rather than assume.- Returns:
The version string, or
Nonebefore the client has connected.
- property subscriptions: SubscriptionManager¶
Bulk introspection/management for this session’s object-subscriptions.
- Returns:
A
SubscriptionManagerover the active session.
Example:
with Niwaki(...) as aci: sub = aci.query(fvBD).subscribe() for info in aci.subscriptions.list(): print(info.path, info.is_stale) aci.subscriptions.close_all()
- property root: NiwakiNode[ManagedObject]¶
Entry point for the ACI object hierarchy.
Returns a
NiwakiNodeat DN"uni"— the root of all ACI objects.- Returns:
NiwakiNodewithdn = "uni".
Example:
bd = aci.root.tenant("prod").bd("web").read()
- node(dn, cls=<class 'niwaki.models.base.ManagedObject'>)[source]¶
Access a node by explicit DN.
- Parameters:
dn (str) – Full Distinguished Name (e.g.
"uni/tn-prod/BD-web").cls (type[T]) – ACI class hint. Defaults to
ManagedObject.
- Returns:
NiwakiNodescoped to the given DN.- Return type:
NiwakiNode[T]
Example:
bd = aci.node("uni/tn-prod/BD-web", fvBD).read()
- query(cls: type[T]) Query[T][source]¶
- query(cls: str) Query[ManagedObject]
Build a global class query for the entire ACI fabric.
Returns a
Querybuilder targeting every instance of cls across the fabric (/api/class/{cls}.json). Add filters, scope constraints, or response enrichment before executing.Also accepts a plain string class name for querying any of the ~15 000 ACI classes — including read-only operational and stats classes that are not in the generated set. Attributes on unregistered classes are accessible via
obj.model_extra["attr"]or directly asobj.attr_name(Pydanticextra="allow").- Parameters:
cls (type[T] | str) – ACI class type (e.g.
fvBD) or plain string class name (e.g."topSystem").- Returns:
Querytargeting the global class index.- Return type:
Query[T]
Example:
with Niwaki(...) as aci: # All tenants tenants = aci.query(fvTenant).fetch() # Filtered bds = aci.query(fvBD).where(arpFlood=True).fetch() # Scoped to a DN bds = aci.query(fvBD).under("uni/tn-prod").fetch() # Count only n = aci.query(fvBD).count() # Unregistered / read-only class nodes = aci.query("topSystem").naming_only().fetch()
- class niwaki.AsyncNiwaki(
- host=None,
- username=None,
- password=None,
- *,
- verify_ssl=True,
- timeout=30.0,
- refresh_threshold=60,
- max_concurrent=10,
- retry=None,
- token_cache=None,
- private_key=None,
- cert_dn=None,
Bases:
objectAsync entry point for the ACI SDK.
Mirrors
Niwakifor async code. Use as an async context manager — authentication happens on__aenter__and the session is closed on__aexit__.- Parameters:
host (str | None) – Base URL of the APIC (e.g.
"https://sandboxapicdc.cisco.com"). Falls back toAPIC_HOSTenvironment variable if omitted.username (str | None) – APIC username. Falls back to
APIC_USERNAMEif omitted.password (str | None) – APIC password. Falls back to
APIC_PASSWORDif omitted.verify_ssl (bool | str) – TLS verification —
True(system CA store), a path to a PEM CA bundle (private/enterprise CA), orFalse(lab only). Default:True.timeout (float) – HTTP request timeout in seconds. Default: 30.
refresh_threshold (int) – Seconds before token expiry at which a proactive refresh is triggered. Default: 60.
max_concurrent (int) – Maximum simultaneous HTTP requests in flight. Default: 10.
retry (RetryConfig | None) – Custom retry policy. Defaults to 3 attempts with exponential back-off. Construct with
RetryConfig.
Example:
async with AsyncNiwaki("https://apic.example.com", "admin", "pass") as aci: tenants = await aci.query(fvTenant).fetch() bd = await aci.root.tenant("prod").bd("web").read() # Concurrent reads via gather() async with AsyncNiwaki("https://apic.example.com", "admin", "pass") as aci: tenants, bd = await aci.gather( aci.query(fvTenant).fetch(), aci.root.tenant("prod").bd("web").read(), )
- async classmethod connect(
- host,
- username,
- password,
- *,
- verify_ssl=True,
- timeout=30.0,
- refresh_threshold=60,
- max_concurrent=10,
- retry=None,
Create an AsyncNiwaki instance and authenticate immediately (async).
The async twin of
Niwaki.connect()— for long-lived services that manage the lifecycle themselves rather than throughasync with. The same session construction and login path as the context manager is used, so every constructor option behaves identically. Pair withclose()on shutdown.- Parameters:
host (str) – Base URL of the APIC.
username (str) – APIC username.
password (str) – APIC password.
verify_ssl (bool | str) – TLS verification —
True, a PEM CA bundle path, orFalse. Default:True.timeout (float) – HTTP timeout in seconds. Default: 30.
refresh_threshold (int) – Proactive refresh threshold in seconds. Default: 60.
max_concurrent (int) – Maximum simultaneous in-flight requests. Default: 10.
retry (RetryConfig | None) – Custom retry policy. Defaults to 3 attempts with exponential back-off.
- Returns:
Authenticated
AsyncNiwakiinstance.- Raises:
LoginError – APIC rejected the credentials.
ConnectionError – APIC host is unreachable.
TimeoutError – Login request timed out.
TLSError – TLS certificate verification failed.
- Return type:
Example:
aci = await AsyncNiwaki.connect( "https://sandboxapicdc.cisco.com", "admin", "ciscopsdt", verify_ssl=False, ) try: bds = await aci.query("fvBD").fetch() finally: await aci.close()
- async close()[source]¶
Close the async HTTP session and release network resources.
Called automatically on
__aexit__. Safe to call explicitly when not usingAsyncNiwakias an async context manager.
- property retry: RetryConfig | None¶
Custom retry policy passed at construction time, or
Nonefor the default.- Returns:
The
RetryConfigoverride, orNonewhen the session default (3 attempts) is in use.
- classmethod with_client(client, host=None, username=None, password=None, **kwargs)[source]¶
Build a client that talks through an
httpxclient you configured.Everything this SDK does not model — an outbound proxy, mutual TLS, a pinned CA bundle, a custom transport, per-route timeouts — is already expressible in
httpx. Rather than mirror each as a parameter, this hands the whole client over; the returned object is identical in every other respect, and closing it closes the client you gave.- Parameters:
client (AsyncClient) – A configured
httpx.AsyncClient. Pass the same host you gave it, since cookies and WebSocket URLs are derived from it.host (str | None) – APIC base URL, or
Noneto readAPIC_HOST.username (str | None) – Login name, or
Noneto readAPIC_USERNAME.password (str | None) – Password, or
Noneto readAPIC_PASSWORD.**kwargs (Any) – Any other constructor argument, validated as usual.
- Returns:
A client using client for every request.
- Return type:
Example:
behind_proxy = httpx.Client(base_url=host, proxy="http://proxy:3128") aci = Niwaki.with_client(behind_proxy, host, user, password)
- property apic_version: str | None¶
The firmware the connected controller stated at login.
Compare it against
niwaki.catalog.schema_version()— the firmware this SDK’s models and vocabulary were generated from — to know whether you are inside the envelope it was built for. A mismatch is not a failure: reads stay tolerant, writes fail loudly. It is a reason to pilot rather than assume.- Returns:
The version string, or
Nonebefore the client has connected.
- property max_concurrent: int¶
Upper bound on simultaneous in-flight HTTP requests for this client.
A staged push inherits this as its own fan-out bound, so
push(mode="staged")runs at most this many operations of a wave at once.push(..., max_concurrent=n)can lower it further but never raise it above this number — construct the client with a higher limit to go wider.- Returns:
The limit set at construction (default
10).
- property subscriptions: AsyncSubscriptionManager¶
Bulk introspection/management for this session’s object-subscriptions.
- Returns:
An
AsyncSubscriptionManagerover the active session.
Example:
async with AsyncNiwaki(...) as aci: sub = await aci.query(fvBD).subscribe() for info in aci.subscriptions.list(): print(info.path, info.is_stale) await aci.subscriptions.close_all()
- property root: AsyncNiwakiNode[ManagedObject]¶
Entry point for the ACI object hierarchy.
- Returns:
AsyncNiwakiNodeat DN"uni".
Example:
bd = await aci.root.tenant("prod").bd("web").read()
- node(dn, cls=<class 'niwaki.models.base.ManagedObject'>)[source]¶
Access a node by explicit DN.
- Parameters:
dn (str) – Full Distinguished Name (e.g.
"uni/tn-prod/BD-web").cls (type[T]) – ACI class hint. Defaults to
ManagedObject.
- Returns:
AsyncNiwakiNodescoped to the given DN.- Return type:
- query(cls: type[T]) AsyncQuery[T][source]¶
- query(cls: str) AsyncQuery[ManagedObject]
Build a global class query for the entire ACI fabric (async variant).
Mirrors
query()for async contexts. The accumulator methods are synchronous; only the execution methods (fetch,first,count,stream) are coroutines, which means they compose naturally withgather().- Parameters:
cls (type[T] | str) – ACI class type (e.g.
fvBD) or plain string class name.- Returns:
AsyncQuerytargeting the global class index.- Return type:
AsyncQuery[T]
Example:
async with AsyncNiwaki(...) as aci: # All tenants tenants = await aci.query(fvTenant).fetch() # Concurrent reads via gather() tenants, bds = await aci.gather( aci.query(fvTenant).fetch(), aci.root.tenant("prod").bd().fetch(), ) # Async streaming async for bd in aci.query(fvBD).stream(): await process(bd)
- async gather(*coros)[source]¶
Run multiple coroutines concurrently under a structured TaskGroup.
The APIC concurrency semaphore (
max_concurrent) is already applied at the session level, so each coroutine naturally respects the limit. On the first coroutine that raises,TaskGroupcancels the others and raises anExceptionGroup(Python 3.11+). Do not usegatherfor concurrent writes: a failure cancels the in-flight siblings, which can leave a second design partially applied.- Parameters:
*coros (Coroutine[Any, Any, Any]) – Coroutines to run concurrently (e.g. results of
.read(),aci.query(...).fetch(),config.push(aci)).- Returns:
Tuple of results in the same order as the input coroutines.
- Raises:
ExceptionGroup – If one or more coroutines raise. Remaining siblings are cancelled (
TaskGroupsemantics), not awaited.- Return type:
Example:
tenants, bd = await aci.gather( aci.query(fvTenant).fetch(), aci.root.tenant("prod").bd("web").read(), )
Nodes¶
- class niwaki.NiwakiNode(niwaki, dn, cls)[source]¶
Bases:
_JargonNavMixin,GenericA DN-scoped handle for observing a single ACI object (sync).
Created via
Niwaki.rootorNiwaki.node(). Navigate the ACI hierarchy withmo()or the vocabulary accessors — each call descends one level and computes the child DN automatically from the parent DN and the child’s RN. Terminal operations are read-side (read(),query()) plusdelete(); configuration goes throughniwaki.design.- Parameters:
- property dn: str¶
Full Distinguished Name of this node (read-only).
- Returns:
DN string, e.g.
"uni/tn-prod/BD-web".
Example:
node = aci.root.mo(fvTenant, name="prod").mo(fvBD, name="web") node.dn # → "uni/tn-prod/BD-web"
- property cls: type[T]¶
ACI class associated with this node.
- Returns:
The
ManagedObjectsubclass used to construct and deserialise objects at this DN.
- mo(cls, **naming_kwargs)[source]¶
Descend to a child node, computing its DN automatically.
Creates a temporary instance of
clspurely to derive the RN from_rn_format. The child DN isself.dn + "/" + child.rn.- Parameters:
cls (type[U]) – ACI class of the child object.
**naming_kwargs (Any) – Naming props required by
cls(e.g.name="web").
- Returns:
A new
NiwakiNodescoped to the child DN.- Raises:
ValidationError – If
naming_kwargsviolate the model constraints forcls.- Return type:
NiwakiNode[U]
Example:
aci.root .mo(fvTenant, name="prod") .mo(fvBD, name="web") .mo(fvSubnet, ip="10.0.0.1/24")
- read()[source]¶
Fetch the ACI object at this DN from the APIC.
- Returns:
Typed
ManagedObjectinstance.- Raises:
NotFoundError – The object does not exist on the APIC.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
T
Example:
bd = aci.root.mo(fvTenant, name="prod").mo(fvBD, name="web").read() print(bd.unicast_routing)
- delete()[source]¶
Delete the ACI object at this DN.
- Raises:
NotFoundError – The object does not exist on the APIC.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
Example:
aci.root.mo(fvTenant, name="prod").mo(fvBD, name="web").delete()
- query(cls: type[U]) Query[U][source]¶
- query(cls: str) Query[ManagedObject]
Build a query scoped to this node’s DN.
Returns all instances of cls that are descendants of this node, using the APIC
query-target=subtree&target-subtree-class=clsquery mode. Chain filter, scope, and enrichment methods before executing withfetch(),first(),count(), orstream().This is also the entry point for querying unregistered classes (the full APIC class catalogue of ~15 000 classes): pass a plain string class name and the result will be a
Query[ManagedObject]whose objects expose all APIC attributes viamodel_extra.- Parameters:
cls (type[U] | str) – ACI class type (e.g.
fvBD) or plain string class name (e.g."topSystem").- Returns:
Queryscoped to this node’s DN.- Return type:
Query[U]
Example:
# All BDs under tenant "prod" bds = aci.root.tenant("prod").query(fvBD).fetch() # Count subnets in a specific BD n = aci.root.tenant("prod").bd("web").query(fvSubnet).count() # Unregistered / read-only class, scoped under this node's subtree faults = aci.root.tenant("prod").query("faultInst").fetch()
- class niwaki.AsyncNiwakiNode(niwaki, dn, cls)[source]¶
Bases:
_JargonNavMixin,GenericA DN-scoped handle for observing a single ACI object (async).
Mirrors
NiwakiNodefor async contexts. Terminal operations (read(),delete()) are coroutines; configuration goes throughniwaki.design.- Parameters:
niwaki (AsyncNiwaki) – Parent
AsyncNiwakiinstance.dn (str) – Full Distinguished Name of this node.
cls (type[T]) – ACI class associated with this node. Used to deserialise reads.
- property dn: str¶
Full Distinguished Name of this node (read-only).
- Returns:
DN string, e.g.
"uni/tn-prod/BD-web".
- property cls: type[T]¶
ACI class associated with this node.
- Returns:
The
ManagedObjectsubclass used to construct and deserialise objects at this DN.
- mo(cls, **naming_kwargs)[source]¶
Descend to a child node, computing its DN automatically.
- Parameters:
cls (type[U]) – ACI class of the child object.
**naming_kwargs (Any) – Naming props required by
cls.
- Returns:
A new
AsyncNiwakiNodescoped to the child DN.- Raises:
ValidationError – If
naming_kwargsviolate model constraints.- Return type:
- async read()[source]¶
Fetch the ACI object at this DN from the APIC.
- Returns:
Typed
ManagedObjectinstance.- Raises:
NotFoundError – The object does not exist.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
T
- async delete()[source]¶
Delete the ACI object at this DN.
- Raises:
NotFoundError – Object does not exist.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- query(cls: type[U]) AsyncQuery[U][source]¶
- query(cls: str) AsyncQuery[ManagedObject]
Build a query scoped to this node’s DN (async variant).
Mirrors
query()for async contexts. All accumulator methods are synchronous; only the execution methods (fetch,first,count,stream) are coroutines.- Parameters:
cls (type[U] | str) – ACI class type or plain string class name.
- Returns:
AsyncQueryscoped to this node’s DN.- Return type:
AsyncQuery[U]
Example:
bds = await aci.root.tenant("prod").query(fvBD).fetch() n = await aci.root.tenant("prod").bd("web").query(fvSubnet).count()
Subscription management¶
aci.subscriptions — bulk introspection/refresh/stop over every
subscription open on the session’s shared WebSocket; see
Subscribing to live changes.
- class niwaki.facade.SubscriptionManager(session)[source]¶
Bases:
objectBulk introspection/management for a session’s object-subscriptions (sync).
Reached via
subscriptions. Operates on whichever subscriptions are currently open on the session’s shared WebSocket — every method is a safe no-op (empty list, or nothing to do) until at least oneaci.query(...).subscribe()call has opened it.- list()[source]¶
List every subscription currently tracked, or
[]if none is open.- Returns:
One
SubscriptionInfoper tracked subscription —.is_staleflags one with at least one recent refresh failure.- Return type:
Example:
for info in aci.subscriptions.list(): print(info.path, info.consecutive_refresh_failures, info.is_stale)
- refresh_all()[source]¶
Force an immediate refresh of every tracked subscription, on demand.
A diagnostic tool, distinct from the automatic background refresh sweep — a failure here never triggers the automatic recovery escalation a missed scheduled refresh would.
- Returns:
The post-refresh snapshot of every subscription.
- Return type:
- close_all()[source]¶
Stop every tracked subscription — the shared socket itself stays open.
Every open
Subscription’s iterator ends with a plainStopIteration. Distinct from closing the wholeNiwakisession: a lateraci.query(...).subscribe()reuses the same still-open socket immediately, with no reconnect needed.
- class niwaki.facade.AsyncSubscriptionManager(session)[source]¶
Bases:
objectAsync counterpart of
SubscriptionManager. Reached viasubscriptions.- list()[source]¶
List every subscription currently tracked, or
[]if none is open.Synchronous even on the async facade — see
list_subscriptions().- Returns:
One
SubscriptionInfoper tracked subscription.- Return type:
Retry policy¶
Retries are configured on the client with RetryConfig
(documented with the transport layer that applies it).