Facades¶
SyncSwitch and AsyncSwitch are the public entry points. They expose the same
operations with the same arguments, and both resolve exactly one backend per
call — see Concepts.
Synchronous¶
Public synchronous read/write facade: SyncSwitch.
- netgear_switch.sync_api.detect_model(host, *, community=None, client=None)[source]¶
Identify a switch’s model over SNMP, WITHOUT already knowing/hardcoding it – the discovery entry point a caller (e.g. gdoc2netcfg) uses BEFORE it can construct a
SyncSwitchat all: call this first, thenregistry.get_model(detected.key)+SyncSwitch(...)oncedetected.keyis notNone. Seemodels.DetectedModel/protocols.snmp.parse.detect_model_from_sysdescrfor exactly how (and why) an unmatched sysDescr honestly yieldskey=Nonerather than a guess.Builds the default net-snmp CLI client from
host/communityunlessclientis injected (tests, or an already-open connection).- Return type:
- class netgear_switch.sync_api.SyncSwitch(
- model,
- host,
- *,
- snmp_community=None,
- snmp_client=None,
- snmp_write_community=None,
- snmp_write_client=None,
- snmp_write_community_resolver=None,
- nsdp_interface=None,
- nsdp_client=None,
- nsdp_write_client=None,
- nsdp_password=None,
- nsdp_password_resolver=None,
- http_client=None,
- http_password=None,
- http_password_resolver=None,
- cli_client=None,
- protected_ports=frozenset(),
- backend=None,
Bases:
objectSynchronous, model-driven read/write facade over one switch.
- close()[source]¶
Release the HTTP client THIS facade built (never one injected by the caller). Safe to call even when no HTTP op was ever dispatched.
- resolve_backend(backend=None)[source]¶
The ONE backend an op with this
backendargument will run on.Resolution order: the per-call
backend, else this facade’s default (thebackend=constructor argument), else the first backend in_BACKEND_PREFERENCEthis model declares (SNMP > NSDP > HTTP > SSH > TELNET > CONSOLE). A named backend the model does not have raises rather than falling back to one it does. Public so a caller can ask “what would this op talk to?” without performing it.- Return type:
- get_syslog(*, backend=None)[source]¶
Remote-logging configuration: whether it is on, and its collectors.
Served over SNMP and the FASTPATH CLI, which agree field-for-field on live hardware.
gs728tppis refused over SNMP by name: it registers no Netgear vendor subtree, and the logging columns are vendor-only, so an empty result would be indistinguishable from a switch with no collectors configured.- Return type:
- get_users(*, backend=None)[source]¶
The switch’s local login accounts.
Served over the FASTPATH CLI.
SwitchUser.access_modekeeps the firmware’s own wording, which differs between images, andprivilegedcarries the normalised reading.- Return type:
- get_services(*, backend=None)[source]¶
Which management services are enabled, and on which ports.
Served over the FASTPATH CLI. A
portofNonemeans the firmware reports no port for that service, not that a default applies.- Return type:
- get_hostname(*, backend=None)[source]¶
The switch’s host name.
Every backend can answer, but from a different place: SNMP reads the standard
sysNamescalar, NSDP theHOSTNAMEtag, the CLIshow hosts, and the web UI the device-identity page – on the two dialects whose page carries the field; the rest refuse by name.The CLI deliberately parses
show hostsrather thanshow running-config: the two report different values on real hardware, and onlyshow hostsagrees withsysName.- Return type:
- nsdp_device()[source]¶
Return the COMPLETE raw
NsdpDevicefor this switch: model, MAC, hostname, mgmt IP, firmware, DHCP mode, port count, serial number, VLAN engine, raw per-port status (speed byte NOT pre-converted to Mbps – seeprotocols.nsdp.types.NsdpPortStatus.speed) and statistics, VLAN membership, PVIDs, plus QoS engine/mirroring/IGMP snooping/broadcast filtering/loop detection.Unlike every other read op, this deliberately bypasses the SNMP/NSDP/HTTP backend-preference dispatch (
_read): NSDP is the ONLY backend that can serve it, so a model without an NSDP backend raisesUnsupportedCapabilityErrordirectly (mirroringidentify()’s bypass of that dispatch below, andNsdpReader.__init__’s own_require_nsdpguard).- Return type:
- identify()[source]¶
Detect this switch’s ACTUAL model via SNMP sysDescr, independent of
self.model.Unlike every other read/write op, this deliberately bypasses the per-op SNMP/NSDP/HTTP backend-preference dispatch (
_read) AND theself.modelSNMP-backend gate entirely: it exists precisely to confirm/discover a switch’s real model when the caller does not yet trust the model this facade happens to have been constructed with (e.g. a placeholder used only to carry host/credentials). Reuses an injectedsnmp_client/snmp_communityexactly like_reader_for(Backend.SNMP)would, but never requiresself.model.backendsto include SNMP.- Return type:
- snapshot(*, backend=None)[source]¶
Aggregate every read op over ONE backend.
backendnames it; None uses this model’s default backend (seeresolve_backend). A field that backend cannot serve degrades to ()/None – it is NOT re-read over a different protocol, so a snapshot describes what one protocol really reports rather than a blend of several. Take two snapshots (one per backend) to compare protocols.- Return type:
- set_port_description(port, description, *, force=False, backend=None)[source]¶
Label a port. Pass
""to clear the label.Cosmetic: it moves no traffic and cannot strand a switch, so it is not force-gated.
forceonly overrides the protected-port guard, for callers who have marked their uplinks protected.
- set_port_speed(port, speed, *, force=False, backend=None)[source]¶
Force a port’s speed/duplex, or return it to auto-negotiation.
Disruptive – applying either bounces the link – so
forceis what overrides the protected-port guard, as forset_pvid.Served over the FASTPATH CLI. The read that pairs with it is
PortStatus.speed_config, NOTspeed_mbps: the first is what the port is configured to, the second what its link negotiated, and on a down port only the first has an answer.A forced 1000 Mbit/s is refused by name: 1000BASE-T requires auto-negotiation, and the firmware’s
speedgrammar omits it accordingly (measured – the switch answers “% Invalid input”).
- set_flow_control(port, enabled, *, force=False, backend=None)[source]¶
Turn IEEE 802.3x flow control on or off for a port.
Served over the FASTPATH CLI. The read that pairs with it is
PortStatus.flow_control, which on that backend is the CONFIGURED setting –show port’s Flow Mode column moves on a link-down port.
- set_syslog_enabled(enabled, *, force=False, backend=None)[source]¶
Turn remote syslog on or off.
Served over SNMP. Adding or removing a COLLECTOR is a separate, unbuilt operation – it needs a row-status write that has not been driven against hardware.
- add_syslog_collector(
- host,
- *,
- port=514,
- severity=6,
- force=False,
- backend=None,
Add a remote syslog collector, and read it back to confirm.
Served over the FASTPATH CLI.
severityis the standard syslog number (0 emergency .. 7 debug); the switch forwards messages at or above it. Refuses if a collector forhostalready exists, because the firmware would otherwise add a second row for the same address and silently duplicate delivery.
- remove_syslog_collector(host, *, force=False, backend=None)[source]¶
Remove a remote syslog collector, and read back to confirm it is gone.
Served over the FASTPATH CLI. Refuses if no collector for
hostis configured, rather than removing a row that is not there.
- set_hostname(name, *, force=False, backend=None)[source]¶
Set the switch’s host name, and read it back to confirm.
Written over SNMP (
sysName) or the FASTPATH CLI (hostname), whichever backend resolves. Not force-gated: renaming cannot strand a switch and is reversible by writing the old name back, unlikeset_mgmt_ip, which drops the session that issues it.The Plus models cannot be renamed by this library yet – they have neither backend, and the NSDP and web-UI routes are unbuilt.
- upload_certificate(cert_pem, key_pem, *, force=False)[source]¶
Upload an HTTPS SSL server certificate + private key to the switch.
Implemented for gsm7228ps/S3300 (grounded multipart web-UI upload). Disruptive (replaces the running certificate), so
force=Trueis required. A model whose real mechanism is known but not yet implemented (m4300 SCP, gs728tpp XML-API) raises NotImplementedError naming the mechanism – NOT UnsupportedCapabilityError, since the hardware can do it; a model with no HTTP backend and no known mechanism raises UnsupportedCapabilityError.
- upload_certificate_scp(*, scp_source, scp_password, remote_dir, chain=False)[source]¶
Deploy an HTTPS SSL server certificate to a FASTPATH switch over SCP.
For the Fully Managed FASTPATH line (M4300 / GSM7252PS) only, whose firmware pulls the certificate with
copy scp://<src> nvram:sslpem-serverrather than an HTTP form. Runs the disable-HTTPS -> copy(server) -> optional copy(root chain) -> re-enable-HTTPS -> save-config sequence over the library’s existing CLI transport (SSH by default). Re-enabling HTTPS loads the new certificate in place; the switch is NOT rebooted.The CALLER must have STAGED the PEM(s) on the SCP source first: the switch pulls
<host-with-dots-as-dashes>-server.pem(and, whenchainis set,<...>-root.pem) fromremote_dironscp_source(auser@host[:port]string). This library only SENDS the copy commands (per the design decision) – it does not run the staging SCP server.Dispatched ONLY for FASTPATH models with a known copy-scp profile (m4300-24x/-16x, gsm7252ps); every other model – including FASTPATH gsm7228ps, whose cert upload is HTTP multipart – raises
UnsupportedCapabilityError.HONESTY: GROUNDED in the certbot-hook
FastpathScpUpdaterprior art and MOCK-TESTED end-to-end, but NOT live-verified (a real run is a production write needing a staging SCP server, which CI lacks) – seecli_write.deploy_certificate_scp.
Asynchronous¶
Public asynchronous read/write facade: AsyncSwitch (mirror of SyncSwitch).
- async netgear_switch.aio_api.async_detect_model(host, *, community=None, client=None)[source]¶
Async twin of
sync_api.detect_model– see there.- Return type:
- class netgear_switch.aio_api.AsyncSwitch(
- model,
- host,
- *,
- snmp_community=None,
- snmp_client=None,
- snmp_write_community=None,
- snmp_write_client=None,
- snmp_write_community_resolver=None,
- nsdp_interface=None,
- nsdp_client=None,
- nsdp_write_client=None,
- nsdp_password=None,
- nsdp_password_resolver=None,
- http_client=None,
- http_password=None,
- http_password_resolver=None,
- protected_ports=frozenset(),
- backend=None,
Bases:
objectAsynchronous, model-driven read/write facade over one switch.
- async aclose()[source]¶
Release the HTTP client THIS facade built (never one injected by the caller). Safe to call even when no HTTP op was ever dispatched.
- resolve_backend(backend=None)[source]¶
Async twin of
SyncSwitch.resolve_backend– see there.- Return type:
- async get_syslog(*, backend=None)[source]¶
Async twin of
SyncSwitch.get_syslog– see there.- Return type:
- async get_users(*, backend=None)[source]¶
Async twin of
SyncSwitch.get_users.AsyncSwitch has no CLI backend, so this refuses on every model – the same honest refusal every async CLI read gives.
- Return type:
- async get_services(*, backend=None)[source]¶
Async twin of
SyncSwitch.get_services– CLI only, see there.- Return type:
- async get_hostname(*, backend=None)[source]¶
Async twin of
SyncSwitch.get_hostname– see there.- Return type:
- async snapshot(*, backend=None)[source]¶
Async twin of
SyncSwitch.snapshot: every read op over ONE backend (named, or this model’s default), with a field that backend cannot serve degrading to ()/None rather than being re-read over another protocol.- Return type:
- async set_port_description(port, description, *, force=False, backend=None)[source]¶
Async twin of
SyncSwitch.set_port_description– see it.
- async set_port_speed(port, speed, *, force=False, backend=None)[source]¶
Async twin of
SyncSwitch.set_port_speed– see it.Every async backend refuses this: the operation is served over the FASTPATH CLI, and all three CLI transports (paramiko SSH, telnet, pyserial console) are synchronous, so
AsyncSwitchhas no CLI backend at all. Kept present so the refusal names the backend rather than surfacing asAttributeError.
- async set_flow_control(port, enabled, *, force=False, backend=None)[source]¶
Async twin of
SyncSwitch.set_flow_control– see it.Like
set_port_speed, every async backend refuses: this op is served over the CLI, whose three transports are all synchronous.
- async clear_poe_fault(
- port,
- *,
- force=False,
- timeouts=_DEFAULT_POE_TIMEOUTS,
- backend=None,
- async set_syslog_enabled(enabled, *, force=False, backend=None)[source]¶
Turn remote syslog on or off.
Served over SNMP here. The CLI serves it too, but not asynchronously – all three CLI transports are synchronous, so
AsyncSwitchhas no CLI backend.
- async add_syslog_collector(
- host,
- *,
- port=514,
- severity=6,
- force=False,
- backend=None,
Async twin of
SyncSwitch.add_syslog_collector– see it.Every async backend refuses: this op is served over the CLI, whose transports are all synchronous.
- async remove_syslog_collector(host, *, force=False, backend=None)[source]¶
Async twin of
SyncSwitch.remove_syslog_collector– see it.
- async set_hostname(name, *, force=False, backend=None)[source]¶
Async twin of
SyncSwitch.set_hostname– see there.Note AsyncSwitch has no CLI backend at all, so on this facade the write resolves over SNMP or not at all.
- async upload_certificate(cert_pem, key_pem, *, force=False)[source]¶
Async twin of
SyncSwitch.upload_certificate– see there.
- async upload_certificate_scp(*, scp_source, scp_password, remote_dir, chain=False)[source]¶
Async twin of
SyncSwitch.upload_certificate_scp(FASTPATH SCP cert deploy). The op is CLI/SCP-based, and CLI transports are SYNCHRONOUS – the async facade has no CLI backend (the same reason async CLI reads and writes are unavailable). The method EXISTS for API-surface parity but honestly raisesUnsupportedCapabilityErrorrather than silently lacking it: useSyncSwitch.upload_certificate_scpfor this op.
Backend resolution¶
The seam both facades share: which backend an operation runs on, and how each backend’s client is built. Internal, but documented because its behaviour is the library’s central guarantee.
Internal backend-resolution seam shared by SyncSwitch and AsyncSwitch.
Only SNMP is wired in this slice. Model-driven dispatch lives here so the two
facades stay identical and Slices 5/6 can add NSDP/HTTP backends without
touching the public facade surface. Transport imports are function-local so
import netgear_switch never requires net-snmp binaries or pysnmp.
- netgear_switch._dispatch.resolve_backend(model, requested, preference)[source]¶
Pick THE ONE backend an op will run on – no silent substitution.
Shared by
SyncSwitch/AsyncSwitchso both facades resolve identically.requestedgiven: that exact backend, orUnsupportedCapabilityErrorif the model does not have it. The caller asked for a protocol; getting a different one back would make any claim about that protocol worthless (the concrete bug this replaced:HttpReader.get_vlansreturning emptyuntagged_portswent unnoticed for months because the facade quietly answered from SNMP instead).requestedNone: the FIRST backend inpreferencethe model declares. Deterministic and independent of the op – the facade never probes one backend, catches its refusal and tries the next.
Note this resolves a backend the MODEL has, not one that necessarily implements the op: an op the resolved backend cannot serve raises rather than being re-routed. That is the point.
- Return type:
- netgear_switch._dispatch.require_snmp_backend(model)[source]¶
Raise unless the model exposes an SNMP read backend.
- netgear_switch._dispatch.require_mac_table(model)[source]¶
Raise unless the model has a readable MAC/FDB table.
- netgear_switch._dispatch.require_nsdp_backend(model)[source]¶
Raise unless the model exposes an NSDP backend.
- netgear_switch._dispatch.build_sync_snmp_client(host, community)[source]¶
Default sync SNMP client (net-snmp CLI). Imported lazily.
- Return type:
- netgear_switch._dispatch.build_async_snmp_client(host, community)[source]¶
Default async SNMP client (pysnmp). Imported lazily.
- Return type:
- netgear_switch._dispatch.build_sync_snmp_write_client(host, write_community)[source]¶
Default sync SNMP write client (net-snmp CLI). Imported lazily.
- Return type:
- netgear_switch._dispatch.build_async_snmp_write_client(host, write_community)[source]¶
Default async SNMP write client (pysnmp). Imported lazily.
- Return type:
- netgear_switch._dispatch.build_sync_nsdp_client(host, interface)[source]¶
Default sync NSDP client (UDP). One client does read AND write, so the return is annotated with the richer
NsdpWriteClientprotocol (the concreteUdpNsdpClientsatisfies it;NsdpWriteClientextendsNsdpClientso a read-only caller can still use it). Lazy import.- Return type:
- netgear_switch._dispatch.build_async_nsdp_client(host, interface)[source]¶
Default async NSDP client (asyncio UDP). Read AND write; annotated with the richer
AsyncNsdpWriteClientprotocol (AsyncUdpNsdpClientsatisfies it and it extendsAsyncNsdpClient, so_reader()can still accept it). Lazy import.- Return type:
- netgear_switch._dispatch.require_http_backend(model)[source]¶
Raise unless the model exposes an HTTP web-UI backend.
- netgear_switch._dispatch.http_reads_supported(model)[source]¶
True only if the model’s web reads/writes are grounded (reads_verified).
The facade uses this to decide whether HTTP may join the per-op backend fallback. An UNVERIFIED-pending-capture model (gsm7228ps cheetah) returns False: its HTTP path is never used for read/write dispatch (SNMP stays authoritative; HTTP is reserved for firmware/reboot). gs110emx’s Gambit login + sysInfo/interface_stats reads ARE grounded (see
protocols/http/endpoints.py), so this returns True for it – but NSDP is still authoritative for every op it serves (ports/VLANs/PVIDs/stats/ mgmt-IP). In practice this means gs110emx’s HTTP get_stats/get_mgmt_ip are NEVER actually reached through the SyncSwitch/AsyncSwitch facade: NSDP’s own get_stats/get_mgmt_ip always return a result rather than raisingUnsupportedCapabilityError(unlike get_macs/get_lldp/ get_sensors/get_poe, which NSDP genuinely doesn’t have and does raise for), so the facade’s per-op backend loop never falls through to HTTP for those two ops – only a directly-constructedHttpReader/AsyncHttpReaderexercises them. The lazy import keepsimport netgear_switchclear of the endpoints module on the hot path (endpoints is pure, but this mirrors the other lazy builders).- Return type:
- netgear_switch._dispatch.require_cli_backend(model)[source]¶
Raise unless the model exposes a CLI (SSH/telnet/console) backend.
- netgear_switch._dispatch.cli_reads_supported(model)[source]¶
True only once a model’s CLI reads are cross-verified (reads_verified).
Mirrors
http_reads_supported. A CLI spec withreads_verified=False(CLI reader output not yet cross-verified against SNMP on live hardware) gates OFF. The FASTPATH models (m4300-24x/-16x, gsm7252ps) are now verified and return True; other models return False and the facade never dispatches a live read to their CLI backend. The parsers and the in-process mock CLI face are exercised independently of this gate.- Return type:
- netgear_switch._dispatch.cli_writes_supported(model)[source]¶
True once a model’s CLI WRITE path is live-verified (
writes_verified).Requires
reads_verifiedtoo, and not incidentally: every CLI write verifies itself by reading back throughCliReader, so a model whose CLI reads are not trusted cannot honestly verify a CLI write either. All four FASTPATH CLI models are verified (seeCliModelSpec.writes_verifiedfor the per-model live-run evidence).- Return type:
- netgear_switch._dispatch.build_sync_cli_client(host, username, password, model)[source]¶
Build the sync CLI session for
model.SSH (paramiko) is the default network CLI transport. A model that exposes TELNET but NOT SSH (the S3300-52X / gsm7228ps, whose FASTPATH CLI listens on telnet port 60000 with no ssh listener anywhere) instead gets the telnet transport dialled at
cli_spec(model).telnet_port. Both transports are imported lazily. The console transport is never auto-selected here (it needs a serial device path, not a host).- Return type:
- netgear_switch._dispatch.build_sync_http_client(host, password, model)[source]¶
Default sync web-UI client (httpx). Imported lazily.
- Return type: