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>
This commit is contained in:
109
internal/shell/cisco/state.go
Normal file
109
internal/shell/cisco/state.go
Normal file
@@ -0,0 +1,109 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user