python-netgear-switch-library 0.0.post332__tar.gz → 0.0.post337__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.
Files changed (390) hide show
  1. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/PKG-INFO +1 -1
  2. python_netgear_switch_library-0.0.post337/docs/superpowers/specs/2026-08-02-snmp-surface-findings.md +151 -0
  3. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/_version.py +2 -2
  4. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli_read.py +9 -0
  5. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli_write.py +50 -2
  6. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/models.py +32 -0
  7. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/nsdp_read.py +16 -0
  8. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/cli/commands.py +12 -0
  9. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/cli/parse.py +41 -0
  10. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/snmp/oids.py +49 -0
  11. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/snmp/parse.py +87 -0
  12. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/snmp_read.py +43 -0
  13. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/snmp_write.py +63 -16
  14. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/cli_fastpath.py +38 -0
  15. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/cli.py +11 -0
  16. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/seed.py +9 -1
  17. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/state.py +7 -0
  18. python_netgear_switch_library-0.0.post337/tests/virtual/test_hostname.py +96 -0
  19. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/.github/workflows/ci.yml +0 -0
  20. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/.github/workflows/deb.yml +0 -0
  21. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/.github/workflows/publish-pypi.yml +0 -0
  22. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/.gitignore +0 -0
  23. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/.readthedocs.yaml +0 -0
  24. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/CLAUDE.md +0 -0
  25. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/LICENSE +0 -0
  26. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/README.md +0 -0
  27. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/RELEASING.md +0 -0
  28. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/debian/changelog +0 -0
  29. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/debian/control +0 -0
  30. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/debian/copyright +0 -0
  31. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/debian/rules +0 -0
  32. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/debian/source/format +0 -0
  33. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/Makefile +0 -0
  34. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_ext/filelinks.py +0 -0
  35. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_ext/support_tables.py +0 -0
  36. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gs105pe.png +0 -0
  37. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gs110emx.png +0 -0
  38. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gs305ep.png +0 -0
  39. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gs728tpp.png +0 -0
  40. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gsm7228ps.jpg +0 -0
  41. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/gsm7252ps.jpg +0 -0
  42. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/m4300-16x.png +0 -0
  43. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/models/m4300-24x.png +0 -0
  44. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/_static/ngsw.css +0 -0
  45. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/cli.rst +0 -0
  46. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/core.rst +0 -0
  47. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/facades.rst +0 -0
  48. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/index.rst +0 -0
  49. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/mcp.rst +0 -0
  50. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/operations.rst +0 -0
  51. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/protocols.rst +0 -0
  52. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/transport.rst +0 -0
  53. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/api/virtual.rst +0 -0
  54. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/cli.rst +0 -0
  55. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/conf.py +0 -0
  56. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/development.rst +0 -0
  57. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/fake/index.rst +0 -0
  58. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/fake/internals.rst +0 -0
  59. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/fake/serving.rst +0 -0
  60. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/fake/testing.rst +0 -0
  61. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/concepts.rst +0 -0
  62. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/configuration.rst +0 -0
  63. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/installation.rst +0 -0
  64. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/principles.rst +0 -0
  65. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/quickstart.rst +0 -0
  66. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/reading.rst +0 -0
  67. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/guide/writing.rst +0 -0
  68. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/index.rst +0 -0
  69. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/mcp.rst +0 -0
  70. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gs105pe.rst +0 -0
  71. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gs110emx.rst +0 -0
  72. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gs305ep.rst +0 -0
  73. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gs728tpp.rst +0 -0
  74. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gsm7228ps.rst +0 -0
  75. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/gsm7252ps.rst +0 -0
  76. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/index.rst +0 -0
  77. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/m4300-16x.rst +0 -0
  78. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/m4300-24x.rst +0 -0
  79. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/models/support.rst +0 -0
  80. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/cli.rst +0 -0
  81. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http/cheetah-form.rst +0 -0
  82. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http/cheetah-v1.rst +0 -0
  83. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http/gambit.rst +0 -0
  84. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http/merge-hash-cgi.rst +0 -0
  85. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http/xml-api.rst +0 -0
  86. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/http.rst +0 -0
  87. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/index.rst +0 -0
  88. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/nsdp.rst +0 -0
  89. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/protocols/snmp.rst +0 -0
  90. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
  91. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
  92. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
  93. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
  94. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
  95. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
  96. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
  97. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
  98. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
  99. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
  100. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
  101. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/packaging/apt-index.html +0 -0
  102. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/packaging/ci-build.sh +0 -0
  103. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/packaging/deb-version.py +0 -0
  104. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/pyproject.toml +0 -0
  105. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/__init__.py +0 -0
  106. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/_dispatch.py +0 -0
  107. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/aio_api.py +0 -0
  108. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/capabilities.py +0 -0
  109. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/__init__.py +0 -0
  110. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/capture.py +0 -0
  111. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/context.py +0 -0
  112. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/format.py +0 -0
  113. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/main.py +0 -0
  114. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/resolve.py +0 -0
  115. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/cli/safety.py +0 -0
  116. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/config.py +0 -0
  117. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/errors.py +0 -0
  118. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/http_read.py +0 -0
  119. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/http_write.py +0 -0
  120. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/mcp/__init__.py +0 -0
  121. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/mcp/server.py +0 -0
  122. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/nsdp_write.py +0 -0
  123. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/__init__.py +0 -0
  124. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/cli/__init__.py +0 -0
  125. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/__init__.py +0 -0
  126. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/crypt.py +0 -0
  127. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/endpoints.py +0 -0
  128. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/forms.py +0 -0
  129. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/parse.py +0 -0
  130. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/session.py +0 -0
  131. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/http/types.py +0 -0
  132. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
  133. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
  134. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/client.py +0 -0
  135. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
  136. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
  137. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/types.py +0 -0
  138. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/nsdp/write.py +0 -0
  139. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
  140. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/snmp/client.py +0 -0
  141. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/protocols/snmp/write.py +0 -0
  142. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/py.typed +0 -0
  143. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/registry.py +0 -0
  144. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/sync_api.py +0 -0
  145. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/__init__.py +0 -0
  146. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/aio/__init__.py +0 -0
  147. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
  148. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
  149. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/cli/__init__.py +0 -0
  150. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/cli/console.py +0 -0
  151. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/cli/session.py +0 -0
  152. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/cli/ssh.py +0 -0
  153. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/cli/telnet.py +0 -0
  154. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/http/__init__.py +0 -0
  155. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/http/client.py +0 -0
  156. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/sync/__init__.py +0 -0
  157. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
  158. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
  159. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/__init__.py +0 -0
  160. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/__init__.py +0 -0
  161. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/http.py +0 -0
  162. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/mibview.py +0 -0
  163. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
  164. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/faces/snmp.py +0 -0
  165. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/server.py +0 -0
  166. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web.py +0 -0
  167. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
  168. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
  169. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
  170. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
  171. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
  172. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
  173. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
  174. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
  175. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/src/netgear_switch/virtual/web_m4300.py +0 -0
  176. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/capture_parity.py +0 -0
  177. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_capture.py +0 -0
  178. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_cli_integration.py +0 -0
  179. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_control_poe_port.py +0 -0
  180. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_control_vlan_ip.py +0 -0
  181. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_format.py +0 -0
  182. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_main_framework.py +0 -0
  183. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_new_commands.py +0 -0
  184. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_op_coverage.py +0 -0
  185. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_prompt_wiring.py +0 -0
  186. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_read_commands.py +0 -0
  187. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_resolve.py +0 -0
  188. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_safety.py +0 -0
  189. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/cli/test_serve.py +0 -0
  190. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/conftest.py +0 -0
  191. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/equivalence.py +0 -0
  192. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/captures/gsm7228ps.json +0 -0
  193. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/captures/gsm7252ps.json +0 -0
  194. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/captures/m4300-16x.json +0 -0
  195. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/captures/m4300-24x.json +0 -0
  196. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
  197. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
  198. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
  199. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
  200. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
  201. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
  202. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
  203. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
  204. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
  205. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
  206. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
  207. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
  208. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
  209. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
  210. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
  211. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
  212. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
  213. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
  214. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
  215. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
  216. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
  217. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
  218. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
  219. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
  220. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
  221. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
  222. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
  223. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
  224. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
  225. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
  226. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
  227. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
  228. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
  229. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
  230. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
  231. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
  232. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
  233. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
  234. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
  235. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
  236. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
  237. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
  238. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
  239. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
  240. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
  241. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_membership.html +0 -0
  242. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_portstats.html +0 -0
  243. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_pvid.html +0 -0
  244. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_status.html +0 -0
  245. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
  246. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
  247. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
  248. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
  249. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_login.html +0 -0
  250. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
  251. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_pvid.html +0 -0
  252. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_redirect.html +0 -0
  253. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
  254. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
  255. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
  256. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_login.html +0 -0
  257. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_membership.html +0 -0
  258. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
  259. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_portstats.html +0 -0
  260. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_pvid.html +0 -0
  261. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
  262. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
  263. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
  264. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
  265. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
  266. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
  267. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
  268. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
  269. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
  270. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
  271. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
  272. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
  273. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
  274. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
  275. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
  276. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
  277. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
  278. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
  279. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
  280. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
  281. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
  282. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
  283. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
  284. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_login.html +0 -0
  285. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
  286. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
  287. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
  288. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
  289. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
  290. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
  291. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
  292. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
  293. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
  294. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
  295. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
  296. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
  297. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
  298. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
  299. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_addresstable.html +0 -0
  300. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
  301. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
  302. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_ports.html +0 -0
  303. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_portstats.html +0 -0
  304. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_pvid.html +0 -0
  305. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_sysinfo.html +0 -0
  306. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
  307. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
  308. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
  309. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
  310. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
  311. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
  312. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/http_specs.py +0 -0
  313. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/cli/test_cli_parse.py +0 -0
  314. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/http/test_crypt.py +0 -0
  315. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/http/test_endpoints.py +0 -0
  316. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/http/test_forms.py +0 -0
  317. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/http/test_parse.py +0 -0
  318. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/nsdp/test_auth.py +0 -0
  319. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/nsdp/test_parsers.py +0 -0
  320. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/nsdp/test_protocol.py +0 -0
  321. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/nsdp/test_write_frame.py +0 -0
  322. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_client.py +0 -0
  323. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_oids.py +0 -0
  324. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
  325. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
  326. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
  327. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_ports.py +0 -0
  328. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_system_info.py +0 -0
  329. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_parse_vlans.py +0 -0
  330. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/protocols/snmp/test_write_encode.py +0 -0
  331. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_aio_api.py +0 -0
  332. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_capabilities.py +0 -0
  333. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_cli_read.py +0 -0
  334. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_cli_vlan_write.py +0 -0
  335. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_cli_write.py +0 -0
  336. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_config.py +0 -0
  337. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_cross_backend_equivalence.py +0 -0
  338. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_deb_version.py +0 -0
  339. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_dispatch.py +0 -0
  340. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_docs_coverage.py +0 -0
  341. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_errors.py +0 -0
  342. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_facade_equivalence.py +0 -0
  343. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_dispatch.py +0 -0
  344. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_equivalence.py +0 -0
  345. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_read.py +0 -0
  346. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_vlan_membership.py +0 -0
  347. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_write.py +0 -0
  348. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_http_xui_writes.py +0 -0
  349. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_import.py +0 -0
  350. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_mcp_server.py +0 -0
  351. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_models.py +0 -0
  352. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_models_snmp_read.py +0 -0
  353. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_nsdp_integration.py +0 -0
  354. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_nsdp_read.py +0 -0
  355. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_nsdp_write.py +0 -0
  356. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_public_api.py +0 -0
  357. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_quality_gates.py +0 -0
  358. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_registry.py +0 -0
  359. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_snmp_integration.py +0 -0
  360. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_snmp_read.py +0 -0
  361. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_snmp_write.py +0 -0
  362. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_sync_api.py +0 -0
  363. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_version.py +0 -0
  364. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/test_write_equivalence.py +0 -0
  365. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/_fastpath_fake.py +0 -0
  366. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_cli_console.py +0 -0
  367. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_cli_scp_cert.py +0 -0
  368. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_cli_telnet.py +0 -0
  369. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_http_client.py +0 -0
  370. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_nsdp_udp_sync.py +0 -0
  371. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_set_transport.py +0 -0
  372. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
  373. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_snmp_pysnmp.py +0 -0
  374. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/transport/test_value_parity.py +0 -0
  375. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_cli_cross_backend.py +0 -0
  376. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
  377. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_gsm7228ps_seed.py +0 -0
  378. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_m4300_seeds.py +0 -0
  379. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_mibview.py +0 -0
  380. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_mutable_state.py +0 -0
  381. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_nsdp_state.py +0 -0
  382. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_serve_daemon.py +0 -0
  383. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_snmp_write_face.py +0 -0
  384. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_state_seed.py +0 -0
  385. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_switchport_vlan_write.py +0 -0
  386. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_virtual_http_face.py +0 -0
  387. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_virtual_nsdp_face.py +0 -0
  388. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_virtual_snmp_face.py +0 -0
  389. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/tests/virtual/test_web_projection.py +0 -0
  390. {python_netgear_switch_library-0.0.post332 → python_netgear_switch_library-0.0.post337}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-netgear-switch-library
