Reading — queries and filters¶
Niwaki query builder — full APIC REST query model as a fluent API.
This module provides two things:
Filter expression DSL — operator functions that produce typed
FilterExprobjects serialisable to APIC filter strings.Query builders —
Query(sync) andAsyncQuery(async) that accumulate query parameters lazily and execute on demand.
Quick-start¶
Reading objects without knowing class names (via jargon navigation):
with Niwaki(...) as aci:
# All BDs in tenant "prod" — no class import needed
bds = aci.root.tenant("prod").bd().fetch()
# Filter with keyword shorthand
bds = aci.root.tenant("prod").bd().where(arpFlood=True).fetch()
# Subtree included in response
bds = aci.query(fvBD).include(fvSubnet).fetch()
Filter DSL:
from niwaki.query import eq, ne, wcard, and_, or_, not_
# Keyword shorthand (simplest — no import needed)
query.where(name="web")
# Explicit operators
query.where(wcard("name", "prod-*"))
query.where(and_(wcard("name", "prod-*"), eq("arpFlood", True)))
query.where(~eq("name", "infra")) # NOT
Scoping:
# Global class query (entire fabric)
aci.query(fvBD).fetch()
# Scoped to a DN
aci.query(fvBD).under("uni/tn-prod").fetch()
# Via jargon navigation (DN inferred automatically)
aci.root.tenant("prod").bd().fetch()
Unregistered classes (read-only, operational, stats…):
# 15 000+ APIC classes accessible by string name
nodes = aci.query("topSystem").naming_only().fetch()
# Uniform read access, generated class or not: .dn and obj["wireName"]
for node in nodes:
print(node.dn, node["role"])
Response enrichment:
aci.root.tenant("prod").bd().with_faults().fetch() # faults embedded on each BD
aci.query(fvBD).with_health().fetch() # include health
aci.query(fvBD).include(fvSubnet).fetch() # embed children
Execution methods:
query.fetch() # list[T] — all pages, in memory
query.first() # T | None — page-size=1 optimisation
query.count() # int — one lightweight request, no objects transferred
query.stream() # Iterator[T] / AsyncIterator[T] — page-by-page
Public API¶
Builders¶
Accumulators are synchronous on both builders; only the executors differ
(fetch/first/count/stream — awaitable on AsyncQuery).
- class niwaki.query.Query(cls, session, *, scope_dn=None)[source]¶
Bases:
_QueryBase[_T]Fluent ACI query builder — synchronous variant.
Build queries by chaining scope, filter, and enrichment methods, then execute with
fetch(),first(),count(), orstream().Every accumulator method returns a new
Queryinstance so partial queries can be safely stored and reused.Created by
query()andquery(), or by jargon navigation without a name argument (e.g.aci.root.tenant("prod").bd()).- Parameters:
cls (type[_T] | str) – ACI class type or plain string class name.
session (ApicSession) – Authenticated
ApicSession.scope_dn (str | None) – Optional DN to scope the query.
Example:
with Niwaki("https://apic.example.com", "admin", "secret") as aci: # All BDs in tenant "prod" bds = aci.root.tenant("prod").bd().fetch() # First BD matching a name pattern bd = aci.query(fvBD).where(name="web").first() # Count BDs with flood enabled n = aci.query(fvBD).where(arpFlood=True).count() # Stream a very large result set for bd in aci.query(fvBD).stream(): process(bd)
- fetch()[source]¶
Execute the query and return all matching objects.
Transparently paginates through all APIC pages. For very large result sets (tens of thousands of objects) consider
stream()to process objects page-by-page without holding everything in memory at once. When the query is limited by a[:n]slice, only that many objects are fetched.- Returns:
List of typed
ManagedObjectinstances. Empty list when no objects match.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient APIC privileges.
ServerError – APIC server-side error.
ConnectionError – Network error after all retry attempts.
- Return type:
list[_T]
Example:
bds = aci.root.tenant("prod").bd().fetch()
- first()[source]¶
Execute the query and return the first matching object, or
None.More efficient than
fetch()[0]— internally requests only a single object (page=0&page-size=1).- Returns:
First matching instance, or
Nonewhen the result set is empty.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
_T | None
Example:
bd = aci.root.tenant("prod").bd().where(name="web").first() if bd is None: print("not found")
- one()[source]¶
Execute the query and return the single matching object.
For queries that must resolve to exactly one object. Fetches at most two objects (
page=0&page-size=2) so it can tell “none”, “one” and “more than one” apart in a single request.- Returns:
The one matching instance.
- Raises:
NoResultError – No object matched — use
first()when no match is acceptable.MultipleResultsError – More than one object matched — narrow the query or use
first()/fetch().
- Return type:
_T
Example:
bd = aci.query(fvBD).where(name="web").one()
- exists()[source]¶
Return whether any object matches — a single lightweight request.
- Returns:
Truewhen at least one object matches,Falseotherwise.- Return type:
Example:
if aci.query(fvBD).where(name="web").exists(): ...
- count()[source]¶
Return the count of matching objects without fetching them.
Issues a single one-object page and reads the APIC
totalCount— this composes with any query and works on every APIC version (6.0 rejects thecount-onlyargument).- Returns:
Integer count of objects matching the current query.
- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
Example:
n = aci.query(fvBD).under("uni/tn-prod").count() print(f"{n} BDs in tenant prod")
- stream()[source]¶
Yield objects one page at a time — O(page_size) memory footprint.
Preferred over
fetch()for large result sets where loading everything into a list would consume excessive memory.- Yields:
Typed
ManagedObjectinstances in APIC-returned order.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
Example:
for bd in aci.query(fvBD).stream(): process(bd)
- subscribe(*, refresh_timeout=None)[source]¶
Subscribe to push notifications for this query.
A subscription is a query plus
subscription=yesat the wire level, so every accumulator that maps onto the same GET mechanism —where(),under(),self_only()/children(),naming_only()/config_only()— carries over unchanged. State with no meaning on an open-ended push stream (order_by(), a slice limit, subtree enrichment,also()) is rejected up front, before any network I/O — see_reject_unstreamable()for exactly why each one is rejected.page_size()is silently ignored (it never reaches a request parameter here — the subscribe response is a single page).The APIC multiplexes every subscription for a session over one WebSocket, opened lazily on the first call to this method on the session; refresh and reconnect-and-resubscribe run automatically in the background, so nothing here needs a caller-driven loop.
- Parameters:
refresh_timeout (int | None) – Override the APIC’s default 60 s subscription timeout. The subscription refreshes itself automatically on a schedule derived from this value regardless.
- Returns:
A
Subscription—.initialfor the synchronous snapshot, then iterate for live push events.- Raises:
ValueError – The query carries state with no meaning on a live stream (
order_by(), a slice limit, subtree enrichment,also()).StatsClassNotSubscribableError – The queried class is a statistics class — the APIC never pushes for it.
SubscribeRejectedError – The APIC rejected the subscribe request.
- Return type:
Subscription[_T]
Example:
with aci.query(fvBD).under("uni/tn-prod").subscribe() as sub: for event in sub: print(event.kind, event.dn)
- execute_raw(path, params)[source]¶
Run raw APIC query params through the typed, paginated pipeline.
The escape hatch for anything
build()cannot express yet: pass an APIC path and parameter dict (often derived frombuild()and then tweaked) and get back typed, fully-paginated objects — unlike the transport’s rawgethelper, which returns a single unparsed page.- Parameters:
- Returns:
All matching objects across every page, typed via
REGISTRY.- Return type:
Example:
path, params = aci.query(fvBD).build() params["rsp-subtree-include"] = "count" objs = aci.query(fvBD).execute_raw(path, params)
- also(*classes)¶
Return additional ACI classes alongside the queried one (scoped only).
Adds to
target-subtree-classso a DN-scoped subtree/children query returns several types at once (e.g. BDs and their subnets) in a single request. Results are polymorphic — each object deserialises to its own type. Only affects a scoped query (under()); it is ignored on a global class query, which addresses a single class by URL.- Parameters:
*classes (type[ManagedObject] | str) – Extra ACI class type(s) or string class name(s) to return.
- Returns:
New query with the extra target classes registered.
- Return type:
Example:
# Every BD and every subnet under the tenant, in one request objs = aci.query(fvBD).under("uni/tn-prod").also(fvSubnet).fetch()
- build()¶
Translate accumulated builder state into an APIC path and param dict.
Returns the APIC-relative path and a flat query-string dict that together represent this query. Useful for inspection, debugging, and testing without executing an HTTP request.
- Returns:
(path, params)tuple where path is relative to the APIC base URL (e.g."/api/class/fvBD.json") and params maps APIC parameter names to string values.- Return type:
Example:
path, params = aci.query(fvBD).where(arpFlood=True).build() # path → "/api/class/fvBD.json" # params → {"query-target-filter": 'eq(fvBD.arpFlood,"yes")'}
- children()¶
Limit to direct children of the scope DN (one level deep).
Sets
query-target=children. Only meaningful when a scope DN is set viaunder()or jargon navigation.- Returns:
New query with
query-target=children.- Return type:
- config_only()¶
Return only configurable properties, omitting read-only APIC metadata.
Sets
rsp-prop-include=config-only. Reduces payload size for write-oriented inventory queries.- Returns:
New query with
rsp-prop-include=config-only.- Return type:
- include(*classes)¶
Include children of the given ACI class(es) in each response object.
Sets
rsp-subtree=childrenandrsp-subtree-classon the APIC request. The matching child objects are accessible via the.childrenattribute of each returned instance.- Parameters:
*classes (type[ManagedObject] | str) – ACI class type(s) or string class name(s) to include.
- Returns:
New query with child inclusion set.
- Return type:
Example:
# Fetch BDs with their subnets embedded bds = aci.query(fvBD).include(fvSubnet).fetch() for bd in bds: for subnet in bd.children: print(subnet.model_extra.get("ip"))
- include_subtree(*kinds)¶
Embed one or more response-subtree facets (
rsp-subtree-include).The typed, exhaustive entry point: every facet the APIC offers is a member of
SubtreeInclude(faults, health, stats, relations, count, the audit/event/fault/health record streams, tasks, …). Thewith_*methods are ergonomic shortcuts for the common ones.- Parameters:
*kinds (SubtreeInclude) – One or more
SubtreeIncludefacets.- Returns:
New query with the facets added.
- Return type:
Example:
from niwaki.query import SubtreeInclude aci.query(fvBD).include_subtree( SubtreeInclude.FAULT_RECORDS, SubtreeInclude.AUDIT_LOGS ).fetch()
- naming_only()¶
Return only naming properties (name/DN).
Sets
rsp-prop-include=naming-only. Ideal for large-scale inventory queries where only identifiers are needed and payload size matters.- Returns:
New query with
rsp-prop-include=naming-only.- Return type:
- only_faulted()¶
Restrict results to objects that carry the embedded subtree items.
Adds the
requiredmodifier torsp-subtree-include: only top-level objects that actually have the requested facet — typically the faults embedded bywith_faults()— are returned.- Returns:
New query with
requiredset.- Return type:
- order_by(prop, *, desc=False)¶
Sort results by a property (chain for multi-key ordering).
The property name is auto-prefixed with the ACI class name when it does not already contain a dot. Calling
order_by()more than once appends additional sort keys, applied left to right.- Parameters:
- Returns:
New query with the ordering key appended.
- Return type:
Example:
aci.query(fvBD).order_by("name").fetch() aci.query(fvBD).order_by("name", desc=True).fetch() # Multi-key: most severe first, then by code aci.query("faultInst").order_by("severity", desc=True).order_by("code").fetch()
- page_size(n)¶
Override the page size for auto-pagination.
The default page size is 500 objects per page. Decrease for slow connections or very large objects; increase if the APIC supports it.
- Parameters:
n (int) – Objects per page. Must be greater than zero.
- Returns:
New query with the page size set.
- Raises:
ValueError – n is zero or negative.
- Return type:
- self_only()¶
Return only the scoped object itself (
query-target=self).Only meaningful with a scope DN (
under()or jargon navigation): the MO at that DN is returned with no descendants. A no-op on a global class query, which already addresses a single class.- Returns:
New query with
query-target=self.- Return type:
- subtree()¶
Include all descendants of the scope DN (unlimited depth).
Sets
query-target=subtree. This is the default when a scope DN is set — call this explicitly only after a precedingchildren()call.- Returns:
New query with
query-target=subtree.- Return type:
- subtree_full()¶
Embed the entire subtree of each object (
rsp-subtree=full).Unlike
include(), which embeds only the named direct children, this returns every descendant at unlimited depth, reachable through the.childrentree of each result.- Returns:
New query with
rsp-subtree=full.- Return type:
- subtree_where(*exprs, **kwargs)¶
Filter the included subtree children by an attribute expression.
Sets
rsp-subtree-filteron the APIC request. This is a response-level filter that restricts which child objects are embedded in the response — unlikewhere()which filters the top-level objects. Requiresinclude()to be called first (orwith_faults()/with_health()) so thatrsp-subtreeis not"no".Accepts the same expression DSL as
where(): explicitFilterExprobjects and keyword shortcutsprop=valueauto-prefixed with the queried class name.- Parameters:
*exprs (FilterExpr) – One or more
FilterExprobjects.**kwargs (FilterValue) – Equality shortcuts applied to the query’s class, e.g.
ip="10.0.0.1/24"becomeseq(fvSubnet.ip,"10.0.0.1/24").
- Returns:
New query with
rsp-subtree-filterset.- Raises:
ValueError – Called with no arguments.
- Return type:
Example:
# Fetch BDs, but embed only subnets in the 10.0.0.0/8 scope from niwaki.query import wcard bds = ( aci.query(fvBD) .include(fvSubnet) .subtree_where(wcard("fvSubnet.ip", "10.*")) .fetch() ) # Keyword form bds = aci.query(fvBD).include(fvSubnet).subtree_where(ip="10.0.0.1/24").fetch()
- under(dn)¶
Scope the query to the subtree rooted at dn.
Converts a global class query into a DN-scoped subtree query. When called on an already-scoped query the scope DN is replaced.
- Parameters:
dn (str) – APIC Distinguished Name (e.g.
"uni/tn-prod").- Returns:
New query with
scope_dnset.- Return type:
Example:
# Global → scoped aci.query(fvBD).under("uni/tn-prod").fetch() # GET /api/mo/uni/tn-prod.json?query-target=subtree&target-subtree-class=fvBD
- where(*exprs, **kwargs)¶
Add a filter to the query.
Accepts explicit
FilterExprobjects (built witheq(),wcard(), …) and a keyword-argument shorthand where eachprop=valuepair becomes an equality check auto-prefixed with the queried class name.Multiple arguments — whether positional or keyword — are combined with a logical AND. Calling
.where()multiple times chains the filters with AND.The keyword form is the quickest path for simple equality checks: no import of operator functions required, and no class name to remember — the property is qualified with the queried class automatically. Explicit
FilterExprobjects are passed through verbatim: qualify their properties ("fvBD.name") or build them withcls_name=.- Parameters:
*exprs (FilterExpr) – One or more
FilterExprobjects, with class-qualified property names.**kwargs (FilterValue) – Equality shortcuts —
name="web"becomeseq(ClassName.name,"web")automatically.
- Returns:
New query with the filter applied.
- Return type:
Example:
from niwaki.query import wcard, and_ # Keyword shorthand — simplest, auto-qualified aci.root.tenant("prod").bd().where(name="web").fetch() # Explicit expression — qualified property aci.query(fvBD).where(wcard("fvBD.name", "prod-*")).fetch() # Chained (implicit AND) aci.query(fvBD).where(wcard("fvBD.name", "prod-*")).where(arpFlood=True).fetch() # Combined in one call aci.query(fvBD).where( wcard("fvBD.name", "prod-*"), arpFlood=True, ).fetch()
- with_faults()¶
Embed fault objects in the subtree response (
rsp-subtree-include=faults).By default this embeds faults on every returned object, faulted or not. Chain
only_faulted()to restrict the result to objects that actually carry a fault.- Returns:
New query with faults embedded.
- Return type:
Example:
# Every BD, faults embedded where present aci.root.tenant("prod").bd().with_faults().fetch() # Only the faulted BDs aci.root.tenant("prod").bd().with_faults().only_faulted().fetch()
- with_health()¶
Embed health score data in the subtree response (
rsp-subtree-include=health).- Returns:
New query with health data embedded.
- Return type:
- with_relations()¶
Embed relation objects (Rs/Rt) —
rsp-subtree-include=relations.- Returns:
New query with relations embedded.
- Return type:
- class niwaki.query.AsyncQuery(cls, session, *, scope_dn=None)[source]¶
Bases:
_QueryBase[_T]Fluent ACI query builder — asynchronous variant.
Mirrors
Querywithasync defexecution methods. Every accumulator method is synchronous and returns a newAsyncQuery(same immutable-builder pattern as the sync variant).Created by
query()andquery(), or by jargon navigation without a name argument on anAsyncNiwakiNode.- Parameters:
cls (type[_T] | str) – ACI class type or plain string class name.
session (AsyncApicSession) – Authenticated
AsyncApicSession.scope_dn (str | None) – Optional DN to scope the query.
Example:
async with AsyncNiwaki("https://apic.example.com", "admin", "secret") as aci: # All BDs in tenant "prod" bds = await aci.root.tenant("prod").bd().fetch() # First matching bd = await aci.query(fvBD).where(name="web").first() # Count n = await aci.query(fvBD).under("uni/tn-prod").count() # Concurrent reads tenants, bds = await aci.gather( aci.query(fvTenant).fetch(), aci.root.tenant("prod").bd().fetch(), )
- async fetch()[source]¶
Execute the query and return all matching objects.
Transparently paginates through all APIC pages. For very large result sets consider
stream()to process objects incrementally.- Returns:
List of typed
ManagedObjectinstances. Empty list when no objects match.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient APIC privileges.
ServerError – APIC server-side error.
ConnectionError – Network error after all retry attempts.
- Return type:
list[_T]
Example:
bds = await aci.root.tenant("prod").bd().fetch()
- async first()[source]¶
Execute the query and return the first matching object, or
None.Requests only a single object (
page=0&page-size=1) — more efficient than(await fetch())[0]for large result sets.- Returns:
First matching instance, or
Nonewhen the result set is empty.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
_T | None
Example:
bd = await aci.root.tenant("prod").bd().where(name="web").first()
- async one()[source]¶
Execute the query and return the single matching object.
For queries that must resolve to exactly one object. Fetches at most two objects (
page=0&page-size=2) so it can tell “none”, “one” and “more than one” apart in a single request.- Returns:
The one matching instance.
- Raises:
NoResultError – No object matched — use
first()when no match is acceptable.MultipleResultsError – More than one object matched — narrow the query or use
first()/fetch().
- Return type:
_T
Example:
bd = await aci.query(fvBD).where(name="web").one()
- async exists()[source]¶
Return whether any object matches — a single lightweight request.
- Returns:
Truewhen at least one object matches,Falseotherwise.- Return type:
Example:
if await aci.query(fvBD).where(name="web").exists(): ...
- async count()[source]¶
Return the count of matching objects without fetching them.
Issues a single one-object page and reads the APIC
totalCount.- Returns:
Integer count of objects matching the current query.
- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
- Return type:
Example:
n = await aci.query(fvBD).under("uni/tn-prod").count()
- async stream()[source]¶
Yield objects one page at a time — O(page_size) memory footprint.
Preferred over
fetch()for large result sets. Eachyieldreturns one page of parsed objects.- Yields:
Typed
ManagedObjectinstances in APIC-returned order.- Raises:
AuthError – Session not authenticated.
ForbiddenError – Insufficient privileges.
ServerError – APIC server-side error.
Example:
async for bd in aci.query(fvBD).stream(): await process(bd)
- async subscribe(*, refresh_timeout=None)[source]¶
Subscribe to push notifications for this query.
Mirrors
subscribe()— a subscription is a query plussubscription=yesat the wire level, so every accumulator that maps onto the same GET mechanism carries over unchanged, and state with no meaning on an open-ended push stream is rejected up front, before any network I/O — see_reject_unstreamable(). Because that rejection is synchronous, it surfaces the moment this coroutine is awaited, still before any network call.The APIC multiplexes every subscription for a session over one WebSocket, opened lazily on the first call to this method on the session; refresh and reconnect-and-resubscribe run automatically in the background, so nothing here needs a caller-driven loop.
- Parameters:
refresh_timeout (int | None) – Override the APIC’s default 60 s subscription timeout. The subscription refreshes itself automatically on a schedule derived from this value regardless.
- Returns:
An
AsyncSubscription—.initialfor the synchronous snapshot, then async-iterate for live push events.- Raises:
ValueError – The query carries state with no meaning on a live stream (
order_by(), a slice limit, subtree enrichment,also()).StatsClassNotSubscribableError – The queried class is a statistics class — the APIC never pushes for it.
SubscribeRejectedError – The APIC rejected the subscribe request.
- Return type:
Example:
async with aci.query(fvBD).under("uni/tn-prod").subscribe() as sub: async for event in sub: print(event.kind, event.dn)
- async execute_raw(path, params)[source]¶
Run raw APIC query params through the typed, paginated pipeline.
The escape hatch for anything
build()cannot express yet: pass an APIC path and parameter dict (often derived frombuild()and then tweaked) and get back typed, fully-paginated objects — unlike the transport’s rawgethelper, which returns a single unparsed page.
- also(*classes)¶
Return additional ACI classes alongside the queried one (scoped only).
Adds to
target-subtree-classso a DN-scoped subtree/children query returns several types at once (e.g. BDs and their subnets) in a single request. Results are polymorphic — each object deserialises to its own type. Only affects a scoped query (under()); it is ignored on a global class query, which addresses a single class by URL.- Parameters:
*classes (type[ManagedObject] | str) – Extra ACI class type(s) or string class name(s) to return.
- Returns:
New query with the extra target classes registered.
- Return type:
Example:
# Every BD and every subnet under the tenant, in one request objs = aci.query(fvBD).under("uni/tn-prod").also(fvSubnet).fetch()
- build()¶
Translate accumulated builder state into an APIC path and param dict.
Returns the APIC-relative path and a flat query-string dict that together represent this query. Useful for inspection, debugging, and testing without executing an HTTP request.
- Returns:
(path, params)tuple where path is relative to the APIC base URL (e.g."/api/class/fvBD.json") and params maps APIC parameter names to string values.- Return type:
Example:
path, params = aci.query(fvBD).where(arpFlood=True).build() # path → "/api/class/fvBD.json" # params → {"query-target-filter": 'eq(fvBD.arpFlood,"yes")'}
- children()¶
Limit to direct children of the scope DN (one level deep).
Sets
query-target=children. Only meaningful when a scope DN is set viaunder()or jargon navigation.- Returns:
New query with
query-target=children.- Return type:
- config_only()¶
Return only configurable properties, omitting read-only APIC metadata.
Sets
rsp-prop-include=config-only. Reduces payload size for write-oriented inventory queries.- Returns:
New query with
rsp-prop-include=config-only.- Return type:
- include(*classes)¶
Include children of the given ACI class(es) in each response object.
Sets
rsp-subtree=childrenandrsp-subtree-classon the APIC request. The matching child objects are accessible via the.childrenattribute of each returned instance.- Parameters:
*classes (type[ManagedObject] | str) – ACI class type(s) or string class name(s) to include.
- Returns:
New query with child inclusion set.
- Return type:
Example:
# Fetch BDs with their subnets embedded bds = aci.query(fvBD).include(fvSubnet).fetch() for bd in bds: for subnet in bd.children: print(subnet.model_extra.get("ip"))
- include_subtree(*kinds)¶
Embed one or more response-subtree facets (
rsp-subtree-include).The typed, exhaustive entry point: every facet the APIC offers is a member of
SubtreeInclude(faults, health, stats, relations, count, the audit/event/fault/health record streams, tasks, …). Thewith_*methods are ergonomic shortcuts for the common ones.- Parameters:
*kinds (SubtreeInclude) – One or more
SubtreeIncludefacets.- Returns:
New query with the facets added.
- Return type:
Example:
from niwaki.query import SubtreeInclude aci.query(fvBD).include_subtree( SubtreeInclude.FAULT_RECORDS, SubtreeInclude.AUDIT_LOGS ).fetch()
- naming_only()¶
Return only naming properties (name/DN).
Sets
rsp-prop-include=naming-only. Ideal for large-scale inventory queries where only identifiers are needed and payload size matters.- Returns:
New query with
rsp-prop-include=naming-only.- Return type:
- only_faulted()¶
Restrict results to objects that carry the embedded subtree items.
Adds the
requiredmodifier torsp-subtree-include: only top-level objects that actually have the requested facet — typically the faults embedded bywith_faults()— are returned.- Returns:
New query with
requiredset.- Return type:
- order_by(prop, *, desc=False)¶
Sort results by a property (chain for multi-key ordering).
The property name is auto-prefixed with the ACI class name when it does not already contain a dot. Calling
order_by()more than once appends additional sort keys, applied left to right.- Parameters:
- Returns:
New query with the ordering key appended.
- Return type:
Example:
aci.query(fvBD).order_by("name").fetch() aci.query(fvBD).order_by("name", desc=True).fetch() # Multi-key: most severe first, then by code aci.query("faultInst").order_by("severity", desc=True).order_by("code").fetch()
- page_size(n)¶
Override the page size for auto-pagination.
The default page size is 500 objects per page. Decrease for slow connections or very large objects; increase if the APIC supports it.
- Parameters:
n (int) – Objects per page. Must be greater than zero.
- Returns:
New query with the page size set.
- Raises:
ValueError – n is zero or negative.
- Return type:
- self_only()¶
Return only the scoped object itself (
query-target=self).Only meaningful with a scope DN (
under()or jargon navigation): the MO at that DN is returned with no descendants. A no-op on a global class query, which already addresses a single class.- Returns:
New query with
query-target=self.- Return type:
- subtree()¶
Include all descendants of the scope DN (unlimited depth).
Sets
query-target=subtree. This is the default when a scope DN is set — call this explicitly only after a precedingchildren()call.- Returns:
New query with
query-target=subtree.- Return type:
- subtree_full()¶
Embed the entire subtree of each object (
rsp-subtree=full).Unlike
include(), which embeds only the named direct children, this returns every descendant at unlimited depth, reachable through the.childrentree of each result.- Returns:
New query with
rsp-subtree=full.- Return type:
- subtree_where(*exprs, **kwargs)¶
Filter the included subtree children by an attribute expression.
Sets
rsp-subtree-filteron the APIC request. This is a response-level filter that restricts which child objects are embedded in the response — unlikewhere()which filters the top-level objects. Requiresinclude()to be called first (orwith_faults()/with_health()) so thatrsp-subtreeis not"no".Accepts the same expression DSL as
where(): explicitFilterExprobjects and keyword shortcutsprop=valueauto-prefixed with the queried class name.- Parameters:
*exprs (FilterExpr) – One or more
FilterExprobjects.**kwargs (FilterValue) – Equality shortcuts applied to the query’s class, e.g.
ip="10.0.0.1/24"becomeseq(fvSubnet.ip,"10.0.0.1/24").
- Returns:
New query with
rsp-subtree-filterset.- Raises:
ValueError – Called with no arguments.
- Return type:
Example:
# Fetch BDs, but embed only subnets in the 10.0.0.0/8 scope from niwaki.query import wcard bds = ( aci.query(fvBD) .include(fvSubnet) .subtree_where(wcard("fvSubnet.ip", "10.*")) .fetch() ) # Keyword form bds = aci.query(fvBD).include(fvSubnet).subtree_where(ip="10.0.0.1/24").fetch()
- under(dn)¶
Scope the query to the subtree rooted at dn.
Converts a global class query into a DN-scoped subtree query. When called on an already-scoped query the scope DN is replaced.
- Parameters:
dn (str) – APIC Distinguished Name (e.g.
"uni/tn-prod").- Returns:
New query with
scope_dnset.- Return type:
Example:
# Global → scoped aci.query(fvBD).under("uni/tn-prod").fetch() # GET /api/mo/uni/tn-prod.json?query-target=subtree&target-subtree-class=fvBD
- where(*exprs, **kwargs)¶
Add a filter to the query.
Accepts explicit
FilterExprobjects (built witheq(),wcard(), …) and a keyword-argument shorthand where eachprop=valuepair becomes an equality check auto-prefixed with the queried class name.Multiple arguments — whether positional or keyword — are combined with a logical AND. Calling
.where()multiple times chains the filters with AND.The keyword form is the quickest path for simple equality checks: no import of operator functions required, and no class name to remember — the property is qualified with the queried class automatically. Explicit
FilterExprobjects are passed through verbatim: qualify their properties ("fvBD.name") or build them withcls_name=.- Parameters:
*exprs (FilterExpr) – One or more
FilterExprobjects, with class-qualified property names.**kwargs (FilterValue) – Equality shortcuts —
name="web"becomeseq(ClassName.name,"web")automatically.
- Returns:
New query with the filter applied.
- Return type:
Example:
from niwaki.query import wcard, and_ # Keyword shorthand — simplest, auto-qualified aci.root.tenant("prod").bd().where(name="web").fetch() # Explicit expression — qualified property aci.query(fvBD).where(wcard("fvBD.name", "prod-*")).fetch() # Chained (implicit AND) aci.query(fvBD).where(wcard("fvBD.name", "prod-*")).where(arpFlood=True).fetch() # Combined in one call aci.query(fvBD).where( wcard("fvBD.name", "prod-*"), arpFlood=True, ).fetch()
- with_faults()¶
Embed fault objects in the subtree response (
rsp-subtree-include=faults).By default this embeds faults on every returned object, faulted or not. Chain
only_faulted()to restrict the result to objects that actually carry a fault.- Returns:
New query with faults embedded.
- Return type:
Example:
# Every BD, faults embedded where present aci.root.tenant("prod").bd().with_faults().fetch() # Only the faulted BDs aci.root.tenant("prod").bd().with_faults().only_faulted().fetch()
- with_health()¶
Embed health score data in the subtree response (
rsp-subtree-include=health).- Returns:
New query with health data embedded.
- Return type:
- with_relations()¶
Embed relation objects (Rs/Rt) —
rsp-subtree-include=relations.- Returns:
New query with relations embedded.
- Return type:
Subscribing (push)¶
Query.subscribe()/AsyncQuery.subscribe() open a live push stream instead
of a one-off read — see Subscribing to live changes.
- class niwaki.query.Subscription(raw)[source]¶
Bases:
GenericA live object-subscription — iterate it for typed push events.
Returned by
subscribe(). Refresh and reconnect-and-resubscribe are handled automatically in the background — nothing here needs a caller-driven loop beyond iterating the stream.A subscription is both an iterator and a context manager:
with aci.query(fvBD).under("uni/tn-prod").subscribe() as sub: for bd in sub.initial: print("already there:", bd.dn) for event in sub: print(event.kind, event.dn, event.mo.model_fields_set if event.mo else None)
- initial¶
The subscribe response’s own synchronous snapshot — a single, un-paginated page (not the exhaustive read
fetch()would give you), typed via the normalfrom_apic()path since it is a real read, not a push event.
- property subscription_id: str¶
The current wire
subscriptionId— for observability/debugging only.This changes across a reconnect (delivered to the stream as a
GAPevent); it is not a stable identity to key application state on.
- property info: SubscriptionInfo¶
Current diagnostic snapshot of this subscription.
See
subscribe()’s session-level counterpart for listing every subscription at once; this is the single-item equivalent.- Raises:
SubscriptionError – This subscription has already been closed.
- refresh_now()[source]¶
Force an immediate refresh of this subscription, outside its schedule.
A manual refresh never triggers the automatic recovery escalation on failure — only the scheduled background refresh does — but a success resets the failure counter exactly like a scheduled refresh succeeding would.
- Returns:
The updated
SubscriptionInfo.- Raises:
SubscriptionError – This subscription has already been closed.
- Return type:
- class niwaki.query.AsyncSubscription(raw)[source]¶
Bases:
GenericA live object-subscription — async-iterate it for typed push events.
Returned by
subscribe(). Refresh and reconnect-and-resubscribe are handled automatically in the background — nothing here needs a caller-driven loop beyond iterating the stream.A subscription is both an async iterator and an async context manager:
async with aci.query(fvBD).under("uni/tn-prod").subscribe() as sub: for bd in sub.initial: print("already there:", bd.dn) async for event in sub: print(event.kind, event.dn, event.mo.model_fields_set if event.mo else None)
- initial¶
The subscribe response’s own synchronous snapshot — a single, un-paginated page (not the exhaustive read
fetch()would give you), typed via the normalfrom_apic()path since it is a real read, not a push event.
- property subscription_id: str¶
The current wire
subscriptionId— for observability/debugging only.This changes across a reconnect (delivered to the stream as a
GAPevent); it is not a stable identity to key application state on.
- property info: SubscriptionInfo¶
Current diagnostic snapshot of this subscription.
See
info()(sync twin).- Raises:
SubscriptionError – This subscription has already been closed.
- async refresh_now()[source]¶
Force an immediate refresh of this subscription, outside its schedule.
See
refresh_now()(sync twin).- Returns:
The updated
SubscriptionInfo.- Raises:
SubscriptionError – This subscription has already been closed.
- Return type:
- class niwaki.query.SubscriptionEvent(kind, mo, subscription_ids, raw)[source]¶
Bases:
GenericOne item from a
Subscription’s live stream.- mo¶
The typed object, deserialised via
from_event()— itsmodel_fields_setreports exactly the fields this event’s payload carried (empty for aDELETEDevent, the full set for aCREATEDone).NoneforGAP/REFRESH_FAILED, which describe the subscription itself, not a watched object.- Type:
T | None
- subscription_ids¶
Every wire
subscriptionIdthis push satisfied. Empty forGAP/REFRESH_FAILED, which are per-subscription already (delivered only into the affected subscription’s stream).
- raw¶
The transport-layer item this event was built from — a
SubscriptionGaporSubscriptionRefreshFailedfor those two kinds, holding their own detail (old/new subscription id, timestamps); the originatingRawSubscriptionEventotherwise.- Type:
RawPushItem
- class niwaki.query.EventKind(*values)[source]¶
Bases:
StrEnumWhat a
SubscriptionEventrepresents.CREATED/MODIFIED/DELETEDvalues equal the wirestatusstring a push carries, soEventKind(raw_status)maps directly.GAP/REFRESH_FAILEDhave no wire equivalent — they are synthesised by the transport layer for conditions of the subscription itself, not the watched object.
- class niwaki.query.SubscriptionInfo(
- local_id,
- subscription_id,
- path,
- params,
- refresh_timeout,
- consecutive_refresh_failures,
- seconds_until_refresh,
Bases:
objectA point-in-time diagnostic snapshot of one tracked subscription.
Returned by
list_subscriptions(),refresh_all_subscriptions(), andinfo— purely informational, not something to act on directly. Userefresh_all_subscriptions(),close_all_subscriptions(), orrefresh_now()for that.- local_id¶
The stable local identity — survives a reconnect, unlike
subscription_id. What lets a caller correlate two snapshots taken at different times.- Type:
- subscription_id¶
The current wire
subscriptionId— debug/ observability only, changes across a reconnect or an escalation recovery.- Type:
- consecutive_refresh_failures¶
How many scheduled refreshes have failed in a row, right now. 0 means healthy.
- Type:
- seconds_until_refresh¶
Time remaining until the next scheduled refresh sweep considers this subscription due.
- Type:
Response-subtree facets¶
include_subtree embeds one or more of these facets into the response
(rsp-subtree-include); with_faults, with_health and with_stats are
shortcuts for the common ones.
Filter expressions¶
Filter expressions compose with the &, | and ~ operators — the
methods below are what those operators call.
- class niwaki.query.FilterExpr(node)[source]¶
Bases:
objectAn APIC filter expression backed by a small render-once AST.
Created by the operator functions (
eq(),ne(),wcard(), …). Combines with&(AND),|(OR), and~(NOT) for ergonomic composition.str(expr)renders the wire filter string.A raw string may still be passed for backward compatibility and as a verbatim escape hatch (identical to
raw()) — it is rendered exactly as given, with no further escaping.- Parameters:
node (_FilterNode | str) – An internal filter AST node, or a raw APIC filter string (e.g.
'eq(fvBD.name,"web")') taken verbatim.
Example:
from niwaki.query import eq, wcard expr = eq("fvBD.name", "web") ~expr # → FilterExpr('not(eq(fvBD.name,"web"))') expr & wcard("fvBD.name", "prod-*") # → FilterExpr('and(...)')
Filter functions¶
- niwaki.query.eq(prop, value, *, cls_name='')[source]¶
Equal:
eq(cls.prop,"value").- Parameters:
prop (str) – Property name. Qualify it yourself (
"fvBD.name") or provide cls_name — an unqualified property is not a valid APIC filter.value (Any) – Comparison value.
boolvalues are coerced to APIC"yes"/"no"; all others via the wire boundary.cls_name (str) – ACI class name prepended to prop when it is not already qualified.
- Returns:
FilterExprrepresenting the equality check.- Return type:
Example:
eq("name", "web", cls_name="fvBD") # → FilterExpr('eq(fvBD.name,"web")')
- niwaki.query.ne(prop, value, *, cls_name='')[source]¶
Not equal:
ne(cls.prop,"value").- Parameters:
- Returns:
- Return type:
- niwaki.query.gt(prop, value, *, cls_name='')[source]¶
Greater than:
gt(cls.prop,"value").- Parameters:
- Returns:
- Return type:
- niwaki.query.ge(prop, value, *, cls_name='')[source]¶
Greater or equal:
ge(cls.prop,"value").- Parameters:
- Returns:
- Return type:
- niwaki.query.lt(prop, value, *, cls_name='')[source]¶
Less than:
lt(cls.prop,"value").- Parameters:
- Returns:
- Return type:
- niwaki.query.le(prop, value, *, cls_name='')[source]¶
Less or equal:
le(cls.prop,"value").- Parameters:
- Returns:
- Return type:
- niwaki.query.bw(prop, start, end, *, cls_name='')[source]¶
Between (inclusive):
bw(cls.prop,"start","end").- Parameters:
- Returns:
- Return type:
- niwaki.query.wcard(prop, pattern, *, cls_name='')[source]¶
Wildcard match:
wcard(cls.prop,"prod-*").The APIC supports
*as a glob wildcard, but it validates the pattern against the property’s own format, which has two consequences (both verified live on 6.0(9c)): a leading*is rejected on most properties (wcard(name,"prod*")works,wcard(name,"*prod*")→ HTTP 301), so prefer a trailing wildcard; and strictly-formatted properties (ip,dn,descr) reject any wildcard value. The pattern is taken as-is — only a literal"is escaped — so wildcards are preserved.- Parameters:
- Returns:
- Return type:
Example:
wcard("name", "prod-*", cls_name="fvBD") # → FilterExpr('wcard(fvBD.name,"prod-*")')
- niwaki.query.and_(*exprs)[source]¶
Logical AND of two or more expressions:
and(e1,e2,...).- Parameters:
*exprs (FilterExpr) – Two or more
FilterExprobjects to combine.- Returns:
FilterExprrepresenting the conjunction.- Raises:
ValueError – Fewer than two expressions provided.
- Return type:
Example:
and_(eq("name", "web"), eq("arpFlood", True)) # → FilterExpr('and(eq(name,"web"),eq(arpFlood,"yes"))')
- niwaki.query.or_(*exprs)[source]¶
Logical OR of two or more expressions:
or(e1,e2,...).- Parameters:
*exprs (FilterExpr) – Two or more
FilterExprobjects to combine.- Returns:
FilterExprrepresenting the disjunction.- Raises:
ValueError – Fewer than two expressions provided.
- Return type:
Example:
or_(wcard("name", "prod-*"), wcard("name", "dev-*")) # → FilterExpr('or(wcard(name,"prod-*"),wcard(name,"dev-*"))')
- niwaki.query.not_(expr)[source]¶
Logical NOT:
not(expr).- Parameters:
expr (FilterExpr) –
FilterExprto negate.- Returns:
FilterExprrepresenting the negation.- Return type:
Example:
not_(eq("name", "infra")) # → FilterExpr('not(eq(name,"infra"))')
- niwaki.query.anybit(prop, flags, *, cls_name='')[source]¶
Any-bit bitmask test:
anybit(cls.prop,"syn,ack").True when at least one of the given flags is set in the property’s bitmask. This is the read counterpart of a
Flagsfield:eqmatches the whole mask exactly,anybitmatches on a single bit regardless of the others.- Parameters:
- Returns:
- Return type:
Example:
anybit("vzEntry.tcpRules", {"syn", "ack"}) # → FilterExpr('anybit(vzEntry.tcpRules,"ack,syn")')
- niwaki.query.allbit(prop, flags, *, cls_name='')[source]¶
All-bit bitmask test — every given flag must be set.
The APIC 6.0 filter grammar has no
allbitoperator (verified live:allbit(...)→ HTTP 301 “no such filter type”), so this compiles to a conjunction ofanybit()tests —and(anybit(prop,a),anybit(prop,b))— which is exactly “bit a set AND bit b set”.- Parameters:
- Returns:
- Raises:
ValueError – No flags were given.
- Return type:
- niwaki.query.xor(*exprs)[source]¶
Logical XOR of two or more expressions:
xor(e1,e2,...).- Parameters:
*exprs (FilterExpr) – Two or more
FilterExprobjects to combine.- Returns:
FilterExprrepresenting the exclusive-or.- Raises:
ValueError – Fewer than two expressions provided.
- Return type:
- niwaki.query.raw(text)[source]¶
Wrap a verbatim APIC filter string — the universal escape hatch.
Every operator the SDK does not model (
true,false,pholder,passive, or any future addition) is reachable this way: the string is embedded exactly as given, with no coercion or escaping, and composes with&/|/~like any other expression.- Parameters:
text (str) – A raw APIC filter expression (e.g.
'true()').- Returns:
FilterExprwrapping text verbatim.- Return type:
Value wrappers¶
Smart values for where(...) — a list means membership, a * means wildcard, a
set stays bitmask equality; these wrappers make the intent explicit.
- niwaki.query.any_of(*values)[source]¶
Match a property against any of several values — a membership OR.
where(code=any_of("F0467", "F1394"))is the explicit form ofwhere(code=["F0467", "F1394"]); both compile toor(eq(...),eq(...))(the APIC filter grammar has noin).- Parameters:
*values (_FilterScalar) – One or more values to match.
- Returns:
A tag consumed by
where().- Raises:
ValueError – No values were given.
- Return type:
_AnyOf
- niwaki.query.like(pattern)[source]¶
Match a property against a wildcard pattern — the explicit form of a glob.
where(name=like("prod-*"))is the explicit form ofwhere(name="prod-*"); use it to force a wildcard where the value would otherwise read as a literal.
- niwaki.query.between(start, end)[source]¶
Match a property within an inclusive range —
bw(prop,"start","end").where(pri=between(1, 5))compiles tobw(cls.pri,"1","5").- Parameters:
start (_FilterScalar) – Lower bound (inclusive).
end (_FilterScalar) – Upper bound (inclusive).
- Returns:
A tag consumed by
where().- Return type:
_Range