The driver framework
A driver is a vendor personality. It owns the full interactive loop for one device:
reading input, parsing commands, and writing responses. rcfg-sim ships five:
cisco_ios, junos, ciena_tl1,
infinera_tl1 and cisco_ons_tl1. Adding more
is deliberately a one-file job.
The Driver interface
Section titled “The Driver interface”Every driver implements four methods:
type Driver interface { Name() string // manifest `template` id, e.g. "cisco_ios" Commands() []string // metric label values this driver can emit RequiresSSHAuth() bool // does it authenticate at the SSH transport? Serve(ctx *sessionCtx) // the full interactive loop}Name()is the id used in the manifesttemplatecolumn and the registry key.Commands()is the closed set of command labels the driver may emit onrcfgsim_command_duration_seconds. The server unions these across all drivers and pre-registers them, so the metric’s cardinality is assembled from drivers rather than hardcoded — and stays bounded.RequiresSSHAuth()is consulted only under--ssh-auth=driver.Serve()runs the session.
How a driver is selected
Section titled “How a driver is selected”At session start the server looks up the device’s manifest template value in the driver
registry (driverFor). An empty value falls back to cisco_ios, so older manifests keep
working unchanged.
new SSH session │ ▼manifest row ── template column ──▶ driver registry │ ┌───────────┬───────────┬──────────┴───┬───────────────┬─────────────┐ cisco_ios junos ciena_tl1 infinera_tl1 cisco_ons_tl1 empty │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ciscoIOS junos cienaTL1 infineraTL1 ciscoONSTL1 ciscoIOS .Serve .Serve .Serve .Serve .Serve (fallback)Shared machinery
Section titled “Shared machinery”Drivers don’t reimplement timing, faults, or metrics. They route every response through two shared helpers on the session context:
applyResponseDelay()— the single place a response sleep happens (base jitter plus theslow_responsefault multiplier). See timing.emit()— writes the response, appliesdisconnect_mid/malformedfaults to streamed config bytes, preserves the zero-copy path for the un-faulted case, and records the command-duration metric.
This is what guarantees that fault and metric behaviour is identical across vendors — a driver author gets all of it for free.
Registration
Section titled “Registration”Each driver registers itself from an init() in its own file
(driver_<vendor>.go), so nothing else in the hot path needs to know it exists:
func init() { registerDriver(ciscoIOS{}) }The shipped drivers
Section titled “The shipped drivers”- Cisco IOS (
cisco_ios) —showcommands, enable mode, prefix matching. Also serves Arista EOS devices, whose CLI is IOS-shaped; see EOS oncisco_ios. - Juniper Junos (
junos) —user@host>prompt, no enable mode,show configuration - Ciena 6500 TL1 (
ciena_tl1) —ACT-USERlogin,RTRV-NE-LISTneighbours - Infinera DTN-X TL1 (
infinera_tl1) —>prompt, pagedRTRV-TIDMAP - Cisco ONS 15454 TL1 (
cisco_ons_tl1) — positionalRTRV-MAP-NETWORKrecords
The three TL1 drivers share one core (internal/sshsrv/tl1.go) and differ where the hardware
does. Those differences are the point, not an inconvenience:
| Ciena 6500 | Infinera DTN-X | Cisco ONS 15454 | |
|---|---|---|---|
| Prompt | < |
> |
< |
| Neighbour command | RTRV-NE-LIST |
RTRV-TIDMAP |
RTRV-MAP-NETWORK |
| Record grammar | keyword, quoted | keyword, empty AID | positional |
| Response | one block | paged | one block |
| Header SID when routing | the addressed TID | its own system name | the addressed TID |
cisco_ios and junos can also answer any command from a per-device file; see
Command files.
Want to add your own? See Writing a new driver.