3
- Version: 0.0.post332
3
+ Version: 0.0.post337
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
@@ -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.post332'
22
- __version_tuple__ = version_tuple = (0, 0, 'post332')
21
+ __version__ = version = '0.0.post337'
22
+ __version_tuple__ = version_tuple = (0, 0, 'post337')
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: st is not None
565
- and st.detect in (PoEDetect.DELIVERING, PoEDetect.SEARCHING),
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(
@@ -119,6 +119,38 @@ class MgmtIpConfig:
119
119
  base_mac: str | None = None
120
120
 
121
121
 
122
+ @dataclass(frozen=True)
123
+ class SyslogServer:
124
+ """One remote syslog collector the switch is configured to send to."""
125
+
126
+ host: str
127
+ port: int
128
+ #: Standard syslog severity, 0 (emergency) to 7 (debug). The switch sends
129
+ #: messages AT OR ABOVE this level. Cross-checked on m4300-24x: the SNMP
130
+ #: column reads 6 where ``show logging hosts`` prints "info".
131
+ severity: int
132
+ #: The switch's own word for the row's state, "Active" in the CLI table.
133
+ active: bool
134
+
135
+
136
+ @dataclass(frozen=True)
137
+ class SyslogConfig:
138
+ """Remote-logging configuration: whether it is on, and where it sends.
139
+
140
+ Deliberately narrower than everything ``show logging`` prints. The console
141
+ and buffered-logging columns are in the same vendor subtree, but only the
142
+ console pair could be decoded against captured CLI output; the buffered
143
+ severity did not match any column read, so it is left out rather than
144
+ guessed at. See ``VendorOids.syslog_*``.
145
+ """
146
+
147
+ enabled: bool
148
+ #: The source port the switch sends FROM (``Logging Client Local Port``),
149
+ #: not the collector's port -- that is per-server in ``servers``.
150
+ local_port: int
151
+ servers: tuple[SyslogServer, ...]
152
+
153
+
122
154
  @dataclass(frozen=True)
123
155
  class DetectedModel:
124
156
  """Result of identifying a switch's model over SNMP (sysObjectID + sysDescr).
@@ -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)
@@ -130,6 +147,32 @@ class VendorOids:
130
147
  """The ONE symbol every call site uses for the DHCP-mode OID. See
131
148
  DHCP_MODE_OID_SUFFIX above — UNVERIFIED, best-effort read only. No call site
132
149
  may hard-code a ``.99.1`` literal; they all reference this field."""
150
+ syslog_admin_mode: str
151
+ syslog_local_port: str
152
+ syslog_host_addr: str
153
+ syslog_host_port: str
154
+ syslog_host_severity: str
155
+ syslog_host_status: str
156
+ """Remote-logging configuration, under ``<base>.14`` on BOTH vendor
157
+ families -- 4526.10 (FASTPATH) and 4526.11 (S3300) share the column layout.
158
+
159
+ Located 2026-08-02 by reading each switch's own ``show logging`` /
160
+ ``show logging hosts`` and then searching a full walk for those values;
161
+ every field of the CLI output is accounted for by a column and the two
162
+ agree. On m4300-24x (10.1.5.13) the host row reads 10.1.5.1 / port 514 /
163
+ severity 6 / status 1 against a CLI table of ``10.1.5.1 info 514
164
+ Active`` -- so severity is the standard syslog scale (6 = info) and status
165
+ 1 = Active.
166
+
167
+ ``<base>.17`` is NOT this: it looks like logging until you notice it holds
168
+ port 123 and the string "NTP Bits". It is SNTP, and this fleet's NTP server
169
+ and syslog server are the same host, which is what makes the confusion easy.
170
+
171
+ The admin-mode enum is ``1 = enabled, 2 = disabled``, confirmed twice over
172
+ on m4300-24x: syslog reads 1 while ``show logging`` says "Syslog Logging :
173
+ enabled", and the console column reads 2 while it says "Console Logging :
174
+ disabled". The console severity column independently reads 3 against a CLI
175
+ "error", matching the same syslog scale."""
133
176
  mgmt_write_addr_unverified: str
134
177
  mgmt_write_netmask_unverified: str
135
178
  mgmt_write_gateway_unverified: str
@@ -219,6 +262,12 @@ def vendor_oids(model: SwitchModel) -> VendorOids:
219
262
  box_psu_power=f"{base}.43.1.8.1.5",
220
263
  box_temp=f"{base}.43.1.15.1.3",
221
264
  dhcp_mode_unverified=f"{base}.{DHCP_MODE_OID_SUFFIX}",
265
+ syslog_admin_mode=f"{base}.14.1.4.1.0",
266
+ syslog_local_port=f"{base}.14.1.4.3.0",
267
+ syslog_host_addr=f"{base}.14.1.4.5.1.3",
268
+ syslog_host_port=f"{base}.14.1.4.5.1.4",
269
+ syslog_host_severity=f"{base}.14.1.4.5.1.5",
270
+ syslog_host_status=f"{base}.14.1.4.5.1.7",
222
271
  mgmt_write_addr_unverified=f"{base}.98.1",
223
272
  mgmt_write_netmask_unverified=f"{base}.98.2",
224
273
  mgmt_write_gateway_unverified=f"{base}.98.3",
@@ -17,6 +17,8 @@ from ...models import (
17
17
  PortStats,
18
18
  PortStatus,
19
19
  Sensor,
20
+ SyslogConfig,
21
+ SyslogServer,
20
22
  VLANInfo,
21
23
  )
22
24
  from .client import SnmpError, SnmpRow
@@ -716,6 +718,70 @@ def _ipv4_from_rfc4293_index(rows: Sequence[SnmpRow]) -> str | None:
716
718
  return None
717
719
 
718
720
 
721
+ #: The vendor admin-mode enum shared by every logging destination column.
722
+ #: 1 = enabled, 2 = disabled -- confirmed twice on m4300-24x against its own
723
+ #: ``show logging``: syslog reads 1 under "Syslog Logging : enabled" and the
724
+ #: console column reads 2 under "Console Logging : disabled".
725
+ _ADMIN_ENABLED = 1
726
+
727
+ #: Row status in the syslog host table. 1 is what the CLI prints as "Active".
728
+ _HOST_STATUS_ACTIVE = 1
729
+
730
+
731
+ def _first_int(rows: Sequence[SnmpRow]) -> int | None:
732
+ """The value of a single-varbind scalar GET, when it is an integer."""
733
+ for row in rows:
734
+ if isinstance(row.value, int):
735
+ return row.value
736
+ return None
737
+
738
+
739
+ def parse_syslog(
740
+ admin_mode: Sequence[SnmpRow],
741
+ local_port: Sequence[SnmpRow],
742
+ host_addr: Sequence[SnmpRow],
743
+ host_port: Sequence[SnmpRow],
744
+ host_severity: Sequence[SnmpRow],
745
+ host_status: Sequence[SnmpRow],
746
+ *,
747
+ addr_base: str,
748
+ port_base: str,
749
+ severity_base: str,
750
+ status_base: str,
751
+ ) -> SyslogConfig:
752
+ """Vendor logging columns -> ``SyslogConfig``.
753
+
754
+ The host table is indexed by an integer row id, and every per-host column is
755
+ matched to the address column by that index rather than by position -- so a
756
+ table with a gap in its indices (a deleted row) cannot silently shift one
757
+ row's port onto another row's address.
758
+
759
+ A row whose address is empty is skipped. The address is the only field that
760
+ makes a row meaningful, and reporting one collector fewer is far better than
761
+ inventing where logs are being sent.
762
+ """
763
+ addresses = index_str_column(host_addr, addr_base)
764
+ ports = index_int_column(host_port, port_base)
765
+ severities = index_int_column(host_severity, severity_base)
766
+ statuses = index_int_column(host_status, status_base)
767
+
768
+ servers = tuple(
769
+ SyslogServer(
770
+ host=address,
771
+ port=ports.get(index, 0),
772
+ severity=severities.get(index, 0),
773
+ active=statuses.get(index) == _HOST_STATUS_ACTIVE,
774
+ )
775
+ for index, address in sorted(addresses.items())
776
+ if address.strip()
777
+ )
778
+ return SyslogConfig(
779
+ enabled=_first_int(admin_mode) == _ADMIN_ENABLED,
780
+ local_port=_first_int(local_port) or 0,
781
+ servers=servers,
782
+ )
783
+
784
+
719
785
  def parse_mgmt_ip(
720
786
  addr: Sequence[SnmpRow],
721
787
  netmask: Sequence[SnmpRow],
@@ -826,6 +892,27 @@ def _scalar_text(rows: Sequence[SnmpRow], oid: str) -> str | None:
826
892
  return None
827
893
 
828
894
 
895
+ def parse_hostname(rows: Sequence[SnmpRow]) -> str:
896
+ """Extract ``sysName`` from one exact-OID GET.
897
+
898
+ Raises rather than returning a placeholder when the scalar is absent. Every
899
+ switch in this fleet answers ``sysName`` -- it is a mandatory MIB-II scalar
900
+ -- so an absent one is a real failure to report, not an empty hostname to
901
+ invent. An empty *string* is a different thing and is passed through: a
902
+ switch with no name configured genuinely has one.
903
+ """
904
+ from . import oids
905
+
906
+ value = _scalar_text(rows, oids.SYS_NAME)
907
+ if value is None:
908
+ raise SnmpError(
909
+ f"switch did not answer sysName ({oids.SYS_NAME}); it is a mandatory "
910
+ "MIB-II scalar, so this is an agent or transport failure rather "
911
+ "than an absent hostname"
912
+ )
913
+ return value
914
+
915
+
829
916
  def parse_system_info(rows: Sequence[SnmpRow]) -> tuple[str | None, str | None]:
830
917
  """Extract the raw sysDescr/sysObjectID scalar text from one combined GET.
831
918
 
@@ -23,6 +23,7 @@ if TYPE_CHECKING:
23
23
  PortStats,
24
24
  PortStatus,
25
25
  Sensor,
26
+ SyslogConfig,
26
27
  VLANInfo,
27
28
  )
