python-netgear-switch-library 0.0.post332__tar.gz → 0.0.post336__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.post332 → python_netgear_switch_library-0.0.post336}/PKG-INFO +1 -1
- python_netgear_switch_library-0.0.post336/docs/superpowers/specs/2026-08-02-snmp-surface-findings.md +151 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/_version.py +2 -2
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli_read.py +9 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli_write.py +50 -2
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/nsdp_read.py +16 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/cli/commands.py +12 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/cli/parse.py +41 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/snmp/oids.py +17 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/snmp/parse.py +21 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/snmp_read.py +12 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/snmp_write.py +63 -16
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/cli_fastpath.py +38 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/cli.py +11 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/seed.py +9 -1
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/state.py +7 -0
- python_netgear_switch_library-0.0.post336/tests/virtual/test_hostname.py +96 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/.github/workflows/ci.yml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/.github/workflows/deb.yml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/.github/workflows/publish-pypi.yml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/.gitignore +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/.readthedocs.yaml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/CLAUDE.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/LICENSE +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/README.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/RELEASING.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/debian/changelog +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/debian/control +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/debian/copyright +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/debian/rules +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/debian/source/format +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/Makefile +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_ext/filelinks.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_ext/support_tables.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gs105pe.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gs110emx.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gs305ep.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gs728tpp.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gsm7228ps.jpg +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/gsm7252ps.jpg +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/m4300-16x.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/models/m4300-24x.png +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/_static/ngsw.css +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/core.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/facades.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/index.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/mcp.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/operations.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/protocols.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/transport.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/api/virtual.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/conf.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/development.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/fake/index.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/fake/internals.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/fake/serving.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/fake/testing.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/concepts.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/configuration.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/installation.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/principles.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/quickstart.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/reading.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/guide/writing.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/index.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/mcp.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gs105pe.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gs110emx.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gs305ep.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gs728tpp.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gsm7228ps.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/gsm7252ps.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/index.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/m4300-16x.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/m4300-24x.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/models/support.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/cli.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http/cheetah-form.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http/cheetah-v1.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http/gambit.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http/merge-hash-cgi.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http/xml-api.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/http.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/index.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/nsdp.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/protocols/snmp.rst +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/packaging/apt-index.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/packaging/ci-build.sh +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/packaging/deb-version.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/pyproject.toml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/aio_api.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/capture.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/context.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/format.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/main.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/resolve.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/cli/safety.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/config.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/errors.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/http_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/http_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/mcp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/mcp/server.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/models.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/crypt.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/forms.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/parse.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/session.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/http/types.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/client.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/types.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/nsdp/write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/snmp/client.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/protocols/snmp/write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/py.typed +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/registry.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/aio/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/cli/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/cli/console.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/cli/session.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/cli/ssh.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/cli/telnet.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/http/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/http/client.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/sync/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/__init__.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/http.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/mibview.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/faces/snmp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/server.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/src/netgear_switch/virtual/web_m4300.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/capture_parity.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_capture.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_cli_integration.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_control_poe_port.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_control_vlan_ip.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_format.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_main_framework.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_new_commands.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_op_coverage.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_prompt_wiring.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_read_commands.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_resolve.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_safety.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/cli/test_serve.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/conftest.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/captures/gsm7228ps.json +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/captures/gsm7252ps.json +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/captures/m4300-16x.json +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/captures/m4300-24x.json +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_membership.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_status.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_login.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_redirect.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_login.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_membership.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_login.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_addresstable.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_ports.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_portstats.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_pvid.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_sysinfo.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/http_specs.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/cli/test_cli_parse.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/http/test_crypt.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/http/test_endpoints.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/http/test_forms.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/http/test_parse.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/nsdp/test_auth.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/nsdp/test_parsers.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/nsdp/test_protocol.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/nsdp/test_write_frame.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_client.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_oids.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_ports.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_system_info.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_parse_vlans.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/protocols/snmp/test_write_encode.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_aio_api.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_capabilities.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_cli_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_cli_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_cli_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_config.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_cross_backend_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_deb_version.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_docs_coverage.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_errors.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_facade_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_dispatch.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_vlan_membership.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_http_xui_writes.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_import.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_mcp_server.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_models.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_models_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_nsdp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_nsdp_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_nsdp_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_public_api.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_quality_gates.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_registry.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_snmp_integration.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_snmp_read.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_snmp_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_sync_api.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_version.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/test_write_equivalence.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/_fastpath_fake.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_cli_console.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_cli_scp_cert.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_cli_telnet.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_http_client.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_nsdp_udp_sync.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_set_transport.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_snmp_pysnmp.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/transport/test_value_parity.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_cli_cross_backend.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_gsm7228ps_seed.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_m4300_seeds.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_mibview.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_mutable_state.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_nsdp_state.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_serve_daemon.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_snmp_write_face.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_state_seed.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_switchport_vlan_write.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_virtual_http_face.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_virtual_nsdp_face.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_virtual_snmp_face.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/tests/virtual/test_web_projection.py +0 -0
- {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/uv.lock +0 -0
{python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post336}/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.post336
|
|
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
|
python_netgear_switch_library-0.0.post336/docs/superpowers/specs/2026-08-02-snmp-surface-findings.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Measured SNMP surface — hostname, logging, users, ports
|
|
2
|
+
|
|
3
|
+
Ground truth for expanding the library's functional coverage. Everything here
|
|
4
|
+
was **measured**, not inferred: each OID below was read from a live switch, and
|
|
5
|
+
where a CLI equivalent exists the two were compared.
|
|
6
|
+
|
|
7
|
+
Captured 2026-08-02 by `snmpbulkwalk -v2c -c public -On <host> 1.3.6.1`.
|
|
8
|
+
All five reachable switches answer on community `public`.
|
|
9
|
+
|
|
10
|
+
| Host | Model | OIDs walked |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| 10.1.5.11 | `gsm7228ps` (S3300-52X) | 103071 |
|
|
13
|
+
| 10.1.5.13 | `m4300-24x` | 190448 |
|
|
14
|
+
| 10.1.5.20 | `m4300-16x` | (walk in progress) |
|
|
15
|
+
| 10.1.5.22 | `gsm7252ps` | (walk in progress) |
|
|
16
|
+
| 10.2.5.10 | `gs728tpp` | (walk in progress) |
|
|
17
|
+
|
|
18
|
+
## How the vendor OIDs were located
|
|
19
|
+
|
|
20
|
+
Not by guessing at a MIB — no Netgear MIB file is available here, and
|
|
21
|
+
`snmptranslate` cannot name even the standard subtrees because only the
|
|
22
|
+
NET-SNMP MIBs are installed.
|
|
23
|
+
|
|
24
|
+
Instead: read the switch's configuration over its CLI first, then **search the
|
|
25
|
+
walk for those exact values**. The syslog server `10.1.5.1`, the local port
|
|
26
|
+
`514`, the severity `info` and the username `admin` are all distinctive enough
|
|
27
|
+
to pin the column they live in. This is faster than the walk/change/walk diff
|
|
28
|
+
and just as grounded, because the CLI output and the OID value are the same
|
|
29
|
+
fact observed twice.
|
|
30
|
+
|
|
31
|
+
It also caught a mislabelling that a MIB-free guess would have shipped: the
|
|
32
|
+
subtree `4526.<family>.17` looks like logging until you notice it holds port
|
|
33
|
+
`123` and the string `"NTP Bits: 0x1c6900a8"`. **`4526.x.17` is SNTP, not
|
|
34
|
+
syslog.** The syslog host `10.1.5.1` appears in both subtrees because the same
|
|
35
|
+
box is this fleet's NTP server and its syslog server.
|
|
36
|
+
|
|
37
|
+
## Functional map of the surface (gsm7228ps, 87684 parsed varbinds)
|
|
38
|
+
|
|
39
|
+
| Varbinds | Area | Library covers it? |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| 42612 | NETGEAR vendor `4526.11` | a few OIDs only |
|
|
42
|
+
| 35777 | RMON-MIB `1.3.6.1.2.1.16` | no |
|
|
43
|
+
| 1502 | IF-MIB `ifTable` | yes (ports, admin/oper status) |
|
|
44
|
+
| 1501 | IF-MIB `ifXTable` | partly (HC counters, `ifHighSpeed`, `ifAlias`) |
|
|
45
|
+
| 1416 | **EtherLike-MIB `dot3` — duplex** | **no** |
|
|
46
|
+
| 1329 | P-BRIDGE-MIB | no |
|
|
47
|
+
| 880 | ENTITY-MIB | yes (fan/PSU inventory) |
|
|
48
|
+
| 780 | BRIDGE-MIB FDB | yes (`get_macs`) |
|
|
49
|
+
| 563 | Q-BRIDGE-MIB | yes (VLANs) |
|
|
50
|
+
| 537 | POWER-ETHERNET-MIB | yes (PoE) |
|
|
51
|
+
| 206 | SNMPv2-MIB system | partly — `sysName` **not** exposed |
|
|
52
|
+
| 194 | SNMP-USER-BASED-SM / VACM | no (SNMP users, distinct from local users) |
|
|
53
|
+
|
|
54
|
+
## Hostname
|
|
55
|
+
|
|
56
|
+
`sysName` — `1.3.6.1.2.1.1.5.0` — reads on **all five** switches. Standard MIB,
|
|
57
|
+
so it does not depend on a vendor subtree, and it is the one hostname source
|
|
58
|
+
that works on the `gs728tpp` (which has no vendor OIDs at all).
|
|
59
|
+
|
|
60
|
+
**`sysName` is not the same value as the configured `hostname`.** Measured on
|
|
61
|
+
`m4300-16x` (10.1.5.20):
|
|
62
|
+
|
|
63
|
+
| Source | Value |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `sysName` (SNMP) | `sw-netgear-m4300-16x-poe-s2` |
|
|
66
|
+
| `show hosts` → "Host name" | `sw-netgear-m4300-16x-poe-s2` |
|
|
67
|
+
| `show running-config \| include hostname` | `manage-sw-netgear-m4300-16x-poe-s2` |
|
|
68
|
+
|
|
69
|
+
So `sysName` tracks `show hosts`, and the configured `hostname` is a *different*
|
|
70
|
+
string. On `gsm7252ps` the divergence is starker: `show running-config | include
|
|
71
|
+
hostname` returns **nothing at all** while `sysName` and `show hosts` both
|
|
72
|
+
report `sw-netgear-gsm7252ps-s1`.
|
|
73
|
+
|
|
74
|
+
A `get_hostname` that reads `sysName` on SNMP and `show running-config` on the
|
|
75
|
+
CLI would therefore return **different answers for the same switch** — which the
|
|
76
|
+
cross-backend equivalence tests would rightly fail. The CLI reader must parse
|
|
77
|
+
`show hosts`, not `show running-config`.
|
|
78
|
+
|
|
79
|
+
## Logging / syslog
|
|
80
|
+
|
|
81
|
+
`4526.<family>.14` on **both** vendor families, with the same column layout —
|
|
82
|
+
`4526.11.14` on the S3300 family and `4526.10.14` on FASTPATH.
|
|
83
|
+
|
|
84
|
+
| OID (family arc as `x`) | Meaning | Measured |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `4526.x.14.1.1.1.0` | console logging admin mode | `1` |
|
|
87
|
+
| `4526.x.14.1.1.4.0` | console severity filter | `1` |
|
|
88
|
+
| `4526.x.14.1.2.1.0` | buffered logging admin mode | `2` |
|
|
89
|
+
| `4526.x.14.1.2.2.0` | buffered severity filter | `3` |
|
|
90
|
+
| `4526.x.14.1.4.1.0` | **syslog admin mode** | `1` (m4300), `2` (S3300) |
|
|
91
|
+
| `4526.x.14.1.4.3.0` | **syslog local port** | `514` |
|
|
92
|
+
| `4526.x.14.1.4.4.0` | syslog max hosts | `8` |
|
|
93
|
+
| `4526.x.14.1.6.2.0` | email-alert from-address | `switch@netgear.com` |
|
|
94
|
+
| `4526.x.14.2.1.0` | log messages received | `82948` |
|
|
95
|
+
| `4526.x.14.2.2.0` | log messages dropped | `0` |
|
|
96
|
+
| `4526.x.14.3.1.0` | buffered log entry count | `82081` |
|
|
97
|
+
| `4526.x.14.3.2.1.2.<n>` | buffered log message text | `<13> Aug 2 05:14:36 …` |
|
|
98
|
+
|
|
99
|
+
### Syslog host table — `4526.10.14.1.4.5.1.<col>.<index>`
|
|
100
|
+
|
|
101
|
+
Present on `m4300-24x`, absent on `gsm7228ps` (which has no syslog host
|
|
102
|
+
configured — consistent, not a MIB difference).
|
|
103
|
+
|
|
104
|
+
| Col | Meaning | Measured | CLI cross-check |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| 2 | address type | `1` | — |
|
|
107
|
+
| 3 | **host address** | `10.1.5.1` | `10.1.5.1` |
|
|
108
|
+
| 4 | **port** | `514` | `514` |
|
|
109
|
+
| 5 | **severity** | `6` | `info` |
|
|
110
|
+
| 7 | **row status / state** | `1` | `Active` |
|
|
111
|
+
| 9, 10 | counters | `1`, `0` | — |
|
|
112
|
+
|
|
113
|
+
Every field of `show logging hosts` is accounted for by a column, and the two
|
|
114
|
+
agree. That is a genuine SNMP↔CLI cross-verification of the same state.
|
|
115
|
+
|
|
116
|
+
### CLI equivalents (measured on 3 FASTPATH switches)
|
|
117
|
+
|
|
118
|
+
- `show logging` — globals. The m4300s additionally report a source interface.
|
|
119
|
+
- `show logging hosts` — the host table. **The column set differs by firmware**:
|
|
120
|
+
the m4300s emit `Index/IP/Severity/Port/Status/Mode/Auth/Cert#`, the
|
|
121
|
+
`gsm7252ps` emits only the first five. A parser fixed to one shape breaks on
|
|
122
|
+
the other.
|
|
123
|
+
- `show syslog` does **not** exist — `% Invalid input detected` on all three.
|
|
124
|
+
|
|
125
|
+
## Local users
|
|
126
|
+
|
|
127
|
+
Found on the S3300 family at `4526.11.1.2.1.3.2.1.<col>.0`:
|
|
128
|
+
|
|
129
|
+
| Col | Measured |
|
|
130
|
+
|---|---|
|
|
131
|
+
| 2 | `"admin"` (username) |
|
|
132
|
+
| 4, 5, 6, 7, 9 | `2`, `1`, `2`, `1`, `0` |
|
|
133
|
+
| 11 | `15` (privilege level) |
|
|
134
|
+
|
|
135
|
+
**The FASTPATH family does not mirror this**: `4526.10.1.2.1.3` is empty on the
|
|
136
|
+
m4300-24x, and no varbind anywhere in its 156077 parsed values equals the string
|
|
137
|
+
`admin`. Local users therefore look absent from FASTPATH SNMP and will need the
|
|
138
|
+
CLI (`show users`) — but that is a statement about one firmware on one SKU and
|
|
139
|
+
must be confirmed on the other FASTPATH units before it is recorded as a limit.
|
|
140
|
+
|
|
141
|
+
Note `1.3.6.1.6.3.15.1.2.2.1.3…` also yields `admin` — that is an **SNMPv3 USM
|
|
142
|
+
user**, a different thing from a local login account. They must not be conflated.
|
|
143
|
+
|
|
144
|
+
## Not yet located
|
|
145
|
+
|
|
146
|
+
- Service protocols (HTTP/HTTPS/telnet/SSH enable state and ports). Searching
|
|
147
|
+
the vendor subtree for the values 22/23/80/443 returns only false positives,
|
|
148
|
+
where the number is a table *index* rather than a configured port.
|
|
149
|
+
- Port speed/duplex **configuration** (as opposed to `ifHighSpeed`, which is the
|
|
150
|
+
negotiated rate). EtherLike-MIB `dot3` at `1.3.6.1.2.1.10.7` carries 1416
|
|
151
|
+
varbinds on the S3300 and is the obvious candidate for duplex status.
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0, '
|
|
21
|
+
__version__ = version = '0.0.post336'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 'post336')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -103,5 +103,14 @@ class CliReader:
|
|
|
103
103
|
def get_mgmt_ip(self) -> MgmtIpConfig:
|
|
104
104
|
return parse.parse_mgmt_ip(self.session.run(self._spec.network_cmd))
|
|
105
105
|
|
|
106
|
+
def get_hostname(self) -> str:
|
|
107
|
+
"""The switch's host name, from ``show hosts``.
|
|
108
|
+
|
|
109
|
+
See ``parse.parse_hostname`` for why this command and not
|
|
110
|
+
``show running-config``: the two report different values, and only this
|
|
111
|
+
one agrees with SNMP's ``sysName``.
|
|
112
|
+
"""
|
|
113
|
+
return parse.parse_hostname(self.session.run(self._spec.hosts_cmd))
|
|
114
|
+
|
|
106
115
|
def identify(self) -> DetectedModel:
|
|
107
116
|
return parse.parse_version(self.session.run(self._spec.version_cmd), MODELS)
|
|
@@ -561,8 +561,10 @@ class CliWriter:
|
|
|
561
561
|
timeouts=timeouts or _DEFAULT_POE_TIMEOUTS,
|
|
562
562
|
sleep=sleep,
|
|
563
563
|
clock=clock,
|
|
564
|
-
recovered=lambda st:
|
|
565
|
-
|
|
564
|
+
recovered=lambda st: (
|
|
565
|
+
st is not None
|
|
566
|
+
and st.detect in (PoEDetect.DELIVERING, PoEDetect.SEARCHING)
|
|
567
|
+
),
|
|
566
568
|
timeout_message=(
|
|
567
569
|
f"PoE port {port} still in FAULT after clear within {{timeout}}s"
|
|
568
570
|
),
|
|
@@ -596,6 +598,52 @@ class CliWriter:
|
|
|
596
598
|
def _port_status(self, port: int) -> PortStatus | None:
|
|
597
599
|
return next((p for p in self._reader.get_ports() if p.port == port), None)
|
|
598
600
|
|
|
601
|
+
# --- host name -----------------------------------------------------------
|
|
602
|
+
|
|
603
|
+
def set_hostname(self, name: str, *, force: bool = False) -> None:
|
|
604
|
+
"""Set the switch's host name, via global-config ``hostname <name>``.
|
|
605
|
+
|
|
606
|
+
Not force-gated: renaming cannot strand the switch and is reversible by
|
|
607
|
+
writing the old name back, unlike ``set_mgmt_ip`` below which drops the
|
|
608
|
+
session that issues it. ``force`` is accepted so the signature matches
|
|
609
|
+
every other writer.
|
|
610
|
+
|
|
611
|
+
Verified by re-reading ``show hosts``. That command, rather than
|
|
612
|
+
``show running-config``, is deliberate and load-bearing here: the two
|
|
613
|
+
report different values on real hardware (see
|
|
614
|
+
``protocols.cli.parse.parse_hostname``), and ``show hosts`` is the one
|
|
615
|
+
that agrees with SNMP, so a CLI write verified this way is also
|
|
616
|
+
observable over SNMP.
|
|
617
|
+
|
|
618
|
+
Nothing is persisted -- no ``write memory`` -- exactly like every other
|
|
619
|
+
write in this module.
|
|
620
|
+
"""
|
|
621
|
+
del force # accepted for a uniform writer signature; nothing to gate
|
|
622
|
+
if not name.strip():
|
|
623
|
+
# `hostname` with no argument is rejected by the device itself
|
|
624
|
+
# ("Command not found / Incomplete command"), so sending it would
|
|
625
|
+
# surface as a confusing CliCommandError from deep in _in_mode.
|
|
626
|
+
# CLEARING a host name is a different operation -- FASTPATH spells
|
|
627
|
+
# it `no hostname` -- and that has not been driven against real
|
|
628
|
+
# hardware here, so it is not offered rather than guessed at.
|
|
629
|
+
raise ValueError(
|
|
630
|
+
"hostname must not be empty; clearing a switch's host name is "
|
|
631
|
+
"`no hostname` on FASTPATH, which this library does not "
|
|
632
|
+
"implement because it has not been verified on a device"
|
|
633
|
+
)
|
|
634
|
+
before = self._reader.get_hostname()
|
|
635
|
+
self._in_mode(
|
|
636
|
+
[self._spec.configure_cmd],
|
|
637
|
+
[self._spec.hostname_config_cmd.format(name=name)],
|
|
638
|
+
)
|
|
639
|
+
after = self._reader.get_hostname()
|
|
640
|
+
if after != name:
|
|
641
|
+
raise WriteVerificationError(
|
|
642
|
+
f"`show hosts` reports {after!r} after setting hostname {name!r}",
|
|
643
|
+
before=before,
|
|
644
|
+
after=after,
|
|
645
|
+
)
|
|
646
|
+
|
|
599
647
|
# --- management IP + reboot --------------------------------------------
|
|
600
648
|
|
|
601
649
|
def set_mgmt_ip(
|
|
@@ -216,6 +216,18 @@ class NsdpReader:
|
|
|
216
216
|
dev = self._device([Tag.PORT_PVID])
|
|
217
217
|
return [(p.port_id, p.vlan_id) for p in dev.port_pvids]
|
|
218
218
|
|
|
219
|
+
def get_hostname(self) -> str:
|
|
220
|
+
"""The switch's host name, from the NSDP ``HOSTNAME`` tag (0x0003).
|
|
221
|
+
|
|
222
|
+
The same value SNMP would report as ``sysName`` -- except that a Plus
|
|
223
|
+
switch has no SNMP agent at all, which is why NSDP carries it.
|
|
224
|
+
|
|
225
|
+
A switch that has never been named answers the tag with nothing, and
|
|
226
|
+
that is a real answer rather than a failure: unlike SNMP's ``sysName``,
|
|
227
|
+
which is a mandatory scalar, this tag is genuinely optional.
|
|
228
|
+
"""
|
|
229
|
+
return self._device([Tag.HOSTNAME]).hostname or ""
|
|
230
|
+
|
|
219
231
|
def get_mgmt_ip(self) -> MgmtIpConfig:
|
|
220
232
|
return _mgmt(
|
|
221
233
|
self._device([Tag.IP_ADDRESS, Tag.NETMASK, Tag.GATEWAY, Tag.DHCP_MODE])
|
|
@@ -270,6 +282,10 @@ class AsyncNsdpReader:
|
|
|
270
282
|
dev = await self._device([Tag.PORT_PVID])
|
|
271
283
|
return [(p.port_id, p.vlan_id) for p in dev.port_pvids]
|
|
272
284
|
|
|
285
|
+
async def get_hostname(self) -> str:
|
|
286
|
+
"""Async twin of ``NsdpReader.get_hostname`` -- see there."""
|
|
287
|
+
return (await self._device([Tag.HOSTNAME])).hostname or ""
|
|
288
|
+
|
|
273
289
|
async def get_mgmt_ip(self) -> MgmtIpConfig:
|
|
274
290
|
return _mgmt(
|
|
275
291
|
await self._device(
|
|
@@ -94,6 +94,18 @@ class CliModelSpec:
|
|
|
94
94
|
environment_cmd: str = "show environment"
|
|
95
95
|
network_cmd: str = "show network"
|
|
96
96
|
interface_stats_cmd: str = "show interface ethernet {iface}"
|
|
97
|
+
# Host name. `show hosts` and NOT `show running-config | include hostname`:
|
|
98
|
+
# the two report DIFFERENT values, measured 2026-08-02. On m4300-16x
|
|
99
|
+
# (10.1.5.20) `show hosts` gives "sw-netgear-m4300-16x-poe-s2" while
|
|
100
|
+
# running-config gives "manage-sw-netgear-m4300-16x-poe-s2", and on
|
|
101
|
+
# gsm7252ps (10.1.5.22) running-config carries no hostname line at all while
|
|
102
|
+
# `show hosts` still reports one. `show hosts` is the one that agrees with
|
|
103
|
+
# SNMP's sysName, so it is what keeps the two backends returning the same
|
|
104
|
+
# answer for the same switch.
|
|
105
|
+
hosts_cmd: str = "show hosts"
|
|
106
|
+
# Global-config directive. Quoted on the wire by the device's own
|
|
107
|
+
# running-config output ('hostname "sw-netgear-m4300-24x"').
|
|
108
|
+
hostname_config_cmd: str = "hostname {name}"
|
|
97
109
|
|
|
98
110
|
# --- physical-interface naming -----------------------------------------
|
|
99
111
|
# How this model's firmware ADDRESSES one physical port in a command
|
|
@@ -30,6 +30,7 @@ from __future__ import annotations
|
|
|
30
30
|
import re
|
|
31
31
|
from typing import TYPE_CHECKING
|
|
32
32
|
|
|
33
|
+
from ...errors import CliCommandError
|
|
33
34
|
from ...models import (
|
|
34
35
|
DetectedModel,
|
|
35
36
|
IpMode,
|
|
@@ -605,6 +606,46 @@ def parse_environment(text: str) -> list[Sensor]:
|
|
|
605
606
|
return out
|
|
606
607
|
|
|
607
608
|
|
|
609
|
+
# ---------------------------------------------------------------------------
|
|
610
|
+
# show hosts -> host name
|
|
611
|
+
# ---------------------------------------------------------------------------
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def parse_hostname(text: str) -> str:
|
|
615
|
+
"""``show hosts`` -> the switch's host name.
|
|
616
|
+
|
|
617
|
+
The command reports far more than the name -- DNS servers, the domain list,
|
|
618
|
+
resolver retry counts, the static host-to-address table -- and only the
|
|
619
|
+
first labelled field is wanted::
|
|
620
|
+
|
|
621
|
+
Host name...................................... sw-netgear-m4300-24x
|
|
622
|
+
Default domain................................. Domain name is not configured
|
|
623
|
+
Name servers (Preference order)................ 8.8.8.8, 10.1.5.1
|
|
624
|
+
|
|
625
|
+
Captured 2026-08-02 from m4300-24x (10.1.5.13), m4300-16x (10.1.5.20) and
|
|
626
|
+
gsm7252ps (10.1.5.22); all three label it exactly "Host name".
|
|
627
|
+
|
|
628
|
+
This is deliberately NOT ``show running-config | include hostname``. The two
|
|
629
|
+
report different values: on m4300-16x running-config holds
|
|
630
|
+
"manage-sw-netgear-m4300-16x-poe-s2" against this command's
|
|
631
|
+
"sw-netgear-m4300-16x-poe-s2", and on gsm7252ps running-config has no
|
|
632
|
+
hostname line at all while this command still answers. ``show hosts`` is the
|
|
633
|
+
one that matches SNMP's sysName, so parsing it is what stops the CLI and
|
|
634
|
+
SNMP backends disagreeing about the same switch.
|
|
635
|
+
|
|
636
|
+
Raises rather than returning "" when the label is absent: every FASTPATH
|
|
637
|
+
switch measured answers it, so silence means the command failed or the
|
|
638
|
+
output drifted, and a blank host name would be a fabrication.
|
|
639
|
+
"""
|
|
640
|
+
name = labelled_values(text).get("Host name")
|
|
641
|
+
if name is None:
|
|
642
|
+
raise CliCommandError(
|
|
643
|
+
"`show hosts` output carries no 'Host name' field; got: "
|
|
644
|
+
+ " ".join(text.split())[:200]
|
|
645
|
+
)
|
|
646
|
+
return name.strip()
|
|
647
|
+
|
|
648
|
+
|
|
608
649
|
# ---------------------------------------------------------------------------
|
|
609
650
|
# show network -> management IP
|
|
610
651
|
# ---------------------------------------------------------------------------
|
|
@@ -21,6 +21,23 @@ SYS_DESCR = "1.3.6.1.2.1.1.1.0" # sysDescr: text incl. the model name
|
|
|
21
21
|
SYS_OBJECT_ID = "1.3.6.1.2.1.1.2.0" # sysObjectID: read-only signal, unused
|
|
22
22
|
# for matching (no known OID->model table exists -- see parse.py's
|
|
23
23
|
# detect_model_from_sysdescr docstring).
|
|
24
|
+
# sysName: the switch's host name. A STANDARD MIB-II scalar, which is why it is
|
|
25
|
+
# the one hostname source that also works on gs728tpp -- that agent publishes no
|
|
26
|
+
# Netgear vendor subtree at all.
|
|
27
|
+
#
|
|
28
|
+
# WRITABLE on every SNMP model in this fleet. Measured 2026-08-02 by SETting the
|
|
29
|
+
# value the switch already had: a zero-impact writability probe, since the device
|
|
30
|
+
# state cannot change but a read-only column still answers notWritable. All five
|
|
31
|
+
# accepted -- gsm7228ps (10.1.5.11) on community `public`, which is the only one
|
|
32
|
+
# it has, and m4300-24x (.13), m4300-16x (.20), gsm7252ps (.22) and gs728tpp
|
|
33
|
+
# (10.2.5.10) on `private`.
|
|
34
|
+
#
|
|
35
|
+
# NOT the same value as the FASTPATH `hostname` running-config directive. On
|
|
36
|
+
# m4300-16x sysName is "sw-netgear-m4300-16x-poe-s2" while running-config holds
|
|
37
|
+
# "manage-sw-netgear-m4300-16x-poe-s2", and on gsm7252ps running-config carries
|
|
38
|
+
# no hostname at all. sysName tracks `show hosts`, which is therefore what the
|
|
39
|
+
# CLI reader parses so that the two backends cannot disagree.
|
|
40
|
+
SYS_NAME = "1.3.6.1.2.1.1.5.0"
|
|
24
41
|
IF_TYPE = "1.3.6.1.2.1.2.2.1.3" # ifType (6=ethernetCsmacd=physical)
|
|
25
42
|
IF_ADMIN_STATUS = "1.3.6.1.2.1.2.2.1.7" # ifAdminStatus (1=up,2=down)
|
|
26
43
|
IF_OPER_STATUS = "1.3.6.1.2.1.2.2.1.8" # ifOperStatus (1=up,2=down)
|
|
@@ -826,6 +826,27 @@ def _scalar_text(rows: Sequence[SnmpRow], oid: str) -> str | None:
|
|
|
826
826
|
return None
|
|
827
827
|
|
|
828
828
|
|
|
829
|
+
def parse_hostname(rows: Sequence[SnmpRow]) -> str:
|
|
830
|
+
"""Extract ``sysName`` from one exact-OID GET.
|
|
831
|
+
|
|
832
|
+
Raises rather than returning a placeholder when the scalar is absent. Every
|
|
833
|
+
switch in this fleet answers ``sysName`` -- it is a mandatory MIB-II scalar
|
|
834
|
+
-- so an absent one is a real failure to report, not an empty hostname to
|
|
835
|
+
invent. An empty *string* is a different thing and is passed through: a
|
|
836
|
+
switch with no name configured genuinely has one.
|
|
837
|
+
"""
|
|
838
|
+
from . import oids
|
|
839
|
+
|
|
840
|
+
value = _scalar_text(rows, oids.SYS_NAME)
|
|
841
|
+
if value is None:
|
|
842
|
+
raise SnmpError(
|
|
843
|
+
f"switch did not answer sysName ({oids.SYS_NAME}); it is a mandatory "
|
|
844
|
+
"MIB-II scalar, so this is an agent or transport failure rather "
|
|
845
|
+
"than an absent hostname"
|
|
846
|
+
)
|
|
847
|
+
return value
|
|
848
|
+
|
|
849
|
+
|
|
829
850
|
def parse_system_info(rows: Sequence[SnmpRow]) -> tuple[str | None, str | None]:
|
|
830
851
|
"""Extract the raw sysDescr/sysObjectID scalar text from one combined GET.
|
|
831
852
|
|
|
@@ -193,6 +193,14 @@ class SnmpReader:
|
|
|
193
193
|
w(oids.IP_ADDRESS_IFINDEX), # RFC-4293 fallback (M4300)
|
|
194
194
|
)
|
|
195
195
|
|
|
196
|
+
def get_hostname(self) -> str:
|
|
197
|
+
"""The switch's host name, from the standard MIB-II ``sysName`` scalar.
|
|
198
|
+
|
|
199
|
+
Standard, so this works on every SNMP model -- including ``gs728tpp``,
|
|
200
|
+
which publishes no Netgear vendor subtree at all.
|
|
201
|
+
"""
|
|
202
|
+
return parse.parse_hostname(self.client.get([oids.SYS_NAME]))
|
|
203
|
+
|
|
196
204
|
def get_system_info(self) -> DetectedModel:
|
|
197
205
|
"""Identify this switch's model via sysDescr (see ``read_system_info``).
|
|
198
206
|
|
|
@@ -320,6 +328,10 @@ class AsyncSnmpReader:
|
|
|
320
328
|
await w(oids.IP_ADDRESS_IFINDEX), # RFC-4293 fallback (M4300)
|
|
321
329
|
)
|
|
322
330
|
|
|
331
|
+
async def get_hostname(self) -> str:
|
|
332
|
+
"""Async twin of ``SnmpReader.get_hostname`` -- see there."""
|
|
333
|
+
return parse.parse_hostname(await self.client.get([oids.SYS_NAME]))
|
|
334
|
+
|
|
323
335
|
async def get_system_info(self) -> DetectedModel:
|
|
324
336
|
"""Async twin of ``SnmpReader.get_system_info`` -- see there."""
|
|
325
337
|
return await async_read_system_info(self.client)
|
|
@@ -238,9 +238,7 @@ def _plan_switchport_membership(
|
|
|
238
238
|
# rebuild it from the membership the port actually has.
|
|
239
239
|
allowed = _vlan_bitmap({untagged_vlan, *want_tagged})
|
|
240
240
|
varbinds.append(
|
|
241
|
-
SetVarbind(
|
|
242
|
-
f"{oids.FASTPATH_SWITCHPORT_ALLOWED_VLANS}.{port}", allowed, "x"
|
|
243
|
-
)
|
|
241
|
+
SetVarbind(f"{oids.FASTPATH_SWITCHPORT_ALLOWED_VLANS}.{port}", allowed, "x")
|
|
244
242
|
)
|
|
245
243
|
varbinds.append(
|
|
246
244
|
SetVarbind(
|
|
@@ -558,9 +556,7 @@ class SnmpWriter:
|
|
|
558
556
|
self.client.set(vb)
|
|
559
557
|
problem = _switchport_divergence(plan, vlan, port, self._reader.get_vlans())
|
|
560
558
|
if problem is not None:
|
|
561
|
-
raise WriteVerificationError(
|
|
562
|
-
problem, before=before, after=self._vlan(vlan)
|
|
563
|
-
)
|
|
559
|
+
raise WriteVerificationError(problem, before=before, after=self._vlan(vlan))
|
|
564
560
|
|
|
565
561
|
def _switchport_vlan_bitmap(self, base_oid: str, port: int) -> bytes:
|
|
566
562
|
"""A switchport VLAN-list column's octets, or an all-zero 512-byte map."""
|
|
@@ -596,11 +592,18 @@ class SnmpWriter:
|
|
|
596
592
|
raw_egress = self._raw_bitmap(oids.DOT1Q_VLAN_STATIC_EGRESS, vlan)
|
|
597
593
|
raw_untagged = self._raw_bitmap(oids.DOT1Q_VLAN_STATIC_UNTAGGED, vlan)
|
|
598
594
|
new_egress, new_untagged = membership_bitmaps(
|
|
599
|
-
mode=mode,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
595
|
+
mode=mode,
|
|
596
|
+
port=port,
|
|
597
|
+
egress=(
|
|
598
|
+
raw_egress
|
|
599
|
+
if raw_egress is not None
|
|
600
|
+
else encode_port_bitmap(before.member_ports)
|
|
601
|
+
),
|
|
602
|
+
untagged=(
|
|
603
|
+
raw_untagged
|
|
604
|
+
if raw_untagged is not None
|
|
605
|
+
else encode_port_bitmap(before.untagged_ports)
|
|
606
|
+
),
|
|
604
607
|
width_bytes=vlan_bitmap_width(self.model),
|
|
605
608
|
)
|
|
606
609
|
egress_vb = SetVarbind(
|
|
@@ -704,6 +707,30 @@ class SnmpWriter:
|
|
|
704
707
|
after=after,
|
|
705
708
|
)
|
|
706
709
|
|
|
710
|
+
def set_hostname(self, name: str, *, force: bool = False) -> None:
|
|
711
|
+
"""Set the switch's host name via the standard MIB-II ``sysName``.
|
|
712
|
+
|
|
713
|
+
GROUNDED, unlike ``set_mgmt_ip`` below: ``sysName`` was confirmed
|
|
714
|
+
writable on every SNMP model in this fleet on 2026-08-02, by SETting
|
|
715
|
+
each switch the value it already held. See ``oids.SYS_NAME`` for the
|
|
716
|
+
hosts and communities, and for why this is NOT the same value as the
|
|
717
|
+
FASTPATH ``hostname`` running-config directive.
|
|
718
|
+
|
|
719
|
+
Not force-gated: renaming a switch cannot strand it the way a mgmt-IP
|
|
720
|
+
write can, and it is trivially reversible by writing the old name back.
|
|
721
|
+
``force`` is accepted so the signature matches every other writer.
|
|
722
|
+
"""
|
|
723
|
+
del force # accepted for a uniform writer signature; nothing to gate
|
|
724
|
+
before = self._reader.get_hostname()
|
|
725
|
+
self.client.set_many([SetVarbind(oids.SYS_NAME, name, "s")])
|
|
726
|
+
after = self._reader.get_hostname()
|
|
727
|
+
if after != name:
|
|
728
|
+
raise WriteVerificationError(
|
|
729
|
+
f"sysName is {after!r} after writing {name!r}",
|
|
730
|
+
before=before,
|
|
731
|
+
after=after,
|
|
732
|
+
)
|
|
733
|
+
|
|
707
734
|
def set_mgmt_ip(
|
|
708
735
|
self, address: str, netmask: str, gateway: str, *, force: bool = False
|
|
709
736
|
) -> None:
|
|
@@ -982,11 +1009,18 @@ class AsyncSnmpWriter:
|
|
|
982
1009
|
raw_egress = await self._raw_bitmap(oids.DOT1Q_VLAN_STATIC_EGRESS, vlan)
|
|
983
1010
|
raw_untagged = await self._raw_bitmap(oids.DOT1Q_VLAN_STATIC_UNTAGGED, vlan)
|
|
984
1011
|
new_egress, new_untagged = membership_bitmaps(
|
|
985
|
-
mode=mode,
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1012
|
+
mode=mode,
|
|
1013
|
+
port=port,
|
|
1014
|
+
egress=(
|
|
1015
|
+
raw_egress
|
|
1016
|
+
if raw_egress is not None
|
|
1017
|
+
else encode_port_bitmap(before.member_ports)
|
|
1018
|
+
),
|
|
1019
|
+
untagged=(
|
|
1020
|
+
raw_untagged
|
|
1021
|
+
if raw_untagged is not None
|
|
1022
|
+
else encode_port_bitmap(before.untagged_ports)
|
|
1023
|
+
),
|
|
990
1024
|
width_bytes=vlan_bitmap_width(self.model),
|
|
991
1025
|
)
|
|
992
1026
|
egress_vb = SetVarbind(
|
|
@@ -1078,6 +1112,19 @@ class AsyncSnmpWriter:
|
|
|
1078
1112
|
after=after,
|
|
1079
1113
|
)
|
|
1080
1114
|
|
|
1115
|
+
async def set_hostname(self, name: str, *, force: bool = False) -> None:
|
|
1116
|
+
"""Async twin of ``SnmpWriter.set_hostname`` -- see there."""
|
|
1117
|
+
del force # accepted for a uniform writer signature; nothing to gate
|
|
1118
|
+
before = await self._reader.get_hostname()
|
|
1119
|
+
await self.client.set_many([SetVarbind(oids.SYS_NAME, name, "s")])
|
|
1120
|
+
after = await self._reader.get_hostname()
|
|
1121
|
+
if after != name:
|
|
1122
|
+
raise WriteVerificationError(
|
|
1123
|
+
f"sysName is {after!r} after writing {name!r}",
|
|
1124
|
+
before=before,
|
|
1125
|
+
after=after,
|
|
1126
|
+
)
|
|
1127
|
+
|
|
1081
1128
|
async def set_mgmt_ip(
|
|
1082
1129
|
self, address: str, netmask: str, gateway: str, *, force: bool = False
|
|
1083
1130
|
) -> None:
|
|
@@ -127,6 +127,44 @@ def render_network(state: VirtualSwitchState) -> str:
|
|
|
127
127
|
)
|
|
128
128
|
|
|
129
129
|
|
|
130
|
+
# --- show hosts -------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def render_hosts(state: VirtualSwitchState) -> str:
|
|
134
|
+
"""``show hosts``, transcribed from real output captured 2026-08-02.
|
|
135
|
+
|
|
136
|
+
From m4300-24x (10.1.5.13), m4300-16x (10.1.5.20) and gsm7252ps
|
|
137
|
+
(10.1.5.22). All three label the name exactly "Host name", and the resolver
|
|
138
|
+
and static-mapping sections around it are reproduced because the reader has
|
|
139
|
+
to pick one field out of them -- a mock emitting only the wanted line would
|
|
140
|
+
not exercise that at all.
|
|
141
|
+
|
|
142
|
+
The trailing static-mapping tables are the empty form all three returned;
|
|
143
|
+
none had a host-to-address mapping configured.
|
|
144
|
+
"""
|
|
145
|
+
return "\n".join(
|
|
146
|
+
[
|
|
147
|
+
_dotted("Host name", state.hostname),
|
|
148
|
+
_dotted("Default domain", "Domain name is not configured"),
|
|
149
|
+
_dotted("Default domain list", "Domain Name List is not configured"),
|
|
150
|
+
_dotted("Domain Name Lookup", "Enabled"),
|
|
151
|
+
_dotted("Number of retries", "2"),
|
|
152
|
+
_dotted("Retry timeout period", "3"),
|
|
153
|
+
_dotted("Name servers (Preference order)", "10.1.5.1"),
|
|
154
|
+
"",
|
|
155
|
+
"Configured host name-to-address mapping:",
|
|
156
|
+
"",
|
|
157
|
+
" Host Addresses",
|
|
158
|
+
"------------------------ ----------------------",
|
|
159
|
+
"No host name is configured to IP address",
|
|
160
|
+
"",
|
|
161
|
+
" Host Total Elapsed Type Addresses",
|
|
162
|
+
"---------------------- ------- ------- ---- --------------",
|
|
163
|
+
"No hostname is mapped to an IP address",
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
130
168
|
# --- show port all ----------------------------------------------------------
|
|
131
169
|
|
|
132
170
|
|
|
@@ -59,6 +59,7 @@ _COPY_RE = re.compile(r"^copy\s+(\S+)\s+(\S+)$")
|
|
|
59
59
|
|
|
60
60
|
# --- configuration-mode commands -------------------------------------------
|
|
61
61
|
_CONFIGURE_RE = re.compile(r"^config(?:ure)?(?: terminal)?$")
|
|
62
|
+
_HOSTNAME_RE = re.compile(r'^hostname\s+("?[^"]+"?)$')
|
|
62
63
|
_VLAN_DATABASE_RE = re.compile(r"^vlan database$")
|
|
63
64
|
_VLAN_CREATE_RE = re.compile(r"^vlan (\d+)$")
|
|
64
65
|
_VLAN_NAME_RE = re.compile(r"^vlan name (\d+) (\S+)$")
|
|
@@ -339,6 +340,14 @@ class VirtualCliFace:
|
|
|
339
340
|
if self._mode == _VLAN_DB:
|
|
340
341
|
return self._vlan_db_command(c)
|
|
341
342
|
if self._mode == _CONFIG:
|
|
343
|
+
m = _HOSTNAME_RE.match(c)
|
|
344
|
+
if m:
|
|
345
|
+
# The device stores the name unquoted; its running-config
|
|
346
|
+
# renders it quoted, and `show hosts` reports it bare. Accept
|
|
347
|
+
# either form on the wire so a caller that quotes is not
|
|
348
|
+
# silently given a name with quotes embedded in it.
|
|
349
|
+
self.state.hostname = m.group(1).strip().strip('"')
|
|
350
|
+
return _ACCEPTED
|
|
342
351
|
m = _INTERFACE_RE.match(c)
|
|
343
352
|
if m:
|
|
344
353
|
port = cli_fastpath.port_for_iface(self.state, m.group(1))
|
|
@@ -406,6 +415,8 @@ class VirtualCliFace:
|
|
|
406
415
|
return cli_fastpath.render_environment(self.state)
|
|
407
416
|
if c == self.spec.network_cmd:
|
|
408
417
|
return cli_fastpath.render_network(self.state)
|
|
418
|
+
if c == self.spec.hosts_cmd:
|
|
419
|
+
return cli_fastpath.render_hosts(self.state)
|
|
409
420
|
m = _SHOW_VLAN_ID_RE.match(c)
|
|
410
421
|
if m:
|
|
411
422
|
return cli_fastpath.render_vlan_detail(self.state, int(m.group(1)))
|
|
@@ -2123,7 +2123,9 @@ _M4300_24X_SWITCHPORT: dict[int, _SwitchportRow] = {
|
|
|
2123
2123
|
# VLAN (90) differs from its NATIVE VLAN (5) -- proving membership follows
|
|
2124
2124
|
# col4 not col3 in trunk mode -- with a genuinely sparse allowed list.
|
|
2125
2125
|
5: (
|
|
2126
|
-
2,
|
|
2126
|
+
2,
|
|
2127
|
+
90,
|
|
2128
|
+
5,
|
|
2127
2129
|
frozenset({1, 5, 6, 7, 10, 20, 41, 90, 99, 121, 141}),
|
|
2128
2130
|
{1, 90},
|
|
2129
2131
|
set(),
|
|
@@ -2393,6 +2395,9 @@ def seed_m4300_24x() -> VirtualSwitchState:
|
|
|
2393
2395
|
mode="static",
|
|
2394
2396
|
)
|
|
2395
2397
|
state = VirtualSwitchState(
|
|
2398
|
+
# Measured 2026-08-02: sysName and `show hosts` both report this on the
|
|
2399
|
+
# real switch. Was empty here, which no real FASTPATH switch is.
|
|
2400
|
+
hostname="sw-netgear-m4300-24x",
|
|
2396
2401
|
model_key="m4300-24x",
|
|
2397
2402
|
ports=ports,
|
|
2398
2403
|
vlans=vlans,
|
|
@@ -2587,6 +2592,9 @@ def seed_m4300_16x() -> VirtualSwitchState:
|
|
|
2587
2592
|
),
|
|
2588
2593
|
]
|
|
2589
2594
|
state = VirtualSwitchState(
|
|
2595
|
+
# Measured 2026-08-02: sysName and `show hosts` both report this on the
|
|
2596
|
+
# real switch. Was empty here, which no real FASTPATH switch is.
|
|
2597
|
+
hostname="sw-netgear-m4300-16x-poe-s2",
|
|
2590
2598
|
model_key="m4300-16x",
|
|
2591
2599
|
ports=ports,
|
|
2592
2600
|
vlans=vlans,
|