python-netgear-switch-library 0.0.post327__tar.gz → 0.0.post330__tar.gz

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