Known Quirks¶
This page documents parser edge cases, depth limits, and other non-obvious behaviours in the Python port.
Pagination is transparent - no silent truncation¶
What it is: All show * commands that list objects (e.g., show zone, show network, show record) iterate an
ibx-nios-sdk resource .list(), which follows the WAPI _paging/next_page_id protocol automatically. Every page
is fetched and merged before output is displayed.
Why it matters: A single unpaged request which NIOS caps at 1000 objects by default. On grids with more than 1000
objects of a given type, the old behaviour would return exactly 1000 results with no warning. This silent truncation
could cause show zone on a large production grid to appear complete when it was not.
Current behaviour: No silent truncation. If a grid has 5000 networks, show network will issue as many paged
requests as needed and display all 5000. The only observable side effect is that large grids take longer to respond.
Use a specific filter to narrow results:
Affected commands: All list-mode show commands. Single-object lookups (e.g., show zone example.com) are unaffected - they never needed pagination.
Set-chain depth cap¶
What it is: The set key value pattern for extensible attributes is implemented via a static chain of
register() calls in each command module. The chain is registered to depth 16 - meaning up to 16 set key value
pairs per command.
Why: The COMMANDS dict is flat and keyed by the full match-line string. To support set key value at arbitrary
depth, the module registers depths 1..16 up front (each depth costs ~3 entries per endpoint). 16 was chosen as a
practical limit that covers any realistic EA load with negligible overhead (COMMANDS has ~2,300 entries total).
Effect: A command with more than 16 set key value pairs:
will fail at the 17th set with:
Workaround: Split across two commands:
configure zone add example.com set k1 v1 ... set k16 v16
configure zone example.com modify set k17 v17 ...
Or just bump the range(16) / range(1, 17) constants in src/ibcli/commands/*.py and re-run the tests.
Affected commands: configure zone add, configure zone modify, configure zone <z> add host, configure
network add, configure network modify, configure network container add/modify, configure template network add.
Tokenizer key=value splitting¶
What it is: The tokenizer (parser._tokenize) splits bare key=value tokens on the first =:
configure zone add example.com comment=hello
→ tokens: ["configure", "zone", "add", "example.com", "comment", "hello"]
Why: The Perl original used the same two-token convention, and the word list registers key=<special> pairs that expand into two-token contexts.
Effect 1 - Values containing =: A value like Owner=alice typed as set Owner=alice is split into set,
Owner, alice. This is correct. But a value that contains = in the value itself (e.g., a base64 string) must be
quoted:
Effect 2 - <name=value> SPECOPS never fires: The <name=value> SPECOPS token (regex ^\S+=\S+$) is in the
registry for completeness but effectively never matches in normal user input, because the tokenizer splits the token
before the parser sees it. Direct programmatic calls to expand_word with a key=value string will match
<name=value>.
IPv4 vs IPv6 routing¶
What it is: Several commands accept <n.n.n.n/mm> as a CIDR argument. The SPECOPS regex for this token accepts
both IPv4 CIDRs (10.0.0.0/8) and IPv6 CIDRs (2001:db8::/32). The address family is detected at the handler level
using cidr_family() from utils.py.
Effect: The tab-completion menu shows <n.n.n.n/mm> for both IPv4 and IPv6 inputs. There is no separate <ipv6/prefix> token - the single token covers both families.
IPv6 is routed to ipv6network, ipv6networkcontainer, etc. automatically. No user action is needed.
Reverse-zone CIDR auto-conversion¶
What it is: When a zone name looks like an IPv4 CIDR (e.g., 192.168.1.0/24), ibcli converts it to arpa form before sending to WAPI:
Where: In zone.py's cli_add_zone, cli_show_zone, cli_modify_zone, and cli_delete_zone, using net_to_arpa() from utils.py.
Why: WAPI expects arpa form; the Perl tool accepted CIDR form as a convenience.
Zone type fallback order¶
What it is: When show zone or configure zone <z> delete/modify is called without explicit type keywords, ibcli
searches zone types in the order: zone_auth → zone_forward → zone_delegated → zone_stub.
Effect: If a zone exists in two types with the same FQDN (unusual but possible), the zone_auth entry is always
found first. To target a specific type, use the appropriate keyword (forward, delegate_to=, etc.).
configure server always uses verify=False (fixed)¶
configure server always uses verify=FalseWhat it was: cli_add_server hardcoded verify=False, and the
-k/--insecure flag never reached it - so TLS verification was always
off regardless of the flag, and the flag itself was inert.
Now: -k sets ctx.verify, which cli_add_server passes to
NiosClient(verify=...). TLS is verified by default; -k opts out. The
same change wired up --wapi-version, which was also parsed and ignored.
Pre-provisioning members needs a valid grid-wide licence¶
What it is: configure grid <name> member add ... fails with:
This is NIOS refusing to admit any new member, not a CLI or SDK problem -
a bare POST /wapi/v2.14/member with no pre_provisioning block at all
fails identically. The IP quoted in the message is NIOS-internal and does
not correspond to the address you submitted, which makes the error look
like a client bug when it is not.
How to confirm it is licensing: check the master's own licences and their expiry. On an HA pair each node has its own bundle, and one expired node is enough to fail the check:
curl -sk -u admin:PASS "https://<gm>/wapi/v2.14/member:license?_return_fields=type,hwid,expiry_date"
curl -sk -u admin:PASS "https://<gm>/wapi/v2.14/license:gridwide"
A GRID entry whose expiry_date is in the past - or an empty
license:gridwide - explains the refusal. Install a current licence on the
master; there is no client-side workaround.
Unaffected: enabling DNS and DHCP services on members that already
exist (configure grid <name> member <fqdn> dns enable,
... dhcp enable ipv4) works normally - that is a service toggle, not
member admission.
Some operations are refused before they reach the grid¶
What it is: NIOS restricts read/create/update/delete per object type. The SDK knows the table and refuses locally:
show dtc
Error: WAPI object type 'dtc' does not support read;
re-run with --allow-restricted to send it anyway
This is the same verdict the grid gives - Operation read not allowed for
dtc - just delivered without a round trip. Confirmed against NIOS 9.1 for
every affected command, by calling the raw WAPI endpoint directly.
Affected commands, and the operation NIOS forbids:
| Command | Object type | Op |
|---|---|---|
show dtc |
dtc |
read |
show deleted_objects |
deleted_objects |
read |
show network_discovery |
network_discovery |
read |
configure license_gridwide add |
license:gridwide |
create |
configure grid <n> member <m> license add |
member:license |
create |
configure mastergrid add / delete |
mastergrid |
create/delete |
configure parental_control subscriber add / delete |
parentalcontrol:subscriber |
create/delete |
configure grid certificate delete |
grid:x509certificate |
delete |
configure hostname_policy add / delete |
hostnamerewritepolicy |
create/delete |
configure discovery diagnostic start |
discovery:diagnostictask |
create |
restart discovery |
discoverytask |
create |
Workaround: --allow-restricted sends the request regardless. The
restriction table was built from a single NIOS build (9.1.0-54969), so a
different version may permit some of these - the flag exists for that case.
On a 9.1 grid the request simply fails at NIOS instead of at the client.
The commands are kept rather than removed (unlike the RADIUS ones, whose object types no longer exist at all) because the restriction is version-specific, not absolute.
WAPI functions look like model fields¶
What it is: ibx-nios-sdk declares callable WAPI operations - upgrade,
restartservices, empty_recycle_bin, run_scavenging, task_control and
46 others across 10 models - as pydantic fields annotated object | None,
and does not list them in READONLY_FIELDS.
They are neither settable nor readable. Requesting one as a return field
makes NIOS answer with an internal error such as
.com.infoblox.one.cluster has no member .reqversion.
How ibcli handles it: anything annotated object | None is treated as a
function and excluded from set <key>=<value> completions and from the deep
sweep's field list (_is_wapi_function in completions_keys.py). Real data
fields always carry a concrete annotation, so the bare object type is a
reliable marker. Call these through their own commands
(restart …, upgrade …) instead.
SDK model mismatches on some NIOS versions¶
What it is: ibx-nios-sdk pins a hand-written pydantic model per WAPI
object. Where a model's type disagrees with what a given NIOS build actually
returns, the response fails to deserialise:
show grid fields=restart_status
Error: ValidationError: the SDK model for Grid does not match what this
grid returned: restart_status (string_type). …
Known case, now fixed: Grid.restart_status was typed str | None in
ibx-nios-sdk 0.1.1 while NIOS 9.1 answers with a grid:servicerestart:status
struct. Resolved by pinning the SDK to v0.1.6, whose models were corrected
against a live 9.1 grid. The pin matters - an unpinned git+… dependency
keeps whatever was installed first and will silently drift back.
Workaround: drop the offending field from fields=. The rest of the
command works - only the named field is unparseable. The fix belongs
upstream in the SDK model.
How to find more: the deep field sweep asks for every SDK-declared field
on every show command and reports what the grid rejects or fails to parse:
Write-only fields are not readable¶
secret, old_password and execute_now exist on their models - you can
set them - but NIOS refuses to return them (Field is not readable: secret).
They are excluded from the deep sweep's field list rather than reported as
findings each run.
help all points to the docs site¶
What it is: help all prints:
The string is hard-coded in src/ibcli/commands/system.py. If the docs
ever move, update it there.
backup_file flag exits with code 2¶
What it is: Passing -b or -f to the Python port exits immediately with code 2 and an error message. Scripts that check for non-zero exit codes will detect this as a failure.
The Perl version silently opened a backup file browser. The Python port does not implement this feature.