Skip to content

Changelog

Unreleased

Breaking Changes

  • Dropped Python 3.9 support. Minimum supported version is now Python 3.10. The CVE-patched releases of requests (>=2.33.0) and urllib3 (>=2.7.0) both dropped 3.9 themselves, so keeping 3.9 support would have meant either staying on vulnerable dependency versions or maintaining a conditional dependency tree. Python 3.9 itself went end-of-life in October 2025; users on 3.9 should upgrade their interpreter. The Ruff target-version was also bumped to py310 so the linter can use 3.10-only syntax.

Dependency CVE Bumps

The dependency-audit CI job (pip-audit --skip-editable) caught seven CVEs in the previous v2.0.0 dependency tree. Minimum version constraints in pyproject.toml were raised to pull in the patched releases:

  • requests>=2.33.0 — fixes GHSA-gc5v-m9x4-r6x2 (predictable filenames in extract_zipped_paths() allowing local file substitution).
  • urllib3>=2.7.0 — fixes a chain of four CVEs in 2.6.x and earlier: PYSEC-2026-141 (ProxyManager cross-origin header leak), GHSA-gm62-xv2j-4w53 (unbounded content-encoding chain DoS), GHSA-2xpw-w6gg-jr37 (streaming decompression bomb), and GHSA-38jv-5279-wg99 (redirect-response decoding bomb).
  • lxml>=6.1.0 — fixes PYSEC-2026-87 (default parser allows local file read via external entities; zeep's forbid_dtd=True setting already mitigates, but the patched parser is the safer default).
  • idna>=3.15 — fixes CVE-2026-45409 (DoS via long crafted inputs to idna.encode; transitive of requests).
  • pytest>=9.0.3 (dev) — fixes GHSA-6w46-j5rx-g56g (predictable /tmp/pytest-of-* paths).
  • mkdocs-material>=9.6.20 and pymdown-extensions>=10.21.3 (docs) — fixes GHSA-62q4-447f-wv8h (path-traversal regression in the snippets preprocessor).

The dependency-audit workflow itself was also corrected: - --skip-editable added so axltoolkit itself (installed via pip install -e .) isn't audited against PyPI (where it isn't published). - --strict removed; it was promoting the harmless "skipped editable" notice into a job failure, masking real findings. pip-audit already exits non-zero on real CVEs without it.

Lint / Code Quality

  • Ruff configpyproject.toml now excludes the auto-generated axltoolkit/_generated_models.py and _generated_enums.py modules from lint and format checks. They contain hundreds of legitimate string forward-references to cross-module enum types (NotRequired["NetworkLocation"]) that trip F821 but are valid at runtime; their style is dictated by the generator script, not the developer.
  • Test F841 ignoredtests/**/*.py is excluded from F841 (unused-variable) because smoke tests legitimately bind return values for debuggability without asserting on them.
  • One-shot reflow — the entire non-generated codebase was passed through ruff format, which produced a large diff on the v2.0.0 landing commit but no semantic changes (309 unit tests still pass).

Security Hardening

  • SOAP envelope redactionlast_request_debug() now redacts secret-bearing elements inside the SOAP body (e.g. <password>, <ldapPassword>, <token>, <apiKey>, <snmpPassword>, <authProtocolPassword>, <privProtocolPassword>), not just the Authorization / Cookie headers. The match is case-insensitive on the element's local name, so namespace prefixes are irrelevant.
  • DTD parsing forbiddenzeep is now constructed with Settings(forbid_dtd=True), blocking DOCTYPE declarations and the XXE / Billion Laughs / parameter-entity attack classes that depend on them.
  • Legacy requirements.txt removed — the file pinned zeep==3.4.0 (known XXE issues) and defusedxml==0.6.0 (known CVEs). pyproject.toml is now the single source of truth, pinning modern, patched versions.
  • pip-audit --strict in CI — a new dependency-audit job runs on every push and fails the build if any installed dependency has a known CVE.
  • UCM_TLS_VERIFY default flipped to true in .env.example and samples/_env.py, with explicit warnings that false is for lab use only.
  • SNMP enum warningsSNMPAuthenticationProtocol and SNMPPrivacyProtocol carry generated docstrings advising against insecure MD5 / DES values in favor of SHA and AES*.
  • Sample scripts use generic placeholders (jsmith, SEP001122334455) instead of real userids/MACs.

Reliability Improvements

  • RISPort session recoveryRISPortClient now detects the "Cisco RISDC needs SOAP clients to start a new session" fault (Error Code = 7), clears its cookies, rebinds the service proxy, and retries the operation once. Long-running scripts and the integration suite no longer fail intermittently after the RIS session expires.
  • _AxlStrEnum base classscripts/generate_models.py now emits a str-subclassing base for every generated enum that preserves the pre-Python-3.11 str() representation. Fixes a serialization break on 3.11+ where the SOAP envelope contained EnumClass.MEMBER instead of the bare member name.

New Features

  • .env-backed credentials workflowpython-dotenv is now part of the dev extras. samples/_env.py and tests/integration/conftest.py load .env from the repo root automatically; .env.example documents every variable.
  • Integration test suitetests/integration/ exercises 376+ AXL/SXML operations against a live UCM cluster, including parametrized add/get/update/remove cycles for 50+ object types.
  • WebdialerClient documentation — the previously-merged WebdialerClient is now covered in the API reference and Quick Start guide.
  • Coverage scripts:
    • scripts/check_coverage.py (CI gate) — verifies every WSDL operation has a Python wrapper. Now also covers the SXML, PAWS, and Webdialer WSDLs via --include-sxml.
    • scripts/check_test_coverage.py (reporting tool) — measures how much of the public client surface is exercised by the integration suite, with parametrized indirect-dispatch awareness.
    • scripts/snapshot_wsdls.py — refreshes the SXML/PAWS/Webdialer WSDL snapshots from a live UCM.
  • Local Development guide — new docs/guide/local-development.md page collects .env setup, the integration test runner, the coverage scripts, CI job descriptions, and the PyPI publishing workflow (including a Test PyPI dry-run path) in one place.
  • Test PyPI workflow.github/workflows/test-pypi.yml provides a manual workflow_dispatch job that builds the sdist and wheel, runs twine check --strict, and uploads to test.pypi.org using a repository-scoped TEST_PYPI_API_TOKEN secret. Lets you verify metadata, README rendering, and clean-install behavior before claiming the version on production PyPI.

Code Quality

  • AXL method dedup — 14 duplicate methods removed from axltoolkit/axl.py; 3 orphaned section headers re-labelled to match the methods underneath them.
  • add_remote_destination refactored for consistency with the rest of the add_* method family.

Packaging

  • PEP 639 license metadatapyproject.toml now declares the license via the modern SPDX-style expression (license = "LicenseRef-Cisco-Sample-Code-License-1.1" with license-files = ["LICENSE"]). The deprecated License :: Other/Proprietary License classifier and the license = {file = "..."} table form (both scheduled for removal by setuptools in Feb 2027) are gone. Built distributions now emit License-Expression: and License-File: headers, no deprecation warnings.
  • LICENSE file at the repo root — the full Cisco Sample Code License v1.1 text is now a top-level file (was previously only embedded in README.md), so PyPI, GitHub, and license-scanning tools all detect it automatically.
  • [project.urls] — PyPI metadata now includes Homepage, Documentation, Repository, Issues, and Changelog links for richer project pages.
  • PyPI keywords and classifiers added for discoverability (cisco, ucm, callmanager, axl, sxml, …; Topic :: Communications :: Telephony, Topic :: System :: Systems Administration, …).
  • setuptools>=77.0 build requirement — needed for PEP 639 LicenseRef-* expression support.

v2.0.0

New Features

  • Modular client architecture — Separate clients for each API: AXLClient, RISPortClient, PerfMonClient, ServiceabilityClient, LogCollectionClient, DimeGetFileClient, PAWSClient, WebdialerClient
  • Full PAWS coveragePAWSClient now wraps all 20 PAWS services (22 methods) including deployment mode, hardware model, upgrade lifecycle (prepare, start, cancel, filter, validate, stage, progress, type), restart system, and switch version operations
  • Full AXL WSDL coverage — All 1,068 AXL operations wrapped as snake_case methods
  • Fluent buildersPhoneBuilder, SipTrunkBuilder, CssBuilder for constructing complex payloads
  • TypedDict models — IDE-friendly type annotations for common AXL objects
  • Auto-generated TypedDict models — Schema-driven TypedDict classes for all add_* (174 models) and update_* (201 models) method payloads, generated from the AXL XSD via scripts/generate_models.py
  • Auto-generated enum types — 170 str, Enum classes for constrained AXL fields (e.g. ProtocolSide, ClockReference) that serialize naturally
  • Typed update_* kwargsUnpack[UpdatePhone] etc. provide IDE autocompletion and type checking for all update method keyword arguments
  • Typed exception hierarchy — Granular error handling with AXLNotFoundError, AXLDuplicateError, AXLSQLError, etc.
  • SQL injection protection — Pattern detection and value escaping for Thin AXL queries
  • Session cookie caching — JSESSIONID reuse across requests for better performance
  • Configurable retry logic — Automatic exponential back-off on transient HTTP errors
  • Request/response history — Built-in debug helper with credential redaction
  • Comprehensive documentation — MkDocs Material site with user guide, API reference, and recipes

Security Improvements

  • TLS verification enabled by default (tls_verify=True)
  • WSDL cache stored in user-scoped directory (~/.cache/axltoolkit/) with restricted permissions
  • server_ip validated at construction time to prevent SSRF
  • Authorization header redacted in debug output
  • HTTPS-only retry adapter (no HTTP fallback)
  • All documentation examples use FQDNs and tls_verify=True

Breaking Changes

  • Legacy class names (AxlToolkit, UcmRisPortToolkit, etc.) are deprecated and emit DeprecationWarning. They will be removed in a future major release.
  • get_cisco_cloud_onboarding() removed — the underlying getCiscoCloudOnboarding AXL operation does not exist in any WSDL schema version. Use list_cisco_cloud_onboarding() instead.
  • add_user_phone_association()associate_user_devices() — the old method was a convenience wrapper around updateUser. The name add_user_phone_association() now wraps the actual AXL addUserPhoneAssociation WSDL operation.
  • run_sql_query()sql_query(), run_sql_update()sql_update()
  • get_service() replaced by .service property
  • Various method renames — see the Migration Guide for the full list

Dependencies

  • zeep>=4.2.1,<5
  • requests>=2.32.0,<3
  • lxml>=4.9.0,<7
  • urllib3>=2.0.0,<3
  • typing_extensions>=4.1 (Python < 3.12 only — provides Unpack, Required, NotRequired)
  • Python 3.10+ required