Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project are documented here.

The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Added

- Configurable HTTP request timeout for Nexus Dashboard: `--request-timeout`, `ND_REQUEST_TIMEOUT_SECONDS`, or `request_timeout_seconds` in YAML (default 60 seconds). Previously the 60-second timeout was hard-coded, so a cluster that answered slowly failed every command with `httpx.ReadTimeout`.

## [0.2.0] - 2026-09-11

### Added
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The authoritative key list and inline comments live in [config.example.yaml](../
| `ca_bundle` | `ND_CA_BUNDLE` | Path to CA bundle |
| `job_timeout_minutes` | `ND_JOB_TIMEOUT_MINUTES` | Analysis job timeout |
| `poll_interval` | `ND_POLL_INTERVAL` | Job poll interval (seconds) |
| `request_timeout_seconds` | `ND_REQUEST_TIMEOUT_SECONDS` | Single HTTP response timeout (seconds), default 60 |
| `delta_detail` | `ND_DELTA_DETAIL` | Default `--detail` for prechange/delta |

Minimal example:
Expand Down
23 changes: 12 additions & 11 deletions docs/nexus-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ Usage: nac-analytics nexus-dashboard [OPTIONS] COMMAND [ARGS]...
Change analysis for Cisco Nexus Dashboard 4.2.1+ (GA REST APIs, ACI).

Configuration:
ND_HOST Nexus Dashboard hostname or IP
ND_USER Login username
ND_PASSWORD Login password
ND_DOMAIN Login domain
ND_FABRIC Default ACI fabric name
ND_VERIFY_SSL Verify TLS certificate (ND_VERIFY_TLS accepted)
ND_CA_BUNDLE Path to CA bundle
ND_JOB_TIMEOUT_MINUTES Minutes to wait for analysis jobs
ND_POLL_INTERVAL Seconds between job status polls
ND_DELTA_DETAIL Default --detail for prechange and delta
ND_CONFIG Path to YAML config file
ND_HOST Nexus Dashboard hostname or IP
ND_USER Login username
ND_PASSWORD Login password
ND_DOMAIN Login domain
ND_FABRIC Default ACI fabric name
ND_VERIFY_SSL Verify TLS certificate (ND_VERIFY_TLS accepted)
ND_CA_BUNDLE Path to CA bundle
ND_JOB_TIMEOUT_MINUTES Minutes to wait for analysis jobs
ND_POLL_INTERVAL Seconds between job status polls
ND_REQUEST_TIMEOUT_SECONDS Seconds to wait for one HTTP response
ND_DELTA_DETAIL Default --detail for prechange and delta
ND_CONFIG Path to YAML config file

In YAML, nest these under a `nexus_dashboard:` section. Settings load from
CLI flags, then environment variables, nac-analytics.yaml, or .env.
Expand Down
29 changes: 16 additions & 13 deletions examples/nexus_dashboard/config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,23 @@
# ---------------------------------------------------------------------------
# Supported variables — nexus_dashboard section
# ---------------------------------------------------------------------------
# Key Env var Description
# host ND_HOST Hostname or IP (HTTPS assumed when no scheme given).
# domain ND_DOMAIN Login domain (DefaultAuth, local, or your LDAP domain).
# verify_ssl ND_VERIFY_SSL Verify TLS cert (ND_VERIFY_TLS also accepted).
# ca_bundle ND_CA_BUNDLE Path to a CA bundle for private/self-signed certs.
# fabric ND_FABRIC Default fabric when a command needs one and --fabric is omitted.
# fabrics — Fabrics `compliance --all` checks (defaults to [fabric]).
# job_timeout_minutes ND_JOB_TIMEOUT_MINUTES Minutes to wait for a pre-change analysis job.
# poll_interval ND_POLL_INTERVAL Seconds between job status polls.
# delta_detail ND_DELTA_DETAIL Default detail: none, resources, anomalies, policy-diff, full
# (prechange defaults to `full`; delta to `resources`).
# Key Env var Description
# host ND_HOST Hostname or IP (HTTPS assumed when no scheme given).
# domain ND_DOMAIN Login domain (DefaultAuth, local, or your LDAP domain).
# verify_ssl ND_VERIFY_SSL Verify TLS cert (ND_VERIFY_TLS also accepted).
# ca_bundle ND_CA_BUNDLE Path to a CA bundle for private/self-signed certs.
# fabric ND_FABRIC Default fabric when a command needs one and --fabric is omitted.
# fabrics — Fabrics `compliance --all` checks (defaults to [fabric]).
# job_timeout_minutes ND_JOB_TIMEOUT_MINUTES Minutes to wait for a pre-change analysis job.
# poll_interval ND_POLL_INTERVAL Seconds between job status polls.
# request_timeout_seconds ND_REQUEST_TIMEOUT_SECONDS Seconds to wait for one HTTP response (default 60). Raise it
# when the cluster is slow to answer.
# delta_detail ND_DELTA_DETAIL Default detail: none, resources, anomalies, policy-diff, full
# (prechange defaults to `full`; delta to `resources`).
#
# Credentials (secrets — set via environment, not here):
# username ND_USER Login username.
# password ND_PASSWORD Login password.
# username ND_USER Login username.
# password ND_PASSWORD Login password.
# ---------------------------------------------------------------------------

nexus_dashboard:
Expand All @@ -41,4 +43,5 @@ nexus_dashboard:
- FABRIC-B
job_timeout_minutes: 30
poll_interval: 15
request_timeout_seconds: 60
delta_detail: resources
1 change: 1 addition & 0 deletions examples/nexus_dashboard/config/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ ND_PASSWORD=change-me
# ND_CA_BUNDLE=/path/to/nd-cluster-ca.pem
# ND_JOB_TIMEOUT_MINUTES=60
# ND_POLL_INTERVAL=15
# ND_REQUEST_TIMEOUT_SECONDS=60
# ND_DELTA_DETAIL=resources
# ND_CONFIG=/path/to/nac-analytics.yaml
23 changes: 12 additions & 11 deletions nac_analytics/products/nexus_dashboard/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@
Change analysis for Cisco Nexus Dashboard 4.2.1+ (GA REST APIs, ACI).

Configuration:
ND_HOST Nexus Dashboard hostname or IP
ND_USER Login username
ND_PASSWORD Login password
ND_DOMAIN Login domain
ND_FABRIC Default ACI fabric name
ND_VERIFY_SSL Verify TLS certificate (ND_VERIFY_TLS accepted)
ND_CA_BUNDLE Path to CA bundle
ND_JOB_TIMEOUT_MINUTES Minutes to wait for analysis jobs
ND_POLL_INTERVAL Seconds between job status polls
ND_DELTA_DETAIL Default --detail for prechange and delta
ND_CONFIG Path to YAML config file
ND_HOST Nexus Dashboard hostname or IP
ND_USER Login username
ND_PASSWORD Login password
ND_DOMAIN Login domain
ND_FABRIC Default ACI fabric name
ND_VERIFY_SSL Verify TLS certificate (ND_VERIFY_TLS accepted)
ND_CA_BUNDLE Path to CA bundle
ND_JOB_TIMEOUT_MINUTES Minutes to wait for analysis jobs
ND_POLL_INTERVAL Seconds between job status polls
ND_REQUEST_TIMEOUT_SECONDS Seconds to wait for one HTTP response
ND_DELTA_DETAIL Default --detail for prechange and delta
ND_CONFIG Path to YAML config file

In YAML, nest these under a `nexus_dashboard:` section. Settings load from
CLI flags, then environment variables, nac-analytics.yaml, or .env."""
Expand Down
16 changes: 14 additions & 2 deletions nac_analytics/products/nexus_dashboard/commands/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
prechange_job_details,
snapshot_details,
)
from nac_analytics.products.nexus_dashboard.config import Config
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_REQUEST_TIMEOUT_SECONDS,
Config,
)
from nac_analytics.products.nexus_dashboard.delta import (
DELTA_DETAIL_LEVELS,
PRECHANGE_DEFAULT_DETAIL,
Expand Down Expand Up @@ -103,6 +106,14 @@
help="Seconds between job status polls.",
),
]
RequestTimeoutOpt = Annotated[
int,
typer.Option(
"--request-timeout",
envvar="ND_REQUEST_TIMEOUT_SECONDS",
help="Seconds to wait for a single HTTP response from Nexus Dashboard.",
),
]
OutputOpt = Annotated[
str,
typer.Option(
Expand Down Expand Up @@ -250,6 +261,7 @@ def _build_config(
ca_bundle: str | None,
timeout: int,
poll_interval: int,
request_timeout: int = DEFAULT_REQUEST_TIMEOUT_SECONDS,
) -> Config:
if not fabric:
raise InputError(
Expand All @@ -263,7 +275,7 @@ def _build_config(
fabric=fabric,
verify_ssl=verify_ssl,
ca_bundle=ca_bundle,
request_timeout_seconds=60.0,
request_timeout_seconds=request_timeout,
poll_interval_seconds=poll_interval,
job_timeout_minutes=timeout,
)
Expand Down
8 changes: 7 additions & 1 deletion nac_analytics/products/nexus_dashboard/commands/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

from nac_analytics.core.exceptions import ApiError, InputError
from nac_analytics.core.progress import note
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
)

from . import _helpers
from ._helpers import (
Expand All @@ -18,6 +21,7 @@
HostOpt,
PasswordOpt,
PollOpt,
RequestTimeoutOpt,
TimeoutOpt,
UserOpt,
VerboseOpt,
Expand Down Expand Up @@ -57,6 +61,7 @@ def analyze(
] = "text",
timeout: TimeoutOpt = 30,
poll_interval: PollOpt = 15,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verify_ssl: VerifyOpt = True,
ca_bundle: CaBundleOpt = None,
verbose: VerboseOpt = False,
Expand All @@ -78,6 +83,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=timeout,
poll_interval=poll_interval,
request_timeout=request_timeout,
)
note(_connect_message(config))
with _helpers.NDClient(config) as client:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from nac_analytics.core.progress import note
from nac_analytics.core.report import MultiFabricResult, Result, render_multi
from nac_analytics.products.nexus_dashboard.compliance import run_compliance_check
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
)
from nac_analytics.products.nexus_dashboard.settings import configured_fabrics

from . import _helpers
Expand All @@ -23,6 +26,7 @@
OutputOpt,
PasswordOpt,
PollOpt,
RequestTimeoutOpt,
SinceOpt,
TimeoutOpt,
UntilOpt,
Expand Down Expand Up @@ -79,6 +83,7 @@ def compliance(
ca_bundle: CaBundleOpt = None,
timeout: TimeoutOpt = 30,
poll_interval: PollOpt = 15,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verbose: VerboseOpt = False,
) -> None:
"""Report compliance rule status for a fabric (or every fabric with --all).
Expand Down Expand Up @@ -109,6 +114,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=timeout,
poll_interval=poll_interval,
request_timeout=request_timeout,
)
results: list[Result] = []
failed: list[str] = []
Expand Down Expand Up @@ -150,6 +156,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=timeout,
poll_interval=poll_interval,
request_timeout=request_timeout,
)
note(_connect_message(config))
with _helpers.NDClient(config) as client:
Expand Down
8 changes: 7 additions & 1 deletion nac_analytics/products/nexus_dashboard/commands/delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
from nac_analytics.core.report import GATE_DEFAULT_OUTPUT, parse_fail_on
from nac_analytics.products.nexus_dashboard.client import resolve_snapshot_ids
from nac_analytics.products.nexus_dashboard.compliance import snapshot_details
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
)
from nac_analytics.products.nexus_dashboard.delta import (
DEFAULT_DELTA_DETAIL,
normalize_delta_detail,
Expand All @@ -32,6 +35,7 @@
PasswordOpt,
PollOpt,
ReportFileOpt,
RequestTimeoutOpt,
SinceOpt,
TimeoutOpt,
UntilOpt,
Expand Down Expand Up @@ -98,6 +102,7 @@ def delta(
ca_bundle: CaBundleOpt = None,
timeout: TimeoutOpt = 30,
poll_interval: PollOpt = 15,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verbose: VerboseOpt = False,
) -> None:
"""Compare two snapshots of a fabric and report what changed.
Expand Down Expand Up @@ -126,6 +131,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=timeout,
poll_interval=poll_interval,
request_timeout=request_timeout,
)
job_name = name or _auto_name("delta")
note(_connect_message(config))
Expand Down
9 changes: 8 additions & 1 deletion nac_analytics/products/nexus_dashboard/commands/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from nac_analytics.core.progress import note
from nac_analytics.core.report import Result
from nac_analytics.products.nexus_dashboard.client import fabric_name, is_aci_fabric
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN, normalise_host
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
normalise_host,
)

from . import _helpers
from ._helpers import (
Expand All @@ -16,6 +20,7 @@
HostOpt,
OutputOpt,
PasswordOpt,
RequestTimeoutOpt,
UserOpt,
VerboseOpt,
VerifyOpt,
Expand All @@ -35,6 +40,7 @@ def doctor(
output: OutputOpt = "text",
verify_ssl: VerifyOpt = True,
ca_bundle: CaBundleOpt = None,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verbose: VerboseOpt = False,
) -> None:
"""Check connectivity, credentials, and fabric visibility.
Expand All @@ -54,6 +60,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=30,
poll_interval=15,
request_timeout=request_timeout,
)
details: dict[str, object] = {
"base_url": config.base_url,
Expand Down
8 changes: 7 additions & 1 deletion nac_analytics/products/nexus_dashboard/commands/prechange.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
from nac_analytics.core.exceptions import ApiError, InputError
from nac_analytics.core.progress import note
from nac_analytics.core.report import GATE_DEFAULT_OUTPUT, parse_fail_on
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
)
from nac_analytics.products.nexus_dashboard.delta import (
PRECHANGE_DEFAULT_DETAIL,
normalize_delta_detail,
Expand All @@ -32,6 +35,7 @@
PasswordOpt,
PollOpt,
ReportFileOpt,
RequestTimeoutOpt,
SinceOpt,
TimeoutOpt,
UntilOpt,
Expand Down Expand Up @@ -106,6 +110,7 @@ def prechange(
ca_bundle: CaBundleOpt = None,
timeout: TimeoutOpt = 30,
poll_interval: PollOpt = 15,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verbose: VerboseOpt = False,
) -> None:
"""Analyse a candidate configuration against a fabric's current state.
Expand Down Expand Up @@ -142,6 +147,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=timeout,
poll_interval=poll_interval,
request_timeout=request_timeout,
)
detail_level = normalize_delta_detail(detail)
upload_content: bytes | None = None
Expand Down
8 changes: 7 additions & 1 deletion nac_analytics/products/nexus_dashboard/commands/snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

from nac_analytics.core.exceptions import InputError
from nac_analytics.core.progress import note
from nac_analytics.products.nexus_dashboard.config import DEFAULT_DOMAIN
from nac_analytics.products.nexus_dashboard.config import (
DEFAULT_DOMAIN,
DEFAULT_REQUEST_TIMEOUT_SECONDS,
)

from . import _helpers
from ._helpers import (
Expand All @@ -17,6 +20,7 @@
FabricOpt,
HostOpt,
PasswordOpt,
RequestTimeoutOpt,
SinceOpt,
UntilOpt,
UserOpt,
Expand Down Expand Up @@ -54,6 +58,7 @@ def snapshots(
] = "text",
verify_ssl: VerifyOpt = True,
ca_bundle: CaBundleOpt = None,
request_timeout: RequestTimeoutOpt = DEFAULT_REQUEST_TIMEOUT_SECONDS,
verbose: VerboseOpt = False,
) -> None:
"""Resolve a fabric snapshot and print its ID (for CI baseline pinning)."""
Expand All @@ -73,6 +78,7 @@ def body() -> None:
ca_bundle=ca_bundle,
timeout=30,
poll_interval=15,
request_timeout=request_timeout,
)
note(_connect_message(config))
with _helpers.NDClient(config) as client:
Expand Down
Loading
Loading