python-netgear-switch-library 0.0.post323__tar.gz → 0.0.post326__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/PKG-INFO +15 -1
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/README.md +13 -0
- python_netgear_switch_library-0.0.post326/docs/_ext/support_tables.py +550 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/cli.rst +1 -1
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/facades.rst +1 -1
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/index.rst +1 -1
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/conf.py +54 -2
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/development.rst +2 -2
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/fake/internals.rst +7 -5
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/fake/serving.rst +1 -1
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/fake/testing.rst +164 -63
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/guide/concepts.rst +79 -27
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/guide/configuration.rst +57 -20
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/guide/installation.rst +1 -1
- python_netgear_switch_library-0.0.post326/docs/guide/quickstart.rst +311 -0
- python_netgear_switch_library-0.0.post326/docs/guide/reading.rst +267 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/guide/writing.rst +73 -31
- python_netgear_switch_library-0.0.post326/docs/index.rst +170 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/mcp.rst +1 -1
- python_netgear_switch_library-0.0.post326/docs/models/gs105pe.rst +43 -0
- python_netgear_switch_library-0.0.post326/docs/models/gs110emx.rst +52 -0
- python_netgear_switch_library-0.0.post326/docs/models/gs305ep.rst +63 -0
- python_netgear_switch_library-0.0.post326/docs/models/gs728tpp.rst +52 -0
- python_netgear_switch_library-0.0.post326/docs/models/gsm7228ps.rst +66 -0
- python_netgear_switch_library-0.0.post326/docs/models/gsm7252ps.rst +54 -0
- python_netgear_switch_library-0.0.post326/docs/models/index.rst +76 -0
- python_netgear_switch_library-0.0.post326/docs/models/m4300-16x.rst +60 -0
- python_netgear_switch_library-0.0.post326/docs/models/m4300-24x.rst +55 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/models/support.rst +10 -2
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/protocols/cli.rst +83 -23
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/protocols/http.rst +17 -3
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/protocols/nsdp.rst +14 -4
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/protocols/snmp.rst +13 -3
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/pyproject.toml +1 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/__init__.py +10 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/_version.py +2 -2
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/aio_api.py +12 -6
- python_netgear_switch_library-0.0.post323/docs/_ext/support_tables.py +0 -291
- python_netgear_switch_library-0.0.post323/docs/guide/quickstart.rst +0 -173
- python_netgear_switch_library-0.0.post323/docs/guide/reading.rst +0 -178
- python_netgear_switch_library-0.0.post323/docs/index.rst +0 -112
- python_netgear_switch_library-0.0.post323/docs/models/index.rst +0 -175
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/.github/workflows/ci.yml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/.github/workflows/deb.yml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/.github/workflows/publish-pypi.yml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/.gitignore +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/.readthedocs.yaml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/CLAUDE.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/LICENSE +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/RELEASING.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/debian/changelog +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/debian/control +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/debian/copyright +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/debian/rules +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/debian/source/format +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/Makefile +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/_ext/filelinks.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/_static/ngsw.css +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/core.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/mcp.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/operations.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/protocols.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/transport.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/api/virtual.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/fake/index.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/guide/principles.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/protocols/index.rst +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/packaging/apt-index.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/packaging/ci-build.sh +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/packaging/deb-version.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/capture.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/context.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/format.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/main.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/resolve.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli/safety.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/cli_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/config.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/errors.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/http_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/http_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/mcp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/mcp/server.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/models.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/nsdp_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/cli/commands.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/cli/parse.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/crypt.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/forms.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/parse.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/session.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/http/types.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/client.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/types.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/nsdp/write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/snmp/client.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/snmp/oids.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/snmp/parse.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/protocols/snmp/write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/py.typed +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/registry.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/snmp_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/aio/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/cli/console.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/cli/session.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/cli/ssh.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/cli/telnet.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/http/client.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/sync/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/cli_fastpath.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/cli.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/http.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/mibview.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/faces/snmp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/seed.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/server.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/state.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/src/netgear_switch/virtual/web_m4300.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/capture_parity.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_capture.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_cli_integration.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_control_poe_port.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_control_vlan_ip.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_format.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_main_framework.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_new_commands.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_op_coverage.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_prompt_wiring.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_read_commands.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_resolve.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_safety.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/cli/test_serve.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/conftest.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/captures/gsm7228ps.json +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/captures/gsm7252ps.json +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/captures/m4300-16x.json +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/captures/m4300-24x.json +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_membership.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_status.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_login.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_redirect.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_login.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_membership.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_login.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_addresstable.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_ports.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/http_specs.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/cli/test_cli_parse.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/http/test_crypt.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/http/test_endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/http/test_forms.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/http/test_parse.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/nsdp/test_auth.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/nsdp/test_parsers.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/nsdp/test_protocol.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/nsdp/test_write_frame.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_client.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_oids.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_ports.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_system_info.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_parse_vlans.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/protocols/snmp/test_write_encode.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_aio_api.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_cli_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_cli_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_cli_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_config.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_cross_backend_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_deb_version.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_errors.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_facade_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_vlan_membership.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_http_xui_writes.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_import.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_mcp_server.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_models.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_models_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_nsdp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_nsdp_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_public_api.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_quality_gates.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_registry.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_snmp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_snmp_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_version.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/test_write_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/_fastpath_fake.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_cli_console.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_cli_scp_cert.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_cli_telnet.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_http_client.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_nsdp_udp_sync.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_set_transport.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/transport/test_value_parity.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_cli_cross_backend.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_gsm7228ps_seed.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_m4300_seeds.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_mibview.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_mutable_state.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_nsdp_state.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_serve_daemon.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_snmp_write_face.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_state_seed.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_switchport_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_virtual_http_face.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_virtual_nsdp_face.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_virtual_snmp_face.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/tests/virtual/test_web_projection.py +0 -0
- {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/uv.lock +0 -0
{python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-netgear-switch-library
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.post326
|
|
4
4
|
Summary: Python library and CLI to query and control Netgear switches over SNMP, NSDP and HTTP.
|
|
5
5
|
Author-email: Tim Ansell <me@mith.ro>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pyserial>=3.5; extra == 'docs'
|
|
|
18
18
|
Requires-Dist: pysnmp>=7.0; extra == 'docs'
|
|
19
19
|
Requires-Dist: sphinx-argparse>=0.5; extra == 'docs'
|
|
20
20
|
Requires-Dist: sphinx-copybutton>=0.5.2; extra == 'docs'
|
|
21
|
+
Requires-Dist: sphinx-design>=0.6; extra == 'docs'
|
|
21
22
|
Requires-Dist: sphinx>=8.1; extra == 'docs'
|
|
22
23
|
Provides-Extra: http
|
|
23
24
|
Requires-Dist: httpx>=0.27; extra == 'http'
|
|
@@ -37,6 +38,19 @@ Description-Content-Type: text/markdown
|
|
|
37
38
|
Query and control all your Netgear switches — SNMP (managed), NSDP and HTTP
|
|
38
39
|
web-UI (Plus) — behind one model-driven Python API and the `ngsw` CLI.
|
|
39
40
|
|
|
41
|
+
> [!WARNING]
|
|
42
|
+
> **This library and its documentation were written by an AI.** Every line of
|
|
43
|
+
> code, every test and every documentation page was generated by a language
|
|
44
|
+
> model, with a human directing and reviewing the work.
|
|
45
|
+
>
|
|
46
|
+
> The device behaviour in it is *measured* — protocol details and firmware
|
|
47
|
+
> quirks come from captured traffic and live runs against the real switches,
|
|
48
|
+
> recorded with the host and firmware they were seen on — and the support tables
|
|
49
|
+
> are generated from the code rather than written by hand. But an AI is
|
|
50
|
+
> perfectly capable of confident, fluent error, and nothing here has had
|
|
51
|
+
> independent human review at scale. Verify anything that matters to you against
|
|
52
|
+
> the source or your own hardware, and please report what you find.
|
|
53
|
+
|
|
40
54
|
**Documentation: <https://python-netgear-switch-library.readthedocs.io/>** — API
|
|
41
55
|
reference, the complete `ngsw` CLI reference, the virtual-switch guide (how to
|
|
42
56
|
test your own tools against a mock), and generated
|
{python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post326}/README.md
RENAMED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
Query and control all your Netgear switches — SNMP (managed), NSDP and HTTP
|
|
4
4
|
web-UI (Plus) — behind one model-driven Python API and the `ngsw` CLI.
|
|
5
5
|
|
|
6
|
+
> [!WARNING]
|
|
7
|
+
> **This library and its documentation were written by an AI.** Every line of
|
|
8
|
+
> code, every test and every documentation page was generated by a language
|
|
9
|
+
> model, with a human directing and reviewing the work.
|
|
10
|
+
>
|
|
11
|
+
> The device behaviour in it is *measured* — protocol details and firmware
|
|
12
|
+
> quirks come from captured traffic and live runs against the real switches,
|
|
13
|
+
> recorded with the host and firmware they were seen on — and the support tables
|
|
14
|
+
> are generated from the code rather than written by hand. But an AI is
|
|
15
|
+
> perfectly capable of confident, fluent error, and nothing here has had
|
|
16
|
+
> independent human review at scale. Verify anything that matters to you against
|
|
17
|
+
> the source or your own hardware, and please report what you find.
|
|
18
|
+
|
|
6
19
|
**Documentation: <https://python-netgear-switch-library.readthedocs.io/>** — API
|
|
7
20
|
reference, the complete `ngsw` CLI reference, the virtual-switch guide (how to
|
|
8
21
|
test your own tools against a mock), and generated
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
"""Sphinx extension: support tables generated from the library, not by hand.
|
|
2
|
+
|
|
3
|
+
Every table this module renders is a projection of
|
|
4
|
+
``src/netgear_switch/registry.py`` and ``src/netgear_switch/capabilities.py``,
|
|
5
|
+
read at build time. Nothing here restates a fact that lives in the code, so a
|
|
6
|
+
model gaining a backend, an endpoint spec losing a page, or a CLI spec flipping
|
|
7
|
+
its verification flag changes these pages in the same commit -- and a table can
|
|
8
|
+
never quietly disagree with what the library will actually do.
|
|
9
|
+
|
|
10
|
+
Directives
|
|
11
|
+
----------
|
|
12
|
+
|
|
13
|
+
``.. ngsw-model-table::``
|
|
14
|
+
Every registered model: class, port counts, backends, verification status.
|
|
15
|
+
|
|
16
|
+
``.. ngsw-protocol-table::``
|
|
17
|
+
Model x backend support grid (the "which protocols can I use?" table).
|
|
18
|
+
|
|
19
|
+
``.. ngsw-operation-table:: [reads|writes]``
|
|
20
|
+
Operation x model grid; each cell names the backends that serve it.
|
|
21
|
+
|
|
22
|
+
``.. ngsw-model-support:: <model-key>``
|
|
23
|
+
One model in full: operation x backend, with the reason for every refusal.
|
|
24
|
+
|
|
25
|
+
``.. ngsw-support-gaps::``
|
|
26
|
+
Every operation a model supports on one backend but not another -- the
|
|
27
|
+
backend-parity gaps, listed rather than buried.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
34
|
+
|
|
35
|
+
from docutils.parsers.rst import directives
|
|
36
|
+
from sphinx.util import logging
|
|
37
|
+
from sphinx.util.docutils import SphinxDirective
|
|
38
|
+
|
|
39
|
+
from netgear_switch.capabilities import (
|
|
40
|
+
READ_OPERATIONS,
|
|
41
|
+
WRITE_OPERATIONS,
|
|
42
|
+
Support,
|
|
43
|
+
backends_for,
|
|
44
|
+
support,
|
|
45
|
+
)
|
|
46
|
+
from netgear_switch.registry import MODEL_ALIASES, MODELS, Backend, get_model
|
|
47
|
+
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
from collections.abc import Iterable, Sequence
|
|
50
|
+
|
|
51
|
+
from docutils import nodes
|
|
52
|
+
from sphinx.application import Sphinx
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
#: Compact per-backend labels used inside grid cells. The CLI backends share a
|
|
57
|
+
#: label because they are one command surface reached over three transports
|
|
58
|
+
#: (see ``src/netgear_switch/registry.py``'s ``Backend``).
|
|
59
|
+
_ABBREV = {
|
|
60
|
+
Backend.SNMP: "S",
|
|
61
|
+
Backend.NSDP: "N",
|
|
62
|
+
Backend.HTTP: "H",
|
|
63
|
+
Backend.SSH: "C",
|
|
64
|
+
Backend.TELNET: "C",
|
|
65
|
+
Backend.CONSOLE: "C",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_YES = "✓" # check mark
|
|
69
|
+
_NO = "—" # em dash
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def matrix_models() -> tuple[str, ...]:
|
|
73
|
+
"""The models the generated tables cover.
|
|
74
|
+
|
|
75
|
+
A model registered from a specification sheet with **no device access**
|
|
76
|
+
(``SwitchModel.verified is False``) is deliberately excluded. Listing one in
|
|
77
|
+
a support matrix would assert per-model, per-backend behaviour that nobody
|
|
78
|
+
has ever observed on that hardware -- the exact difference between a
|
|
79
|
+
measurement and an inference this project refuses to blur. They are named in
|
|
80
|
+
prose instead, so their existence is not hidden either.
|
|
81
|
+
"""
|
|
82
|
+
return tuple(key for key, model in MODELS.items() if model.verified)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def unverified_models() -> tuple[str, ...]:
|
|
86
|
+
"""Registered models with no capture behind them, excluded from the tables."""
|
|
87
|
+
return tuple(key for key, model in MODELS.items() if not model.verified)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _rst_escape(text: str) -> str:
|
|
91
|
+
"""Neutralise inline reST markup in generated cell text."""
|
|
92
|
+
for char in ("\\", "*", "`", "|", "_"):
|
|
93
|
+
text = text.replace(char, "\\" + char)
|
|
94
|
+
return text
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _list_table(
|
|
98
|
+
title: str,
|
|
99
|
+
header: Sequence[str],
|
|
100
|
+
rows: Iterable[Sequence[str]],
|
|
101
|
+
*,
|
|
102
|
+
widths: str = "",
|
|
103
|
+
) -> str:
|
|
104
|
+
"""Render a reST ``list-table`` block."""
|
|
105
|
+
out = [f".. list-table:: {title}" if title else ".. list-table::"]
|
|
106
|
+
out.append(" :header-rows: 1")
|
|
107
|
+
if widths:
|
|
108
|
+
out.append(f" :widths: {widths}")
|
|
109
|
+
out.append(" :class: ngsw-support-table")
|
|
110
|
+
out.append("")
|
|
111
|
+
for row in (header, *rows):
|
|
112
|
+
cells = list(row)
|
|
113
|
+
out.append(f" * - {cells[0]}")
|
|
114
|
+
out.extend(f" - {cell}" for cell in cells[1:])
|
|
115
|
+
out.append("")
|
|
116
|
+
return "\n".join(out)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class _GeneratedTable(SphinxDirective):
|
|
120
|
+
"""Base: build reST text, then parse it into nodes."""
|
|
121
|
+
|
|
122
|
+
has_content = False
|
|
123
|
+
|
|
124
|
+
def body(self) -> str: # pragma: no cover - overridden
|
|
125
|
+
raise NotImplementedError
|
|
126
|
+
|
|
127
|
+
def run(self) -> list[nodes.Node]:
|
|
128
|
+
return self.parse_text_to_nodes(self.body(), offset=self.lineno)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ModelTable(_GeneratedTable):
|
|
132
|
+
"""Every registered model and its headline attributes."""
|
|
133
|
+
|
|
134
|
+
def body(self) -> str:
|
|
135
|
+
aliases: dict[str, list[str]] = {}
|
|
136
|
+
for alias, canonical in MODEL_ALIASES.items():
|
|
137
|
+
aliases.setdefault(canonical, []).append(alias)
|
|
138
|
+
rows = []
|
|
139
|
+
for key in matrix_models():
|
|
140
|
+
model = MODELS[key]
|
|
141
|
+
also = aliases.get(key)
|
|
142
|
+
name = f"``{key}``"
|
|
143
|
+
if also:
|
|
144
|
+
name += " (also " + ", ".join(f"``{a}``" for a in sorted(also)) + ")"
|
|
145
|
+
rows.append(
|
|
146
|
+
[
|
|
147
|
+
name,
|
|
148
|
+
_rst_escape(model.display_name),
|
|
149
|
+
model.switch_class.value.replace("_", " "),
|
|
150
|
+
str(model.port_count),
|
|
151
|
+
str(model.poe_port_count),
|
|
152
|
+
", ".join(b.name for b in backends_for(model)),
|
|
153
|
+
"live-verified" if model.verified else "**unverified**",
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
return _list_table(
|
|
157
|
+
"",
|
|
158
|
+
[
|
|
159
|
+
"Key",
|
|
160
|
+
"Model",
|
|
161
|
+
"Class",
|
|
162
|
+
"Ports",
|
|
163
|
+
"PoE ports",
|
|
164
|
+
"Backends",
|
|
165
|
+
"Status",
|
|
166
|
+
],
|
|
167
|
+
rows,
|
|
168
|
+
widths="14 18 14 7 8 22 17",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ProtocolTable(_GeneratedTable):
|
|
173
|
+
"""Model x backend grid."""
|
|
174
|
+
|
|
175
|
+
_COLUMNS = (
|
|
176
|
+
Backend.SNMP,
|
|
177
|
+
Backend.NSDP,
|
|
178
|
+
Backend.HTTP,
|
|
179
|
+
Backend.SSH,
|
|
180
|
+
Backend.TELNET,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def body(self) -> str:
|
|
184
|
+
rows = []
|
|
185
|
+
for key in matrix_models():
|
|
186
|
+
model = MODELS[key]
|
|
187
|
+
cells = [_YES if b in model.backends else _NO for b in self._COLUMNS]
|
|
188
|
+
rows.append([f"``{key}``", *cells])
|
|
189
|
+
return _list_table(
|
|
190
|
+
"",
|
|
191
|
+
["Model", *(b.name for b in self._COLUMNS)],
|
|
192
|
+
rows,
|
|
193
|
+
widths="26 12 12 12 12 12",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class OperationTable(_GeneratedTable):
|
|
198
|
+
"""Operation x model grid; cells name the backends that serve the op."""
|
|
199
|
+
|
|
200
|
+
optional_arguments = 1
|
|
201
|
+
option_spec: ClassVar[dict[str, Any]] = {"kind": directives.unchanged}
|
|
202
|
+
|
|
203
|
+
def _operations(self) -> tuple[Any, ...]:
|
|
204
|
+
kind = (self.arguments[0] if self.arguments else "reads").lower()
|
|
205
|
+
if kind.startswith("read"):
|
|
206
|
+
return READ_OPERATIONS
|
|
207
|
+
if kind.startswith("write"):
|
|
208
|
+
return WRITE_OPERATIONS
|
|
209
|
+
raise self.error(f"ngsw-operation-table: unknown kind {kind!r}")
|
|
210
|
+
|
|
211
|
+
def body(self) -> str:
|
|
212
|
+
keys = list(matrix_models())
|
|
213
|
+
rows = []
|
|
214
|
+
for op in self._operations():
|
|
215
|
+
cells = []
|
|
216
|
+
for key in keys:
|
|
217
|
+
letters = []
|
|
218
|
+
for backend in backends_for(key):
|
|
219
|
+
if support(key, backend, op).supported:
|
|
220
|
+
letter = _ABBREV[backend]
|
|
221
|
+
if letter not in letters:
|
|
222
|
+
letters.append(letter)
|
|
223
|
+
cells.append(" ".join(letters) if letters else _NO)
|
|
224
|
+
rows.append([f"``{op.name}``", *cells])
|
|
225
|
+
return _list_table("", ["Operation", *keys], rows)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ModelSupportTable(_GeneratedTable):
|
|
229
|
+
"""One model in full: operation x backend, with every refusal explained."""
|
|
230
|
+
|
|
231
|
+
required_arguments = 1
|
|
232
|
+
|
|
233
|
+
def body(self) -> str:
|
|
234
|
+
key = self.arguments[0]
|
|
235
|
+
model = get_model(key)
|
|
236
|
+
backends = backends_for(model)
|
|
237
|
+
rows = []
|
|
238
|
+
notes: list[str] = []
|
|
239
|
+
seen: dict[str, int] = {}
|
|
240
|
+
for op in (*READ_OPERATIONS, *WRITE_OPERATIONS):
|
|
241
|
+
cells = []
|
|
242
|
+
for backend in backends:
|
|
243
|
+
cap = support(model, backend, op)
|
|
244
|
+
if cap.supported:
|
|
245
|
+
cells.append(_YES)
|
|
246
|
+
continue
|
|
247
|
+
reason = _rst_escape(cap.reason)
|
|
248
|
+
index = seen.get(reason)
|
|
249
|
+
if index is None:
|
|
250
|
+
index = len(notes) + 1
|
|
251
|
+
seen[reason] = index
|
|
252
|
+
notes.append(reason)
|
|
253
|
+
marker = _NO if cap.support is Support.UNSUPPORTED else "?"
|
|
254
|
+
# Citation labels, not numbered footnotes: several of these
|
|
255
|
+
# tables share a page, and plain ``[1]`` labels would collide
|
|
256
|
+
# across them. The model key makes each label unique.
|
|
257
|
+
cells.append(f"{marker} [{model.key}-{index}]_")
|
|
258
|
+
rows.append([f"``{op.name}``", op.summary, *cells])
|
|
259
|
+
|
|
260
|
+
table = _list_table(
|
|
261
|
+
"",
|
|
262
|
+
["Operation", "What it does", *(b.name for b in backends)],
|
|
263
|
+
rows,
|
|
264
|
+
)
|
|
265
|
+
if not notes:
|
|
266
|
+
return table
|
|
267
|
+
citations = "\n".join(
|
|
268
|
+
f".. [{model.key}-{i}] {text}" for i, text in enumerate(notes, 1)
|
|
269
|
+
)
|
|
270
|
+
return f"{table}\n{citations}\n"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class SupportGaps(_GeneratedTable):
|
|
274
|
+
"""Operations one backend of a model serves and another does not."""
|
|
275
|
+
|
|
276
|
+
def body(self) -> str:
|
|
277
|
+
rows = []
|
|
278
|
+
for key in matrix_models():
|
|
279
|
+
backends = backends_for(key)
|
|
280
|
+
if len(backends) < 2:
|
|
281
|
+
continue
|
|
282
|
+
for op in (*READ_OPERATIONS, *WRITE_OPERATIONS):
|
|
283
|
+
if op.backends is not None:
|
|
284
|
+
continue # fixed-backend ops are not a parity question
|
|
285
|
+
caps = [support(key, b, op) for b in backends]
|
|
286
|
+
yes = [c for c in caps if c.supported]
|
|
287
|
+
no = [c for c in caps if not c.supported]
|
|
288
|
+
if not yes or not no:
|
|
289
|
+
continue
|
|
290
|
+
rows.append(
|
|
291
|
+
[
|
|
292
|
+
f"``{key}``",
|
|
293
|
+
f"``{op.name}``",
|
|
294
|
+
", ".join(sorted({c.backend.name for c in yes})),
|
|
295
|
+
", ".join(sorted({c.backend.name for c in no})),
|
|
296
|
+
_rst_escape(no[0].reason),
|
|
297
|
+
]
|
|
298
|
+
)
|
|
299
|
+
if not rows:
|
|
300
|
+
return "Every backend of every model serves the same operations.\n"
|
|
301
|
+
return _list_table(
|
|
302
|
+
"",
|
|
303
|
+
["Model", "Operation", "Served by", "Not served by", "Why"],
|
|
304
|
+
rows,
|
|
305
|
+
widths="12 16 12 14 46",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
_BACKEND_BY_NAME = {b.name: b for b in Backend}
|
|
310
|
+
|
|
311
|
+
#: CLI is one command surface over three transports, so a "CLI" column means
|
|
312
|
+
#: whichever of SSH/TELNET/CONSOLE a model actually registers.
|
|
313
|
+
_CLI = (Backend.SSH, Backend.TELNET, Backend.CONSOLE)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _named_backends(name: str) -> tuple[Backend, ...]:
|
|
317
|
+
key = name.strip().upper()
|
|
318
|
+
if key == "CLI":
|
|
319
|
+
return _CLI
|
|
320
|
+
try:
|
|
321
|
+
return (_BACKEND_BY_NAME[key],)
|
|
322
|
+
except KeyError:
|
|
323
|
+
raise ValueError(f"unknown backend: {name!r}") from None
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _models_with(backends: tuple[Backend, ...]) -> tuple[str, ...]:
|
|
327
|
+
return tuple(
|
|
328
|
+
key
|
|
329
|
+
for key in matrix_models()
|
|
330
|
+
if any(b in MODELS[key].backends for b in backends)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class BackendModelTable(_GeneratedTable):
|
|
335
|
+
"""Which models expose one backend, and how they are reached."""
|
|
336
|
+
|
|
337
|
+
required_arguments = 1
|
|
338
|
+
|
|
339
|
+
def body(self) -> str:
|
|
340
|
+
backends = _named_backends(self.arguments[0])
|
|
341
|
+
rows = []
|
|
342
|
+
for key in _models_with(backends):
|
|
343
|
+
model = MODELS[key]
|
|
344
|
+
reached = ", ".join(
|
|
345
|
+
b.name for b in backends_for(model) if b in backends
|
|
346
|
+
)
|
|
347
|
+
rows.append(
|
|
348
|
+
[
|
|
349
|
+
# Absolute doc path: this table is included from
|
|
350
|
+
# docs/protocols/, so a relative target would resolve there.
|
|
351
|
+
f":doc:`{key} </models/{key}>`",
|
|
352
|
+
_rst_escape(model.display_name),
|
|
353
|
+
model.switch_class.value.replace("_", " "),
|
|
354
|
+
str(model.port_count),
|
|
355
|
+
reached,
|
|
356
|
+
]
|
|
357
|
+
)
|
|
358
|
+
if not rows:
|
|
359
|
+
return "No registered model exposes this backend.\n"
|
|
360
|
+
return _list_table(
|
|
361
|
+
"",
|
|
362
|
+
["Model", "Product", "Class", "Ports", "Reached over"],
|
|
363
|
+
rows,
|
|
364
|
+
widths="16 24 18 10 32",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class BackendOperationTable(_GeneratedTable):
|
|
369
|
+
"""Operation x model grid for ONE backend: what it can do, where."""
|
|
370
|
+
|
|
371
|
+
required_arguments = 1
|
|
372
|
+
|
|
373
|
+
def body(self) -> str:
|
|
374
|
+
backends = _named_backends(self.arguments[0])
|
|
375
|
+
keys = _models_with(backends)
|
|
376
|
+
if not keys:
|
|
377
|
+
return "No registered model exposes this backend.\n"
|
|
378
|
+
rows = []
|
|
379
|
+
notes: list[str] = []
|
|
380
|
+
seen: dict[str, int] = {}
|
|
381
|
+
label = self.arguments[0].strip().upper()
|
|
382
|
+
for op in (*READ_OPERATIONS, *WRITE_OPERATIONS):
|
|
383
|
+
cells = []
|
|
384
|
+
for key in keys:
|
|
385
|
+
backend = next(
|
|
386
|
+
(b for b in backends_for(key) if b in backends), None
|
|
387
|
+
)
|
|
388
|
+
if backend is None: # pragma: no cover - filtered by _models_with
|
|
389
|
+
cells.append(_NO)
|
|
390
|
+
continue
|
|
391
|
+
cap = support(key, backend, op)
|
|
392
|
+
if cap.supported:
|
|
393
|
+
cells.append(_YES)
|
|
394
|
+
continue
|
|
395
|
+
reason = _rst_escape(cap.reason)
|
|
396
|
+
index = seen.get(reason)
|
|
397
|
+
if index is None:
|
|
398
|
+
index = len(notes) + 1
|
|
399
|
+
seen[reason] = index
|
|
400
|
+
notes.append(reason)
|
|
401
|
+
cells.append(f"{_NO} [{label}-{index}]_")
|
|
402
|
+
# The facade class is documented under its defining module, so a
|
|
403
|
+
# `netgear_switch.SyncSwitch.x` target does not resolve and would
|
|
404
|
+
# render as plain text rather than a link.
|
|
405
|
+
rows.append(
|
|
406
|
+
[f":py:meth:`~netgear_switch.sync_api.SyncSwitch.{op.name}`", *cells]
|
|
407
|
+
)
|
|
408
|
+
table = _list_table("", ["Operation", *keys], rows)
|
|
409
|
+
if not notes:
|
|
410
|
+
return table
|
|
411
|
+
citations = "\n".join(
|
|
412
|
+
f".. [{label}-{i}] {text}" for i, text in enumerate(notes, 1)
|
|
413
|
+
)
|
|
414
|
+
return f"{table}\n{citations}\n"
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class ModelFacts(_GeneratedTable):
|
|
418
|
+
"""The registry record for one model, as a field list."""
|
|
419
|
+
|
|
420
|
+
required_arguments = 1
|
|
421
|
+
|
|
422
|
+
def body(self) -> str:
|
|
423
|
+
model = get_model(self.arguments[0])
|
|
424
|
+
aliases = sorted(k for k, v in MODEL_ALIASES.items() if v == model.key)
|
|
425
|
+
vendor = model.snmp_vendor_base or "none (standard MIBs only)"
|
|
426
|
+
dialect = {
|
|
427
|
+
"qbridge": "Q-BRIDGE static PortLists (read-modify-write)",
|
|
428
|
+
"fastpath_switchport": "FASTPATH vendor switchport table",
|
|
429
|
+
}.get(model.snmp_vlan_write, model.snmp_vlan_write)
|
|
430
|
+
rows = [
|
|
431
|
+
["Registry key", f"``{model.key}``"],
|
|
432
|
+
["Product name", _rst_escape(model.display_name)],
|
|
433
|
+
["Class", model.switch_class.value.replace("_", " ")],
|
|
434
|
+
["Ports", str(model.port_count)],
|
|
435
|
+
["PoE (PSE) ports", str(model.poe_port_count)],
|
|
436
|
+
[
|
|
437
|
+
"Backends",
|
|
438
|
+
", ".join(f"``{b.name}``" for b in backends_for(model)),
|
|
439
|
+
],
|
|
440
|
+
["SNMP vendor subtree", f"``{vendor}``"],
|
|
441
|
+
]
|
|
442
|
+
if Backend.SNMP in model.backends:
|
|
443
|
+
rows.append(["VLAN write dialect", dialect])
|
|
444
|
+
if model.snmp_vlan_split_membership_writes:
|
|
445
|
+
rows.append(
|
|
446
|
+
[
|
|
447
|
+
"VLAN write quirk",
|
|
448
|
+
"egress and untagged PortLists must travel in "
|
|
449
|
+
"SEPARATE PDUs, egress first",
|
|
450
|
+
]
|
|
451
|
+
)
|
|
452
|
+
if aliases:
|
|
453
|
+
rows.append(
|
|
454
|
+
["Aliases", ", ".join(f"``{a}``" for a in aliases)]
|
|
455
|
+
)
|
|
456
|
+
rows.append(
|
|
457
|
+
[
|
|
458
|
+
"MAC/FDB table",
|
|
459
|
+
"yes" if model.has_mac_table else "no",
|
|
460
|
+
]
|
|
461
|
+
)
|
|
462
|
+
return _list_table("", ["Field", "Value"], rows, widths="30 70")
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
class ModelPhoto(_GeneratedTable):
|
|
466
|
+
"""Render a photo of the switch, if one has been added to the repository.
|
|
467
|
+
|
|
468
|
+
Photos live at ``docs/_static/models/<key>.<ext>``. Nothing is emitted when
|
|
469
|
+
the file is absent -- a missing photo must not render as a broken image --
|
|
470
|
+
and every gap is reported once at the end of the build.
|
|
471
|
+
"""
|
|
472
|
+
|
|
473
|
+
required_arguments = 1
|
|
474
|
+
option_spec: ClassVar[dict[str, Any]] = {"alt": directives.unchanged}
|
|
475
|
+
|
|
476
|
+
_EXTENSIONS = (".jpg", ".jpeg", ".png", ".webp")
|
|
477
|
+
|
|
478
|
+
def body(self) -> str:
|
|
479
|
+
key = self.arguments[0]
|
|
480
|
+
model = get_model(key)
|
|
481
|
+
static = Path(str(self.env.srcdir)) / "_static" / "models"
|
|
482
|
+
for ext in self._EXTENSIONS:
|
|
483
|
+
if (static / f"{key}{ext}").is_file():
|
|
484
|
+
alt = self.options.get("alt", f"{model.display_name} switch")
|
|
485
|
+
return (
|
|
486
|
+
f".. figure:: /_static/models/{key}{ext}\n"
|
|
487
|
+
f" :alt: {alt}\n"
|
|
488
|
+
" :align: center\n"
|
|
489
|
+
" :class: ngsw-model-photo\n\n"
|
|
490
|
+
f" {_rst_escape(model.display_name)}\n"
|
|
491
|
+
)
|
|
492
|
+
MISSING_PHOTOS.add(key)
|
|
493
|
+
return ""
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
#: Models whose photo slot was empty during this build; reported at the end.
|
|
497
|
+
MISSING_PHOTOS: set[str] = set()
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
class UnverifiedNote(_GeneratedTable):
|
|
501
|
+
"""Name the models the tables leave out, and say why."""
|
|
502
|
+
|
|
503
|
+
def body(self) -> str:
|
|
504
|
+
keys = unverified_models()
|
|
505
|
+
if not keys:
|
|
506
|
+
return ""
|
|
507
|
+
listed = ", ".join(f"``{k}`` ({MODELS[k].display_name})" for k in keys)
|
|
508
|
+
return (
|
|
509
|
+
".. note::\n\n"
|
|
510
|
+
f" The registry also carries {listed}, which the tables above "
|
|
511
|
+
"deliberately leave out.\n"
|
|
512
|
+
" No device of either kind has ever been reachable from this "
|
|
513
|
+
"project, so there is no\n"
|
|
514
|
+
" capture, no live run, and nothing measured to report. They are "
|
|
515
|
+
"registered only so a\n"
|
|
516
|
+
" caller can construct a facade for them; treat every one of "
|
|
517
|
+
"their fields as a\n"
|
|
518
|
+
" specification-sheet guess until a real capture exists.\n"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _report_missing_photos(app: Sphinx, exception: Exception | None) -> None:
|
|
523
|
+
"""Say which model pages have no photo yet, rather than failing the build.
|
|
524
|
+
|
|
525
|
+
A photo is content the repository may simply not have; the build must not
|
|
526
|
+
break over it, but the gap should be visible instead of silently invisible.
|
|
527
|
+
"""
|
|
528
|
+
if exception is not None or not MISSING_PHOTOS:
|
|
529
|
+
return
|
|
530
|
+
logger.info(
|
|
531
|
+
"ngsw: no photo for %d model(s): %s "
|
|
532
|
+
"(add docs/_static/models/<key>.jpg to fill the slot)",
|
|
533
|
+
len(MISSING_PHOTOS),
|
|
534
|
+
", ".join(sorted(MISSING_PHOTOS)),
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def setup(app: Sphinx) -> dict[str, Any]:
|
|
539
|
+
app.add_directive("ngsw-unverified-note", UnverifiedNote)
|
|
540
|
+
app.add_directive("ngsw-model-table", ModelTable)
|
|
541
|
+
app.add_directive("ngsw-protocol-table", ProtocolTable)
|
|
542
|
+
app.add_directive("ngsw-operation-table", OperationTable)
|
|
543
|
+
app.add_directive("ngsw-model-support", ModelSupportTable)
|
|
544
|
+
app.add_directive("ngsw-model-facts", ModelFacts)
|
|
545
|
+
app.add_directive("ngsw-model-photo", ModelPhoto)
|
|
546
|
+
app.add_directive("ngsw-backend-models", BackendModelTable)
|
|
547
|
+
app.add_directive("ngsw-backend-operations", BackendOperationTable)
|
|
548
|
+
app.add_directive("ngsw-support-gaps", SupportGaps)
|
|
549
|
+
app.connect("build-finished", _report_missing_photos)
|
|
550
|
+
return {"version": "1.0", "parallel_read_safe": True, "parallel_write_safe": True}
|
|
@@ -17,7 +17,7 @@ Switch and credential resolution
|
|
|
17
17
|
--------------------------------
|
|
18
18
|
|
|
19
19
|
The shared path the CLI *and* the MCP server both use to turn arguments,
|
|
20
|
-
environment variables and an inventory into a ready
|
|
20
|
+
environment variables and an inventory into a ready :py:obj:`~netgear_switch.sync_api.SyncSwitch`.
|
|
21
21
|
|
|
22
22
|
.. automodule:: netgear_switch.cli.resolve
|
|
23
23
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Facades
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
:py:obj:`~netgear_switch.sync_api.SyncSwitch` and :py:obj:`~netgear_switch.aio_api.AsyncSwitch` are the public entry points. They expose the same
|
|
5
5
|
operations with the same arguments, and both resolve exactly one backend per
|
|
6
6
|
call — see :doc:`../guide/concepts`.
|
|
7
7
|
|
|
@@ -5,7 +5,7 @@ Complete reference for every module in the ``netgear_switch`` package,
|
|
|
5
5
|
generated from the source.
|
|
6
6
|
|
|
7
7
|
Most callers need only :doc:`core` (the data model, the registry, the
|
|
8
|
-
capability oracle) and :doc:`facades` (
|
|
8
|
+
capability oracle) and :doc:`facades` (:py:obj:`~netgear_switch.sync_api.SyncSwitch` and :py:obj:`~netgear_switch.aio_api.AsyncSwitch`).
|
|
9
9
|
The rest is there for anyone reading the source, extending a backend, or
|
|
10
10
|
building on the protocol layer directly.
|
|
11
11
|
|