28
29
  from .protocols.snmp.client import AsyncSnmpClient, SnmpClient
@@ -193,6 +194,44 @@ class SnmpReader:
193
194
  w(oids.IP_ADDRESS_IFINDEX), # RFC-4293 fallback (M4300)
194
195
  )
195
196
 
197
+ def get_hostname(self) -> str:
198
+ """The switch's host name, from the standard MIB-II ``sysName`` scalar.
199
+
200
+ Standard, so this works on every SNMP model -- including ``gs728tpp``,
201
+ which publishes no Netgear vendor subtree at all.
202
+ """
203
+ return parse.parse_hostname(self.client.get([oids.SYS_NAME]))
204
+
205
+ def get_syslog(self) -> SyslogConfig:
206
+ """Remote-logging configuration: whether it is on, and where it sends.
207
+
208
+ VENDOR columns, so a model with no Netgear subtree cannot serve this.
209
+ ``gs728tpp`` is exactly that model -- a walk of ``1.3.6.1.4.1.4526``
210
+ answers ``noSuchObject`` -- and it is refused by name rather than
211
+ returned empty, which would read as "no collectors configured".
212
+ """
213
+ if not oids.has_vendor_oids(self.model):
214
+ raise UnsupportedCapabilityError(
215
+ f"model {self.model.key!r} registers no Netgear vendor OID "
216
+ "subtree, and the logging columns are vendor-only; an empty "
217
+ "result here would be indistinguishable from a switch with no "
218
+ "syslog collectors configured"
219
+ )
220
+ vo = oids.vendor_oids(self.model)
221
+ w = self.client.walk
222
+ return parse.parse_syslog(
223
+ self.client.get([vo.syslog_admin_mode]),
224
+ self.client.get([vo.syslog_local_port]),
225
+ w(vo.syslog_host_addr),
226
+ w(vo.syslog_host_port),
227
+ w(vo.syslog_host_severity),
228
+ w(vo.syslog_host_status),
229
+ addr_base=vo.syslog_host_addr,
230
+ port_base=vo.syslog_host_port,
231
+ severity_base=vo.syslog_host_severity,
232
+ status_base=vo.syslog_host_status,
233
+ )
234
+
196
235
  def get_system_info(self) -> DetectedModel:
197
236
  """Identify this switch's model via sysDescr (see ``read_system_info``).
198
237
 
@@ -320,6 +359,10 @@ class AsyncSnmpReader:
320
359
  await w(oids.IP_ADDRESS_IFINDEX), # RFC-4293 fallback (M4300)
321
360
  )
322
361
 
362
+ async def get_hostname(self) -> str:
363
+ """Async twin of ``SnmpReader.get_hostname`` -- see there."""
364
+ return parse.parse_hostname(await self.client.get([oids.SYS_NAME]))
365
+
323
366
  async def get_system_info(self) -> DetectedModel:
324
367
  """Async twin of ``SnmpReader.get_system_info`` -- see there."""
325
368
  return await async_read_system_info(self.client)