gswitch
gswitch is a small Cisco-like CLI emulator for tests. It supports SSH password
and public-key authentication, optional Telnet, and an in-memory running
configuration. It is not a production SSH server or a full IOS emulator.
Start a test device
# From the Gnetcli checkout:
go install ./cmd/gswitch
export PATH="$(go env GOPATH)/bin:$PATH"
gswitch -host 127.0.0.1 -port 2223 -username test -password test
These are test credentials. Do not expose the emulator on an untrusted network.
Use -authorized-keys /path/to/authorized_keys to also permit public-key auth.
For a container-only test network, bind to -host 0.0.0.0 without publishing
its SSH port on the host.
Configuration survives reconnects
All SSH sessions served by one listener share the device configuration. The CLI
process also shares it with Telnet when -enable-telnet is set. Modes and the
selected interface remain local to each session, and concurrent reads/writes
are synchronized.
An initial configuration can be loaded with -config-file:
interface Ethernet1
description before
!
gswitch -host 127.0.0.1 -port 2223 -username test -password test \
-config-file initial.cfg -ready-file /tmp/gswitch-ready.json
The supported configuration subset is intentionally small:
conf t/configure terminalenters global configuration mode.interface NAMEselects or creates an interface; the prompt becomes(config-if)#.description TEXTreplaces the interface's previous description.no descriptionorno description TEXTremoves its description.exitleaves interface mode, then global configuration mode;endleaves both.no interface NAMEremoves an interface and its configuration.- Other simple global/interface lines are stored as text;
no LINEremoves the corresponding stored line. This is not a complete model of IOS defaults or syntax. show running-configrenders a stable, sorted snapshot with interface indentation.copy running-config startup-configandwrite memoryare accepted no-ops.show versionandshow clockprovide fixed test output.
Unknown operational commands return an error recognized by Gnetcli's Cisco
driver. The config-file format supports flat global statements and indented
interface blocks, with optional !/end separators; other nested blocks are
not supported. Configuration changes stay in memory and do not modify the
input file or survive process restart.
A test can configure an interface, disconnect, reconnect, and fetch the updated
configuration. This supports Annet's diff -> patch -> deploy -> empty diff
scenario using a Cisco device (breed: ios12) and Cisco generators. The separate
Annet Arista example must be adapted before switching its test fixture to gswitch.
Readiness and lifecycle testing
-ready-file PATH writes a JSON object after all requested listeners are bound:
{"ssh":"127.0.0.1:2223","telnet":"127.0.0.1:2224"}
Only enabled transports are included. -port 0 and -telnet-port 0 allocate
free ports and the file reports their actual addresses. The file is replaced
atomically and removed on normal SIGINT/SIGTERM shutdown. Use a dedicated file
path: an old readiness file at that path is removed on startup.
-command-delay 30s delays each command, including session setup, for tests that
need an operation to remain in flight. Cancellation interrupts the delay and
closes active connections. The default delay is zero.
Library use
SSHServerOptions.Config accepts a *RunningConfig. Its Load(string) error
method initializes a configuration fragment; String() returns a synchronized,
deterministic snapshot. A failed load does not replace the current configuration.
A nil Config creates a separate device state per ServeSSH/ServeTelnet call.
Pass the same NewRunningConfig() pointer to both functions to share one device
between transports. Listener and connection ownership belongs to the serving
function; context cancellation closes them.
Tests
From the Gnetcli repository:
go test -race ./pkg/gswitch ./cmd/gswitch
go test -race ./...