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) andurllib3(>=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 Rufftarget-versionwas also bumped topy310so 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— fixesGHSA-gc5v-m9x4-r6x2(predictable filenames inextract_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), andGHSA-38jv-5279-wg99(redirect-response decoding bomb).lxml>=6.1.0— fixesPYSEC-2026-87(default parser allows local file read via external entities;zeep'sforbid_dtd=Truesetting already mitigates, but the patched parser is the safer default).idna>=3.15— fixesCVE-2026-45409(DoS via long crafted inputs toidna.encode; transitive ofrequests).pytest>=9.0.3(dev) — fixesGHSA-6w46-j5rx-g56g(predictable/tmp/pytest-of-*paths).mkdocs-material>=9.6.20andpymdown-extensions>=10.21.3(docs) — fixesGHSA-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 config —
pyproject.tomlnow excludes the auto-generatedaxltoolkit/_generated_models.pyand_generated_enums.pymodules 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 ignored —
tests/**/*.pyis 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 redaction —
last_request_debug()now redacts secret-bearing elements inside the SOAP body (e.g.<password>,<ldapPassword>,<token>,<apiKey>,<snmpPassword>,<authProtocolPassword>,<privProtocolPassword>), not just theAuthorization/Cookieheaders. The match is case-insensitive on the element's local name, so namespace prefixes are irrelevant. - DTD parsing forbidden —
zeepis now constructed withSettings(forbid_dtd=True), blocking DOCTYPE declarations and the XXE / Billion Laughs / parameter-entity attack classes that depend on them. - Legacy
requirements.txtremoved — the file pinnedzeep==3.4.0(known XXE issues) anddefusedxml==0.6.0(known CVEs).pyproject.tomlis now the single source of truth, pinning modern, patched versions. pip-audit --strictin CI — a newdependency-auditjob runs on every push and fails the build if any installed dependency has a known CVE.UCM_TLS_VERIFYdefault flipped totruein.env.exampleandsamples/_env.py, with explicit warnings thatfalseis for lab use only.- SNMP enum warnings —
SNMPAuthenticationProtocolandSNMPPrivacyProtocolcarry generated docstrings advising against insecureMD5/DESvalues in favor ofSHAandAES*. - Sample scripts use generic placeholders (
jsmith,SEP001122334455) instead of real userids/MACs.
Reliability Improvements¶
- RISPort session recovery —
RISPortClientnow 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. _AxlStrEnumbase class —scripts/generate_models.pynow emits astr-subclassing base for every generated enum that preserves the pre-Python-3.11str()representation. Fixes a serialization break on 3.11+ where the SOAP envelope containedEnumClass.MEMBERinstead of the bare member name.
New Features¶
.env-backed credentials workflow —python-dotenvis now part of thedevextras.samples/_env.pyandtests/integration/conftest.pyload.envfrom the repo root automatically;.env.exampledocuments every variable.- Integration test suite —
tests/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
WebdialerClientis 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.mdpage collects.envsetup, 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.ymlprovides a manualworkflow_dispatchjob that builds the sdist and wheel, runstwine check --strict, and uploads to test.pypi.org using a repository-scopedTEST_PYPI_API_TOKENsecret. 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_destinationrefactored for consistency with the rest of theadd_*method family.
Packaging¶
- PEP 639 license metadata —
pyproject.tomlnow declares the license via the modern SPDX-style expression (license = "LicenseRef-Cisco-Sample-Code-License-1.1"withlicense-files = ["LICENSE"]). The deprecatedLicense :: Other/Proprietary Licenseclassifier and thelicense = {file = "..."}table form (both scheduled for removal by setuptools in Feb 2027) are gone. Built distributions now emitLicense-Expression:andLicense-File:headers, no deprecation warnings. LICENSEfile at the repo root — the full Cisco Sample Code License v1.1 text is now a top-level file (was previously only embedded inREADME.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.0build requirement — needed for PEP 639LicenseRef-*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 coverage —
PAWSClientnow 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 builders —
PhoneBuilder,SipTrunkBuilder,CssBuilderfor constructing complex payloads - TypedDict models — IDE-friendly type annotations for common AXL objects
- Auto-generated TypedDict models — Schema-driven
TypedDictclasses for alladd_*(174 models) andupdate_*(201 models) method payloads, generated from the AXL XSD viascripts/generate_models.py - Auto-generated enum types — 170
str, Enumclasses for constrained AXL fields (e.g.ProtocolSide,ClockReference) that serialize naturally - Typed
update_*kwargs —Unpack[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_ipvalidated at construction time to prevent SSRFAuthorizationheader 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 emitDeprecationWarning. They will be removed in a future major release. get_cisco_cloud_onboarding()removed — the underlyinggetCiscoCloudOnboardingAXL operation does not exist in any WSDL schema version. Uselist_cisco_cloud_onboarding()instead.add_user_phone_association()→associate_user_devices()— the old method was a convenience wrapper aroundupdateUser. The nameadd_user_phone_association()now wraps the actual AXLaddUserPhoneAssociationWSDL operation.run_sql_query()→sql_query(),run_sql_update()→sql_update()get_service()replaced by.serviceproperty- Various method renames — see the Migration Guide for the full list
Dependencies¶
zeep>=4.2.1,<5requests>=2.32.0,<3lxml>=4.9.0,<7urllib3>=2.0.0,<3typing_extensions>=4.1(Python < 3.12 only — providesUnpack,Required,NotRequired)- Python 3.10+ required