This repository has been archived on 2026-03-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
oubliette/internal/shell/cisco/state.go
Torjus Håkestad 5ba62afec3 feat: add Cisco IOS shell with mode state machine and abbreviation matching (PLAN.md 3.2)
Implements a Cisco IOS CLI emulator with four modes (user exec, privileged exec,
global config, interface config), Cisco-style command abbreviation (e.g. sh run,
conf t), enable password flow, and realistic show command output including
running-config, interfaces, IP routes, and VLANs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:58:26 +01:00

110 lines
2.8 KiB
Go

package cisco
import "fmt"
// iosMode represents the current CLI mode of the IOS state machine.
type iosMode int
const (
modeUserExec iosMode = iota // Router>
modePrivilegedExec // Router#
modeGlobalConfig // Router(config)#
modeInterfaceConfig // Router(config-if)#
)
// ifaceInfo holds interface metadata for show commands.
type ifaceInfo struct {
name string
ip string
mask string
status string
protocol string
mac string
bandwidth string
mtu int
rxPackets int
txPackets int
rxBytes int
txBytes int
shutdown bool
desc string
}
// iosState holds all mutable state for the Cisco IOS shell session.
type iosState struct {
mode iosMode
hostname string
model string
iosVersion string
serial string
enablePass string
interfaces []ifaceInfo
currentIf string
}
func newIOSState(hostname, model, iosVersion, enablePass string) *iosState {
return &iosState{
mode: modeUserExec,
hostname: hostname,
model: model,
iosVersion: iosVersion,
serial: "FTX1524Z0P3",
enablePass: enablePass,
interfaces: defaultInterfaces(),
}
}
func defaultInterfaces() []ifaceInfo {
return []ifaceInfo{
{
name: "GigabitEthernet0/0", ip: "192.168.1.1", mask: "255.255.255.0",
status: "up", protocol: "up", mac: "0050.7966.6800",
bandwidth: "1000000 Kbit", mtu: 1500,
rxPackets: 148253, txPackets: 93127, rxBytes: 19284732, txBytes: 8291043,
},
{
name: "GigabitEthernet0/1", ip: "10.0.0.1", mask: "255.255.255.252",
status: "up", protocol: "up", mac: "0050.7966.6801",
bandwidth: "1000000 Kbit", mtu: 1500,
rxPackets: 52104, txPackets: 48891, rxBytes: 4182934, txBytes: 3901284,
},
{
name: "GigabitEthernet0/2", ip: "unassigned", mask: "",
status: "administratively down", protocol: "down", mac: "0050.7966.6802",
bandwidth: "1000000 Kbit", mtu: 1500, shutdown: true,
},
{
name: "Vlan1", ip: "172.16.0.1", mask: "255.255.0.0",
status: "up", protocol: "up", mac: "0050.7966.6810",
bandwidth: "1000000 Kbit", mtu: 1500,
rxPackets: 8421, txPackets: 7103, rxBytes: 512384, txBytes: 423901,
},
}
}
// prompt returns the IOS prompt string for the current mode.
func (s *iosState) prompt() string {
switch s.mode {
case modeUserExec:
return fmt.Sprintf("%s>", s.hostname)
case modePrivilegedExec:
return fmt.Sprintf("%s#", s.hostname)
case modeGlobalConfig:
return fmt.Sprintf("%s(config)#", s.hostname)
case modeInterfaceConfig:
return fmt.Sprintf("%s(config-if)#", s.hostname)
default:
return fmt.Sprintf("%s>", s.hostname)
}
}
// findInterface returns a pointer to the interface with the given name, or nil.
func (s *iosState) findInterface(name string) *ifaceInfo {
for i := range s.interfaces {
if s.interfaces[i].name == name {
return &s.interfaces[i]
}
}
return nil
}