python-netgear-switch-library 0.0.post327__tar.gz → 0.0.post330__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.post327 → python_netgear_switch_library-0.0.post330}/PKG-INFO +1 -1
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/_ext/support_tables.py +313 -18
- python_netgear_switch_library-0.0.post330/docs/_static/models/gs105pe.png +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/models/gs110emx.png +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/models/gs728tpp.png +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/models/gsm7228ps.jpg +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/models/m4300-16x.png +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/models/m4300-24x.png +0 -0
- python_netgear_switch_library-0.0.post330/docs/_static/ngsw.css +68 -0
- python_netgear_switch_library-0.0.post330/docs/protocols/http/cheetah-form.rst +60 -0
- python_netgear_switch_library-0.0.post330/docs/protocols/http/cheetah-v1.rst +57 -0
- python_netgear_switch_library-0.0.post330/docs/protocols/http/gambit.rst +49 -0
- python_netgear_switch_library-0.0.post330/docs/protocols/http/merge-hash-cgi.rst +59 -0
- python_netgear_switch_library-0.0.post330/docs/protocols/http/xml-api.rst +54 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/protocols/http.rst +24 -46
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/_version.py +2 -2
- python_netgear_switch_library-0.0.post330/tests/test_docs_coverage.py +97 -0
- python_netgear_switch_library-0.0.post327/docs/_static/ngsw.css +0 -38
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/.github/workflows/ci.yml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/.github/workflows/deb.yml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/.github/workflows/publish-pypi.yml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/.gitignore +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/.readthedocs.yaml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/CLAUDE.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/LICENSE +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/README.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/RELEASING.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/debian/changelog +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/debian/control +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/debian/copyright +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/debian/rules +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/debian/source/format +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/Makefile +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/_ext/filelinks.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/core.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/facades.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/index.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/mcp.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/operations.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/protocols.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/transport.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/api/virtual.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/conf.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/development.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/fake/index.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/fake/internals.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/fake/serving.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/fake/testing.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/concepts.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/configuration.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/installation.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/principles.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/quickstart.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/reading.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/guide/writing.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/index.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/mcp.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gs105pe.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gs110emx.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gs305ep.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gs728tpp.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gsm7228ps.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/gsm7252ps.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/index.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/m4300-16x.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/m4300-24x.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/models/support.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/protocols/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/protocols/index.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/protocols/nsdp.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/protocols/snmp.rst +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/packaging/apt-index.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/packaging/ci-build.sh +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/packaging/deb-version.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/pyproject.toml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/aio_api.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/capture.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/context.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/format.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/main.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/resolve.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli/safety.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/cli_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/config.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/errors.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/http_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/http_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/mcp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/mcp/server.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/models.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/nsdp_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/cli/commands.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/cli/parse.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/crypt.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/forms.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/parse.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/session.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/http/types.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/client.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/types.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/nsdp/write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/snmp/client.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/snmp/oids.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/snmp/parse.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/protocols/snmp/write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/py.typed +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/registry.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/snmp_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/aio/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/cli/console.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/cli/session.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/cli/ssh.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/cli/telnet.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/http/client.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/sync/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/cli_fastpath.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/cli.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/http.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/mibview.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/faces/snmp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/seed.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/server.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/state.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/src/netgear_switch/virtual/web_m4300.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/capture_parity.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_capture.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_cli_integration.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_control_poe_port.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_control_vlan_ip.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_format.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_main_framework.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_new_commands.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_op_coverage.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_prompt_wiring.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_read_commands.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_resolve.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_safety.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/cli/test_serve.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/conftest.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/captures/gsm7228ps.json +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/captures/gsm7252ps.json +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/captures/m4300-16x.json +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/captures/m4300-24x.json +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_membership.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_status.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_login.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_redirect.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_login.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_membership.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_login.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_addresstable.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_ports.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/http_specs.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/cli/test_cli_parse.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/http/test_crypt.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/http/test_endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/http/test_forms.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/http/test_parse.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/nsdp/test_auth.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/nsdp/test_parsers.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/nsdp/test_protocol.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/nsdp/test_write_frame.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_client.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_oids.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_ports.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_system_info.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_parse_vlans.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/protocols/snmp/test_write_encode.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_aio_api.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_cli_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_cli_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_cli_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_config.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_cross_backend_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_deb_version.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_errors.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_facade_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_vlan_membership.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_http_xui_writes.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_import.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_mcp_server.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_models.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_models_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_nsdp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_nsdp_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_public_api.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_quality_gates.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_registry.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_snmp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_snmp_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_version.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/test_write_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/_fastpath_fake.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_cli_console.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_cli_scp_cert.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_cli_telnet.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_http_client.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_nsdp_udp_sync.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_set_transport.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/transport/test_value_parity.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_cli_cross_backend.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_gsm7228ps_seed.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_m4300_seeds.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_mibview.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_mutable_state.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_nsdp_state.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_serve_daemon.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_snmp_write_face.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_state_seed.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_switchport_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_virtual_http_face.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_virtual_nsdp_face.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_virtual_snmp_face.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/tests/virtual/test_web_projection.py +0 -0
- {python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/uv.lock +0 -0
{python_netgear_switch_library-0.0.post327 → python_netgear_switch_library-0.0.post330}/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.post330
|
|
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
|
|
@@ -32,9 +32,12 @@ from __future__ import annotations
|
|
|
32
32
|
from pathlib import Path
|
|
33
33
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
34
34
|
|
|
35
|
+
from docutils import nodes
|
|
35
36
|
from docutils.parsers.rst import directives
|
|
37
|
+
from sphinx.transforms.post_transforms import SphinxPostTransform
|
|
36
38
|
from sphinx.util import logging
|
|
37
39
|
from sphinx.util.docutils import SphinxDirective
|
|
40
|
+
from sphinx.util.nodes import make_refnode
|
|
38
41
|
|
|
39
42
|
from netgear_switch.capabilities import (
|
|
40
43
|
READ_OPERATIONS,
|
|
@@ -43,12 +46,12 @@ from netgear_switch.capabilities import (
|
|
|
43
46
|
backends_for,
|
|
44
47
|
support,
|
|
45
48
|
)
|
|
49
|
+
from netgear_switch.protocols.http.endpoints import HTTP_SPECS
|
|
46
50
|
from netgear_switch.registry import MODEL_ALIASES, MODELS, Backend, get_model
|
|
47
51
|
|
|
48
52
|
if TYPE_CHECKING:
|
|
49
53
|
from collections.abc import Iterable, Sequence
|
|
50
54
|
|
|
51
|
-
from docutils import nodes
|
|
52
55
|
from sphinx.application import Sphinx
|
|
53
56
|
|
|
54
57
|
logger = logging.getLogger(__name__)
|
|
@@ -87,6 +90,151 @@ def unverified_models() -> tuple[str, ...]:
|
|
|
87
90
|
return tuple(key for key, model in MODELS.items() if not model.verified)
|
|
88
91
|
|
|
89
92
|
|
|
93
|
+
#: The page documenting each backend. SSH, TELNET and CONSOLE are three
|
|
94
|
+
#: transports over one command surface, so all three land on the CLI page.
|
|
95
|
+
_BACKEND_PAGES = {
|
|
96
|
+
Backend.SNMP: "protocols/snmp",
|
|
97
|
+
Backend.NSDP: "protocols/nsdp",
|
|
98
|
+
Backend.HTTP: "protocols/http",
|
|
99
|
+
Backend.SSH: "protocols/cli",
|
|
100
|
+
Backend.TELNET: "protocols/cli",
|
|
101
|
+
Backend.CONSOLE: "protocols/cli",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#: Each web-UI login scheme gets its own page. They share a transport and
|
|
105
|
+
#: nothing else: different login mechanics, different session handling and
|
|
106
|
+
#: different page sets, which is why one "HTTP" page could not describe them.
|
|
107
|
+
_HTTP_SCHEME_PAGES = {
|
|
108
|
+
"MERGE_HASH_CGI": "protocols/http/merge-hash-cgi",
|
|
109
|
+
"GAMBIT": "protocols/http/gambit",
|
|
110
|
+
"CHEETAH_FORM": "protocols/http/cheetah-form",
|
|
111
|
+
"CHEETAH_V1": "protocols/http/cheetah-v1",
|
|
112
|
+
"XML_API": "protocols/http/xml-api",
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#: Literal text -> the page that documents it. Everything a generated table or a
|
|
116
|
+
#: docstring can name: every model key, every alias, every backend name, every
|
|
117
|
+
#: web-UI login scheme, and ``CLI`` for the command surface as a whole.
|
|
118
|
+
_LINKABLE: dict[str, str] = {
|
|
119
|
+
**{key: f"models/{key}" for key in MODELS},
|
|
120
|
+
**{alias: f"models/{key}" for alias, key in MODEL_ALIASES.items()},
|
|
121
|
+
**{backend.name: page for backend, page in _BACKEND_PAGES.items()},
|
|
122
|
+
**_HTTP_SCHEME_PAGES,
|
|
123
|
+
"CLI": "protocols/cli",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ReferenceLinks(SphinxPostTransform):
|
|
128
|
+
"""Link every inline literal that names something this documentation defines.
|
|
129
|
+
|
|
130
|
+
Three kinds of name are resolved, in order: a switch model key or alias, a
|
|
131
|
+
backend name, and any Python object autodoc has documented (matched on its
|
|
132
|
+
full dotted path, so there is no ambiguity to guess at).
|
|
133
|
+
|
|
134
|
+
Doing this in a post-transform rather than at each use is what makes the
|
|
135
|
+
linking total, and it exists because per-use linking kept missing cases:
|
|
136
|
+
|
|
137
|
+
* The generated tables wrote a model key as a bare literal in three of the
|
|
138
|
+
seven builders, so ``docs/models/index.rst`` listed eight model keys and
|
|
139
|
+
linked none of them, while the protocol pages linked theirs.
|
|
140
|
+
* ``conf.py`` sets ``default_role = "literal"``, so every single-backtick
|
|
141
|
+
name renders as text. The module lists at the bottom of each protocol
|
|
142
|
+
page are written that way -- ```netgear_switch.http_read``` -- and were
|
|
143
|
+
silently not links, even though the class names beside them, written with
|
|
144
|
+
an explicit role, were.
|
|
145
|
+
|
|
146
|
+
Neither failure could be caught by ``nitpicky``: an unresolved *reference*
|
|
147
|
+
fails the build, but a literal that should have been a reference is
|
|
148
|
+
indistinguishable from ordinary text. Only linking them automatically
|
|
149
|
+
closes that gap.
|
|
150
|
+
|
|
151
|
+
Matching is exact and confined to inline literals, so prose about "the HTTP
|
|
152
|
+
backend" is untouched while ``` ``HTTP`` ``` becomes a link.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
# After filelinks' post-transform (400), whose repository links are already
|
|
156
|
+
# `reference` nodes by this point and are skipped below.
|
|
157
|
+
default_priority = 410
|
|
158
|
+
|
|
159
|
+
#: The facade a bare operation name should resolve to when several classes
|
|
160
|
+
#: define it. ``get_vlans`` is documented on nine classes -- both facades and
|
|
161
|
+
#: every reader -- and the one a reader of prose means is the public
|
|
162
|
+
#: synchronous entry point.
|
|
163
|
+
_PREFERRED_OWNER = "netgear_switch.sync_api.SyncSwitch."
|
|
164
|
+
|
|
165
|
+
def _short_index(self) -> dict[str, list[str]]:
|
|
166
|
+
"""Last path component -> the full names that end with it."""
|
|
167
|
+
objects = self.env.domaindata.get("py", {}).get("objects", {})
|
|
168
|
+
index: dict[str, list[str]] = {}
|
|
169
|
+
for name, entry in objects.items():
|
|
170
|
+
if getattr(entry, "aliased", False):
|
|
171
|
+
continue # a duplicate of the canonical entry
|
|
172
|
+
index.setdefault(name.rsplit(".", 1)[-1], []).append(name)
|
|
173
|
+
return index
|
|
174
|
+
|
|
175
|
+
def _python_target(self, text: str) -> tuple[str, str] | None:
|
|
176
|
+
"""``(docname, node_id)`` for a documented Python object, if any.
|
|
177
|
+
|
|
178
|
+
An exact dotted path always wins. A bare name is resolved only when it
|
|
179
|
+
is shaped like an API name -- CamelCase, SCREAMING_CASE or containing an
|
|
180
|
+
underscore -- and resolves unambiguously, or to the preferred facade.
|
|
181
|
+
|
|
182
|
+
The shape test is what keeps the linking honest. ``port``, ``name``,
|
|
183
|
+
``host`` and ``model`` are all documented dataclass fields *and* ordinary
|
|
184
|
+
words that appear as literals throughout these docstrings; linking every
|
|
185
|
+
occurrence to one arbitrary dataclass would be worse than not linking.
|
|
186
|
+
"""
|
|
187
|
+
objects = self.env.domaindata.get("py", {}).get("objects", {})
|
|
188
|
+
entry = objects.get(text)
|
|
189
|
+
if entry is not None:
|
|
190
|
+
return entry.docname, entry.node_id
|
|
191
|
+
if not ("_" in text or (text[:1].isupper() and text.isalnum())):
|
|
192
|
+
return None
|
|
193
|
+
candidates = self._short_index().get(text)
|
|
194
|
+
if not candidates:
|
|
195
|
+
return None
|
|
196
|
+
if len(candidates) > 1:
|
|
197
|
+
preferred = [c for c in candidates if c.startswith(self._PREFERRED_OWNER)]
|
|
198
|
+
if len(preferred) != 1:
|
|
199
|
+
return None
|
|
200
|
+
candidates = preferred
|
|
201
|
+
entry = objects[candidates[0]]
|
|
202
|
+
return entry.docname, entry.node_id
|
|
203
|
+
|
|
204
|
+
def run(self, **kwargs: Any) -> None:
|
|
205
|
+
counts = self.env.domaindata.setdefault("ngsw_reflinks", {"linked": 0})
|
|
206
|
+
for node in list(self.document.findall(nodes.literal)):
|
|
207
|
+
if isinstance(node.parent, nodes.reference):
|
|
208
|
+
continue # already a link (a resolved xref, or filelinks')
|
|
209
|
+
if "xref" in node["classes"]:
|
|
210
|
+
continue # an unresolved cross-reference, not a plain literal
|
|
211
|
+
text = node.astext().strip()
|
|
212
|
+
|
|
213
|
+
docname = _LINKABLE.get(text)
|
|
214
|
+
if docname is not None and docname in self.env.found_docs:
|
|
215
|
+
node_id = ""
|
|
216
|
+
else:
|
|
217
|
+
found = self._python_target(text)
|
|
218
|
+
if found is None:
|
|
219
|
+
continue
|
|
220
|
+
docname, node_id = found
|
|
221
|
+
|
|
222
|
+
# A page linking to itself is noise; an anchor on the same page is
|
|
223
|
+
# not -- it jumps to the definition.
|
|
224
|
+
if docname == self.env.docname and not node_id:
|
|
225
|
+
continue
|
|
226
|
+
link = make_refnode(
|
|
227
|
+
self.app.builder,
|
|
228
|
+
self.env.docname,
|
|
229
|
+
docname,
|
|
230
|
+
node_id or None,
|
|
231
|
+
node.deepcopy(),
|
|
232
|
+
)
|
|
233
|
+
link["classes"].append("ngsw-ref")
|
|
234
|
+
node.replace_self(link)
|
|
235
|
+
counts["linked"] += 1
|
|
236
|
+
|
|
237
|
+
|
|
90
238
|
def _rst_escape(text: str) -> str:
|
|
91
239
|
"""Neutralise inline reST markup in generated cell text."""
|
|
92
240
|
for char in ("\\", "*", "`", "|", "_"):
|
|
@@ -99,14 +247,22 @@ def _list_table(
|
|
|
99
247
|
header: Sequence[str],
|
|
100
248
|
rows: Iterable[Sequence[str]],
|
|
101
249
|
*,
|
|
102
|
-
|
|
250
|
+
classes: Sequence[str] = (),
|
|
103
251
|
) -> str:
|
|
104
|
-
"""Render a reST ``list-table`` block.
|
|
252
|
+
"""Render a reST ``list-table`` block.
|
|
253
|
+
|
|
254
|
+
No ``:widths:`` is emitted, deliberately. ``docs/_static/ngsw.css`` gives
|
|
255
|
+
these tables ``white-space: nowrap`` inside a horizontally scrolling box,
|
|
256
|
+
so the browser's own table layout sizes every column to its content --
|
|
257
|
+
which is always right, and cannot go stale the way a hand-picked
|
|
258
|
+
percentage does when a model name or a backend list changes.
|
|
259
|
+
|
|
260
|
+
``classes`` adds CSS classes beyond ``ngsw-support-table``. Only a table
|
|
261
|
+
whose last column holds prose wants ``ngsw-reason-table``.
|
|
262
|
+
"""
|
|
105
263
|
out = [f".. list-table:: {title}" if title else ".. list-table::"]
|
|
106
264
|
out.append(" :header-rows: 1")
|
|
107
|
-
|
|
108
|
-
out.append(f" :widths: {widths}")
|
|
109
|
-
out.append(" :class: ngsw-support-table")
|
|
265
|
+
out.append(" :class: " + " ".join(("ngsw-support-table", *classes)))
|
|
110
266
|
out.append("")
|
|
111
267
|
for row in (header, *rows):
|
|
112
268
|
cells = list(row)
|
|
@@ -149,7 +305,7 @@ class ModelTable(_GeneratedTable):
|
|
|
149
305
|
model.switch_class.value.replace("_", " "),
|
|
150
306
|
str(model.port_count),
|
|
151
307
|
str(model.poe_port_count),
|
|
152
|
-
", ".join(b.name for b in backends_for(model)),
|
|
308
|
+
", ".join(f"``{b.name}``" for b in backends_for(model)),
|
|
153
309
|
"live-verified" if model.verified else "**unverified**",
|
|
154
310
|
]
|
|
155
311
|
)
|
|
@@ -165,7 +321,6 @@ class ModelTable(_GeneratedTable):
|
|
|
165
321
|
"Status",
|
|
166
322
|
],
|
|
167
323
|
rows,
|
|
168
|
-
widths="14 18 14 7 8 22 17",
|
|
169
324
|
)
|
|
170
325
|
|
|
171
326
|
|
|
@@ -188,9 +343,8 @@ class ProtocolTable(_GeneratedTable):
|
|
|
188
343
|
rows.append([f"``{key}``", *cells])
|
|
189
344
|
return _list_table(
|
|
190
345
|
"",
|
|
191
|
-
["Model", *(b.name for b in self._COLUMNS)],
|
|
346
|
+
["Model", *(f"``{b.name}``" for b in self._COLUMNS)],
|
|
192
347
|
rows,
|
|
193
|
-
widths="26 12 12 12 12 12",
|
|
194
348
|
)
|
|
195
349
|
|
|
196
350
|
|
|
@@ -259,7 +413,7 @@ class ModelSupportTable(_GeneratedTable):
|
|
|
259
413
|
|
|
260
414
|
table = _list_table(
|
|
261
415
|
"",
|
|
262
|
-
["Operation", "What it does", *(b.name for b in backends)],
|
|
416
|
+
["Operation", "What it does", *(f"``{b.name}``" for b in backends)],
|
|
263
417
|
rows,
|
|
264
418
|
)
|
|
265
419
|
if not notes:
|
|
@@ -302,7 +456,7 @@ class SupportGaps(_GeneratedTable):
|
|
|
302
456
|
"",
|
|
303
457
|
["Model", "Operation", "Served by", "Not served by", "Why"],
|
|
304
458
|
rows,
|
|
305
|
-
|
|
459
|
+
classes=("ngsw-reason-table",),
|
|
306
460
|
)
|
|
307
461
|
|
|
308
462
|
|
|
@@ -341,12 +495,12 @@ class BackendModelTable(_GeneratedTable):
|
|
|
341
495
|
rows = []
|
|
342
496
|
for key in _models_with(backends):
|
|
343
497
|
model = MODELS[key]
|
|
344
|
-
reached = ", ".join(
|
|
498
|
+
reached = ", ".join(
|
|
499
|
+
f"``{b.name}``" for b in backends_for(model) if b in backends
|
|
500
|
+
)
|
|
345
501
|
rows.append(
|
|
346
502
|
[
|
|
347
|
-
|
|
348
|
-
# docs/protocols/, so a relative target would resolve there.
|
|
349
|
-
f":doc:`{key} </models/{key}>`",
|
|
503
|
+
f"``{key}``",
|
|
350
504
|
_rst_escape(model.display_name),
|
|
351
505
|
model.switch_class.value.replace("_", " "),
|
|
352
506
|
str(model.port_count),
|
|
@@ -359,7 +513,6 @@ class BackendModelTable(_GeneratedTable):
|
|
|
359
513
|
"",
|
|
360
514
|
["Model", "Product", "Class", "Ports", "Reached over"],
|
|
361
515
|
rows,
|
|
362
|
-
widths="16 24 18 10 32",
|
|
363
516
|
)
|
|
364
517
|
|
|
365
518
|
|
|
@@ -410,6 +563,136 @@ class BackendOperationTable(_GeneratedTable):
|
|
|
410
563
|
return f"{table}\n{citations}\n"
|
|
411
564
|
|
|
412
565
|
|
|
566
|
+
#: Every page an ``HttpModelSpec`` can carry a path for, in the order a reader
|
|
567
|
+
#: meets them. ``None`` on a spec means that firmware ships no such page, which
|
|
568
|
+
#: is why the generated coverage table can show a real gap rather than implying
|
|
569
|
+
#: the operation is merely unimplemented here.
|
|
570
|
+
_HTTP_PAGE_FIELDS = (
|
|
571
|
+
("Dashboard / port status", "dashboard_path"),
|
|
572
|
+
("Port statistics", "stats_path"),
|
|
573
|
+
("PoE status", "poe_status_path"),
|
|
574
|
+
("PoE configuration", "poe_config_path"),
|
|
575
|
+
("VLAN configuration", "vlan_config_path"),
|
|
576
|
+
("VLAN membership", "vlan_membership_path"),
|
|
577
|
+
("PVIDs", "pvid_path"),
|
|
578
|
+
("MAC / FDB table", "mac_table_path"),
|
|
579
|
+
("LLDP neighbours", "lldp_path"),
|
|
580
|
+
("System info", "sysinfo_path"),
|
|
581
|
+
("Management IP", "mgmt_ip_path"),
|
|
582
|
+
("Port configuration", "port_config_path"),
|
|
583
|
+
("Certificate upload", "cert_upload_path"),
|
|
584
|
+
("Reboot", "reboot_path"),
|
|
585
|
+
("Logout", "logout_path"),
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _scheme_specs(scheme: str) -> list[Any]:
|
|
590
|
+
return [s for s in HTTP_SPECS.values() if s.scheme.name == scheme]
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def _transport(spec: Any) -> str:
|
|
594
|
+
"""How the web UI is reached: scheme and port, where they are not default."""
|
|
595
|
+
proto = "HTTPS" if spec.secure else "HTTP"
|
|
596
|
+
return f"{proto}, port {spec.web_port}" if spec.web_port else proto
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class HttpSchemeTable(_GeneratedTable):
|
|
600
|
+
"""Every web-UI login scheme, with the models and dialects behind it."""
|
|
601
|
+
|
|
602
|
+
def body(self) -> str:
|
|
603
|
+
rows = []
|
|
604
|
+
for scheme, page in _HTTP_SCHEME_PAGES.items():
|
|
605
|
+
specs = _scheme_specs(scheme)
|
|
606
|
+
if not specs: # pragma: no cover - every scheme has a model today
|
|
607
|
+
continue
|
|
608
|
+
dialects = sorted({s.html_dialect.name for s in specs})
|
|
609
|
+
rows.append(
|
|
610
|
+
[
|
|
611
|
+
f":doc:`{scheme} </{page}>`",
|
|
612
|
+
", ".join(f"``{s.model_key}``" for s in specs),
|
|
613
|
+
", ".join(f"``{d}``" for d in dialects),
|
|
614
|
+
_transport(specs[0]),
|
|
615
|
+
]
|
|
616
|
+
)
|
|
617
|
+
return _list_table(
|
|
618
|
+
"",
|
|
619
|
+
["Login scheme", "Switches", "HTML dialect", "Reached over"],
|
|
620
|
+
rows,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
class HttpSchemeFacts(_GeneratedTable):
|
|
625
|
+
"""One login scheme: the models on it and how each one is talked to."""
|
|
626
|
+
|
|
627
|
+
required_arguments = 1
|
|
628
|
+
|
|
629
|
+
def body(self) -> str:
|
|
630
|
+
scheme = self.arguments[0].strip().upper()
|
|
631
|
+
specs = _scheme_specs(scheme)
|
|
632
|
+
if not specs:
|
|
633
|
+
raise self.error(f"ngsw-http-scheme: no model uses scheme {scheme!r}")
|
|
634
|
+
rows = []
|
|
635
|
+
for spec in specs:
|
|
636
|
+
if spec.session_token_field:
|
|
637
|
+
session = f"token, ``{spec.session_token_field}``"
|
|
638
|
+
elif spec.cookie_name:
|
|
639
|
+
session = f"cookie, ``{spec.cookie_name}``"
|
|
640
|
+
else:
|
|
641
|
+
session = "none"
|
|
642
|
+
rows.append(
|
|
643
|
+
[
|
|
644
|
+
f"``{spec.model_key}``",
|
|
645
|
+
f"``{spec.html_dialect.name}``",
|
|
646
|
+
_transport(spec),
|
|
647
|
+
f"``{spec.login_path}``",
|
|
648
|
+
", ".join(
|
|
649
|
+
f"``{f}``"
|
|
650
|
+
for f in (spec.username_field, spec.password_field)
|
|
651
|
+
if f
|
|
652
|
+
),
|
|
653
|
+
session,
|
|
654
|
+
]
|
|
655
|
+
)
|
|
656
|
+
return _list_table(
|
|
657
|
+
"",
|
|
658
|
+
[
|
|
659
|
+
"Switch",
|
|
660
|
+
"HTML dialect",
|
|
661
|
+
"Reached over",
|
|
662
|
+
"Login path",
|
|
663
|
+
"Fields",
|
|
664
|
+
"Session",
|
|
665
|
+
],
|
|
666
|
+
rows,
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
class HttpSchemePages(_GeneratedTable):
|
|
671
|
+
"""Which pages each model on a scheme actually ships."""
|
|
672
|
+
|
|
673
|
+
required_arguments = 1
|
|
674
|
+
|
|
675
|
+
def body(self) -> str:
|
|
676
|
+
scheme = self.arguments[0].strip().upper()
|
|
677
|
+
specs = _scheme_specs(scheme)
|
|
678
|
+
if not specs:
|
|
679
|
+
raise self.error(f"ngsw-http-scheme-pages: no model uses {scheme!r}")
|
|
680
|
+
rows = []
|
|
681
|
+
for label, field in _HTTP_PAGE_FIELDS:
|
|
682
|
+
cells = []
|
|
683
|
+
for spec in specs:
|
|
684
|
+
path = getattr(spec, field, None)
|
|
685
|
+
cells.append(f"``{path}``" if path else _NO)
|
|
686
|
+
if all(cell == _NO for cell in cells):
|
|
687
|
+
continue
|
|
688
|
+
rows.append([label, *cells])
|
|
689
|
+
return _list_table(
|
|
690
|
+
"",
|
|
691
|
+
["Page", *(f"``{s.model_key}``" for s in specs)],
|
|
692
|
+
rows,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
|
|
413
696
|
class ModelFacts(_GeneratedTable):
|
|
414
697
|
"""The registry record for one model, as a field list."""
|
|
415
698
|
|
|
@@ -453,7 +736,7 @@ class ModelFacts(_GeneratedTable):
|
|
|
453
736
|
"yes" if model.has_mac_table else "no",
|
|
454
737
|
]
|
|
455
738
|
)
|
|
456
|
-
return _list_table("", ["Field", "Value"], rows
|
|
739
|
+
return _list_table("", ["Field", "Value"], rows)
|
|
457
740
|
|
|
458
741
|
|
|
459
742
|
class ModelPhoto(_GeneratedTable):
|
|
@@ -594,6 +877,13 @@ def _report_missing_photos(app: Sphinx, exception: Exception | None) -> None:
|
|
|
594
877
|
)
|
|
595
878
|
|
|
596
879
|
|
|
880
|
+
def _report_reference_links(app: Sphinx, exception: Exception | None) -> None:
|
|
881
|
+
if exception is not None:
|
|
882
|
+
return
|
|
883
|
+
linked = app.env.domaindata.get("ngsw_reflinks", {}).get("linked", 0)
|
|
884
|
+
logger.info("ngsw: linked %d model/backend/API reference(s)", linked)
|
|
885
|
+
|
|
886
|
+
|
|
597
887
|
def setup(app: Sphinx) -> dict[str, Any]:
|
|
598
888
|
app.add_directive("ngsw-unverified-note", UnverifiedNote)
|
|
599
889
|
app.add_directive("ngsw-model-table", ModelTable)
|
|
@@ -606,5 +896,10 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
|
|
606
896
|
app.add_directive("ngsw-backend-models", BackendModelTable)
|
|
607
897
|
app.add_directive("ngsw-backend-operations", BackendOperationTable)
|
|
608
898
|
app.add_directive("ngsw-support-gaps", SupportGaps)
|
|
899
|
+
app.add_directive("ngsw-http-scheme-table", HttpSchemeTable)
|
|
900
|
+
app.add_directive("ngsw-http-scheme", HttpSchemeFacts)
|
|
901
|
+
app.add_directive("ngsw-http-scheme-pages", HttpSchemePages)
|
|
902
|
+
app.add_post_transform(ReferenceLinks)
|
|
609
903
|
app.connect("build-finished", _report_missing_photos)
|
|
904
|
+
app.connect("build-finished", _report_reference_links)
|
|
610
905
|
return {"version": "1.0", "parallel_read_safe": True, "parallel_write_safe": True}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Generated support tables are wide by nature -- one column per switch model.
|
|
2
|
+
Let them scroll inside their own box rather than stretch the page. */
|
|
3
|
+
table.ngsw-support-table {
|
|
4
|
+
display: block;
|
|
5
|
+
overflow-x: auto;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
white-space: nowrap;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
table.ngsw-support-table td,
|
|
11
|
+
table.ngsw-support-table th {
|
|
12
|
+
vertical-align: top;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* A table whose LAST column carries full sentences (the parity-gaps "Why"
|
|
16
|
+
column): let it wrap, and give it room to be worth wrapping.
|
|
17
|
+
|
|
18
|
+
Scoped to that one class deliberately. As a blanket
|
|
19
|
+
`.ngsw-support-table td:last-child` rule this forced 20rem onto the last
|
|
20
|
+
column of every generated table -- so the model table's Status column, whose
|
|
21
|
+
widest value is the 13-character "live-verified", was rendered 320px wide. */
|
|
22
|
+
table.ngsw-reason-table td:last-child {
|
|
23
|
+
white-space: normal;
|
|
24
|
+
min-width: 20rem;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Links produced by docs/_ext/filelinks.py: a file path that is also a link.
|
|
28
|
+
Keep the monospace look, add the underline links normally get. */
|
|
29
|
+
a.repofile code,
|
|
30
|
+
a.repofile tt {
|
|
31
|
+
text-decoration: underline;
|
|
32
|
+
text-decoration-style: dotted;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Product photos (docs/_ext/support_tables.py's ngsw-model-photo).
|
|
36
|
+
|
|
37
|
+
The class sits on the <img>, not the <figure>: reST's `figure` directive
|
|
38
|
+
applies its :class: to the image node, unlike the port map below, which is
|
|
39
|
+
raw HTML and carries the class on the figure itself. */
|
|
40
|
+
img.ngsw-model-photo {
|
|
41
|
+
max-width: 100%;
|
|
42
|
+
height: auto;
|
|
43
|
+
border-radius: 6px;
|
|
44
|
+
/* These are product shots on a white background. Painting that background on
|
|
45
|
+
explicitly, rather than leaving whatever the image happens to carry, keeps
|
|
46
|
+
them from reading as a bright cut-out in the dark theme. */
|
|
47
|
+
background: #fff;
|
|
48
|
+
padding: 0.75rem;
|
|
49
|
+
}
|
|
50
|
+
figure:has(> img.ngsw-model-photo) {
|
|
51
|
+
margin: 1rem 0 1.5rem;
|
|
52
|
+
}
|
|
53
|
+
figure:has(> img.ngsw-model-photo) figcaption {
|
|
54
|
+
font-size: 0.85em;
|
|
55
|
+
opacity: 0.75;
|
|
56
|
+
margin-top: 0.4rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Generated port maps (docs/_ext/support_tables.py's ngsw-model-diagram). */
|
|
60
|
+
figure.ngsw-port-map {
|
|
61
|
+
margin: 1rem 0 1.5rem;
|
|
62
|
+
text-align: center;
|
|
63
|
+
}
|
|
64
|
+
figure.ngsw-port-map figcaption {
|
|
65
|
+
font-size: 0.85em;
|
|
66
|
+
opacity: 0.75;
|
|
67
|
+
margin-top: 0.4rem;
|
|
68
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
CHEETAH_FORM — the Cheetah login form
|
|
2
|
+
=====================================
|
|
3
|
+
|
|
4
|
+
The managed switches' web UI. A plain HTML form: username and password posted
|
|
5
|
+
in the clear to ``/base/cheetah_login.html``, answered with a session cookie.
|
|
6
|
+
No nonce, no hashing.
|
|
7
|
+
|
|
8
|
+
Switches
|
|
9
|
+
--------
|
|
10
|
+
|
|
11
|
+
.. ngsw-http-scheme:: CHEETAH_FORM
|
|
12
|
+
|
|
13
|
+
Two dialects on one login
|
|
14
|
+
-------------------------
|
|
15
|
+
|
|
16
|
+
``gsm7252ps`` and ``gsm7228ps`` log in identically and then serve the same
|
|
17
|
+
Cheetah XE page grid, so both reuse the ``parse_xe_*`` parsers for ports,
|
|
18
|
+
statistics, PVIDs, VLANs, PoE and LLDP.
|
|
19
|
+
|
|
20
|
+
They are separate dialects because two pages genuinely differ on the S3300: its
|
|
21
|
+
MAC table has shifted columns and escaped ``1/gN`` port names, and its sensor
|
|
22
|
+
page differs too. Everything else is shared.
|
|
23
|
+
|
|
24
|
+
The XUI shape worth knowing
|
|
25
|
+
---------------------------
|
|
26
|
+
|
|
27
|
+
These pages carry two ``<FORM>`` elements, and the interesting fields are not
|
|
28
|
+
always the visible ones. Two consequences:
|
|
29
|
+
|
|
30
|
+
**Errors arrive with HTTP 200.** A write that "succeeded" by status code may have
|
|
31
|
+
failed in the body, so the library reads the body's status fields rather than
|
|
32
|
+
trusting the response code.
|
|
33
|
+
|
|
34
|
+
**A write must replay the whole form**, including hidden and list-navigation
|
|
35
|
+
fields. This is where "the GSM7252PS refuses PoE writes over HTTP" came from —
|
|
36
|
+
it was not firmware. The form needs a list-unit field (``v_1_1_1``) that the
|
|
37
|
+
writer was not sending. With it, PoE writes apply cleanly.
|
|
38
|
+
|
|
39
|
+
That is worth stating plainly because it is the exact failure mode this project
|
|
40
|
+
treats as a bug in our code until proven otherwise: a missing field is rejected
|
|
41
|
+
by the firmware in a way that looks indistinguishable from a device limitation.
|
|
42
|
+
|
|
43
|
+
Pages
|
|
44
|
+
-----
|
|
45
|
+
|
|
46
|
+
.. ngsw-http-scheme-pages:: CHEETAH_FORM
|
|
47
|
+
|
|
48
|
+
Certificate upload
|
|
49
|
+
------------------
|
|
50
|
+
|
|
51
|
+
Both models accept an HTTPS certificate through this UI, and the S3300's flow is
|
|
52
|
+
grounded in a working prior implementation rather than inferred. See
|
|
53
|
+
:py:meth:`~netgear_switch.sync_api.SyncSwitch.upload_certificate`.
|
|
54
|
+
|
|
55
|
+
API
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
* ``netgear_switch.protocols.http.parse`` — the ``parse_xe_*`` parsers.
|
|
59
|
+
* ``netgear_switch.protocols.http.forms`` — form scraping and replay.
|
|
60
|
+
* ``netgear_switch.virtual.web_fastpath_xui`` — the mock's XUI renderer.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
CHEETAH_V1 — the M4300 ``/v1`` UI
|
|
2
|
+
=================================
|
|
3
|
+
|
|
4
|
+
The M4300's web interface. It posts both a username and a password, and then
|
|
5
|
+
guards every subsequent request with a ``Referer`` header rather than a CSRF
|
|
6
|
+
token field.
|
|
7
|
+
|
|
8
|
+
Switches
|
|
9
|
+
--------
|
|
10
|
+
|
|
11
|
+
.. ngsw-http-scheme:: CHEETAH_V1
|
|
12
|
+
|
|
13
|
+
How the login works
|
|
14
|
+
-------------------
|
|
15
|
+
|
|
16
|
+
**Both fields are required.** ``uname`` and ``pwd`` are posted together;
|
|
17
|
+
password-only login is rejected.
|
|
18
|
+
|
|
19
|
+
**The CSRF defence is the Referer header.** Requests without a plausible
|
|
20
|
+
``Referer`` are refused, which is why the spec carries ``needs_referer``. This
|
|
21
|
+
is unusual enough that a client written against any of the other four schemes
|
|
22
|
+
will fail here for reasons that look like an authentication problem.
|
|
23
|
+
|
|
24
|
+
**The session id has a short life.** The RID the UI issues expires in about 60
|
|
25
|
+
seconds of inactivity, so a long-running read sequence must expect to
|
|
26
|
+
re-authenticate rather than assume one login covers the whole run.
|
|
27
|
+
|
|
28
|
+
The two SKUs are not reached the same way
|
|
29
|
+
-----------------------------------------
|
|
30
|
+
|
|
31
|
+
They share a dialect and a login, and differ in transport: the M4300-16X serves
|
|
32
|
+
its UI over **HTTPS on port 49152**, while the M4300-24X is plain HTTP on the
|
|
33
|
+
default port. The generated table above states each one's transport rather than
|
|
34
|
+
letting the shared dialect imply a shared endpoint.
|
|
35
|
+
|
|
36
|
+
That difference is measured, not assumed — the same pair of switches also runs
|
|
37
|
+
different firmware (12.0.19.15 vs 12.0.13.8) and behaves differently on SNMP
|
|
38
|
+
VLAN writes. Treating either as a proxy for the other has produced wrong answers
|
|
39
|
+
in this project before.
|
|
40
|
+
|
|
41
|
+
A dead end worth recording
|
|
42
|
+
--------------------------
|
|
43
|
+
|
|
44
|
+
The M4300's read-page URLs cannot be recovered by reading its HTML: the server
|
|
45
|
+
builds its menu dynamically, so static analysis of the login page yields
|
|
46
|
+
nothing. The paths in the spec came from driving the live UI.
|
|
47
|
+
|
|
48
|
+
Pages
|
|
49
|
+
-----
|
|
50
|
+
|
|
51
|
+
.. ngsw-http-scheme-pages:: CHEETAH_V1
|
|
52
|
+
|
|
53
|
+
API
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
* ``netgear_switch.protocols.http.session`` — Referer handling and the RID.
|
|
57
|
+
* ``netgear_switch.virtual.web_m4300`` — the mock's renderer for these pages.
|