Skip to content

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. rConfig-sim ships five: cisco_ios, junos, ciena_tl1, infinera_tl1 and cisco_ons_tl1. Adding more is deliberately a one-file job.

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 manifest template column and the registry key.
  • Commands() is the closed set of command labels the driver may emit on rcfgsim_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.

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)

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 the slow_response fault multiplier). See timing.
  • emit() — writes the response, applies disconnect_mid / malformed faults 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.

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 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.