diff --git a/.clab/configs/ceos-startup.cfg b/.clab/configs/ceos-startup.cfg
new file mode 100644
index 0000000..864617c
--- /dev/null
+++ b/.clab/configs/ceos-startup.cfg
@@ -0,0 +1,37 @@
+! Command: show running-config
+! device: ceos (cEOSLab, EOS-4.28.0F-26924507.4280F (engineering build))
+!
+no aaa root
+!
+username admin privilege 15 role network-admin secret sha512 $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname ceos
+!
+spanning-tree mode mstp
+!
+management api http-commands
+ no shutdown
+!
+management api gnmi
+ transport grpc default
+!
+management api netconf
+ transport ssh default
+!
+interface Ethernet1
+!
+interface Ethernet2
+!
+interface Management0
+ ip address 172.20.20.14/24
+ ipv6 address 2001:172:20:20::5/64
+!
+ip routing
+!
+ip route 0.0.0.0/0 172.20.20.1
+!
+end
\ No newline at end of file
diff --git a/.clab/configs/xrv9k-startup.cfg b/.clab/configs/xrv9k-startup.cfg
new file mode 100644
index 0000000..361588a
--- /dev/null
+++ b/.clab/configs/xrv9k-startup.cfg
@@ -0,0 +1,80 @@
+hostname vr-xrv9k
+username boxen
+ group root-lr
+ group cisco-support
+ secret 5 $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+!
+username clab
+ group root-lr
+ group cisco-support
+ password 7 1511070D060A7A767B
+!
+call-home
+ service active
+ contact smart-licensing
+ profile CiscoTAC-1
+ active
+ destination transport-method http
+ !
+!
+interface MgmtEth0/RP0/CPU0/0
+ ipv4 address 10.0.0.15 255.255.255.0
+!
+interface GigabitEthernet0/0/0/0
+ shutdown
+!
+interface GigabitEthernet0/0/0/1
+ shutdown
+!
+interface GigabitEthernet0/0/0/2
+ shutdown
+!
+interface GigabitEthernet0/0/0/3
+ shutdown
+!
+interface GigabitEthernet0/0/0/4
+ shutdown
+!
+interface GigabitEthernet0/0/0/5
+ shutdown
+!
+interface GigabitEthernet0/0/0/6
+ shutdown
+!
+interface GigabitEthernet0/0/0/7
+ shutdown
+!
+interface GigabitEthernet0/0/0/8
+ shutdown
+!
+interface GigabitEthernet0/0/0/9
+ shutdown
+!
+interface GigabitEthernet0/0/0/10
+ shutdown
+!
+interface GigabitEthernet0/0/0/11
+ shutdown
+!
+interface GigabitEthernet0/0/0/12
+ shutdown
+!
+interface GigabitEthernet0/0/0/13
+ shutdown
+!
+interface GigabitEthernet0/0/0/14
+ shutdown
+!
+interface GigabitEthernet0/0/0/15
+ shutdown
+!
+grpc
+ port 57400
+ no-tls
+!
+netconf-yang agent
+ ssh
+!
+ssh server rate-limit 600
+ssh server v2
+ssh server netconf vrf default
\ No newline at end of file
diff --git a/.clab/topo-ci.yaml b/.clab/topo-ci.yaml
new file mode 100644
index 0000000..4b4d50c
--- /dev/null
+++ b/.clab/topo-ci.yaml
@@ -0,0 +1,12 @@
+---
+name: scrapligo
+
+topology:
+ kinds:
+ srl:
+ image: ghcr.io/nokia/srlinux:21.11.3
+ nodes:
+ srl:
+ kind: srl
+ mgmt_ipv4: 172.20.20.16
+ mgmt_ipv6: 2001:172:20:20::16
\ No newline at end of file
diff --git a/.clab/topo-full.yaml b/.clab/topo-full.yaml
new file mode 100644
index 0000000..95e1d8f
--- /dev/null
+++ b/.clab/topo-full.yaml
@@ -0,0 +1,50 @@
+---
+name: scrapligo
+
+topology:
+ kinds:
+ vr-csr:
+ image: boxen_cisco_csr1000v:16.12.03
+ vr-xrv9k:
+ image: boxen_cisco_xrv9k:6.5.3
+ vr-n9kv:
+ image: boxen_cisco_n9kv:9.2.4
+ ceos:
+ image: ceos:4.28.0F
+ vr-vqfx:
+ image: boxen_juniper_vsrx:17.3R2.10
+ srl:
+ image: ghcr.io/nokia/srlinux:21.11.3
+ nodes:
+ vr-csr:
+ kind: vr-csr
+ mgmt_ipv4: 172.20.20.11
+ mgmt_ipv6: 2001:172:20:20::11
+ vr-xrv9k:
+ kind: vr-xrv9k
+ mgmt_ipv4: 172.20.20.12
+ mgmt_ipv6: 2001:172:20:20::12
+ binds:
+ - configs/xrv9k-startup.cfg:/startup.cfg
+ env:
+ STARTUP_CONFIG: /startup.cfg
+ vr-n9kv:
+ kind: vr-n9kv
+ mgmt_ipv4: 172.20.20.13
+ mgmt_ipv6: 2001:172:20:20::13
+ ceos:
+ kind: ceos
+ mgmt_ipv4: 172.20.20.14
+ mgmt_ipv6: 2001:172:20:20::14
+ startup-config: configs/ceos-startup.cfg
+ vr-vqfx:
+ kind: vr-vqfx
+ mgmt_ipv4: 172.20.20.15
+ mgmt_ipv6: 2001:172:20:20::15
+ srl:
+ kind: srl
+ mgmt_ipv4: 172.20.20.16
+ mgmt_ipv6: 2001:172:20:20::16
+ links:
+ # forces "front panel port" for ceos, without this there is no ip routing
+ - endpoints: ["ceos:eth1", "ceos:eth2"]
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5c9b4df..b2f3733 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -3,8 +3,7 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
- interval: "weekly"
- day: "sunday"
+ interval: "monthly"
timezone: "PST8PDT"
time: "03:00"
- target-branch: "develop"
+ target-branch: "main"
diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml
index 4eda9e6..53b98c2 100644
--- a/.github/workflows/commit.yaml
+++ b/.github/workflows/commit.yaml
@@ -3,20 +3,61 @@ name: Commit
on: [push, pull_request, workflow_dispatch]
jobs:
- build_posix:
+ unit-test:
runs-on: ${{ matrix.os }}
strategy:
- max-parallel: 2
+ max-parallel: 6
matrix:
os: [ubuntu-latest, macos-latest]
- go-version: [1.16.x]
+ go-version: ["1.16", "1.17", "1.18"]
steps:
- - uses: actions/checkout@v2
- - name: set up go ${{ matrix.version }}
+ - name: checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: set up go ${{ matrix.go-version }}
+ uses: actions/setup-go@v3
+ with:
+ go-version: ${{ matrix.go-version }}
+ - name: lint
uses: golangci/golangci-lint-action@v3
with:
- version: v1.45
- - name: golangci-lint
- run: golangci-lint run
+ version: v1.46
+ - name: install gotestsum
+ run: go install gotest.tools/gotestsum@latest
+ - name: tests
+ run: make test-race
+
+ functional-test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ go-version: ["1.18"]
+ runtime:
+ - "docker"
+ needs:
+ - unit-test
+ steps:
+ - name: checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: set up go ${{ matrix.go-version }}
+ uses: actions/setup-go@v3
+ with:
+ go-version: ${{ matrix.go-version }}
+ - name: install gotestsum
+ run: go install gotest.tools/gotestsum@latest
+ # pull the srl image before installing clab so that clab has no need to pull it when starting
+ # -- make sure this is the same image version that is in the clab ci topo!
+ - name: pull srlinux image
+ run: docker pull ghcr.io/nokia/srlinux:21.11.3
+ - name: install clab
+ run: bash -c "$(curl -sL https://get.containerlab.dev)" -- -v 0.26.2
+ - name: start clab ci topo
+ run: make deploy-clab-ci
+ - name: wait for srlinux node
+ run: sleep 60
- name: tests
- run: go test -cover -v ./channel/... ./driver/... ./netconf/... ./logging/... ./transport/...
+ run: make test-ci
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 4e3ec33..6ce0675 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# goland
+# jetbrains goodies
.idea
# macos stuff
@@ -20,13 +20,17 @@
# private dir for notes and such
private/
+.private/
# log output
*.log
-# mkdocs site
-site/*
-
# vscode
.vscode
-.devcontainer
\ No newline at end of file
+.devcontainer
+
+# coverage
+cover.out
+
+# clab build dir
+.clab/clab-scrapligo
\ No newline at end of file
diff --git a/.golangci.yaml b/.golangci.yaml
index 249a3f4..e7a61d4 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -6,8 +6,6 @@ linters-settings:
funlen:
lines: 100
statements: 50
- gci:
- local-prefixes: github.com/golangci/golangci-lint
goconst:
min-len: 2
min-occurrences: 2
@@ -44,6 +42,9 @@ linters-settings:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 100
+ stylecheck:
+ checks:
+ - "-ST1000"
misspell:
locale: US
nolintlint:
@@ -51,11 +52,14 @@ linters-settings:
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+ revive:
+ rules:
+ - name: exported
linters:
- # please, do not use `enable-all`: it's deprecated and will be removed soon.
- # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
- disable-all: true
enable:
+ - nlreturn
+ - forbidigo
+ - gofumpt
- bodyclose
- deadcode
- depguard
@@ -105,49 +109,30 @@ linters:
- wsl
issues:
- # Excluding configuration per-path, per-linter, per-text and per-source
+ # https://github.com/golangci/golangci-lint/issues/2439#issuecomment-1002912465
+ exclude-use-default: false
exclude-rules:
- path: _test\.go
linters:
- gomnd
- # ignoring complaint about junos/xr core drivers having dupl lines
- - path: driver/core
- text: "9-59 lines are duplicate"
- linters:
- dupl
- # ignoring complaint about iosxe/nxos cfg platforms having dupl lines
- - path: cfg/
- text: "58-87 lines are duplicate"
+ - structcheck
+ - unused
+ - unparam
+ # ignoring long lines due to json/yaml tags in platform
+ - path: platform/definition.go
linters:
- - dupl
- - path: cfg/
- text: "67-99 lines are duplicate"
- linters:
- - dupl
- # ignoring complaint about channel auth having dupl lines
- - path: channel/
- text: "31-135 lines are duplicate"
- linters:
- - dupl
- - path: channel/
- text: "137-241 lines are duplicate"
- linters:
- - dupl
-# - text: "TODO/BUG/FIXME"
-# linters:
-# - godox
+ - lll
run:
+ # running w/ 1.17 because we dont actually need/use 1.18 things and 1.18 breaks some linters.
+ go: '1.17'
skip-dirs:
- private
- - test/testdata_etc
- - internal/cache
- - internal/renameio
- - internal/robustio
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
- golangci-lint-version: 1.45.x # use the fixed version to not introduce new linters unexpectedly
+ golangci-lint-version: 1.46.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 5f4329c..250723a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,37 @@
.DEFAULT_GOAL := help
+help:
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+
lint: ## Run linters
- gofmt -w -s .
+ gofumpt -w .
goimports -w .
golines -w .
golangci-lint run
-test: ## Test execution
- go test -cover -v ./cfg/... ./channel/... ./driver/... ./netconf/... ./logging/... ./transport/...
+test: ## Run unit tests
+ gotestsum --format testname --hide-summary=skipped -- -coverprofile=cover.out ./...
-test_race: ## Test execution with race flag
- go test -cover -race -v ./cfg/... ./channel/... ./driver/... ./netconf/... ./logging/... ./transport/...
+test-race: ## Run unit tests with race flag
+ gotestsum --format testname --hide-summary=skipped -- -coverprofile=cover.out ./... -race
-test_functional: ## Test execution including functional tests
- go test -v ./driver/network/... -functional
+test-functional: ## Run functional tests against "full" test topology
+ gotestsum --format testname --hide-summary=skipped -- ./... -functional
-help:
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
\ No newline at end of file
+test-ci: ## Run functional tests against "ci" test topology with race flag
+ gotestsum --format testname --hide-summary=skipped -- ./... -functional -platforms nokia_srl -race
+
+cov: ## Produce html coverage report
+ go tool cover -html=cover.out
+
+deploy-clab-full: ## Deploy "full" test topology
+ cd .clab && sudo clab deploy -t topo-full.yaml
+
+destroy-clab-full: ## Destroy "full" test topology
+ cd .clab && sudo clab destroy -t topo-full.yaml
+
+deploy-clab-ci: ## Deploy "ci" test topology
+ cd .clab && sudo clab deploy -t topo-ci.yaml
+
+destroy-clab-ci: ## Destroy "ci" test topology
+ cd .clab && sudo clab destroy -t topo-ci.yaml
\ No newline at end of file
diff --git a/README.md b/README.md
index cc9a802..9b9b211 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,13 @@
**Examples**: https://github.com/scrapli/scrapligo/tree/master/examples
+**Go Docs**: https://pkg.go.dev/github.com/scrapli/scrapligo
+
---
scrapligo -- scrap(e c)li (but in go!) -- is a Go library focused on connecting to devices, specifically network devices
(routers/switches/firewalls/etc.) via SSH and NETCONF.
-**NOTE** this is a work in progress, use with caution!
-
#### Key Features:
@@ -26,10 +26,14 @@ scrapligo -- scrap(e c)li (but in go!) -- is a Go library focused on connecting
scrapli to go is of course even faster than its python sibling!
- __But wait, there's more!__: Have NETCONF devices in your environment, but love the speed and simplicity of
scrapli? You're in luck! NETCONF support is built right into scrapligo!
+- __Sounds great, but I'm a Pythonista__: No worries! scrapligo is inspired by (and is sort of a
+ port of) [scrapli](https://github.com/carlmontanari/scrapli), so check that out!
+
## Running the Examples
-You need [Go 1.16+](https://golang.org/doc/install) installed. Clone the repo and `go run` any of the examples in the [examples](/examples) folder.
+You need [Go 1.16+](https://golang.org/doc/install) installed. Clone the repo and `go run` any
+of the examples in the [examples](/examples) folder. Below are a few example outputs.
### Executing a number of commands (from a file)
@@ -59,54 +63,94 @@ SW Version: 16.9.3
Uptime: 18 minutes
```
+
## Code Example
```go
package main
import (
- "fmt"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
+ "fmt"
+ "github.com/scrapli/scrapligo/driver/options"
+ "github.com/scrapli/scrapligo/platform"
)
func main() {
- d, err := core.NewCoreDriver(
- "localhost",
- "cisco_iosxe",
- base.WithPort(21022),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("vrnetlab"),
- base.WithAuthPassword("VR-netlab9"),
- base.WithAuthSecondary("VR-netlab9"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
+ p, err := platform.NewPlatform(
+ // cisco_iosxe refers to the included cisco iosxe platform definition
+ "cisco_iosxe",
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ )
+ if err != nil {
+ fmt.Printf("failed to create platform; error: %+v\n", err)
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
return
- }
- defer d.Close()
-
- // send some configs
- configs := []string{
- "interface loopback0",
- "interface loopback0 description tacocat",
- "no interface loopback0",
- }
-
- _, err = d.SendConfigs(configs)
- if err != nil {
- fmt.Printf("failed to send configs; error: %+v\n", err)
- return
- }
+ }
+
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ fmt.Printf("failed to fetch network driver from the platform; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ r, err := d.SendCommand("show version")
+ if err != nil {
+ fmt.Printf("failed to send command; error: %+v\n", err)
+ return
+ }
+
+ fmt.Printf(
+ "sent command '%s', output received (SendCommand):\n %s\n\n\n",
+ r.Input,
+ r.Result,
+ )
}
```
+## Migrating From Pre 1.0.0
+
+Scrapligo has had a very significant overhaul from v0.x.x versions to the v1.0.0 version, while
+the user facing API stayed *similar* (with the very notable exception to actual import paths),
+here are some (maybe not fully inclusive) list of changes to take note of:
+
+- "cfg" behavior has moved to its own [repo]("https://github.com/scrapli/scrapligocfg)
+ - "cfg" also changed a fair bit -- but mostly just from an organizational perspective, so
+ imports are not where you left them, but everything else should be more or less the same!
+- There is no more "base" driver -- this has all been rolled into the "generic" driver.
+- All driver options now live in "driver/options" -- these are options that you can pass during
+ driver creation to modify the drivers behavior.
+- All "operation" options now live in "driver/opoptions" -- these are options like
+ "WithPrivilegeLevel" and similar.
+- Channel level operations no longer require explicit arguments and instead accept options (you
+ *probably* shouldn't be using the Channel directly anyway, so this shouldn't matter much!).
+- All bool options now accept no arguments, when these options are provided they simply negate
+ the default behavior rather than accept true/false and set that.
+- Logging is no longer "global" for scrapligo, and is instead set per driver -- much more like
+ scrapli Python.
+- On "X" (OnOpen/OnClose) functions now have two flavors -- Generic and Network -- which
+ correspond to the driver type they are attached to. For the most part you can get away with
+ Generic OnX functions as you are usually just disabling paging or setting terminal flags, but
+ the Network flavor exists if you need to care about privilege levels and such.
+- On "X" functions are now set via options (as opposed to manually setting them like pre 1.0.0).
+- There are no more "core" platforms/drivers -- these have all moved to simple YAML definitions.
+ The core drivers you know and love still exist as an embedded YAML asset.
+- Platforms now support "variants" (as in scrapli community (Python)) which allow for multiple
+ "variations" of a driver/platform type.
+
+
+
+
* gopher artwork by [@egonelbre](https://github.com/egonelbre/gophers)
\ No newline at end of file
diff --git a/assets/assets.go b/assets/assets.go
new file mode 100644
index 0000000..071fd22
--- /dev/null
+++ b/assets/assets.go
@@ -0,0 +1,7 @@
+package assets
+
+import "embed"
+
+//go:embed platforms/*
+// Assets is the embedded assets objects for the included platform yaml data.
+var Assets embed.FS
diff --git a/assets/platforms/arista_eos.yaml b/assets/platforms/arista_eos.yaml
new file mode 100644
index 0000000..40c31d3
--- /dev/null
+++ b/assets/platforms/arista_eos.yaml
@@ -0,0 +1,48 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}>\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'disable'
+ escalate: 'enable'
+ escalate-auth: true
+ escalate-prompt: '(?im)^[pP]assword:\s?$'
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'privilege-exec'
+ failed-when-contains:
+ - "% Ambiguous command"
+ - "% Error"
+ - "% Incomplete command"
+ - "% Invalid input"
+ - "% Cannot commit"
+ - "% Unavailable command"
+ textfsm-platform: 'arista_eos' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 32767'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/cisco_iosxe.yaml b/assets/platforms/cisco_iosxe.yaml
new file mode 100644
index 0000000..55feebf
--- /dev/null
+++ b/assets/platforms/cisco_iosxe.yaml
@@ -0,0 +1,56 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}>$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}#$'
+ previous-priv: 'exec'
+ deescalate: 'disable'
+ escalate: 'enable'
+ escalate-auth: true
+ escalate-prompt: '(?im)^(?:enable\s){0,1}password:\s?$'
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$'
+ not-contains:
+ - 'tcl)'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ tclsh:
+ name: 'tclsh'
+ pattern: '(?im)^([\w.\-@/+>:]+\(tcl\)[>#]|\+>)$'
+ previous-priv: 'privilege-exec'
+ deescalate: 'tclquit'
+ escalate: 'tclsh'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'privilege-exec'
+ failed-when-contains:
+ - '% Ambiguous command'
+ - '% Incomplete command'
+ - '% Invalid input detected'
+ - '% Unknown command'
+ textfsm-platform: 'cisco_iosxe' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 512'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/cisco_iosxr.yaml b/assets/platforms/cisco_iosxr.yaml
new file mode 100644
index 0000000..29e4338
--- /dev/null
+++ b/assets/platforms/cisco_iosxr.yaml
@@ -0,0 +1,46 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}#\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ configuration-exclusive:
+ name: 'configuration-exclusive'
+ pattern: '(?im)^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'end'
+ escalate: 'configure exclusive'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - '% Ambiguous command'
+ - '% Incomplete command'
+ - '% Invalid input detected'
+ - '% Unknown command'
+ textfsm-platform: 'cisco_iosxr' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 512'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/cisco_nxos.yaml b/assets/platforms/cisco_nxos.yaml
new file mode 100644
index 0000000..39c77fc
--- /dev/null
+++ b/assets/platforms/cisco_nxos.yaml
@@ -0,0 +1,58 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-]{1,63}>\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'disable'
+ escalate: 'enable'
+ escalate-auth: true
+ escalate-prompt: '(?im)^[pP]assword:\s?$'
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
+ not-contains:
+ - 'config-tcl'
+ - 'config-s'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ tclsh:
+ name: 'tclsh'
+ pattern: '(?im)(^[\w.\-@/:]{1,63}\-tcl#\s?$)|(^[\w.\-@/:]{1,63}\(config\-tcl\)#\s?$)|(^>\s?$)'
+ previous-priv: 'privilege-exec'
+ deescalate: 'tclquit'
+ escalate: 'tclsh'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'privilege-exec'
+ failed-when-contains:
+ - '% Ambiguous command'
+ - '% Incomplete command'
+ - '% Invalid input detected'
+ - '% Unknown command'
+ - 'ERROR:'
+ textfsm-platform: 'cisco_nxos' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 511'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/example.yaml b/assets/platforms/example.yaml
new file mode 100644
index 0000000..82d1636
--- /dev/null
+++ b/assets/platforms/example.yaml
@@ -0,0 +1,77 @@
+---
+default:
+ # driver-type can be either "generic" or "network" for the corresponding scrapli driver base class
+ driver-type: 'network'
+
+ # privilege-levels is a map/dict of privilege level name : privilege level data
+ privilege-levels:
+ example:
+ name: 'example'
+ # pattern represents the regex that is used to map a prompt to a privilege level
+ pattern: '(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$'
+ # not contains is a list of strings that if seen in the prompt means we are *not* in this prompt
+ # level -- this is useful to make regexs simpler and to not need lookarounds (because cant in go
+ # with std library anyway)
+ not-contains:
+ - 'tcl)'
+ # the "previous" or "lower" privilege level that you normally would deescalate to from this
+ # priv
+ previous-priv: 'privilege-exec'
+ # the command used to deescalate from this privilege level to the "previous-priv"
+ deescalate: 'end'
+ # the command used to escalate *into* this privilege level (from the previous-priv)
+ escalate: 'configure terminal'
+ # true/false there is escalation authentication (like enable secret)
+ escalate-auth: false
+ # option regex pattern to use to find the escalation prompt
+ escalate-prompt:
+
+ # default desired priv is the "main" privilege level scrapli operates in -- the one you would
+ # normally send "commands" (not configs) in, in cisco/eos land that is privilege-exec
+ default-desired-privilege-level: 'privilege-exec'
+
+ # a list of strings that if seen in output indicates the command/config failed
+ failed-when-contains:
+ - '% Ambiguous command'
+ - '% Incomplete command'
+ - '% Invalid input detected'
+ - '% Unknown command'
+
+ # textfsm-platform - only applicable in scrapli python, maps this platform to the ntc-templates
+ # platform string for ntc-templates/textfsm integration
+ textfsm-platform: 'cisco_iosxe'
+
+ # list of operations to run in the "on-open" phase of connection establishment. this can include as
+ # many operations as you'd like, or of course you could just do this in code manually yourself. the
+ # idea here is to disable paging and any fancy prompt things before doing "real" scrapli work.
+ # the available operation types are:
+ # channel.write:
+ # description:
+ # writes data to the channel at whatever privilege level you are currently in. does not send
+ # a return/new-line, just writes what you ask it to
+ # args:
+ # input: the input to write
+ # channel.return:
+ # description: sends a return, thats it
+ # acquire-priv:
+ # description: acquire a privilege level
+ # args:
+ # target: the target privilege level name, if not provided will be default desired privilege
+ # driver.send-command:
+ # description: send a command like "normal"
+ # args:
+ # command: the command to send
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 512'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+
+ # list of any operations to run in the "on-close" phase of things. typically this will just be
+ # something like "exit" and a return to terminate the session
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/ipinfusion_ocnos.yaml b/assets/platforms/ipinfusion_ocnos.yaml
new file mode 100644
index 0000000..455b5c1
--- /dev/null
+++ b/assets/platforms/ipinfusion_ocnos.yaml
@@ -0,0 +1,54 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ linux:
+ name: 'linux'
+ pattern: '(?im)^\S+@\S+\:\S+[\#\?]\s*$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}>\s*$'
+ previous-priv: "linux"
+ deescalate: "logout"
+ escalate: "cmlsh"
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}#\s*$'
+ previous-priv: "exec"
+ deescalate: "disable"
+ escalate: "enable"
+ escalate-auth: false
+ escalate-prompt:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#\s*$'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - "% Ambiguous command"
+ - "% Incomplete command"
+ - "% Invalid input detected"
+ - "% Unknown command"
+ textfsm-platform: '' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ - operation: 'driver.send-command'
+ command: 'terminal width 511'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/juniper_junos.yaml b/assets/platforms/juniper_junos.yaml
new file mode 100644
index 0000000..61dba3c
--- /dev/null
+++ b/assets/platforms/juniper_junos.yaml
@@ -0,0 +1,74 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^({\w+:\d}\n){0,1}[\w\-@()/:]{1,63}>\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit configuration-mode'
+ escalate: 'configure'
+ escalate-auth: false
+ escalate-prompt:
+ configuration-exclusive:
+ name: 'configuration-exclusive'
+ pattern: '(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit configuration-mode'
+ escalate: 'configure exclusive'
+ escalate-auth: false
+ escalate-prompt:
+ configuration-private:
+ name: 'configuration-private'
+ pattern: '(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit configuration-mode'
+ escalate: 'configure private'
+ escalate-auth: false
+ escalate-prompt:
+ shell:
+ name: 'shell'
+ pattern: '(?im)^.*[%$]\s?$'
+ not-contains:
+ - "root"
+ previous-priv: 'exec'
+ deescalate: 'exit'
+ escalate: 'start shell'
+ escalate-auth: false
+ escalate-prompt:
+ root-shell:
+ name: 'root-shell'
+ pattern: '(?im)^.*root@[[:ascii:]]*?:?[[:ascii:]]*?[%#]\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit'
+ escalate: 'start shell user root'
+ escalate-auth: true
+ escalate-prompt: '(?im)^[pP]assword:\s?$'
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - 'is ambiguous'
+ - 'No valid completions'
+ - 'unknown command'
+ - 'syntax error'
+ textfsm-platform: 'juniper_junos' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'set cli screen-width 511'
+ - operation: 'driver.send-command'
+ command: 'set cli screen-length 0'
+ - operation: 'driver.send-command'
+ command: 'set cli complete-on-space off'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/nokia_srl.yaml b/assets/platforms/nokia_srl.yaml
new file mode 100644
index 0000000..206fb07
--- /dev/null
+++ b/assets/platforms/nokia_srl.yaml
@@ -0,0 +1,35 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^--{(\s\[FACTORY\])?[\+\*\s]{1,}running\s}--\[.+?\]--\s*\n[abcd]:\S+#\s*$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^--{(\s\[FACTORY\])?[\+\*\!\s]{1,}candidate\sprivate\s[\-\w\s]+}--\[.+?\]--\s*\n[abcd]:\S+#\s*$'
+ previous-priv: 'exec'
+ deescalate: 'discard now'
+ escalate: 'enter candidate private'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - 'Error:'
+ textfsm-platform: '' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'environment cli-engine type basic'
+ - operation: 'driver.send-command'
+ command: 'environment complete-on-space false'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'quit'
+ - operation: 'channel.return'
diff --git a/assets/platforms/nokia_sros.yaml b/assets/platforms/nokia_sros.yaml
new file mode 100644
index 0000000..25f3abe
--- /dev/null
+++ b/assets/platforms/nokia_sros.yaml
@@ -0,0 +1,48 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^\[.*\]\n[abcd]:\S+@\S+#\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ # configuration privilege level maps to the exclusive config mode on SR OS
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^\*?\(ex\)\[/?\]\n[abcd]:\S+@\S+#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'quit-config'
+ escalate: 'edit-config exclusive'
+ escalate-auth: false
+ escalate-prompt:
+ configuration-with-path:
+ name: 'configuration-with-path'
+ pattern: '(?im)^\*?\(ex\)\[\S{2,}.+\]\n[abcd]:\S+@\S+#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit all'
+ escalate: ''
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - "CRITICAL:"
+ - "MAJOR:"
+ - "MINOR:"
+ textfsm-platform: '' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'environment console width 512'
+ - operation: 'driver.send-command'
+ command: 'environment more false'
+ - operation: 'driver.send-command'
+ command: 'environment command-completion space false'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'logout'
+ - operation: 'channel.return'
diff --git a/assets/platforms/nokia_sros_classic.yaml b/assets/platforms/nokia_sros_classic.yaml
new file mode 100644
index 0000000..5b026f7
--- /dev/null
+++ b/assets/platforms/nokia_sros_classic.yaml
@@ -0,0 +1,28 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^\*?[abcd]:\S+#\s*$'
+ previous-priv: ''
+ deescalate: ''
+ escalate: ''
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'configuration'
+ failed-when-contains:
+ - "CRITICAL:"
+ - "MAJOR:"
+ - "MINOR:"
+ - "Error:"
+ textfsm-platform: '' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'environment no more'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'logout'
+ - operation: 'channel.return'
diff --git a/assets/platforms/paloalto_panos.yaml b/assets/platforms/paloalto_panos.yaml
new file mode 100644
index 0000000..308a9f1
--- /dev/null
+++ b/assets/platforms/paloalto_panos.yaml
@@ -0,0 +1,39 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w\._-]+@[\w\.\(\)_-]+>\s?'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w\._-]+@[\w\.\(\)_-]+#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'exit'
+ escalate: 'configure'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'exec'
+ failed-when-contains:
+ - "Unknown command:"
+ - "Invalid Syntax."
+ - "Validation Error:"
+ textfsm-platform: '' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'set cli scripting-mode on'
+ - operation: 'driver.send-command'
+ command: 'set cli pager off'
+ - operation: 'driver.send-command'
+ command: 'environment command-completion space false'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
diff --git a/cfg/aristaeos.go b/cfg/aristaeos.go
deleted file mode 100644
index a82e615..0000000
--- a/cfg/aristaeos.go
+++ /dev/null
@@ -1,375 +0,0 @@
-package cfg
-
-import (
- "fmt"
- "regexp"
- "strings"
- "sync"
- "time"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-type eosPatterns struct {
- globalCommentLinePattern *regexp.Regexp
- bannerPattern *regexp.Regexp
- endPattern *regexp.Regexp
-}
-
-var (
- eosPatternsInstance *eosPatterns //nolint:gochecknoglobals
- eosPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func getEOSPatterns() *eosPatterns {
- eosPatternsInstanceOnce.Do(func() {
- eosPatternsInstance = &eosPatterns{
- globalCommentLinePattern: regexp.MustCompile(`(?im)^! .*$`),
- bannerPattern: regexp.MustCompile(`(?ims)^banner.*EOF$`),
- endPattern: regexp.MustCompile(`end$`),
- }
- })
-
- return eosPatternsInstance
-}
-
-type EOSCfg struct {
- conn *network.Driver
- VersionPattern *regexp.Regexp
- configCommandMap map[string]string
- configSessionName string
-}
-
-// NewEOSCfg return a cfg instance setup for an Arista EOS device.
-func NewEOSCfg(
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- options = append([]Option{WithConfigSources([]string{"running", "startup"})}, options...)
-
- c, err := newCfg(conn, options...)
- if err != nil {
- return nil, err
- }
-
- c.Platform = &EOSCfg{
- conn: conn,
- VersionPattern: regexp.MustCompile(`(?i)\d+\.\d+\.[a-z0-9\-]+(\.\d+[a-z]?)?`),
- configCommandMap: map[string]string{
- "running": "show running-config",
- "startup": "show startup-config",
- },
- }
-
- err = setPlatformOptions(c.Platform, options...)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-func (p *EOSCfg) ClearConfigSession() {
- p.configSessionName = ""
-}
-
-// GetVersion get the version from the device.
-func (p *EOSCfg) GetVersion() (string, []*base.Response, error) {
- versionResult, err := p.conn.SendCommand("show version | i Software image version")
- if err != nil {
- return "", nil, err
- }
-
- return p.VersionPattern.FindString(
- versionResult.Result,
- ), []*base.Response{
- versionResult,
- }, nil
-}
-
-// GetConfig get the configuration of a source datastore from the device.
-func (p *EOSCfg) GetConfig(source string) (string, []*base.Response, error) {
- cmd, err := getConfigCommand(p.configCommandMap, source)
- if err != nil {
- return "", nil, err
- }
-
- configResult, err := p.conn.SendCommand(cmd)
-
- if err != nil {
- return "", nil, err
- }
-
- return configResult.Result, []*base.Response{configResult}, nil
-}
-
-func (p *EOSCfg) prepareConfigPayloads(config string) (stdConfig, eagerConfig string) {
- patterns := getEOSPatterns()
-
- // remove comment lines
- config = patterns.globalCommentLinePattern.ReplaceAllString(config, "!")
-
- // remove "end" at the end of the config - if its present it will drop scrapli out
- // of the config session which we do not want
- config = patterns.endPattern.ReplaceAllString(config, "!")
-
- // find all sections that need to be "eagerly" sent; remove those sections from the "normal"
- // config, then join all the eager sections into a single string
- eagerSections := patterns.bannerPattern.FindStringSubmatch(config)
- eagerConfig = strings.Join(eagerSections, "\n")
-
- for _, section := range eagerSections {
- config = strings.Replace(config, section, "!", -1)
- }
-
- return config, eagerConfig
-}
-
-// RegisterConfigSession register a configuration session in EOS.
-func (p *EOSCfg) RegisterConfigSession(sessionName string) error {
- _, ok := p.conn.PrivilegeLevels[sessionName]
-
- if ok {
- return ErrConfigSessionAlreadyExists
- }
-
- sessionPrompt := regexp.QuoteMeta(sessionName[:6])
- sessionPromptPattern := fmt.Sprintf(
- `(?im)^[\w.\-@()/:\s]{1,63}\(config\-s\-%s[\w.\-@_/:]{0,32}\)#\s?$`,
- sessionPrompt,
- )
-
- sessionPrivilegeLevel := &base.PrivilegeLevel{
- Pattern: sessionPromptPattern,
- Name: sessionName,
- PreviousPriv: execPrivLevel,
- Deescalate: "end",
- Escalate: fmt.Sprintf("configure session %s", sessionName),
- EscalateAuth: false,
- EscalatePrompt: "",
- }
-
- p.conn.PrivilegeLevels[sessionName] = sessionPrivilegeLevel
- p.conn.UpdatePrivilegeLevels()
-
- return nil
-}
-
-func (p *EOSCfg) loadConfig(
- stdConfig, eagerConfig string,
- replace bool,
-) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- if replace {
- rollbackCleanConfigResult, rollbackErr := p.conn.SendConfig("rollback clean-config",
- base.WithDesiredPrivilegeLevel(p.configSessionName))
- if rollbackErr != nil {
- return scrapliResponses, rollbackErr
- }
-
- scrapliResponses = append(scrapliResponses, rollbackCleanConfigResult)
- }
-
- configResult, stdConfigErr := p.conn.SendConfig(
- stdConfig,
- base.WithDesiredPrivilegeLevel(p.configSessionName),
- )
- if stdConfigErr != nil || configResult.Failed != nil {
- return scrapliResponses, stdConfigErr
- }
-
- scrapliResponses = append(scrapliResponses, configResult)
-
- eagerResult, eagerConfigErr := p.conn.SendConfig(
- eagerConfig,
- base.WithDesiredPrivilegeLevel(p.configSessionName),
- base.WithSendEager(true),
- )
-
- if eagerConfigErr != nil {
- return scrapliResponses, eagerConfigErr
- }
-
- if eagerResult.Failed != nil {
- return scrapliResponses, eagerConfigErr
- }
-
- scrapliResponses = append(scrapliResponses, eagerResult)
-
- return scrapliResponses, nil
-}
-
-// LoadConfig load a candidate configuration.
-func (p *EOSCfg) LoadConfig(
- config string,
- replace bool,
- options *OperationOptions,
-) ([]*base.Response, error) {
- // options are unused for eos load config
- _ = options
-
- stdConfig, eagerConfig := p.prepareConfigPayloads(config)
-
- if p.configSessionName == "" {
- p.configSessionName = fmt.Sprintf("scrapli_cfg_%d", time.Now().Unix())
-
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "debug",
- fmt.Sprintf("configuration session name will be %s", p.configSessionName),
- ),
- )
-
- err := p.RegisterConfigSession(p.configSessionName)
- if err != nil {
- return nil, err
- }
- }
-
- return p.loadConfig(stdConfig, eagerConfig, replace)
-}
-
-// AbortConfig abort the loaded candidate configuration.
-func (p *EOSCfg) AbortConfig() ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- err := p.conn.AcquirePriv(p.configSessionName)
- if err != nil {
- return scrapliResponses, err
- }
-
- _, err = p.conn.Channel.SendInput("abort", false, false, p.conn.Channel.TimeoutOps)
- if err != nil {
- return scrapliResponses, err
- }
-
- p.conn.CurrentPriv = execPrivLevel
-
- return scrapliResponses, nil
-}
-
-// CommitConfig commit the loaded candidate configuration.
-func (p *EOSCfg) CommitConfig(source string) ([]*base.Response, error) {
- if source != runningConfig {
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "warning",
- "eos only supports committing to running config, running config is automatically copied to "+
- "startup during commit operation",
- ),
- )
- }
-
- var scrapliResponses []*base.Response
-
- commands := []string{
- fmt.Sprintf("configure session %s commit", p.configSessionName),
- "copy running-config startup-config",
- }
-
- m, err := p.conn.SendCommands(commands)
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, m.Responses...)
-
- return scrapliResponses, err
-}
-
-func (p *EOSCfg) normalizeSourceAndCandidateConfigs(
- sourceConfig, candidateConfig string,
-) (normalizedSourceConfig, normalizedCandidateConfig string) {
- patterns := getEOSPatterns()
-
- // Remove all comment lines from both the source and candidate configs -- this is only done
- // here pre-diff, so we dont modify the user provided candidate config which can totally have
- // those comment lines - we only remove "global" (top level) comments though... user comments
- // attached to interfaces and the stuff will remain
- normalizedSourceConfig = patterns.globalCommentLinePattern.ReplaceAllString(sourceConfig, "")
- normalizedSourceConfig = strings.Replace(normalizedSourceConfig, "\n\n", "\n", -1)
-
- normalizedCandidateConfig = patterns.globalCommentLinePattern.ReplaceAllString(
- candidateConfig,
- "",
- )
- normalizedCandidateConfig = strings.Replace(normalizedCandidateConfig, "\n\n", "\n", -1)
-
- return normalizedSourceConfig, normalizedCandidateConfig
-}
-
-// DiffConfig diff the candidate configuration against a source config.
-func (p *EOSCfg) DiffConfig(
- source, candidateConfig string,
-) (responses []*base.Response,
- normalizedSourceConfig,
- normalizedCandidateConfig,
- deviceDiff string, err error) {
- if source != runningConfig {
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "warning",
- "eos only supports diffing against the running config",
- ),
- )
- }
-
- var scrapliResponses []*base.Response
-
- diffResult, err := p.conn.SendConfig(
- "show session-config diffs",
- base.WithDesiredPrivilegeLevel(p.configSessionName),
- )
- if err != nil {
- return scrapliResponses, "", "", "", err
- }
-
- scrapliResponses = append(scrapliResponses, diffResult)
-
- if diffResult.Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed generating diff for config session",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- deviceDiff = diffResult.Result
-
- sourceConfig, getConfigR, err := p.GetConfig(source)
- if err != nil {
- return scrapliResponses, "", "", "", nil
- }
-
- scrapliResponses = append(scrapliResponses, getConfigR[0])
-
- if getConfigR[0].Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed fetching source config for diff comparison",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- normalizedSourceConfig, normalizedCandidateConfig = p.normalizeSourceAndCandidateConfigs(
- sourceConfig,
- candidateConfig,
- )
-
- return scrapliResponses, normalizedSourceConfig, normalizedCandidateConfig, deviceDiff, nil
-}
diff --git a/cfg/base.go b/cfg/base.go
deleted file mode 100644
index ba22382..0000000
--- a/cfg/base.go
+++ /dev/null
@@ -1,584 +0,0 @@
-package cfg
-
-import (
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-const (
- runningConfig = "running"
-)
-
-var ErrNoConfigSourcesProvided = errors.New("no configuration sources provided, cannot continue")
-var ErrInvalidConfigTarget = errors.New("provided config source is not valid")
-
-var ErrConnectionNotOpen = errors.New(
- "underlying scrapli connection is not open and `DedicatedConnection` is false, cannot continue")
-var ErrVersionError = errors.New("failed getting or parsing device version")
-
-var ErrPrepareNotCalled = errors.New("the Prepare method has not been called, cannot continue")
-
-var ErrConfigSessionAlreadyExists = errors.New(
- "configuration session name already exists, cannot create it")
-
-var ErrInvalidSource = errors.New("invalid config source/target provided")
-
-var ErrFailedToDetermineDeviceState = errors.New(
- "failed to determine device state, ex: failed to determine available space")
-var ErrInsufficientSpaceAvailable = errors.New("insufficient space available on device")
-
-var ErrGetConfigFailed = errors.New("get config operation failed")
-var ErrLoadConfigFailed = errors.New("load config operation failed")
-var ErrAbortConfigFailed = errors.New("abort config operation failed")
-var ErrCommitConfigFailed = errors.New("commit config operation failed")
-var ErrDiffConfigFailed = errors.New("diff config operation failed")
-
-// Platform -- interface describing the methods the vendor specific platforms must implement, note
-// that this is also similar (but not the same!) to the same api surface of the Cfg object that
-// users see.
-type Platform interface {
- GetVersion() (string, []*base.Response, error)
- GetConfig(source string) (string, []*base.Response, error)
- LoadConfig(config string, replace bool, options *OperationOptions) ([]*base.Response, error)
- AbortConfig() ([]*base.Response, error)
- CommitConfig(source string) ([]*base.Response, error)
- DiffConfig(source, candidateConfig string) ([]*base.Response, string, string, string, error)
- ClearConfigSession()
-}
-
-func FormatLogMessage(conn *network.Driver, level, msg string) string {
- return logging.FormatLogMessage(level, conn.Host, conn.Transport.BaseTransportArgs.Port, msg)
-}
-
-func setPlatformOptions(p Platform, options ...Option) error {
- for _, option := range options {
- err := option(p)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return err
- }
- }
- }
-
- return nil
-}
-
-func parseOperationOptions(o []OperationOption) *OperationOptions {
- opts := &OperationOptions{Source: "running", DiffColorize: true, AutoClean: true}
-
- if len(o) > 0 && o[0] != nil {
- for _, option := range o {
- option(opts)
- }
- }
-
- return opts
-}
-
-func determineCandidateConfigFilename(c string) string {
- candidateConfigFilename := c
-
- if c == "" {
- candidateConfigFilename = fmt.Sprintf("scrapli_cfg_%d", time.Now().Unix())
- }
-
- return candidateConfigFilename
-}
-
-// Cfg primary/base cfg platform struct.
-type Cfg struct {
- ConfigSources []string
- OnPrepare func(*network.Driver) error
- DedicatedConnection bool
- IgnoreVersion bool
-
- CandidateConfig string
- VersionString string
- prepared bool
-
- Platform Platform
- conn *network.Driver
-}
-
-// newCfg returns a new instance of Cfg; private because users should be calling the platform
-// specific new functions (or using the factory).
-func newCfg(
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- c := &Cfg{
- OnPrepare: nil,
- DedicatedConnection: false,
- IgnoreVersion: false,
- prepared: false,
- conn: conn,
- }
-
- for _, option := range options {
- err := option(c)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- if len(c.ConfigSources) == 0 {
- // if for some reason we dont have config sources we cant really do anything... this should
- // be set by the specific platform so this *shouldn't* happen but... who knows!
- return nil, ErrNoConfigSourcesProvided
- }
-
- return c, nil
-}
-
-func (d *Cfg) invalidConfigSource(err error) error {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "invalid configuration source",
- ),
- )
-
- return err
-}
-
-func (d *Cfg) prepareOk() error {
- if d.OnPrepare != nil && !d.prepared {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "OnPrepare provided, but prepare method not called. call prepare method prior "+
- "to using the Cfg object",
- ),
- )
-
- return ErrPrepareNotCalled
- }
-
- return nil
-}
-
-func (d *Cfg) versionOk() error {
- if !d.IgnoreVersion && d.VersionString == "" {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "IgnoreVersion is false, but version has not yet been fetched. call prepare method prior "+
- "to using the Cfg object to ensure version is properly gathered",
- ),
- )
-
- return ErrPrepareNotCalled
- }
-
- return nil
-}
-
-func (d *Cfg) operationOk() error {
- prepareErr := d.prepareOk()
- if prepareErr != nil {
- return prepareErr
- }
-
- versionErr := d.versionOk()
-
- if versionErr != nil {
- return versionErr
- }
-
- return nil
-}
-
-func (d *Cfg) validateAndSetVersion(versionResponse *Response) error {
- if versionResponse.Failed != nil {
- logging.LogError(FormatLogMessage(d.conn, "error", "failed getting version from device"))
- return ErrVersionError
- }
-
- if versionResponse.Result == "" {
- logging.LogError(
- FormatLogMessage(d.conn, "error", "failed parsing version string from device output"),
- )
-
- return ErrVersionError
- }
-
- d.VersionString = versionResponse.Result
-
- return nil
-}
-
-func (d *Cfg) open() error {
- if d.conn.Transport.IsAlive() {
- // nothing to do, connection is already open!
- return nil
- }
-
- if d.DedicatedConnection {
- err := d.conn.Open()
- return err
- }
-
- return ErrConnectionNotOpen
-}
-
-// Prepare the connection.
-func (d *Cfg) Prepare() error {
- logging.LogDebug(FormatLogMessage(d.conn, "info", "preparing cfg connection"))
-
- err := d.open()
- if err != nil {
- return err
- }
-
- if !d.IgnoreVersion {
- logging.LogDebug(
- FormatLogMessage(d.conn, "debug", "IgnoreVersion is false, fetching device version"),
- )
-
- versionResponse, getVersionErr := d.GetVersion()
-
- if getVersionErr != nil {
- return getVersionErr
- }
-
- validateVersionErr := d.validateAndSetVersion(versionResponse)
- if validateVersionErr != nil {
- return validateVersionErr
- }
- }
-
- if d.OnPrepare != nil {
- logging.LogDebug(FormatLogMessage(d.conn, "debug", "OnPrepare provided, executing now"))
-
- prepareErr := d.OnPrepare(d.conn)
- if prepareErr != nil {
- return prepareErr
- }
- }
-
- d.prepared = true
-
- return nil
-}
-
-func (d *Cfg) clearConfigSession() {
- logging.LogDebug(
- FormatLogMessage(
- d.conn,
- "debug",
- "resetting config session data",
- ),
- )
-
- d.CandidateConfig = ""
- d.Platform.ClearConfigSession()
-}
-
-func (d *Cfg) close() error {
- if d.DedicatedConnection && d.conn.Transport.IsAlive() {
- logging.LogDebug(
- FormatLogMessage(
- d.conn,
- "info",
- "DedicatedConnection is true, closing scrapli connection",
- ),
- )
-
- err := d.conn.Close()
-
- return err
- }
-
- return nil
-}
-
-// Cleanup cleans up the cfg session.
-func (d *Cfg) Cleanup() error {
- err := d.close()
- if err != nil {
- return err
- }
-
- d.VersionString = ""
- d.prepared = false
-
- d.clearConfigSession()
-
- return nil
-}
-
-// RenderSubstitutedConfig renders a config with provided substitutions.
-func (d *Cfg) RenderSubstitutedConfig() (string, error) {
- return "", nil
-}
-
-func (d *Cfg) configSourceValid(source string) bool {
- for _, configSource := range d.ConfigSources {
- if configSource == source {
- return true
- }
- }
-
- return false
-}
-
-// GetVersion gets the version from the device.
-func (d *Cfg) GetVersion() (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "get version requested"),
- )
-
- r := NewResponse(d.conn.Host, "GetVersion", nil)
-
- versionString, scrapliResponses, err := d.Platform.GetVersion()
-
- r.Record(scrapliResponses, versionString)
-
- if r.Failed != nil {
- logging.LogDebug(FormatLogMessage(d.conn, "warning", "failed to fetch device version"))
- }
-
- if r.Result == "" {
- logging.LogDebug(FormatLogMessage(d.conn, "warning", "failed to parse device version"))
- }
-
- return r, err
-}
-
-// GetConfig gets the configuration of a source datastore from the device.
-func (d *Cfg) GetConfig(source string) (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(
- d.conn,
- "info",
- fmt.Sprintf("get config requested for config source '%s'", source),
- ),
- )
-
- r := NewResponse(d.conn.Host, "GetConfig", ErrGetConfigFailed)
-
- operationOkErr := d.operationOk()
- if operationOkErr != nil {
- return r, operationOkErr
- }
-
- if !d.configSourceValid(source) {
- return r, d.invalidConfigSource(ErrInvalidSource)
- }
-
- cfgString, scrapliResponses, err := d.Platform.GetConfig(source)
-
- r.Record(scrapliResponses, cfgString)
-
- if r.Failed != nil {
- logging.LogError(FormatLogMessage(d.conn, "debug", "failed to fetch config from device"))
- }
-
- return r, err
-}
-
-// LoadConfig loads a candidate configuration.
-func (d *Cfg) LoadConfig(
- config string,
- replace bool,
- options ...OperationOption,
-) (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "load config requested"),
- )
-
- opts := parseOperationOptions(options)
-
- d.CandidateConfig = config
- r := NewResponse(d.conn.Host, "LoadConfig", ErrLoadConfigFailed)
-
- operationOkErr := d.operationOk()
- if operationOkErr != nil {
- return r, operationOkErr
- }
-
- scrapliResponses, err := d.Platform.LoadConfig(config, replace, opts)
-
- r.Record(scrapliResponses, "")
-
- if r.Failed != nil {
- logging.LogError(
- FormatLogMessage(d.conn, "error", "failed to load candidate configuration"),
- )
- }
-
- return r, err
-}
-
-// LoadConfigFromFile loads a candidate configuration from a provided file.
-func (d *Cfg) LoadConfigFromFile(
- f string,
- replace bool,
- options ...OperationOption,
-) (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "load config from file requested"),
- )
-
- c, err := util.LoadFileLines(f)
- if err != nil {
- return nil, err
- }
-
- return d.LoadConfig(strings.Join(c, "\n"), replace, options...)
-}
-
-// AbortConfig aborts the loaded candidate configuration.
-func (d *Cfg) AbortConfig() (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "abort config requested"),
- )
-
- r := NewResponse(d.conn.Host, "AbortConfig", ErrAbortConfigFailed)
-
- if d.CandidateConfig == "" {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "no candidate configuration exists, you must load a config in order to abort it!",
- ),
- )
-
- return r, ErrAbortConfigFailed
- }
-
- operationOkErr := d.operationOk()
- if operationOkErr != nil {
- return r, operationOkErr
- }
-
- scrapliResponses, err := d.Platform.AbortConfig()
-
- r.Record(scrapliResponses, "")
-
- if r.Failed != nil {
- logging.LogError(
- FormatLogMessage(d.conn, "error", "failed to abort candidate configuration"),
- )
- }
-
- d.clearConfigSession()
-
- return r, err
-}
-
-// CommitConfig commits the loaded candidate configuration.
-func (d *Cfg) CommitConfig(options ...OperationOption) (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "commit config requested"),
- )
-
- opts := parseOperationOptions(options)
-
- r := NewResponse(d.conn.Host, "CommitConfig", ErrCommitConfigFailed)
-
- if d.CandidateConfig == "" {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "no candidate configuration exists, you must load a config in order to commit it!",
- ),
- )
-
- return r, ErrCommitConfigFailed
- }
-
- operationOkErr := d.operationOk()
- if operationOkErr != nil {
- return r, operationOkErr
- }
-
- if !d.configSourceValid(opts.Source) {
- return r, d.invalidConfigSource(ErrInvalidSource)
- }
-
- scrapliResponses, err := d.Platform.CommitConfig(opts.Source)
-
- r.Record(scrapliResponses, "")
-
- if r.Failed != nil {
- logging.LogError(
- FormatLogMessage(d.conn, "error", "failed to commit candidate configuration"),
- )
- }
-
- d.clearConfigSession()
-
- return r, err
-}
-
-// DiffConfig diffs the candidate configuration against a source config.
-func (d *Cfg) DiffConfig(options ...OperationOption) (*DiffResponse, error) {
- logging.LogDebug(
- FormatLogMessage(d.conn, "info", "diff config requested"),
- )
-
- opts := parseOperationOptions(options)
-
- r := NewDiffResponse(d.conn.Host, opts.Source, opts.DiffColorize, opts.DiffSideBySideWidth)
-
- operationOkErr := d.operationOk()
- if operationOkErr != nil {
- return r, operationOkErr
- }
-
- if d.CandidateConfig == "" {
- logging.LogError(
- FormatLogMessage(
- d.conn,
- "error",
- "no candidate configuration exists, you must load a config in order to diff it!",
- ),
- )
-
- return r, ErrDiffConfigFailed
- }
-
- if !d.configSourceValid(opts.Source) {
- return r, d.invalidConfigSource(ErrDiffConfigFailed)
- }
-
- scrapliResponses, sourceConfig, candidateConfig, deviceDiff, err := d.Platform.DiffConfig(
- opts.Source,
- d.CandidateConfig,
- )
-
- r.Record(scrapliResponses, "")
- r.RecordDiff(sourceConfig, candidateConfig, deviceDiff)
-
- if r.Failed != nil {
- logging.LogError(
- FormatLogMessage(d.conn, "error", "failed to diff configuration"),
- )
- }
-
- return r, err
-}
diff --git a/cfg/ciscoiosxe.go b/cfg/ciscoiosxe.go
deleted file mode 100644
index e2012c5..0000000
--- a/cfg/ciscoiosxe.go
+++ /dev/null
@@ -1,551 +0,0 @@
-package cfg
-
-import (
- "fmt"
- "regexp"
- "strings"
- "sync"
-
- "github.com/scrapli/scrapligo/channel"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-const (
- filePromptNoisy = "noisy"
- filePromptQuiet = "quiet"
- filePromptAlert = "alert"
-)
-
-type iosxePatterns struct {
- bytesFreePattern *regexp.Regexp
- filePromptModePattern *regexp.Regexp
- outputHeaderPattern *regexp.Regexp
-}
-
-var (
- iosxePatternsInstance *iosxePatterns //nolint:gochecknoglobals
- iosxePatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func getIOSXEPatterns() *iosxePatterns {
- iosxePatternsInstanceOnce.Do(func() {
- iosxePatternsInstance = &iosxePatterns{
- bytesFreePattern: regexp.MustCompile(
- `(?i)(?P\d+)(?: bytes free)`,
- ),
- filePromptModePattern: regexp.MustCompile(`(?i)(?:file prompt )(?P\w+)`),
- // sort of a bad name, but it matches python version -- used to find the version
- // string in the config so we can remove anything in front of it
- outputHeaderPattern: regexp.MustCompile(`(?im)(^version \d+\.\d+$)`),
- }
- })
-
- return iosxePatternsInstance
-}
-
-type IOSXECfg struct {
- conn *network.Driver
- VersionPattern *regexp.Regexp
- Filesystem string
- filesystemSpaceAvailBufferPerc float32
- configCommandMap map[string]string
- CandidateConfigFilename string
- candidateConfigFilename string
- replaceConfig bool
-}
-
-// NewIOSXECfg return a cfg instance setup for a Cisco IOSXE device.
-func NewIOSXECfg( //nolint:dupl
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- options = append(
- []Option{WithConfigSources([]string{"running", "startup"}), WithFilesystem("flash:")},
- options...)
-
- c, err := newCfg(conn, options...)
- if err != nil {
- return nil, err
- }
-
- c.Platform = &IOSXECfg{
- conn: conn,
- VersionPattern: regexp.MustCompile(`(?i)\d+\.[a-z0-9\(\).]+`),
- configCommandMap: map[string]string{
- "running": "show running-config",
- "startup": "show startup-config",
- },
- filesystemSpaceAvailBufferPerc: 10.0,
- }
-
- err = setPlatformOptions(c.Platform, options...)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-func (p *IOSXECfg) ClearConfigSession() {
- p.candidateConfigFilename = ""
-}
-
-// GetVersion get the version from the device.
-func (p *IOSXECfg) GetVersion() (string, []*base.Response, error) {
- versionResult, err := p.conn.SendCommand("show version | i Version")
- if err != nil {
- return "", nil, err
- }
-
- return p.VersionPattern.FindString(
- versionResult.Result,
- ), []*base.Response{
- versionResult,
- }, nil
-}
-
-// GetConfig get the configuration of a source datastore from the device.
-func (p *IOSXECfg) GetConfig(source string) (string, []*base.Response, error) {
- cmd, err := getConfigCommand(p.configCommandMap, source)
- if err != nil {
- return "", nil, err
- }
-
- configResult, err := p.conn.SendCommand(cmd)
-
- if err != nil {
- return "", nil, err
- }
-
- return configResult.Result, []*base.Response{configResult}, nil
-}
-
-func (p *IOSXECfg) cleanConfig(config string) string {
- patterns := getIOSXEPatterns()
-
- configSectionIndices := patterns.outputHeaderPattern.FindStringIndex(config)
-
- if len(configSectionIndices) == 0 {
- // didnt find the header pattern
- return config
- }
-
- if len(configSectionIndices) == 2 { //nolint:gomnd
- return config[configSectionIndices[0]:]
- }
-
- panic("stripping config header failed, this is a bug, provided config is wonky, or both...")
-}
-
-func (p *IOSXECfg) prepareConfigPayload(config string) string {
- tcslhStartFile := fmt.Sprintf(
- `puts [open "%s%s" w+] {`,
- p.Filesystem,
- p.candidateConfigFilename,
- )
- tclshEndFile := "}"
-
- return strings.Join([]string{tcslhStartFile, config, tclshEndFile}, "\n")
-}
-
-func (p *IOSXECfg) getFilesystemSpaceAvail() (int, error) {
- patterns := getIOSXEPatterns()
-
- filesystemSizeResult, err := p.conn.SendCommand(fmt.Sprintf("dir %s | i bytes", p.Filesystem))
- if err != nil {
- return -1, ErrFailedToDetermineDeviceState
- }
-
- return parseSpaceAvail(patterns.bytesFreePattern, filesystemSizeResult)
-}
-
-// LoadConfig load a candidate configuration.
-func (p *IOSXECfg) LoadConfig(
- config string,
- replace bool,
- options *OperationOptions,
-) ([]*base.Response, error) {
- p.replaceConfig = replace
-
- var scrapliResponses []*base.Response
-
- if options.AutoClean {
- config = p.cleanConfig(config)
- }
-
- filesystemBytesAvail, err := p.getFilesystemSpaceAvail()
- if err != nil {
- return nil, err
- }
-
- spaceSufficient := isSpaceSufficient(
- filesystemBytesAvail,
- p.filesystemSpaceAvailBufferPerc,
- config,
- )
- if !spaceSufficient {
- return nil, ErrInsufficientSpaceAvailable
- }
-
- if p.candidateConfigFilename == "" {
- p.candidateConfigFilename = determineCandidateConfigFilename(p.CandidateConfigFilename)
-
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "debug",
- fmt.Sprintf(
- "candidate configuration filename name will be %s",
- p.candidateConfigFilename,
- ),
- ),
- )
- }
-
- config = p.prepareConfigPayload(config)
-
- originalReturnChar := p.conn.Channel.CommsReturnChar
- tclCommsReturnChar := "\r"
-
- err = p.conn.AcquirePriv("tclsh")
- if err != nil {
- return nil, err
- }
-
- p.conn.Channel.CommsReturnChar = tclCommsReturnChar
-
- r, err := p.conn.SendConfig(config, base.WithDesiredPrivilegeLevel("tclsh"))
- if err != nil {
- return nil, err
- }
-
- scrapliResponses = append(scrapliResponses, r)
-
- err = p.conn.AcquirePriv(p.conn.DefaultDesiredPriv)
- if err != nil {
- return scrapliResponses, err
- }
-
- p.conn.Channel.CommsReturnChar = originalReturnChar
-
- return scrapliResponses, nil
-}
-
-func (p *IOSXECfg) determineFilePromptMode() (string, error) {
- r, err := p.conn.SendCommand("show run | i file prompt")
- if err != nil {
- return "", err
- }
-
- patterns := getIOSXEPatterns()
-
- filePromptMatch := patterns.filePromptModePattern.FindString(r.Result)
-
- if filePromptMatch == "" {
- return filePromptAlert, nil
- }
-
- if strings.Contains(filePromptMatch, filePromptNoisy) {
- return filePromptNoisy, nil
- }
-
- return filePromptQuiet, nil
-}
-
-// AbortConfig abort the loaded candidate configuration.
-func (p *IOSXECfg) AbortConfig() ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- r, err := p.deleteCandidateConfigFile()
-
- scrapliResponses = append(scrapliResponses, r)
-
- return scrapliResponses, err
-}
-
-func (p *IOSXECfg) commitConfigMerge() (*base.Response, error) {
- filePromptMode, err := p.determineFilePromptMode()
- if err != nil {
- return nil, err
- }
-
- var mergeEvents []*channel.SendInteractiveEvent
-
- if filePromptMode == filePromptAlert {
- mergeEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: fmt.Sprintf(
- "copy %s%s running-config",
- p.Filesystem,
- p.candidateConfigFilename,
- ),
- ChannelResponse: "Destination filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- } else if filePromptMode == filePromptNoisy {
- mergeEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: fmt.Sprintf(
- "copy %s%s running-config", p.Filesystem, p.candidateConfigFilename),
- ChannelResponse: "Source filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "Destination filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- } else {
- mergeEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: fmt.Sprintf(
- "copy %s%s running-config", p.Filesystem, p.candidateConfigFilename),
- ChannelResponse: "",
- HideInput: false,
- },
- }
- }
-
- return p.conn.SendInteractive(mergeEvents)
-}
-
-// SaveConfig writes running config to startup config.
-func (p *IOSXECfg) SaveConfig() (*base.Response, error) {
- filePromptMode, err := p.determineFilePromptMode()
- if err != nil {
- return nil, err
- }
-
- var saveEvents []*channel.SendInteractiveEvent
-
- if filePromptMode == filePromptAlert {
- saveEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: "copy running-config startup-config",
- ChannelResponse: "Destination filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- } else if filePromptMode == filePromptNoisy {
- saveEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: "copy running-config startup-config",
- ChannelResponse: "Source filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "Destination filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- } else {
- saveEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: "copy running-config startup-config",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- }
-
- return p.conn.SendInteractive(saveEvents)
-}
-
-func (p *IOSXECfg) deleteCandidateConfigFile() (*base.Response, error) {
- filePromptMode, err := p.determineFilePromptMode()
- if err != nil {
- return nil, err
- }
-
- var saveEvents []*channel.SendInteractiveEvent
-
- if filePromptMode == filePromptAlert || filePromptMode == filePromptNoisy {
- saveEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: fmt.Sprintf(
- "delete %s%s",
- p.Filesystem,
- p.candidateConfigFilename,
- ),
- ChannelResponse: "Delete filename",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "[confirm]",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- } else {
- saveEvents = []*channel.SendInteractiveEvent{
- {
- ChannelInput: fmt.Sprintf("delete %s%s", p.Filesystem, p.candidateConfigFilename),
- ChannelResponse: "[confirm]",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- }
- }
-
- return p.conn.SendInteractive(saveEvents)
-}
-
-// CommitConfig commit the loaded candidate configuration.
-func (p *IOSXECfg) CommitConfig(source string) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- var commitResult *base.Response
-
- var err error
-
- if p.replaceConfig {
- commitResult, err = p.conn.SendCommand(
- fmt.Sprintf("configure replace %s%s force", p.Filesystem, p.candidateConfigFilename),
- )
- } else {
- commitResult, err = p.commitConfigMerge()
- }
-
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, commitResult)
-
- saveResult, err := p.SaveConfig()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, saveResult)
-
- cleanupResult, err := p.deleteCandidateConfigFile()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, cleanupResult)
-
- return scrapliResponses, nil
-}
-
-func (p *IOSXECfg) getDiffCommand(source string) string {
- if p.replaceConfig {
- return fmt.Sprintf(
- "show archive config differences system:%s-config %s%s",
- source,
- p.Filesystem,
- p.candidateConfigFilename,
- )
- }
-
- return fmt.Sprintf(
- "show archive config incremental-diffs %s%s ignorecase",
- p.Filesystem,
- p.candidateConfigFilename,
- )
-}
-
-func (p *IOSXECfg) normalizeSourceAndCandidateConfigs(
- sourceConfig, candidateConfig string,
-) (normalizedSourceConfig, normalizedCandidateConfig string) {
- // remove any of the leading timestamp/building config/config size/last change lines in both the
- // source and candidate configs so they dont need to be compared
- normalizedSourceConfig = p.cleanConfig(sourceConfig)
- normalizedCandidateConfig = p.cleanConfig(candidateConfig)
-
- return normalizedSourceConfig, normalizedCandidateConfig
-}
-
-// DiffConfig diff the candidate configuration against a source config.
-func (p *IOSXECfg) DiffConfig(
- source, candidateConfig string,
-) (responses []*base.Response,
- normalizedSourceConfig,
- normalizedCandidateConfig,
- deviceDiff string, err error) {
- var scrapliResponses []*base.Response
-
- diffResult, err := p.conn.SendCommand(p.getDiffCommand(source))
- if err != nil {
- return scrapliResponses, "", "", "", err
- }
-
- scrapliResponses = append(scrapliResponses, diffResult)
-
- if diffResult.Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed generating diff for config session",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- deviceDiff = diffResult.Result
-
- sourceConfig, getConfigR, err := p.GetConfig(source)
- if err != nil {
- return scrapliResponses, "", "", "", nil
- }
-
- scrapliResponses = append(scrapliResponses, getConfigR[0])
-
- if getConfigR[0].Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed fetching source config for diff comparison",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- normalizedSourceConfig, normalizedCandidateConfig = p.normalizeSourceAndCandidateConfigs(
- sourceConfig,
- candidateConfig,
- )
-
- return scrapliResponses, normalizedSourceConfig, normalizedCandidateConfig, deviceDiff, nil
-}
diff --git a/cfg/ciscoiosxr.go b/cfg/ciscoiosxr.go
deleted file mode 100644
index 88949d0..0000000
--- a/cfg/ciscoiosxr.go
+++ /dev/null
@@ -1,374 +0,0 @@
-package cfg
-
-import (
- "fmt"
- "regexp"
- "strings"
- "sync"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/channel"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-type iosxrPatterns struct {
- bannerDelimPattern *regexp.Regexp
- timestampPattern *regexp.Regexp
- buildConfigPattern *regexp.Regexp
- configVersionPattern *regexp.Regexp
- configChangePattern *regexp.Regexp
- outputHeaderPattern *regexp.Regexp
- endPattern *regexp.Regexp
-}
-
-var (
- iosxrPatternsInstance *iosxrPatterns //nolint:gochecknoglobals
- iosxrPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func getIOSXRPatterns() *iosxrPatterns {
- iosxrPatternsInstanceOnce.Do(func() {
- iosxrPatternsInstance = &iosxrPatterns{
- bannerDelimPattern: regexp.MustCompile(
- `(?im)(^banner\s(?:exec|incoming|login|motd|prompt-timeout|slip-ppp)\s(.))`,
- ),
- timestampPattern: regexp.MustCompile(
- `(?im)^(mon|tue|wed|thur|fri|sat|sun)\s+` +
- `(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)` +
- `\s+\d+\s+\d+:\d+:\d+((\.\d+\s\w+)|\s\d+)$`,
- ),
- buildConfigPattern: regexp.MustCompile(`(?im)(^building configuration\.{3}$)`),
- configVersionPattern: regexp.MustCompile(`(?im)(^!! ios xr.*$)`),
- configChangePattern: regexp.MustCompile(`(?im)(^!! last config.*$)`),
- endPattern: regexp.MustCompile(`end$`),
- }
-
- iosxrPatternsInstance.outputHeaderPattern = regexp.MustCompile(
- fmt.Sprintf(
- `(?im)%s|%s|%s|%s`,
- iosxrPatternsInstance.timestampPattern.String(),
- iosxrPatternsInstance.buildConfigPattern.String(),
- iosxrPatternsInstance.configVersionPattern.String(),
- iosxrPatternsInstance.configChangePattern.String(),
- ),
- )
- })
-
- return iosxrPatternsInstance
-}
-
-type IOSXRCfg struct {
- conn *network.Driver
- VersionPattern *regexp.Regexp
- configCommandMap map[string]string
- replaceConfig bool
- configInProgress bool
- configPrivLevel string
-}
-
-// NewIOSXRCfg return a cfg instance setup for an Cisco IOSXR device.
-func NewIOSXRCfg(
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- options = append([]Option{WithConfigSources([]string{"running"})}, options...)
-
- c, err := newCfg(conn, options...)
- if err != nil {
- return nil, err
- }
-
- c.Platform = &IOSXRCfg{
- conn: conn,
- VersionPattern: regexp.MustCompile(`(?i)\d+\.\d+\.\d+`),
- configCommandMap: map[string]string{
- "running": "show running-config",
- },
- configPrivLevel: "configuration",
- }
-
- err = setPlatformOptions(c.Platform, options...)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-func (p *IOSXRCfg) ClearConfigSession() {
- p.configInProgress = false
- p.configPrivLevel = "configuration"
-}
-
-// GetVersion get the version from the device.
-func (p *IOSXRCfg) GetVersion() (string, []*base.Response, error) {
- var versionResult *base.Response
-
- var err error
-
- if !p.configInProgress {
- versionResult, err = p.conn.SendCommand("show version | i Version")
- } else {
- versionResult, err = p.conn.SendConfig("do show version | i Version")
- }
-
- if err != nil {
- return "", nil, err
- }
-
- return p.VersionPattern.FindString(
- versionResult.Result,
- ), []*base.Response{
- versionResult,
- }, nil
-}
-
-// GetConfig get the configuration of a source datastore from the device.
-func (p *IOSXRCfg) GetConfig(source string) (string, []*base.Response, error) {
- cmd, err := getConfigCommand(p.configCommandMap, source)
- if err != nil {
- return "", nil, err
- }
-
- var configResult *base.Response
-
- if !p.configInProgress {
- configResult, err = p.conn.SendCommand(cmd)
- } else {
- configResult, err = p.conn.SendConfig(cmd, base.WithDesiredPrivilegeLevel(p.configPrivLevel))
- }
-
- if err != nil {
- return "", nil, err
- }
-
- return configResult.Result, []*base.Response{configResult}, nil
-}
-
-func (p *IOSXRCfg) prepareConfigPayloads(config string) (stdConfig, eagerConfig string) {
- patterns := getIOSXRPatterns()
-
- // remove comment lines
- config = patterns.outputHeaderPattern.ReplaceAllString(config, "!")
-
- // remove "end" at the end of the config - if its present it will drop scrapli out
- // of the config session which we do not want
- config = patterns.endPattern.ReplaceAllString(config, "!")
-
- // find all sections that need to be "eagerly" sent; remove those sections from the "normal"
- // config, then join all the eager sections into a single string
- eagerSections := make([]string, 0)
- bannerSections := patterns.bannerDelimPattern.FindAllString(config, -1)
-
- for _, bannerHeader := range bannerSections {
- bannerDelim := bannerHeader[len(bannerHeader)-1:]
-
- currentBannerPattern := regexp.MustCompile(
- fmt.Sprintf(
- `(?ims)^%s.*?%s$`,
- regexp.QuoteMeta(bannerHeader),
- regexp.QuoteMeta(bannerDelim),
- ),
- )
-
- currentBanner := currentBannerPattern.FindString(config)
- eagerSections = append(eagerSections, currentBanner)
-
- config = strings.Replace(config, currentBanner, "!", 1)
- }
-
- return config, strings.Join(eagerSections, "\n")
-}
-
-// LoadConfig load a candidate configuration.
-func (p *IOSXRCfg) LoadConfig(
- config string,
- replace bool,
- options *OperationOptions,
-) ([]*base.Response, error) {
- p.replaceConfig = replace
- p.configInProgress = true
-
- // the actual value is irrelevant, if there is a key "exclusive" w/ any value we assume user is
- // wanting to use configuration_exclusive config mode
- _, ok := options.Kwargs["exclusive"]
- if ok {
- p.configPrivLevel = configExclusivePrivLevel
- }
-
- var scrapliResponses []*base.Response
-
- stdConfig, eagerConfig := p.prepareConfigPayloads(config)
-
- configResult, stdConfigErr := p.conn.SendConfig(
- stdConfig, base.WithDesiredPrivilegeLevel(p.configPrivLevel),
- )
- if stdConfigErr != nil || configResult.Failed != nil {
- return scrapliResponses, stdConfigErr
- }
-
- scrapliResponses = append(scrapliResponses, configResult)
-
- eagerResult, eagerConfigErr := p.conn.SendConfig(
- eagerConfig,
- base.WithSendEager(true),
- base.WithDesiredPrivilegeLevel(p.configPrivLevel),
- )
-
- if eagerConfigErr != nil {
- return scrapliResponses, eagerConfigErr
- }
-
- if eagerResult.Failed != nil {
- return scrapliResponses, eagerConfigErr
- }
-
- scrapliResponses = append(scrapliResponses, eagerResult)
-
- return scrapliResponses, nil
-}
-
-// AbortConfig abort the loaded candidate configuration.
-func (p *IOSXRCfg) AbortConfig() ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- _, err := p.conn.Channel.SendInput("abort", false, false, p.conn.Channel.TimeoutOps)
- if err != nil {
- return scrapliResponses, err
- }
-
- p.conn.CurrentPriv = "privilege_exec"
- p.configInProgress = false
-
- return scrapliResponses, nil
-}
-
-// CommitConfig commit the loaded candidate configuration.
-func (p *IOSXRCfg) CommitConfig(source string) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- var commitResult *base.Response
-
- var err error
-
- if p.replaceConfig {
- replaceEvents := []*channel.SendInteractiveEvent{
- {ChannelInput: "commit replace", ChannelResponse: "proceed?", HideInput: false},
- {ChannelInput: "yes", ChannelResponse: "", HideInput: false},
- }
- commitResult, err = p.conn.SendInteractive(
- replaceEvents,
- base.WithDesiredPrivilegeLevel(p.configPrivLevel),
- )
- } else {
- commitResult, err = p.conn.SendConfig("commit", base.WithDesiredPrivilegeLevel(p.configPrivLevel))
- }
-
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, commitResult)
-
- p.configInProgress = false
-
- return scrapliResponses, nil
-}
-
-func (p *IOSXRCfg) getDiffCommand() string {
- if p.replaceConfig {
- return "show configuration changes diff"
- }
-
- return "show commit changes diff"
-}
-
-func (p *IOSXRCfg) normalizeSourceAndCandidateConfigs(
- sourceConfig, candidateConfig string,
-) (normalizedSourceConfig, normalizedCandidateConfig string) {
- patterns := getIOSXRPatterns()
-
- normalizedSourceConfig = patterns.outputHeaderPattern.ReplaceAllString(sourceConfig, "")
- normalizedSourceConfig = strings.Replace(normalizedSourceConfig, "\n\n", "\n", -1)
-
- normalizedCandidateConfig = patterns.outputHeaderPattern.ReplaceAllString(
- candidateConfig,
- "",
- )
- normalizedCandidateConfig = strings.Replace(normalizedCandidateConfig, "\n\n", "\n", -1)
-
- return normalizedSourceConfig, normalizedCandidateConfig
-}
-
-// DiffConfig diff the candidate configuration against a source config.
-func (p *IOSXRCfg) DiffConfig(
- source, candidateConfig string,
-) (responses []*base.Response,
- normalizedSourceConfig,
- normalizedCandidateConfig,
- deviceDiff string, err error) {
- if source != runningConfig {
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "warning",
- "eos only supports diffing against the running config",
- ),
- )
- }
-
- var scrapliResponses []*base.Response
-
- diffResult, err := p.conn.SendConfig(
- p.getDiffCommand(), base.WithDesiredPrivilegeLevel(p.configPrivLevel),
- )
-
- if err != nil {
- return scrapliResponses, "", "", "", err
- }
-
- scrapliResponses = append(scrapliResponses, diffResult)
-
- if diffResult.Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed generating diff for config session",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- deviceDiff = diffResult.Result
-
- sourceConfig, getConfigR, err := p.GetConfig(source)
- if err != nil {
- return scrapliResponses, "", "", "", nil
- }
-
- scrapliResponses = append(scrapliResponses, getConfigR[0])
-
- if getConfigR[0].Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed fetching source config for diff comparison",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- normalizedSourceConfig, normalizedCandidateConfig = p.normalizeSourceAndCandidateConfigs(
- sourceConfig,
- candidateConfig,
- )
-
- return scrapliResponses, normalizedSourceConfig, normalizedCandidateConfig, deviceDiff, nil
-}
diff --git a/cfg/cisconxos.go b/cfg/cisconxos.go
deleted file mode 100644
index b3343a9..0000000
--- a/cfg/cisconxos.go
+++ /dev/null
@@ -1,427 +0,0 @@
-package cfg
-
-import (
- "errors"
- "fmt"
- "regexp"
- "strings"
- "sync"
- "time"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-var ErrGetCheckpointFailed = errors.New("get checkpoint operation failed")
-
-type nxosPatterns struct {
- bytesFreePattern *regexp.Regexp
- buildConfigPattern *regexp.Regexp
- configVersionPattern *regexp.Regexp
- configChangePattern *regexp.Regexp
- outputHeaderPattern *regexp.Regexp
- checkpointLinePattern *regexp.Regexp
-}
-
-var (
- nxosPatternsInstance *nxosPatterns //nolint:gochecknoglobals
- nxosPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func getNXOSPatterns() *nxosPatterns {
- nxosPatternsInstanceOnce.Do(func() {
- nxosPatternsInstance = &nxosPatterns{
- bytesFreePattern: regexp.MustCompile(
- `(?i)(?P\d+)(?: bytes free)`,
- ),
- buildConfigPattern: regexp.MustCompile(`(?im)(^!command:.*$)`),
- configVersionPattern: regexp.MustCompile(
- `(?im)(^!running configuration last done.*$)`,
- ),
- configChangePattern: regexp.MustCompile(`(?im)(^!time.*$)`),
- checkpointLinePattern: regexp.MustCompile(`(?m)^\s*!#.*$`),
- }
-
- nxosPatternsInstance.outputHeaderPattern = regexp.MustCompile(
- fmt.Sprintf(
- `(?im)%s|%s|%s`,
- nxosPatternsInstance.buildConfigPattern.String(),
- nxosPatternsInstance.configVersionPattern.String(),
- nxosPatternsInstance.configChangePattern.String(),
- ),
- )
- })
-
- return nxosPatternsInstance
-}
-
-type NXOSCfg struct {
- conn *network.Driver
- VersionPattern *regexp.Regexp
- Filesystem string
- filesystemSpaceAvailBufferPerc float32
- configCommandMap map[string]string
- replaceConfig bool
- CandidateConfigFilename string
- candidateConfigFilename string
-}
-
-// NewNXOSCfg return a cfg instance setup for an Cisco NXOS device.
-func NewNXOSCfg( //nolint:dupl
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- options = append(
- []Option{
- WithConfigSources([]string{"running", "startup"}),
- WithFilesystem("bootflash:"),
- },
- options...)
-
- c, err := newCfg(conn, options...)
- if err != nil {
- return nil, err
- }
-
- c.Platform = &NXOSCfg{
- conn: conn,
- VersionPattern: regexp.MustCompile(`(?i)\d+\.[a-z0-9\(\).]+`),
- configCommandMap: map[string]string{
- "running": "show running-config",
- "startup": "show startup-config",
- },
- filesystemSpaceAvailBufferPerc: 10.0,
- }
-
- err = setPlatformOptions(c.Platform, options...)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-func (p *NXOSCfg) ClearConfigSession() {
- p.candidateConfigFilename = ""
-}
-
-// GetVersion get the version from the device.
-func (p *NXOSCfg) GetVersion() (string, []*base.Response, error) {
- versionResult, err := p.conn.SendCommand("show version | i \"NXOS: version\"")
- if err != nil {
- return "", nil, err
- }
-
- return p.VersionPattern.FindString(
- versionResult.Result,
- ), []*base.Response{
- versionResult,
- }, nil
-}
-
-// GetConfig get the configuration of a source datastore from the device.
-func (p *NXOSCfg) GetConfig(source string) (string, []*base.Response, error) {
- cmd, err := getConfigCommand(p.configCommandMap, source)
- if err != nil {
- return "", nil, err
- }
-
- configResult, err := p.conn.SendCommand(cmd)
-
- if err != nil {
- return "", nil, err
- }
-
- return configResult.Result, []*base.Response{configResult}, nil
-}
-
-func (p *NXOSCfg) getFilesystemSpaceAvail() (int, error) {
- patterns := getNXOSPatterns()
-
- filesystemSizeResult, err := p.conn.SendCommand(fmt.Sprintf("dir %s | i bytes", p.Filesystem))
- if err != nil {
- return -1, ErrFailedToDetermineDeviceState
- }
-
- return parseSpaceAvail(patterns.bytesFreePattern, filesystemSizeResult)
-}
-
-func (p *NXOSCfg) prepareConfigPayload(config string) string {
- tclshFilesystem := fmt.Sprintf("/%s/", strings.TrimSuffix(p.Filesystem, ":"))
- tcslhStartFile := fmt.Sprintf(
- `set fl [open "%s%s" wb+]`,
- tclshFilesystem,
- p.candidateConfigFilename,
- )
-
- splitConfig := strings.Split(config, "\n")
-
- tclshConfig := make([]string, 0)
-
- for _, configLine := range splitConfig {
- tclshConfig = append(tclshConfig, fmt.Sprintf("puts -nonewline $fl {%s\n}", configLine))
- }
-
- tclshEndFile := "close $fl"
-
- return strings.Join(
- []string{tcslhStartFile, strings.Join(tclshConfig, "\n"), tclshEndFile},
- "\n",
- )
-}
-
-// LoadConfig load a candidate configuration.
-func (p *NXOSCfg) LoadConfig(
- config string,
- replace bool,
- options *OperationOptions,
-) ([]*base.Response, error) {
- p.replaceConfig = replace
-
- var scrapliResponses []*base.Response
-
- filesystemBytesAvail, err := p.getFilesystemSpaceAvail()
- if err != nil {
- return nil, err
- }
-
- spaceSufficient := isSpaceSufficient(
- filesystemBytesAvail,
- p.filesystemSpaceAvailBufferPerc,
- config,
- )
-
- if !spaceSufficient {
- return nil, ErrInsufficientSpaceAvailable
- }
-
- if p.candidateConfigFilename == "" {
- p.candidateConfigFilename = determineCandidateConfigFilename(p.CandidateConfigFilename)
-
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "debug",
- fmt.Sprintf(
- "candidate configuration filename name will be %s",
- p.candidateConfigFilename,
- ),
- ),
- )
- }
-
- config = p.prepareConfigPayload(config)
-
- err = p.conn.AcquirePriv("tclsh")
- if err != nil {
- return nil, err
- }
-
- r, err := p.conn.SendConfig(config, base.WithDesiredPrivilegeLevel("tclsh"))
- if err != nil {
- return nil, err
- }
-
- err = p.conn.AcquirePriv(p.conn.DefaultDesiredPriv)
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, r)
-
- return scrapliResponses, nil
-}
-
-func (p *NXOSCfg) deleteCandidateConfigFile() (*base.MultiResponse, error) {
- deleteCommands := []string{
- "terminal dont-ask",
- fmt.Sprintf("delete %s%s", p.Filesystem, p.candidateConfigFilename),
- }
-
- return p.conn.SendCommands(deleteCommands)
-}
-
-// AbortConfig abort the loaded candidate configuration.
-func (p *NXOSCfg) AbortConfig() ([]*base.Response, error) {
- scrapliResponses := make([]*base.Response, 0)
-
- r, err := p.deleteCandidateConfigFile()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, r.Responses...)
-
- return scrapliResponses, err
-}
-
-func (p *NXOSCfg) SaveConfig() (*base.Response, error) {
- return p.conn.SendCommand("copy running-config startup-config")
-}
-
-// CommitConfig commit the loaded candidate configuration.
-func (p *NXOSCfg) CommitConfig(source string) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- var commitResult *base.Response
-
- var err error
-
- if p.replaceConfig {
- commitResult, err = p.conn.SendCommand(
- fmt.Sprintf(
- "rollback running-config file %s%s",
- p.Filesystem,
- p.candidateConfigFilename,
- ),
- )
- } else {
- commitResult, err = p.conn.SendCommand(
- fmt.Sprintf("copy %s%s running-config", p.Filesystem, p.candidateConfigFilename),
- )
- }
-
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, commitResult)
-
- saveResult, err := p.SaveConfig()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, saveResult)
-
- cleanupResult, err := p.deleteCandidateConfigFile()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, cleanupResult.Responses...)
-
- return scrapliResponses, nil
-}
-
-func (p *NXOSCfg) getDiffCommand(source string) string {
- if p.replaceConfig {
- return fmt.Sprintf(
- "show diff rollback-patch %s-config file %s%s",
- source,
- p.Filesystem,
- p.candidateConfigFilename,
- )
- }
-
- return ""
-}
-
-func (p *NXOSCfg) normalizeSourceAndCandidateConfigs(
- sourceConfig, candidateConfig string,
-) (normalizedSourceConfig, normalizedCandidateConfig string) {
- patterns := getNXOSPatterns()
-
- normalizedSourceConfig = patterns.outputHeaderPattern.ReplaceAllString(sourceConfig, "")
- normalizedSourceConfig = strings.Replace(normalizedSourceConfig, "\n\n", "\n", -1)
-
- normalizedCandidateConfig = patterns.checkpointLinePattern.ReplaceAllString(candidateConfig, "")
- normalizedCandidateConfig = patterns.outputHeaderPattern.ReplaceAllString(
- normalizedCandidateConfig,
- "",
- )
- normalizedCandidateConfig = strings.Replace(normalizedCandidateConfig, "\n\n", "\n", -1)
-
- return normalizedSourceConfig, normalizedCandidateConfig
-}
-
-// DiffConfig diff the candidate configuration against a source config.
-func (p *NXOSCfg) DiffConfig(
- source, candidateConfig string,
-) (responses []*base.Response,
- normalizedSourceConfig,
- normalizedCandidateConfig,
- deviceDiff string, err error) {
- var scrapliResponses []*base.Response
-
- deviceDiff = ""
-
- diffCmd := p.getDiffCommand(source)
-
- if diffCmd != "" {
- diffResult, diffErr := p.conn.SendCommand(p.getDiffCommand(source))
- if diffErr != nil {
- return scrapliResponses, "", "", "", diffErr
- }
-
- scrapliResponses = append(scrapliResponses, diffResult)
-
- if diffResult.Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed generating diff for config session",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- deviceDiff = diffResult.Result
- }
-
- sourceConfig, getConfigR, err := p.GetConfig(source)
- if err != nil {
- return scrapliResponses, "", "", "", nil
- }
-
- scrapliResponses = append(scrapliResponses, getConfigR[0])
-
- if getConfigR[0].Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed fetching source config for diff comparison",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- normalizedSourceConfig, normalizedCandidateConfig = p.normalizeSourceAndCandidateConfigs(
- sourceConfig,
- candidateConfig,
- )
-
- return scrapliResponses, normalizedSourceConfig, normalizedCandidateConfig, deviceDiff, nil
-}
-
-// GetCheckpoint return a checkpoint file from the target device.
-func (p *NXOSCfg) GetCheckpoint() (*Response, error) {
- logging.LogDebug(
- FormatLogMessage(p.conn, "info", "get checkpoint requested"),
- )
-
- r := NewResponse(p.conn.Host, "GetCheckpoint", ErrGetCheckpointFailed)
-
- timestamp := time.Now().Unix()
- checkpointCommands := []string{
- "terminal dont-ask",
- fmt.Sprintf("checkpoint file %sscrapli_cfg_tmp_%d", p.Filesystem, timestamp),
- fmt.Sprintf("show file %sscrapli_cfg_tmp_%d", p.Filesystem, timestamp),
- fmt.Sprintf("delete %sscrapli_cfg_tmp_%d", p.Filesystem, timestamp),
- }
-
- scrapliResponses, err := p.conn.SendCommands(checkpointCommands)
- if err != nil {
- return r, err
- }
-
- r.Record(scrapliResponses.Responses, "")
-
- return r, nil
-}
diff --git a/cfg/common_test.go b/cfg/common_test.go
deleted file mode 100644
index d018f30..0000000
--- a/cfg/common_test.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package cfg_test
-
-import (
- "testing"
-
- "github.com/scrapli/scrapligo/cfg"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-func createCfgDriver(t *testing.T, d *network.Driver, platform string) *cfg.Cfg {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
-
- c, cfgErr := cfg.NewCfgDriver(d, platform)
-
- if cfgErr != nil {
- t.Fatalf("failed creating cfg test device: %v", cfgErr)
- }
-
- prepareErr := c.Prepare()
-
- if prepareErr != nil {
- t.Fatalf("failed running prepare method: %v", prepareErr)
- }
-
- return c
-}
diff --git a/cfg/factory.go b/cfg/factory.go
deleted file mode 100644
index d830864..0000000
--- a/cfg/factory.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package cfg
-
-import (
- "errors"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// ErrUnknownCfgPlatform raised when user provides an unknown cfg platform... duh.
-var ErrUnknownCfgPlatform = errors.New("unknown cfg platform provided")
-
-// SupportedPlatforms pseudo constant providing slice of all core cfg platform types.
-func SupportedPlatforms() []string {
- return []string{"cisco_iosxe",
- "cisco_iosxr",
- "cisco_nxos",
- "arista_eos",
- "juniper_junos",
- }
-}
-
-// NewCfgDriver return a new cfg driver for a given platform.
-func NewCfgDriver(
- conn *network.Driver,
- platform string,
- options ...Option,
-) (*Cfg, error) {
- switch platform {
- case "cisco_iosxe":
- return NewIOSXECfg(conn, options...)
- case "cisco_iosxr":
- return NewIOSXRCfg(conn, options...)
- case "cisco_nxos":
- return NewNXOSCfg(conn, options...)
- case "arista_eos":
- return NewEOSCfg(conn, options...)
- case "juniper_junos":
- return NewJUNOSCfg(conn, options...)
- }
-
- return nil, ErrUnknownCfgPlatform
-}
diff --git a/cfg/getconfig_test.go b/cfg/getconfig_test.go
deleted file mode 100644
index b808684..0000000
--- a/cfg/getconfig_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package cfg_test
-
-import (
- "fmt"
- "os"
- "testing"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/scrapli/scrapligo/util/testhelper"
-
- "github.com/scrapli/scrapligo/cfg"
-)
-
-func configSourceMap() map[string]string {
- return map[string]string{
- "cisco_iosxe": "running",
- "cisco_iosxr": "running",
- "cisco_nxos": "running",
- "arista_eos": "running",
- "juniper_junos": "running",
- }
-}
-
-func testGetConfig(c *cfg.Cfg, source string, expected []byte) func(t *testing.T) {
- return func(t *testing.T) {
- r, cmdErr := c.GetConfig(source)
- if cmdErr != nil {
- t.Fatalf("failed running GetConfig: %v", cmdErr)
- }
-
- if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
- }
-
- if diff := cmp.Diff(r.Result, string(expected)); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
- }
-}
-
-func TestGetConfig(t *testing.T) {
- configSource := configSourceMap()
-
- for _, platform := range cfg.SupportedPlatforms() {
- sessionFile := fmt.Sprintf("../test_data/cfg/getconfig/%s", platform)
- expectedFile := fmt.Sprintf(
- "../test_data/cfg/getconfig/%s_expected",
- platform,
- )
-
- expected, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
- t.Fatalf("failed opening expected output file '%s' err: %v", expectedFile, expectedErr)
- }
-
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
- c := createCfgDriver(t, d, platform)
-
- f := testGetConfig(c, configSource[platform], expected)
-
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
- }
-}
diff --git a/cfg/getversion_test.go b/cfg/getversion_test.go
deleted file mode 100644
index 2fdfb88..0000000
--- a/cfg/getversion_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package cfg_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/scrapli/scrapligo/util/testhelper"
-
- "github.com/scrapli/scrapligo/cfg"
-)
-
-func expectedVersionMap() map[string]string {
- return map[string]string{
- "cisco_iosxe": "16.12.03",
- "cisco_iosxr": "6.5.3",
- "cisco_nxos": "9.2(4)",
- "arista_eos": "4.22.1F",
- "juniper_junos": "17.3R2.10",
- }
-}
-
-func testGetVersion(c *cfg.Cfg, expected string) func(t *testing.T) {
- return func(t *testing.T) {
- r, cmdErr := c.GetVersion()
- if cmdErr != nil {
- t.Fatalf("failed running GetVersion: %v", cmdErr)
- }
-
- if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
- }
-
- if diff := cmp.Diff(r.Result, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
- }
-}
-
-func TestGetVersion(t *testing.T) {
- versionMap := expectedVersionMap()
-
- for _, platform := range cfg.SupportedPlatforms() {
- sessionFile := fmt.Sprintf("../test_data/cfg/getversion/%s", platform)
-
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
- c := createCfgDriver(t, d, platform)
-
- f := testGetVersion(c, versionMap[platform])
-
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
- }
-}
diff --git a/cfg/juniperjunos.go b/cfg/juniperjunos.go
deleted file mode 100644
index c3131d9..0000000
--- a/cfg/juniperjunos.go
+++ /dev/null
@@ -1,324 +0,0 @@
-package cfg
-
-import (
- "fmt"
- "regexp"
- "strings"
- "sync"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-type junosPatterns struct {
- outputHeaderPattern *regexp.Regexp
- editPattern *regexp.Regexp
-}
-
-var (
- junosPatternsInstance *junosPatterns //nolint:gochecknoglobals
- junosPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func getJUNOSPatterns() *junosPatterns {
- junosPatternsInstanceOnce.Do(func() {
- junosPatternsInstance = &junosPatterns{
- outputHeaderPattern: regexp.MustCompile(`(?im)^## last commit.*$\nversion.*$`),
- editPattern: regexp.MustCompile(`(?m)^\[edit\]$`),
- }
- })
-
- return junosPatternsInstance
-}
-
-type JUNOSCfg struct {
- conn *network.Driver
- VersionPattern *regexp.Regexp
- Filesystem string
- replaceConfig bool
- configInProgress bool
- CandidateConfigFilename string
- candidateConfigFilename string
- configSetStyle bool
-}
-
-// NewJUNOSCfg return a cfg instance setup for a Juniper JunOS device.
-func NewJUNOSCfg(
- conn *network.Driver,
- options ...Option,
-) (*Cfg, error) {
- options = append(
- []Option{
- WithConfigSources([]string{"running"}),
- WithFilesystem("/config/"),
- },
- options...)
-
- c, err := newCfg(conn, options...)
- if err != nil {
- return nil, err
- }
-
- c.Platform = &JUNOSCfg{
- conn: conn,
- VersionPattern: regexp.MustCompile(`\d+\.[\w-]+\.\w+`),
- candidateConfigFilename: "",
- }
-
- err = setPlatformOptions(c.Platform, options...)
- if err != nil {
- return nil, err
- }
-
- return c, nil
-}
-
-func (p *JUNOSCfg) ClearConfigSession() {
- p.candidateConfigFilename = ""
- p.configInProgress = false
- p.configSetStyle = false
-}
-
-// GetVersion get the version from the device.
-func (p *JUNOSCfg) GetVersion() (string, []*base.Response, error) {
- var versionResult *base.Response
-
- var err error
-
- if !p.configInProgress {
- versionResult, err = p.conn.SendCommand("show version")
- } else {
- versionResult, err = p.conn.SendConfig("run show version")
- }
-
- if err != nil {
- return "", nil, err
- }
-
- return p.VersionPattern.FindString(
- versionResult.Result,
- ), []*base.Response{
- versionResult,
- }, nil
-}
-
-// GetConfig get the configuration of a source datastore from the device.
-func (p *JUNOSCfg) GetConfig(source string) (string, []*base.Response, error) {
- var configResult *base.Response
-
- var err error
-
- if !p.configInProgress {
- configResult, err = p.conn.SendCommand("show configuration")
- } else {
- configResult, err = p.conn.SendConfig("run show configuration")
- }
-
- if err != nil {
- return "", nil, err
- }
-
- return configResult.Result, []*base.Response{configResult}, nil
-}
-
-func (p *JUNOSCfg) prepareConfigPayload(config string) string {
- finalConfigs := make([]string, 0)
-
- for _, configLine := range strings.Split(config, "\n") {
- finalConfigs = append(
- finalConfigs,
- fmt.Sprintf("echo >> %s%s '%s'", p.Filesystem, p.candidateConfigFilename, configLine),
- )
- }
-
- return strings.Join(
- finalConfigs,
- "\n",
- )
-}
-
-// LoadConfig load a candidate configuration.
-func (p *JUNOSCfg) LoadConfig(
- config string,
- replace bool,
- options *OperationOptions,
-) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- p.replaceConfig = replace
-
- // the actual value is irrelevant, if there is a key "set" w/ any value we assume user is
- // loading a "set" style config
- _, ok := options.Kwargs["set"]
- if ok {
- p.configSetStyle = true
- }
-
- if p.candidateConfigFilename == "" {
- p.candidateConfigFilename = determineCandidateConfigFilename(p.CandidateConfigFilename)
-
- logging.LogDebug(
- FormatLogMessage(
- p.conn,
- "debug",
- fmt.Sprintf(
- "candidate configuration filename name will be %s",
- p.candidateConfigFilename,
- ),
- ),
- )
- }
-
- config = p.prepareConfigPayload(config)
-
- r, err := p.conn.SendConfig(config, base.WithDesiredPrivilegeLevel("root_shell"))
- if err != nil {
- return nil, err
- }
-
- p.configInProgress = true
-
- scrapliResponses = append(scrapliResponses, r)
-
- loadCommand := fmt.Sprintf("load override %s%s", p.Filesystem, p.candidateConfigFilename)
- if !p.replaceConfig {
- loadCommand = fmt.Sprintf("load merge %s%s", p.Filesystem, p.candidateConfigFilename)
- if p.configSetStyle {
- loadCommand = fmt.Sprintf("load set %s%s", p.Filesystem, p.candidateConfigFilename)
- }
- }
-
- loadResult, err := p.conn.SendConfig(loadCommand)
- if err != nil {
- return nil, err
- }
-
- scrapliResponses = append(scrapliResponses, loadResult)
-
- return scrapliResponses, nil
-}
-
-func (p *JUNOSCfg) deleteCandidateConfigFile() (*base.Response, error) {
- deleteCommand := fmt.Sprintf("rm %s%s", p.Filesystem, p.candidateConfigFilename)
-
- return p.conn.SendConfig(deleteCommand, base.WithDesiredPrivilegeLevel("root_shell"))
-}
-
-// AbortConfig abort the loaded candidate configuration.
-func (p *JUNOSCfg) AbortConfig() ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- rollbackResponse, err := p.conn.SendConfig("rollback 0")
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, rollbackResponse)
-
- deleteResponse, err := p.deleteCandidateConfigFile()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, deleteResponse)
-
- return scrapliResponses, nil
-}
-
-// CommitConfig commit the loaded candidate configuration.
-func (p *JUNOSCfg) CommitConfig(source string) ([]*base.Response, error) {
- var scrapliResponses []*base.Response
-
- commitResult, err := p.conn.SendConfig("commit")
-
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, commitResult)
-
- cleanupResult, err := p.deleteCandidateConfigFile()
- if err != nil {
- return scrapliResponses, err
- }
-
- scrapliResponses = append(scrapliResponses, cleanupResult)
-
- return scrapliResponses, nil
-}
-
-func (p *JUNOSCfg) normalizeSourceAndCandidateConfigs(
- sourceConfig, candidateConfig string,
-) (normalizedSourceConfig, normalizedCandidateConfig string) {
- patterns := getJUNOSPatterns()
-
- normalizedSourceConfig = patterns.outputHeaderPattern.ReplaceAllString(sourceConfig, "")
- normalizedSourceConfig = patterns.editPattern.ReplaceAllString(normalizedSourceConfig, "")
- normalizedSourceConfig = strings.Replace(normalizedSourceConfig, "\n\n", "\n", -1)
-
- normalizedCandidateConfig = patterns.outputHeaderPattern.ReplaceAllString(
- candidateConfig,
- "",
- )
- normalizedCandidateConfig = strings.Replace(normalizedCandidateConfig, "\n\n", "\n", -1)
- normalizedCandidateConfig = patterns.editPattern.ReplaceAllString(normalizedCandidateConfig, "")
-
- return normalizedSourceConfig, normalizedCandidateConfig
-}
-
-// DiffConfig diff the candidate configuration against a source config.
-func (p *JUNOSCfg) DiffConfig(
- source, candidateConfig string,
-) (responses []*base.Response,
- normalizedSourceConfig,
- normalizedCandidateConfig,
- deviceDiff string, err error) {
- var scrapliResponses []*base.Response
-
- diffResult, diffErr := p.conn.SendConfig("show | compare")
- if diffErr != nil {
- return scrapliResponses, "", "", "", diffErr
- }
-
- scrapliResponses = append(scrapliResponses, diffResult)
-
- if diffResult.Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed generating diff for config session",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- sourceConfig, getConfigR, err := p.GetConfig(source)
- if err != nil {
- return scrapliResponses, "", "", "", nil
- }
-
- scrapliResponses = append(scrapliResponses, getConfigR[0])
-
- if getConfigR[0].Failed != nil {
- logging.LogError(
- FormatLogMessage(
- p.conn,
- "error",
- "failed fetching source config for diff comparison",
- ),
- )
-
- return scrapliResponses, "", "", "", nil
- }
-
- normalizedSourceConfig, normalizedCandidateConfig = p.normalizeSourceAndCandidateConfigs(
- sourceConfig,
- candidateConfig,
- )
-
- return scrapliResponses, normalizedSourceConfig, normalizedCandidateConfig, deviceDiff, nil
-}
diff --git a/cfg/options.go b/cfg/options.go
deleted file mode 100644
index 7797b60..0000000
--- a/cfg/options.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package cfg
-
-import (
- "errors"
- "reflect"
- "regexp"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-var ErrIgnoredOption = errors.New("option ignored, for different instance type")
-var ErrInvalidPlatformAttr = errors.New("invalid platform attribute")
-
-// Option function to set cfg platform options.
-type Option func(interface{}) error
-
-// base Cfg options
-
-// WithConfigSources modify the default config sources for your platform.
-func WithConfigSources(sources []string) Option {
- return func(c interface{}) error {
- cfgObj, ok := c.(*Cfg)
-
- if ok {
- cfgObj.ConfigSources = sources
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithOnPrepare provide an OnPrepare callable for the Cfg instance.
-func WithOnPrepare(onPrepare func(*network.Driver) error) Option {
- return func(c interface{}) error {
- cfgObj, ok := c.(*Cfg)
-
- if ok {
- cfgObj.OnPrepare = onPrepare
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithDedicatedConnection set dedicated connection for Cfg instance.
-func WithDedicatedConnection(dedicatedConnection bool) Option {
- return func(c interface{}) error {
- cfgObj, ok := c.(*Cfg)
-
- if ok {
- cfgObj.DedicatedConnection = dedicatedConnection
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithIgnoreVersion set ignore version for Cfg instance.
-func WithIgnoreVersion(ignoreVersion bool) Option {
- return func(c interface{}) error {
- cfgObj, ok := c.(*Cfg)
-
- if ok {
- cfgObj.IgnoreVersion = ignoreVersion
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// platform specific options
-
-func setPlatformAttr(attrName string, attrValue, p interface{}) error {
- _, ok := p.(*Cfg)
-
- if ok {
- // this func only sets attrs for the platforms, so if we see a *Cfg we know we can bail
- return ErrIgnoredOption
- }
-
- v := reflect.ValueOf(p).Elem()
-
- fieldNames := map[string]int{}
-
- for i := 0; i < v.NumField(); i++ {
- fieldNames[v.Type().Field(i).Name] = i
- }
-
- attrIndex := -1
-
- for name, i := range fieldNames {
- if name == attrName {
- attrIndex = i
- break
- }
- }
-
- if attrIndex == -1 {
- // for some reason the platform doesnt have the specified attribute, this should *not*
- // happen... in theory :)
- return ErrInvalidPlatformAttr
- }
-
- fieldVal := v.Field(attrIndex)
- fieldVal.Set(reflect.ValueOf(attrValue))
-
- return nil
-}
-
-// WithVersionPattern set version pattern for the platform instance.
-func WithVersionPattern(versionPattern *regexp.Regexp) Option {
- return func(p interface{}) error {
- err := setPlatformAttr("VersionPattern", versionPattern, p)
-
- if err != nil {
- if !errors.Is(err, ErrIgnoredOption) {
- return err
- }
- }
-
- return nil
- }
-}
-
-// WithFilesystem set string name of filesystem to use.
-func WithFilesystem(fs string) Option {
- return func(p interface{}) error {
- err := setPlatformAttr("Filesystem", fs, p)
-
- if err != nil {
- if !errors.Is(err, ErrIgnoredOption) {
- return err
- }
- }
-
- return nil
- }
-}
-
-// WithCandidateConfigFilename set the default candidate config filename for your platform. Better
-// to use only in the testing env where a definite constant filename of config file on the device
-// is helpful. If, however, you use this for "normal" operations, make sure you abort any loaded
-// configuration between if doing subsequent load operations as the candidate config will remain the
-// same when using this option!
-func WithCandidateConfigFilename(fn string) Option {
- return func(p interface{}) error {
- err := setPlatformAttr("CandidateConfigFilename", fn, p)
-
- if err != nil {
- if !errors.Is(err, ErrIgnoredOption) {
- return err
- }
- }
-
- return nil
- }
-}
-
-// operation specific options
-
-// OperationOptions struct for options for any "operation" (LoadConfig, CommitConfig, etc.).
-type OperationOptions struct {
- Source string
- DiffColorize bool
- DiffSideBySideWidth int
- AutoClean bool
- Kwargs map[string]string
-}
-
-// OperationOption function to set options for cfg operations.
-type OperationOption func(*OperationOptions)
-
-// WithConfigSource set version pattern for the platform instance.
-func WithConfigSource(source string) OperationOption {
- return func(o *OperationOptions) {
- o.Source = source
- }
-}
-
-// WithDiffColorize set colorize attribute of diff response object.
-func WithDiffColorize(c bool) OperationOption {
- return func(o *OperationOptions) {
- o.DiffColorize = c
- }
-}
-
-// WithDiffSideBySideWidth set side by side diff width of diff response object.
-func WithDiffSideBySideWidth(i int) OperationOption {
- return func(o *OperationOptions) {
- o.DiffSideBySideWidth = i
- }
-}
-
-// WithAutoClean set auto clean for IOSXE platform.
-func WithAutoClean(a bool) OperationOption {
- return func(o *OperationOptions) {
- o.AutoClean = a
- }
-}
-
-// WithKwargs option that accepts a map to act like python kwargs a little.
-func WithKwargs(m map[string]string) OperationOption {
- return func(o *OperationOptions) {
- o.Kwargs = m
- }
-}
diff --git a/cfg/platformbase.go b/cfg/platformbase.go
deleted file mode 100644
index 506d9b8..0000000
--- a/cfg/platformbase.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package cfg
-
-import (
- "regexp"
- "strconv"
-
- "github.com/scrapli/scrapligo/driver/base"
-)
-
-const (
- execPrivLevel = "privilege_exec"
- configExclusivePrivLevel = "configuration_exclusive"
-)
-
-func getConfigCommand(configCommandMap map[string]string, source string) (string, error) {
- cmd, ok := configCommandMap[source]
-
- if !ok {
- return "", ErrInvalidConfigTarget
- }
-
- return cmd, nil
-}
-
-func parseSpaceAvail(
- bytesFreePattern *regexp.Regexp,
- filesystemSizeResult *base.Response,
-) (int, error) {
- var err error
-
- bytesAvailMatch := bytesFreePattern.FindStringSubmatch(filesystemSizeResult.Result)
-
- bytesAvail := -1
-
- for i, name := range bytesFreePattern.SubexpNames() {
- if i != 0 && name == "bytes_available" {
- bytesAvail, err = strconv.Atoi(bytesAvailMatch[i])
- if err != nil {
- return -1, err
- }
- }
- }
-
- return bytesAvail, nil
-}
-
-func isSpaceSufficient(
- filesystemBytesAvail int,
- filesystemSpaceAvailBufferPerc float32,
- config string,
-) bool {
- return float32(
- filesystemBytesAvail,
- ) >= float32(
- len(config),
- )/(filesystemSpaceAvailBufferPerc/100.0)+float32(
- len(config),
- )
-}
diff --git a/cfg/response.go b/cfg/response.go
deleted file mode 100644
index e1bdec9..0000000
--- a/cfg/response.go
+++ /dev/null
@@ -1,237 +0,0 @@
-package cfg
-
-import (
- "fmt"
- "strings"
- "time"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "golang.org/x/term"
-
- "github.com/carlmontanari/difflibgo/difflibgo"
-)
-
-const (
- subtraction = "- "
- addition = "+ "
- unknown = "? "
-)
-
-// Response cfg response object that gets returned from cfg operations.
-type Response struct {
- Host string
- OperationType string
- Result string
- StartTime time.Time
- EndTime time.Time
- ElapsedTime float64
- ScrapliResponses []*base.Response
- ErrorType error
- Failed error
-}
-
-// NewResponse create a new cfg response object.
-func NewResponse(
- host,
- opType string,
- raiseError error,
-) *Response {
- r := &Response{
- Host: host,
- OperationType: opType,
- Result: "",
- StartTime: time.Now(),
- EndTime: time.Time{},
- ElapsedTime: 0,
- ErrorType: raiseError,
- }
-
- return r
-}
-
-func (r *Response) Record(scrapliResponses []*base.Response, result string) {
- r.EndTime = time.Now()
- r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
-
- r.Result = result
- r.ScrapliResponses = scrapliResponses
-
- for _, resp := range r.ScrapliResponses {
- if resp.Failed != nil {
- r.Failed = r.ErrorType
- break
- }
- }
-}
-
-// DiffResponse cfg diff response object that gets returned from diff operations.
-type DiffResponse struct {
- *Response
- Source string
- SourceConfig string
- CandidateConfig string
- DeviceDiff string
- difflines []string
- additions []string
- subtractions []string
- sideBySideDiff string
- unifiedDiff string
- colorize bool
- sideBySideWidth int
-}
-
-// NewDiffResponse create a new cfg diff response object.
-func NewDiffResponse(
- host string,
- source string,
- colorize bool,
- sideBySideWidth int,
-) *DiffResponse {
- r := &Response{
- Host: host,
- OperationType: "DiffConfig",
- Result: "",
- StartTime: time.Now(),
- EndTime: time.Time{},
- ElapsedTime: 0,
- ErrorType: ErrDiffConfigFailed,
- }
-
- dr := &DiffResponse{
- Response: r,
- Source: source,
- colorize: colorize,
- sideBySideWidth: sideBySideWidth,
- }
-
- return dr
-}
-
-func (r *DiffResponse) RecordDiff(sourceConfig, candidateConfig, deviceDiff string) {
- r.SourceConfig = sourceConfig
- r.CandidateConfig = candidateConfig
- r.DeviceDiff = deviceDiff
-
- d := difflibgo.Differ{}
- r.difflines = d.Compare(
- strings.Split(r.SourceConfig, "\n"),
- strings.Split(r.CandidateConfig, "\n"),
- )
-
- for _, v := range r.difflines {
- if v[:2] == addition {
- r.additions = append(r.additions, v[2:])
- } else if v[:2] == subtraction {
- r.subtractions = append(r.subtractions, v[2:])
- }
- }
-}
-
-func (r *DiffResponse) generateColors() (unknown, subtraction, addition, end string) {
- if !r.colorize {
- return "? ", "- ", "+ ", ""
- }
-
- return "\033[93m", "\033[91m", "\033[92m", "\033[0m"
-}
-
-func (r *DiffResponse) terminalWidth() int {
- if r.sideBySideWidth != 0 {
- return r.sideBySideWidth
- }
-
- width, _, _ := term.GetSize(0)
-
- return width
-}
-
-func (r *DiffResponse) SideBySideDiff() string {
- if len(r.sideBySideDiff) > 0 {
- return r.sideBySideDiff
- }
-
- yellow, red, green, end := r.generateColors()
-
- termWidth := r.terminalWidth()
-
- halfTermWidth := termWidth / 2
- diffSideWidth := halfTermWidth - 5
-
- sideBySideDiffLines := make([]string, 0)
-
- for _, line := range r.difflines {
- var current, candidate string
-
- trimLen := diffSideWidth
- difflineLen := len(line)
-
- if difflineLen-1 <= trimLen {
- trimLen = difflineLen - 2
- }
-
- switch line[:2] {
- case unknown:
- current = yellow + fmt.Sprintf(
- "%-*s",
- halfTermWidth,
- strings.TrimRight(line[2:][:trimLen], " "),
- ) + end
- candidate = yellow + strings.TrimRight(line[2:][:trimLen], " ") + end
- case subtraction:
- current = red + fmt.Sprintf(
- "%-*s",
- halfTermWidth,
- strings.TrimRight(line[2:][:trimLen], " "),
- ) + end
- candidate = ""
- case addition:
- current = strings.Repeat(" ", halfTermWidth)
- candidate = green + strings.TrimRight(line[2:][:trimLen], " ") + end
- default:
- current = fmt.Sprintf(
- "%-*s",
- halfTermWidth,
- strings.TrimRight(line[2:][:trimLen], " "),
- )
- candidate = strings.TrimRight(line[2:][:trimLen], " ")
- }
-
- sideBySideDiffLines = append(sideBySideDiffLines, current+candidate)
- }
-
- r.sideBySideDiff = strings.Join(sideBySideDiffLines, "\n")
-
- return r.sideBySideDiff
-}
-
-func (r *DiffResponse) UnifiedDiff() string {
- if len(r.unifiedDiff) > 0 {
- return r.unifiedDiff
- }
-
- yellow, red, green, end := r.generateColors()
-
- unifiedDiffLines := make([]string, 0)
-
- for _, line := range r.difflines {
- var diffLine string
-
- switch line[:2] {
- case unknown:
- diffLine = yellow + line[2:] + end
- case subtraction:
- diffLine = red + line[2:] + end
- case addition:
- diffLine = green + line[2:] + end
- default:
- diffLine = line[2:]
- }
-
- unifiedDiffLines = append(unifiedDiffLines, diffLine)
- }
-
- r.unifiedDiff = strings.Join(unifiedDiffLines, "\n")
-
- return r.unifiedDiff
-}
diff --git a/channel/auth.go b/channel/auth.go
new file mode 100644
index 0000000..2252200
--- /dev/null
+++ b/channel/auth.go
@@ -0,0 +1,225 @@
+package channel
+
+import (
+ "fmt"
+ "regexp"
+ "sync"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ usernameSeenMax = 2
+ passwordSeenMax = 2
+ passphraseSeenMax = 2
+)
+
+type authPatterns struct {
+ username *regexp.Regexp
+ password *regexp.Regexp
+ passphrase *regexp.Regexp
+}
+
+var (
+ authPatternsInstance *authPatterns //nolint:gochecknoglobals
+ authPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
+)
+
+func getAuthPatterns() *authPatterns {
+ authPatternsInstanceOnce.Do(func() {
+ authPatternsInstance = &authPatterns{
+ username: regexp.MustCompile(`(?im)^(.*username:)|(.*login:)\s?$`),
+ password: regexp.MustCompile(`(?im)(.*@.*)?password:\s?$`),
+ passphrase: regexp.MustCompile(`(?i)enter passphrase for key`),
+ }
+ })
+
+ return authPatternsInstance
+}
+
+func (c *Channel) authenticateSSH(p, pp []byte) *result { //nolint:dupl
+ pCount := 0
+
+ ppCount := 0
+
+ var b []byte
+
+ for {
+ nb, err := c.ReadUntilAnyPrompt(
+ []*regexp.Regexp{c.PromptPattern, c.PasswordPattern, c.PassphrasePattern},
+ )
+ if err != nil {
+ return &result{nil, err}
+ }
+
+ b = append(b, nb...)
+
+ if c.PromptPattern.Match(b) {
+ return &result{b, nil}
+ }
+
+ if c.PasswordPattern.Match(b) { //nolint:nestif
+ b = []byte{}
+
+ pCount++
+
+ if pCount > passwordSeenMax {
+ c.l.Critical("password prompt seen multiple times, assuming authentication failed")
+
+ return &result{
+ nil,
+ fmt.Errorf(
+ "%w: password prompt seen multiple times, assuming authentication failed",
+ util.ErrAuthError,
+ ),
+ }
+ }
+
+ err = c.WriteAndReturn(p, true)
+ if err != nil {
+ return &result{nil, err}
+ }
+ } else if c.PassphrasePattern.Match(b) {
+ b = []byte{}
+
+ ppCount++
+
+ if ppCount > passphraseSeenMax {
+ c.l.Critical(
+ "private key passphrase prompt seen multiple times, assuming authentication failed",
+ )
+
+ return &result{
+ nil,
+ fmt.Errorf(
+ "%w: private key passphrase prompt seen multiple times, assuming authentication failed",
+ util.ErrAuthError,
+ ),
+ }
+ }
+
+ err = c.WriteAndReturn(pp, true)
+ if err != nil {
+ return &result{nil, err}
+ }
+ }
+ }
+}
+
+// AuthenticateSSH handles "in channel" SSH authentication.
+func (c *Channel) AuthenticateSSH(p, pp []byte) ([]byte, error) {
+ cr := make(chan *result)
+
+ go func() {
+ cr <- c.authenticateSSH(p, pp)
+ }()
+
+ t := time.NewTimer(c.TimeoutOps)
+
+ select {
+ case r := <-cr:
+ return r.b, r.err
+ case <-t.C:
+ c.l.Critical("channel timeout during in channel ssh authentication")
+
+ return nil, fmt.Errorf(
+ "%w: channel timeout during in channel ssh authentication",
+ util.ErrTimeoutError,
+ )
+ }
+}
+
+func (c *Channel) authenticateTelnet(u, p []byte) *result { //nolint:dupl
+ uCount := 0
+
+ pCount := 0
+
+ var b []byte
+
+ for {
+ nb, err := c.ReadUntilAnyPrompt(
+ []*regexp.Regexp{c.PromptPattern, c.UsernamePattern, c.PasswordPattern},
+ )
+ if err != nil {
+ return &result{nil, err}
+ }
+
+ b = append(b, nb...)
+
+ if c.PromptPattern.Match(b) {
+ return &result{b, nil}
+ }
+
+ if c.UsernamePattern.Match(b) { //nolint:nestif
+ b = []byte{}
+
+ uCount++
+
+ if uCount > usernameSeenMax {
+ c.l.Critical(
+ "username prompt seen multiple times, assuming authentication failed",
+ )
+
+ return &result{
+ nil,
+ fmt.Errorf(
+ "%w: username prompt seen multiple times, assuming authentication failed",
+ util.ErrAuthError,
+ ),
+ }
+ }
+
+ err = c.WriteAndReturn(u, true)
+ if err != nil {
+ return &result{nil, err}
+ }
+ } else if c.PasswordPattern.Match(b) {
+ b = []byte{}
+
+ pCount++
+
+ if pCount > passwordSeenMax {
+ c.l.Critical(
+ "password prompt seen multiple times, assuming authentication failed",
+ )
+
+ return &result{
+ nil,
+ fmt.Errorf(
+ "%w: password prompt seen multiple times, assuming authentication failed",
+ util.ErrAuthError,
+ ),
+ }
+ }
+
+ err = c.WriteAndReturn(p, true)
+ if err != nil {
+ return &result{nil, err}
+ }
+ }
+ }
+}
+
+// AuthenticateTelnet handles "in channel" telnet authentication.
+func (c *Channel) AuthenticateTelnet(u, p []byte) ([]byte, error) {
+ cr := make(chan *result)
+
+ go func() {
+ cr <- c.authenticateTelnet(u, p)
+ }()
+
+ t := time.NewTimer(c.TimeoutOps)
+
+ select {
+ case r := <-cr:
+ return r.b, r.err
+ case <-t.C:
+ c.l.Critical("channel timeout during in channel telnet authentication")
+
+ return nil, fmt.Errorf(
+ "%w: channel timeout during in channel telnet authentication",
+ util.ErrTimeoutError,
+ )
+ }
+}
diff --git a/channel/auth_test.go b/channel/auth_test.go
new file mode 100644
index 0000000..8da8975
--- /dev/null
+++ b/channel/auth_test.go
@@ -0,0 +1,137 @@
+package channel_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testAuthenticateSSH(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ c, fileTransportObj := prepareChannel(t, testName, testCase.PayloadFile)
+
+ actualOut, err := c.AuthenticateSSH([]byte("some-password"), nil)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running Channel AuthenticateSSH, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := string(readFile(t, fmt.Sprintf("golden/%s-out.txt", testName)))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(string(actualOut), expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestAuthenticateSSH(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "auth-simple": {
+ Description: "simple in channel auth test",
+ PayloadFile: "auth-simple.txt",
+ },
+ "auth-two-attempts": {
+ Description: "simple in channel auth where first attempt fails for some reason test",
+ PayloadFile: "auth-two-attempts.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testAuthenticateSSH(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testAuthenticateTelnet(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ c, fileTransportObj := prepareChannel(t, testName, testCase.PayloadFile)
+
+ actualOut, err := c.AuthenticateTelnet([]byte("some-username"), []byte("some-password"))
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running Channel AuthenticateTelnet, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := string(readFile(t, fmt.Sprintf("golden/%s-out.txt", testName)))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(string(actualOut), expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestAuthenticateTelnet(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "auth-telnet-simple": {
+ Description: "simple in channel auth test",
+ PayloadFile: "auth-telnet-simple.txt",
+ },
+ "auth-telnet-two-attempts": {
+ Description: "simple in channel auth where first attempt fails for some reason test",
+ PayloadFile: "auth-telnet-two-attempts.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testAuthenticateTelnet(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/channel/authenticate.go b/channel/authenticate.go
deleted file mode 100644
index 763b900..0000000
--- a/channel/authenticate.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package channel
-
-import (
- "bytes"
- "regexp"
- "sync"
- "time"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-type AuthPatterns struct {
- telnetLoginPattern *regexp.Regexp
- passwordPattern *regexp.Regexp
- passphrasePattern *regexp.Regexp
-}
-
-var (
- AuthPatternsInstance *AuthPatterns //nolint:gochecknoglobals
- authPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
-)
-
-func GetAuthPatterns() *AuthPatterns {
- authPatternsInstanceOnce.Do(func() {
- AuthPatternsInstance = &AuthPatterns{
- telnetLoginPattern: regexp.MustCompile(`(?im)^(.*username:)|(.*login:)\s?$`),
- passwordPattern: regexp.MustCompile(`(?im)(.*@.*)?password:\s?$`),
- passphrasePattern: regexp.MustCompile(`(?i)enter passphrase for key`),
- }
- })
-
- return AuthPatternsInstance
-}
-
-func (c *Channel) authenticateSSH( //nolint:dupl
- authPassword, authPassphrase []byte,
-) *channelResult {
- logging.LogDebug(c.FormatLogMessage("debug", "attempting in channel ssh authentication"))
-
- patterns := GetAuthPatterns()
- passwordPattern := patterns.passwordPattern
- passphrasePattern := patterns.passphrasePattern
-
- if c.AuthPasswordPattern != nil {
- passwordPattern = c.AuthPasswordPattern
- }
-
- if c.AuthPassphrasePattern != nil {
- passphrasePattern = c.AuthPassphrasePattern
- }
-
- var passwordCount = 0
-
- var passphraseCount = 0
-
- var b []byte
-
- for {
- chunk, err := c.Read()
-
- if err != nil {
- return &channelResult{
- result: b,
- error: err,
- }
- }
-
- processedChunk := bytes.ToLower(bytes.Trim(chunk, "\x00"))
-
- b = append(b, processedChunk...)
-
- passwordMatch := passwordPattern.Match(b)
- passphraseMatch := passphrasePattern.Match(b)
-
- if passwordMatch { //nolint:nestif
- b = []byte{}
- passwordCount++
-
- if passwordCount > passwordSeenMax {
- return &channelResult{
- result: []byte{},
- error: ErrAuthFailedPassword,
- }
- }
-
- logging.LogDebug(c.FormatLogMessage("debug", "found password prompt, sending password"))
-
- err = c.WriteAndReturn(authPassword, true)
- if err != nil {
- return &channelResult{
- result: []byte{},
- error: err,
- }
- }
- } else if passphraseMatch {
- b = []byte{}
- passphraseCount++
-
- if passwordCount > passphraseSeenMax {
- return &channelResult{
- result: []byte{},
- error: ErrAuthFailedPassphrase,
- }
- }
-
- logging.LogDebug(c.FormatLogMessage("debug", "found passphrase prompt, sending passphrase"))
-
- err = c.WriteAndReturn(authPassphrase, true)
- if err != nil {
- return &channelResult{
- result: []byte{},
- error: err,
- }
- }
- }
-
- promptMatch := c.CommsPromptPattern.Match(b)
- if promptMatch {
- logging.LogDebug(c.FormatLogMessage("debug", "ssh authentication complete"))
-
- return &channelResult{
- result: b,
- error: nil,
- }
- }
- }
-}
-
-// AuthenticateSSH Handles "in channel" SSH authentication (for "system" transport).
-func (c *Channel) AuthenticateSSH(authPassword, authPassphrase string) ([]byte, error) {
- var _c = make(chan *channelResult)
-
- go func() {
- r := c.authenticateSSH([]byte(authPassword), []byte(authPassphrase))
- _c <- r
- close(_c)
- }()
-
- timer := time.NewTimer(c.DetermineOperationTimeout(c.TimeoutOps))
-
- select {
- case r := <-_c:
- return r.result, r.error
- case <-timer.C:
- logging.LogError(c.FormatLogMessage("error", "timed out during ssh authentication"))
-
- return []byte{}, ErrAuthTimeout
- }
-}
-
-func (c *Channel) authenticateTelnet( //nolint:dupl
- authUsername, authPassword []byte,
-) *channelResult {
- logging.LogDebug(c.FormatLogMessage("debug", "attempting in channel telnet authentication"))
-
- patterns := GetAuthPatterns()
- usernamePattern := patterns.telnetLoginPattern
- passwordPattern := patterns.passwordPattern
-
- if c.AuthUsernamePattern != nil {
- usernamePattern = c.AuthUsernamePattern
- }
-
- if c.AuthPasswordPattern != nil {
- passwordPattern = c.AuthPasswordPattern
- }
-
- var usernameCount = 0
-
- var passwordCount = 0
-
- var b []byte
-
- for {
- chunk, err := c.Read()
-
- if err != nil {
- return &channelResult{
- result: b,
- error: err,
- }
- }
-
- processedChunk := bytes.ToLower(bytes.Trim(chunk, "\x00"))
-
- b = append(b, processedChunk...)
-
- usernameMatch := usernamePattern.Match(b)
- passwordMatch := passwordPattern.Match(b)
-
- if usernameMatch { //nolint:nestif
- b = []byte{}
- usernameCount++
-
- if usernameCount > loginSeenMax {
- return &channelResult{
- result: []byte{},
- error: ErrAuthFailedPassword,
- }
- }
-
- logging.LogDebug(c.FormatLogMessage("debug", "found login prompt, sending username"))
-
- err = c.WriteAndReturn(authUsername, false)
- if err != nil {
- return &channelResult{
- result: []byte{},
- error: err,
- }
- }
- } else if passwordMatch {
- b = []byte{}
- passwordCount++
-
- if passwordCount > passwordSeenMax {
- return &channelResult{
- result: []byte{},
- error: ErrAuthFailedPassword,
- }
- }
-
- logging.LogDebug(c.FormatLogMessage("debug", "found password prompt, sending password"))
-
- err = c.WriteAndReturn(authPassword, true)
- if err != nil {
- return &channelResult{
- result: []byte{},
- error: err,
- }
- }
- }
-
- promptMatch := c.CommsPromptPattern.Match(b)
- if promptMatch {
- logging.LogDebug(c.FormatLogMessage("debug", "telnet authentication complete"))
-
- return &channelResult{
- result: b,
- error: nil,
- }
- }
- }
-}
-
-// AuthenticateTelnet Handles "in channel" Telnet authentication (for "telnet" transport).
-func (c *Channel) AuthenticateTelnet(authUsername, authPassword string) ([]byte, error) {
- var _c = make(chan *channelResult)
-
- go func() {
- r := c.authenticateTelnet([]byte(authUsername), []byte(authPassword))
- _c <- r
- close(_c)
- }()
-
- timer := time.NewTimer(c.DetermineOperationTimeout(c.TimeoutOps))
-
- select {
- case r := <-_c:
- return r.result, r.error
- case <-timer.C:
- logging.LogError(c.FormatLogMessage("error", "timed out during telnet authentication"))
-
- return []byte{}, ErrAuthTimeout
- }
-}
diff --git a/channel/authenticate_test.go b/channel/authenticate_test.go
deleted file mode 100644
index 9b38fed..0000000
--- a/channel/authenticate_test.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package channel_test
-
-import (
- "testing"
-
- "github.com/scrapli/scrapligo/channel"
-)
-
-// TestGetAuthPatternsRace ensures that the global singleton-y auth patterns getter is goroutine
-// safe.
-func TestGetAuthPatternsRace(t *testing.T) {
- for i := 1; i < 5; i++ {
- go channel.GetAuthPatterns()
- }
-}
diff --git a/channel/base.go b/channel/base.go
deleted file mode 100644
index 6d9e252..0000000
--- a/channel/base.go
+++ /dev/null
@@ -1,239 +0,0 @@
-package channel
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "regexp"
- "time"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/transport"
-)
-
-// ErrAuthTimeout error for channel auth timeouts.
-var ErrAuthTimeout = errors.New("channel authentication timed out")
-
-// ErrAuthFailedPassword raised when password prompt seen too many times during in channel auth.
-var ErrAuthFailedPassword = errors.New(
- "password prompt seen more than twice, assuming auth failed",
-)
-
-// ErrAuthFailedPassphrase raised when passphrase prompt seen too many times during in channel auth.
-var ErrAuthFailedPassphrase = errors.New(
- "passphrase prompt seen more than twice, assuming auth failed",
-)
-
-// ErrChannelTimeout error for channel operation timeouts.
-var ErrChannelTimeout = errors.New("channel operation timed out")
-
-const (
- loginSeenMax = 2
- passwordSeenMax = 2
- passphraseSeenMax = 2
- // MaxTimeout maximum allowable timeout value -- one day.
- MaxTimeout = 86_400
-
- redactedLog = "REDACTED"
-)
-
-// Channel struct representing the channel object.
-type Channel struct {
- Transport *transport.Transport
- CommsPromptPattern *regexp.Regexp
- CommsReturnChar string
- CommsPromptSearchDepth int
- TimeoutOps time.Duration
- Host string
- Port int
- ChannelLog io.Writer
-
- // fields to override default in channel authentication patterns
- AuthUsernamePattern *regexp.Regexp
- AuthPasswordPattern *regexp.Regexp
- AuthPassphrasePattern *regexp.Regexp
-}
-
-type channelResult struct {
- result []byte
- error error
-}
-
-// Write writes bytes input into the channel, redacted (currently unused) signals that the input
-// should not be written in the log output.
-func (c *Channel) Write(channelInput []byte, redacted bool) error {
- logOutput := string(channelInput)
- if redacted {
- logOutput = redactedLog
- }
-
- logging.LogDebug(c.FormatLogMessage("write", fmt.Sprintf("write: %s", logOutput)))
-
- err := c.Transport.Write(channelInput)
-
- return err
-}
-
-// SendReturn convenience function to send the return character.
-func (c *Channel) SendReturn() error {
- return c.Write([]byte(c.CommsReturnChar), false)
-}
-
-// WriteAndReturn convenience function to write input and send the return character.
-func (c *Channel) WriteAndReturn(channelInput []byte, redacted bool) error {
- err := c.Write(channelInput, redacted)
- if err != nil {
- return err
- }
-
- err = c.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *Channel) readUntilInput(channelInput []byte) ([]byte, error) {
- var b []byte
-
- if len(channelInput) == 0 {
- return b, nil
- }
-
- for {
- chunk, err := c.Read()
- b = append(b, chunk...)
-
- if err != nil {
- return b, err
- }
-
- if bytes.Contains(b, channelInput) {
- return b, err
- }
- }
-}
-
-func (c *Channel) readUntilPrompt() ([]byte, error) {
- matchPattern := c.CommsPromptPattern
-
- var b []byte
-
- for {
- chunk, err := c.Read()
- b = append(b, chunk...)
-
- if err != nil {
- return b, err
- }
-
- channelMatch := matchPattern.Match(b)
- if channelMatch {
- logging.LogDebug(c.FormatLogMessage("debug", "found prompt match"))
-
- return b, err
- }
- }
-}
-
-func (c *Channel) readUntilExplicitPrompt(prompts []*regexp.Regexp) ([]byte, error) {
- var b []byte
-
- for {
- chunk, err := c.Read()
- b = append(b, chunk...)
-
- if err != nil {
- return b, err
- }
-
- for _, prompt := range prompts {
- channelMatch := prompt.Match(b)
-
- if channelMatch {
- logging.LogDebug(c.FormatLogMessage("debug", "found prompt match"))
-
- return b, err
- }
- }
- }
-}
-
-// Read reads bytes off the transport, handles some basic "massaging" of data to remove null bytes,
-// \r characters, as well as stripping out any ANSI characters in the output.
-func (c *Channel) Read() ([]byte, error) {
- chunk, err := c.Transport.Read()
-
- // is there ever a time when we should *not* replace *all* null bytes? previously this was just
- // a trim, but for some connections (nxos telnet in particular) null byte would sneak in and
- // not be leading or trailing... it would cause chaos....
- b := bytes.ReplaceAll(chunk, []byte("\x00"), []byte(""))
- b = bytes.ReplaceAll(b, []byte("\r"), []byte(""))
-
- if bytes.Contains(b, []byte("\x1b")) {
- logging.LogDebug(c.FormatLogMessage("debug", "stripping ansi chars..."))
-
- b = util.StripAnsi(b)
- }
-
- logging.LogDebug(c.FormatLogMessage("debug", fmt.Sprintf("read: %s", b)))
-
- if c.ChannelLog != nil {
- _, channelLogErr := c.ChannelLog.Write(b)
-
- if channelLogErr != nil {
- logging.LogError(c.FormatLogMessage("error", "error writing to channel log"))
- }
- }
-
- return b, err
-}
-
-// RestructureOutput strips prompt (if necessary) from output and trim any null space.
-func (c *Channel) RestructureOutput(output []byte, stripPrompt bool) []byte {
- outputLines := bytes.Split(output, []byte("\n"))
-
- cleanLines := make([][]byte, 0)
-
- for _, l := range outputLines {
- cl := bytes.TrimRight(l, " ")
- cleanLines = append(cleanLines, cl)
- }
-
- cleanOutput := bytes.Join(cleanLines, []byte("\n"))
-
- if stripPrompt {
- cleanOutput = c.CommsPromptPattern.ReplaceAll(cleanOutput, []byte(""))
- }
-
- cleanOutput = bytes.TrimLeft(cleanOutput, c.CommsReturnChar)
- cleanOutput = bytes.TrimRight(cleanOutput, " ")
- cleanOutput = bytes.TrimRight(cleanOutput, "\n")
-
- return cleanOutput
-}
-
-// DetermineOperationTimeout determines timeout to use for channel operation.
-func (c *Channel) DetermineOperationTimeout(timeoutOps time.Duration) time.Duration {
- opTimeout := c.TimeoutOps
-
- if timeoutOps > 0 {
- opTimeout = timeoutOps
- }
-
- if opTimeout <= 0 {
- opTimeout = MaxTimeout * time.Second
- }
-
- return opTimeout
-}
-
-// FormatLogMessage formats log message payload, adding contextual info about the host.
-func (c *Channel) FormatLogMessage(level, msg string) string {
- return logging.FormatLogMessage(level, c.Host, c.Port, msg)
-}
diff --git a/channel/base_test.go b/channel/base_test.go
deleted file mode 100644
index 6fa3229..0000000
--- a/channel/base_test.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package channel_test
-
-import (
- "os"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/scrapli/scrapligo/util/testhelper"
-)
-
-func TestWrite(t *testing.T) {
- c := testhelper.NewPatchedChannel(t, nil)
-
- channelInput := []byte("something witty")
-
- writeErr := c.Write(channelInput, false)
-
- if writeErr != nil {
- t.Fatalf("error writing to mock channel: %v", writeErr)
- }
-
- testTransp := testhelper.FetchTestTransport(c, t)
-
- if diff := cmp.Diff(testTransp.CapturedWrites[0], channelInput); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestSendReturn(t *testing.T) {
- c := testhelper.NewPatchedChannel(t, nil)
-
- writeErr := c.SendReturn()
-
- if writeErr != nil {
- t.Fatalf("error writing to mock channel: %v", writeErr)
- }
-
- testTransp := testhelper.FetchTestTransport(c, t)
-
- if diff := cmp.Diff(testTransp.CapturedWrites[0], []byte(c.CommsReturnChar)); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestWriteAndReturn(t *testing.T) {
- c := testhelper.NewPatchedChannel(t, nil)
-
- channelInput := []byte("something witty")
- finalChannelInput := [][]byte{channelInput, []byte(c.CommsReturnChar)}
-
- writeErr := c.WriteAndReturn(channelInput, false)
-
- if writeErr != nil {
- t.Fatalf("error writing to mock channel: %v", writeErr)
- }
-
- testTransp := testhelper.FetchTestTransport(c, t)
-
- if diff := cmp.Diff(testTransp.CapturedWrites, finalChannelInput); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestRead(t *testing.T) {
- fakeSession := "read"
- expectedFile := "../test_data/channel/read_expected"
-
- expected, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
- t.Fatalf("failed opening expected output file '%s' err: %v", expectedFile, expectedErr)
- }
-
- c := testhelper.NewPatchedChannel(t, &fakeSession)
- testhelper.SetTestTransportStandardReadSize(c, t)
-
- b, readErr := c.Read()
-
- if readErr != nil {
- t.Fatalf("error reading from mock channel: %v", readErr)
- }
-
- if diff := cmp.Diff(b, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestRestructureOutput(t *testing.T) {
- output := []byte(" some output\nlocalhost# ")
- expected := []byte(" some output\nlocalhost#")
-
- c := testhelper.NewPatchedChannel(t, nil)
-
- actual := c.RestructureOutput(output, false)
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestRestructureOutputStripPrompt(t *testing.T) {
- output := []byte(" some output \nlocalhost# ")
- expected := []byte(" some output")
-
- c := testhelper.NewPatchedChannel(t, nil)
-
- actual := c.RestructureOutput(output, true)
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestDetermineOperationTimeoutDefault(t *testing.T) {
- expected := 30 * time.Second
-
- c := testhelper.NewPatchedChannel(t, nil)
-
- actual := c.DetermineOperationTimeout(0)
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestDetermineOperationTimeoutMax(t *testing.T) {
- expected := 24 * time.Hour
-
- c := testhelper.NewPatchedChannel(t, nil)
- c.TimeoutOps = 0 * time.Second
-
- actual := c.DetermineOperationTimeout(0)
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestDetermineOperationTimeoutPerCommand(t *testing.T) {
- expected := 1 * time.Minute
-
- c := testhelper.NewPatchedChannel(t, nil)
- c.TimeoutOps = 0 * time.Second
- actual := c.DetermineOperationTimeout(1 * time.Minute)
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
-
-func TestFormatLogMessage(t *testing.T) {
- expected := "debug::localhost::22::some log message"
-
- c := testhelper.NewPatchedChannel(t, nil)
-
- actual := c.FormatLogMessage("debug", "some log message")
-
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
diff --git a/channel/channel.go b/channel/channel.go
new file mode 100644
index 0000000..62114ab
--- /dev/null
+++ b/channel/channel.go
@@ -0,0 +1,201 @@
+package channel
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "regexp"
+ "sync"
+ "time"
+
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ // DefaultTimeoutOpsSeconds is the default time value for operations -- 60 seconds.
+ DefaultTimeoutOpsSeconds = 60
+ // DefaultReadDelayMicroSeconds is the default value for the delay between reads of the
+ // transport -- 5 microseconds.
+ DefaultReadDelayMicroSeconds = 5
+ // DefaultReturnChar is the character used to send an "enter" key to the device, "\n".
+ DefaultReturnChar = "\n"
+
+ redacted = "redacted"
+)
+
+var (
+ promptPattern *regexp.Regexp //nolint:gochecknoglobals
+ promptPatternOnce sync.Once //nolint:gochecknoglobals
+)
+
+func getPromptPattern() *regexp.Regexp {
+ promptPatternOnce.Do(func() {
+ promptPattern = regexp.MustCompile(`(?im)^[a-z\d.\-@()/:]{1,48}[#>$]\s*$`)
+ })
+
+ return promptPattern
+}
+
+// NewChannel returns a scrapligo Channel object.
+func NewChannel(
+ l *logging.Instance,
+ t *transport.Transport,
+ options ...util.Option,
+) (*Channel, error) {
+ patterns := getAuthPatterns()
+
+ c := &Channel{
+ l: l,
+ t: t,
+
+ TimeoutOps: DefaultTimeoutOpsSeconds * time.Second,
+ ReadDelay: DefaultReadDelayMicroSeconds * time.Microsecond,
+
+ UsernamePattern: patterns.username,
+ PasswordPattern: patterns.password,
+ PassphrasePattern: patterns.passphrase,
+
+ PromptPattern: getPromptPattern(),
+ ReturnChar: []byte(DefaultReturnChar),
+
+ done: make(chan bool),
+
+ Q: util.NewQueue(),
+ Errs: make(chan error),
+
+ ChannelLog: nil,
+ }
+
+ for _, option := range options {
+ err := option(c)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return c, nil
+}
+
+// Channel is an object that sits "on top" of a scrapligo Transport object, its purpose in life is
+// to read data from the transport into its Q, and provide methods to read "until" an input or an
+// expected prompt is seen.
+type Channel struct {
+ l *logging.Instance
+ t *transport.Transport
+
+ TimeoutOps time.Duration
+ ReadDelay time.Duration
+
+ AuthBypass bool
+
+ UsernamePattern *regexp.Regexp
+ PasswordPattern *regexp.Regexp
+ PassphrasePattern *regexp.Regexp
+
+ PromptPattern *regexp.Regexp
+ ReturnChar []byte
+
+ done chan bool
+
+ Q *util.Queue
+ Errs chan error
+
+ ChannelLog io.Writer
+}
+
+// Open opens the underlying Transport and begins the `read` goroutine, this also kicks off any
+// in channel authentication (if necessary).
+func (c *Channel) Open() error {
+ err := c.t.Open()
+ if err != nil {
+ c.l.Criticalf("error opening channel, error: %s", err)
+
+ return err
+ }
+
+ c.l.Debug("starting channel read loop")
+
+ go c.read()
+
+ if !c.AuthBypass {
+ var b []byte
+
+ switch tt := c.t.Impl.(type) {
+ case *transport.System:
+ c.l.Debug("transport type is 'system', begin in channel ssh authentication")
+
+ b, err = c.AuthenticateSSH([]byte(c.t.Args.Password), []byte(tt.SSHArgs.PrivateKeyPassPhrase))
+ if err != nil {
+ return err
+ }
+ case *transport.Telnet:
+ c.l.Debug("transport type is 'telnet', begin in channel telnet authentication")
+
+ b, err = c.AuthenticateTelnet([]byte(c.t.Args.User), []byte(c.t.Args.Password))
+ if err != nil {
+ return err
+ }
+ }
+
+ if len(b) > 0 {
+ // requeue any buffer data we get during in channel authentication back onto the
+ // read buffer. mostly this should only be relevant for netconf where we need to
+ // read the server capabilities.
+ c.Q.Requeue(b)
+ }
+ } else {
+ c.l.Debug("auth bypass is enabled, skipping in channel auth check")
+ }
+
+ return nil
+}
+
+// Close signals to stop the channel read loop and closes the underlying Transport object.
+func (c *Channel) Close() error {
+ c.done <- true
+
+ return c.t.Close(false)
+}
+
+type result struct {
+ b []byte
+ err error
+}
+
+func (c *Channel) processOut(b []byte, strip bool) []byte {
+ lines := bytes.Split(b, []byte("\n"))
+
+ cleanLines := make([][]byte, len(lines))
+ for i, l := range lines {
+ cleanLines[i] = bytes.TrimRight(l, " ")
+ }
+
+ b = bytes.Join(cleanLines, []byte("\n"))
+
+ if strip {
+ b = c.PromptPattern.ReplaceAll(b, nil)
+ }
+
+ b = bytes.Trim(b, string(c.ReturnChar))
+ b = bytes.Trim(b, "\n")
+
+ return b
+}
+
+// GetTimeout returns the target timeout for an operation based on the TimeoutOps attribute of the
+// Channel and the value t.
+func (c *Channel) GetTimeout(t time.Duration) time.Duration {
+ if t == -1 {
+ return c.TimeoutOps
+ }
+
+ if t == 0 {
+ return util.MaxTimeout * time.Second
+ }
+
+ return t
+}
diff --git a/channel/channel_test.go b/channel/channel_test.go
new file mode 100644
index 0000000..3e73b4d
--- /dev/null
+++ b/channel/channel_test.go
@@ -0,0 +1,109 @@
+package channel_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/transport"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func resolveFile(t *testing.T, f string) string {
+ f, err := filepath.Abs(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return f
+}
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualIn, actualOut []byte) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+ goldenIn := filepath.Join("test-fixtures", "golden", testName+"-in.txt")
+
+ err := os.WriteFile(goldenOut, actualOut, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.WriteFile(goldenIn, actualIn, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func prepareChannel(
+ t *testing.T,
+ testName,
+ payloadFile string,
+) (*channel.Channel, *transport.File) {
+ l, _ := logging.NewInstance()
+
+ transportObj, err := transport.NewTransport(
+ l,
+ "dummy",
+ transport.FileTransport,
+ options.WithFileTransportFile(resolveFile(t, payloadFile)),
+ options.WithTransportReadSize(1),
+ )
+ if err != nil {
+ t.Errorf("%s: encountered error creating File Transport, error: %s", testName, err)
+ }
+
+ c, err := channel.NewChannel(l, transportObj)
+ if err != nil {
+ t.Errorf("%s: encountered error creating Channel, error: %s", testName, err)
+ }
+
+ err = c.Open()
+ if err != nil {
+ t.Errorf("%s: encountered error opening Channel, error: %s", testName, err)
+ }
+
+ fileTransportObj, ok := transportObj.Impl.(*transport.File)
+ if !ok {
+ t.Fatal("transport implementation is not Transport File")
+ }
+
+ return c, fileTransportObj
+}
diff --git a/channel/getprompt.go b/channel/getprompt.go
index 5846ee9..05d05e9 100644
--- a/channel/getprompt.go
+++ b/channel/getprompt.go
@@ -1,63 +1,46 @@
package channel
import (
+ "fmt"
"time"
- "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
)
-func (c *Channel) getPrompt() *channelResult {
- err := c.SendReturn()
- if err != nil {
- return &channelResult{
- error: err,
- }
- }
+// GetPrompt returns a byte slice containing the current "prompt" of the connected ssh/telnet
+// server.
+func (c *Channel) GetPrompt() ([]byte, error) {
+ c.l.Debug("channel GetPrompt requested")
- var b []byte
+ cr := make(chan *result)
- for {
- chunk, readErr := c.Read()
- b = append(b, chunk...)
+ go func() {
+ err := c.WriteReturn()
+ if err != nil {
+ cr <- &result{b: nil, err: err}
- if readErr != nil {
- return &channelResult{
- error: readErr,
- }
+ return
}
- channelMatch := c.CommsPromptPattern.Match(b)
- if channelMatch {
- return &channelResult{
- result: b,
- error: nil,
- }
- }
- }
-}
+ var b []byte
-// GetPrompt fetch the current prompt.
-func (c *Channel) GetPrompt() (string, error) {
- _c := make(chan *channelResult)
+ b, err = c.ReadUntilPrompt()
- go func() {
- r := c.getPrompt()
- _c <- r
- close(_c)
+ cr <- &result{b: b, err: err}
}()
- timer := time.NewTimer(c.DetermineOperationTimeout(c.TimeoutOps))
+ timer := time.NewTimer(c.TimeoutOps)
select {
- case r := <-_c:
- if r.error != nil {
- return "", r.error
+ case r := <-cr:
+ if r.err != nil {
+ return nil, r.err
}
- return string(r.result), nil
+ return r.b, nil
case <-timer.C:
- logging.LogError(c.FormatLogMessage("error", "timed out sending getting prompt"))
+ c.l.Critical("channel timeout fetching prompt")
- return "", ErrChannelTimeout
+ return nil, fmt.Errorf("%w: channel timeout fetching prompt", util.ErrTimeoutError)
}
}
diff --git a/channel/getprompt_test.go b/channel/getprompt_test.go
index 3a61991..4110dae 100644
--- a/channel/getprompt_test.go
+++ b/channel/getprompt_test.go
@@ -1,26 +1,66 @@
package channel_test
import (
+ "bytes"
+ "fmt"
"testing"
+ "github.com/scrapli/scrapligo/util"
+
"github.com/google/go-cmp/cmp"
- "github.com/scrapli/scrapligo/util/testhelper"
)
-func TestGetPrompt(t *testing.T) {
- expected := "localhost#"
+func testGetPrompt(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ c, fileTransportObj := prepareChannel(t, testName, testCase.PayloadFile)
+
+ actualOut, err := c.GetPrompt()
+ if err != nil {
+ t.Errorf("%s: encountered error running Channel GetPrompt, error: %s", testName, err)
+ }
+
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
- fakeSession := "getprompt"
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
- c := testhelper.NewPatchedChannel(t, &fakeSession)
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
- actual, promptErr := c.GetPrompt()
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
- if promptErr != nil {
- t.Fatalf("error getting prompt from mock channel: %v", promptErr)
+ if !cmp.Equal(actualOut, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
}
+}
+
+func TestGetPrompt(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "get-prompt-simple": {
+ Description: "simple get prompt test",
+ PayloadFile: "get-prompt-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testGetPrompt(testName, testCase)
- if diff := cmp.Diff(expected, actual); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
+ t.Run(testName, f)
}
}
diff --git a/channel/operation.go b/channel/operation.go
new file mode 100644
index 0000000..846cc11
--- /dev/null
+++ b/channel/operation.go
@@ -0,0 +1,47 @@
+package channel
+
+import (
+ "errors"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ defaultStripPrompt = true
+ defaultEager = false
+ defaultTimeout = -1
+)
+
+// OperationOptions is a struct containing "operation" options -- things like if the channel should
+// strip the device prompt out, send eagerly (without reading until the input), and the timeout for
+// the given operation.
+type OperationOptions struct {
+ StripPrompt bool
+ Eager bool
+ Timeout time.Duration
+ CompletePatterns []*regexp.Regexp
+}
+
+// NewOperation returns a new OperationOptions object with the defaults set and any provided options
+// applied.
+func NewOperation(options ...util.Option) (*OperationOptions, error) {
+ o := &OperationOptions{
+ StripPrompt: defaultStripPrompt,
+ Eager: defaultEager,
+ Timeout: defaultTimeout,
+ CompletePatterns: make([]*regexp.Regexp, 0),
+ }
+
+ for _, option := range options {
+ err := option(o)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return o, nil
+}
diff --git a/channel/read.go b/channel/read.go
new file mode 100644
index 0000000..3e61333
--- /dev/null
+++ b/channel/read.go
@@ -0,0 +1,158 @@
+package channel
+
+import (
+ "bytes"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+func (c *Channel) read() {
+ for {
+ select {
+ case <-c.done:
+ return
+ default:
+ }
+
+ b, err := c.t.Read()
+ if err != nil {
+ // we got a transport error, put it into the error channel for processing during
+ // the next read activity
+ c.Errs <- err
+ }
+
+ // not 100% this is required, but has existed in scrapli/scrapligo for a long time and am
+ // afraid to remove it!
+ b = bytes.ReplaceAll(b, []byte("\r"), []byte(""))
+
+ // trim out all the space we padded in the buffer to read into
+ b = bytes.ReplaceAll(b, []byte("\x00"), []byte(""))
+
+ if bytes.Contains(b, []byte("\x1b")) {
+ b = util.StripANSI(b)
+ }
+
+ c.Q.Enqueue(b)
+
+ if c.ChannelLog != nil {
+ _, err = c.ChannelLog.Write(b)
+ if err != nil {
+ c.l.Criticalf("error writing to channel log, ignoring. error: %s", err)
+ }
+ }
+
+ time.Sleep(c.ReadDelay)
+ }
+}
+
+// Read reads and returns the first available bytes from the channel Q object. If there are any
+// errors on the Errs channel (these would come from the underlying transport), the error is
+// returned with nil for the byte slice.
+func (c *Channel) Read() ([]byte, error) {
+ select {
+ case err := <-c.Errs:
+ return nil, err
+ default:
+ }
+
+ b := c.Q.Dequeue()
+
+ return b, nil
+}
+
+// ReadAll reads and returns *all* available bytes form the channel Q object. If there are any
+// errors on the Errs channel (these would come from the underlying transport), the error is
+// returned with nil for the byte slice. Be careful using this as it is possible to dequeue "too
+// much" from the channel causing us to not be able to "find" the prompt or inputs during normal
+// operations. In general, this should probably only be used when connecting to consoles/files.
+func (c *Channel) ReadAll() ([]byte, error) {
+ select {
+ case err := <-c.Errs:
+ return nil, err
+ default:
+ }
+
+ b := c.Q.DequeueAll()
+
+ return b, nil
+}
+
+// ReadUntilInput reads bytes out of the channel Q object until the "input" bytes b are "seen" in
+// the channel output. Once b is seen, all read bytes are returned.
+func (c *Channel) ReadUntilInput(b []byte) ([]byte, error) {
+ if len(b) == 0 {
+ return nil, nil
+ }
+
+ return c.ReadUntilExplicit(b)
+}
+
+// ReadUntilPrompt reads bytes out of the channel Q object until the channel PromptPattern regex
+// pattern is seen in the output. Once that pattern is seen, all read bytes are returned.
+func (c *Channel) ReadUntilPrompt() ([]byte, error) {
+ var rb []byte
+
+ for {
+ select {
+ case err := <-c.Errs:
+ return nil, err
+ default:
+ }
+
+ rb = append(rb, c.Q.Dequeue()...)
+
+ if c.PromptPattern.Match(rb) {
+ c.l.Debugf("channel read %#v", string(rb))
+
+ return rb, nil
+ }
+ }
+}
+
+// ReadUntilAnyPrompt reads bytes out of the channel Q object until any of the prompts in the
+// "prompts" argument are seen in the output. Once any pattern is seen, all read bytes are returned.
+func (c *Channel) ReadUntilAnyPrompt(prompts []*regexp.Regexp) ([]byte, error) {
+ var rb []byte
+
+ for {
+ select {
+ case err := <-c.Errs:
+ return nil, err
+ default:
+ }
+
+ rb = append(rb, c.Q.Dequeue()...)
+
+ for _, p := range prompts {
+ if p.Match(rb) {
+ c.l.Debugf("channel read %#v", string(rb))
+
+ return rb, nil
+ }
+ }
+ }
+}
+
+// ReadUntilExplicit reads bytes out of the channel Q object until the bytes b are seen in the
+// output. Once the bytes are seen all read bytes are returned.
+func (c *Channel) ReadUntilExplicit(b []byte) ([]byte, error) {
+ var rb []byte
+
+ for {
+ select {
+ case err := <-c.Errs:
+ return nil, err
+ default:
+ }
+
+ rb = append(rb, c.Q.Dequeue()...)
+
+ if bytes.Contains(rb, b) {
+ c.l.Debugf("channel read %#v", string(rb))
+
+ return rb, nil
+ }
+ }
+}
diff --git a/channel/sendinput.go b/channel/sendinput.go
index 38d0485..9ca6b91 100644
--- a/channel/sendinput.go
+++ b/channel/sendinput.go
@@ -4,90 +4,78 @@ import (
"fmt"
"time"
- "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
)
-func (c *Channel) sendInput(channelInput []byte, stripPrompt, eager bool) *channelResult {
- logging.LogDebug(
- c.FormatLogMessage(
- "info",
- fmt.Sprintf(
- "\"sending channelInput: %s; stripPrompt: %t; eager: %v",
- channelInput,
- stripPrompt,
- eager,
- ),
- ),
- )
-
- var b []byte
-
- err := c.Write(channelInput, false)
+// SendInputB sends the given input bytes to the device and returns the bytes read.
+func (c *Channel) SendInputB(input []byte, opts ...util.Option) ([]byte, error) {
+ c.l.Debugf("channel SendInput requested, sending input '%s'", input)
+
+ op, err := NewOperation(opts...)
if err != nil {
- return &channelResult{
- result: []byte(""),
- error: nil,
- }
+ return nil, err
}
- _, err = c.readUntilInput(channelInput)
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: err,
+ cr := make(chan *result)
+
+ go func() {
+ var b []byte
+
+ err = c.Write(input, false)
+ if err != nil {
+ cr <- &result{b: b, err: err}
+
+ return
}
- }
- err = c.SendReturn()
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: err,
+ _, err = c.ReadUntilInput(input)
+ if err != nil {
+ cr <- &result{b: b, err: err}
+
+ return
}
- }
- if !eager {
- postInputBuf, readErr := c.readUntilPrompt()
+ err = c.WriteReturn()
+ if err != nil {
+ cr <- &result{b: b, err: err}
- if readErr != nil {
- return &channelResult{
- result: []byte(""),
- error: readErr,
- }
+ return
}
- b = append(b, postInputBuf...)
- }
+ if !op.Eager {
+ var nb []byte
- return &channelResult{
- result: c.RestructureOutput(b, stripPrompt),
- error: nil,
- }
-}
+ nb, err = c.ReadUntilPrompt()
+ if err != nil {
+ cr <- &result{b: b, err: err}
+ }
-// SendInput send input to the device, optionally strips prompt out of the returned output. Eager
-// flag should generally not be used unless you know what you are doing! The `timeoutOps` argument
-// modifies the base timeout argument just for the duration of this send operation.
-func (c *Channel) SendInput(
- channelInput string,
- stripPrompt, eager bool,
- timeoutOps time.Duration,
-) ([]byte, error) {
- _c := make(chan *channelResult)
+ b = append(b, nb...)
+ }
- go func() {
- r := c.sendInput([]byte(channelInput), stripPrompt, eager)
- _c <- r
- close(_c)
+ cr <- &result{
+ b: c.processOut(b, op.StripPrompt),
+ err: nil,
+ }
}()
- timer := time.NewTimer(c.DetermineOperationTimeout(timeoutOps))
+ timer := time.NewTimer(c.GetTimeout(op.Timeout))
select {
- case r := <-_c:
- return r.result, r.error
+ case r := <-cr:
+ if r.err != nil {
+ return nil, r.err
+ }
+
+ return r.b, nil
case <-timer.C:
- logging.LogError(c.FormatLogMessage("error", "timed out sending input to device"))
- return []byte{}, ErrChannelTimeout
+ c.l.Critical("channel timeout sending input to device")
+
+ return nil, fmt.Errorf("%w: channel timeout sending input to device", util.ErrTimeoutError)
}
}
+
+// SendInput sends the input string to the target device. Any bytes output is returned.
+func (c *Channel) SendInput(input string, opts ...util.Option) ([]byte, error) {
+ return c.SendInputB([]byte(input), opts...)
+}
diff --git a/channel/sendinput_test.go b/channel/sendinput_test.go
index 6fbd781..c7b23b1 100644
--- a/channel/sendinput_test.go
+++ b/channel/sendinput_test.go
@@ -1,32 +1,99 @@
package channel_test
import (
- "os"
+ "bytes"
+ "fmt"
"testing"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/scrapli/scrapligo/util"
+
"github.com/google/go-cmp/cmp"
- "github.com/scrapli/scrapligo/util/testhelper"
)
-func TestSendInput(t *testing.T) {
- fakeSession := "sendinput"
+type sendInputTestcase struct {
+ description string
+ input string
+ payloadFile string
+ noStripPrompt bool
+ eager bool
+}
- expectedFile := "../test_data/channel/sendinput_expected"
+func testSendInput(testName string, testCase *sendInputTestcase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
- expected, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
- t.Fatalf("failed opening expected output file '%s' err: %v", expectedFile, expectedErr)
- }
+ c, fileTransportObj := prepareChannel(t, testName, testCase.payloadFile)
+
+ var opts []util.Option
+
+ if testCase.eager {
+ opts = append(opts, opoptions.WithEager())
+ }
+
+ if testCase.noStripPrompt {
+ opts = append(opts, opoptions.WithNoStripPrompt())
+ }
- c := testhelper.NewPatchedChannel(t, &fakeSession)
+ actualOut, err := c.SendInput(
+ testCase.input,
+ opts...,
+ )
+ if err != nil {
+ t.Errorf("%s: encountered error running Channel SendInput, error: %s", testName, err)
+ }
- actual, promptErr := c.SendInput("show version", false, false, 0)
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
- if promptErr != nil {
- t.Fatalf("error sending input to mock channel: %v", promptErr)
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendInput(t *testing.T) {
+ cases := map[string]*sendInputTestcase{
+ "send-input-simple": {
+ description: "simple send input test",
+ input: "show run int vlan1",
+ payloadFile: "send-input-simple.txt",
+ noStripPrompt: true,
+ eager: false,
+ },
+ "send-input-simple-strip-prompt": {
+ description: "simple send input test",
+ input: "show run int vlan1",
+ payloadFile: "send-input-simple.txt",
+ noStripPrompt: false,
+ eager: false,
+ },
}
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
+ for testName, testCase := range cases {
+ f := testSendInput(testName, testCase)
+
+ t.Run(testName, f)
}
}
diff --git a/channel/sendinteractive.go b/channel/sendinteractive.go
index 37bcd4e..a187b20 100644
--- a/channel/sendinteractive.go
+++ b/channel/sendinteractive.go
@@ -5,133 +5,119 @@ import (
"regexp"
"time"
- "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
)
-// SendInteractiveEvent struct used to represent each iteration of a `SendInteractive` operation.
+// SendInteractiveEvent is a struct representing a single "event" that can be sent to
+// SendInteractive this event contains the input to send, the response to expect, and whether
+// scrapligo should expect to see the input or if it is hidden (as is the case with passwords for
+// privilege escalation).
type SendInteractiveEvent struct {
ChannelInput string
ChannelResponse string
HideInput bool
}
-func (c *Channel) sendInteractive(
+// SendInteractive sends a slice of SendInteractiveEvent to the device. This is typically used to
+// handle any well understood "interactive" prompts on a device -- things like "clear logging" which
+// prompts the user to confirm, or handling privilege escalation where there is a password prompt.
+func (c *Channel) SendInteractive( //nolint: gocognit,gocyclo
events []*SendInteractiveEvent,
- interactionCompletePatterns []string,
-) *channelResult {
- var b []byte
-
- for _, event := range events {
- channelInput := []byte(event.ChannelInput)
- channelResponse := event.ChannelResponse
+ opts ...util.Option,
+) ([]byte, error) {
+ c.l.Debugf("channel SendInteractive requested, processing events '%v'", events)
- prompts := make([]*regexp.Regexp, 0)
- if len(channelResponse) > 0 {
- prompts = append(prompts, regexp.MustCompile(channelResponse))
- } else {
- prompts = append(prompts, c.CommsPromptPattern)
- }
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
- for _, interactionCompletePattern := range interactionCompletePatterns {
- prompts = append(prompts, regexp.MustCompile(interactionCompletePattern))
- }
+ cr := make(chan *result)
- hideInput := event.HideInput
+ var b []byte
- logChannelInput := string(channelInput)
- if hideInput {
- logChannelInput = redactedLog
- }
+ go func() {
+ for i, e := range events {
+ prompts := op.CompletePatterns
+ if e.ChannelResponse != "" {
+ prompts = append(prompts, regexp.MustCompile(e.ChannelResponse))
+ } else {
+ prompts = append(prompts, c.PromptPattern)
+ }
- logging.LogDebug(
- c.FormatLogMessage(
- "info",
- fmt.Sprintf(
- "\"sending interactive input: %s; expecting: %s; hidden input: %v",
- logChannelInput,
- channelResponse,
- hideInput,
- ),
- ),
- )
+ err = c.Write([]byte(e.ChannelInput), e.HideInput)
+ if err != nil {
+ cr <- &result{b: nil, err: err}
- err := c.Write(channelInput, hideInput)
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: err,
+ return
}
- }
- if channelResponse == "" || hideInput {
- returnErr := c.SendReturn()
- if returnErr != nil {
- return &channelResult{
- result: []byte(""),
- error: returnErr,
- }
- }
- } else {
- newBuf, readErr := c.readUntilInput(channelInput)
- if readErr != nil {
- return &channelResult{
- result: []byte(""),
- error: readErr,
+ if e.ChannelResponse != "" && !e.HideInput {
+ var nb []byte
+
+ nb, err = c.ReadUntilInput([]byte(e.ChannelInput))
+ if err != nil {
+ cr <- &result{b: nil, err: err}
+
+ return
}
+
+ b = append(b, nb...)
}
- b = append(b, newBuf...)
- returnErr := c.SendReturn()
- if returnErr != nil {
- return &channelResult{
- result: []byte(""),
- error: returnErr,
- }
+
+ err = c.WriteReturn()
+ if err != nil {
+ cr <- &result{b: nil, err: err}
+
+ return
}
- }
- postInputBuf, err := c.readUntilExplicitPrompt(prompts)
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: nil,
+ var pb []byte
+
+ pb, err = c.ReadUntilAnyPrompt(prompts)
+ if err != nil {
+ cr <- &result{b: nil, err: err}
+
+ return
}
- }
- b = append(b, postInputBuf...)
- }
+ b = append(b, pb...)
- return &channelResult{
- result: c.RestructureOutput(b, false),
- error: nil,
- }
-}
+ if i < len(events)-1 && len(op.CompletePatterns) > 0 {
+ var done bool
-// SendInteractive send "interactive" inputs to a device. Accepts a slice of `SendInteractiveEvent`
-// which is basically a struct defining the input and what the expected output of that command is.
-// Used for dealing with "prompting" from a target device.
-func (c *Channel) SendInteractive(
- events []*SendInteractiveEvent,
- interactionCompletePatterns []string,
- timeoutOps time.Duration,
-) ([]byte, error) {
- _c := make(chan *channelResult)
+ for _, p := range op.CompletePatterns {
+ if p.Match(pb) {
+ done = true
- go func() {
- r := c.sendInteractive(events, interactionCompletePatterns)
- _c <- r
- close(_c)
+ break
+ }
+ }
+
+ if done {
+ break
+ }
+ }
+ }
+
+ cr <- &result{b: c.processOut(b, false), err: nil}
}()
- timer := time.NewTimer(c.DetermineOperationTimeout(timeoutOps))
+ timer := time.NewTimer(c.GetTimeout(op.Timeout))
select {
- case r := <-_c:
- return r.result, r.error
+ case r := <-cr:
+ if r.err != nil {
+ return nil, r.err
+ }
+
+ return r.b, nil
case <-timer.C:
- logging.LogError(
- c.FormatLogMessage("error", "timed out sending interactive input to device"),
- )
+ c.l.Critical("channel timeout sending interactive input to device")
- return []byte{}, ErrChannelTimeout
+ return nil, fmt.Errorf(
+ "%w: channel timeout sending interactive input to device",
+ util.ErrTimeoutError,
+ )
}
}
diff --git a/channel/sendinteractive_test.go b/channel/sendinteractive_test.go
index 5c02490..4e7a813 100644
--- a/channel/sendinteractive_test.go
+++ b/channel/sendinteractive_test.go
@@ -1,46 +1,91 @@
package channel_test
import (
- "os"
+ "bytes"
+ "fmt"
+ "regexp"
"testing"
- "github.com/google/go-cmp/cmp"
-
"github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/util/testhelper"
+
+ "github.com/google/go-cmp/cmp"
)
-func TestSendInteractive(t *testing.T) {
- fakeSession := "sendinteractive"
+type sendInteractiveTestCase struct {
+ description string
+ events []*channel.SendInteractiveEvent
+ completePatterns []*regexp.Regexp
+ payloadFile string
+}
- expectedFile := "../test_data/channel/sendinteractive"
+func testSendInteractive(testName string, testCase *sendInteractiveTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
- expected, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
- t.Fatalf("failed opening expected output file '%s' err: %v", expectedFile, expectedErr)
- }
+ c, fileTransportObj := prepareChannel(t, testName, testCase.payloadFile)
- c := testhelper.NewPatchedChannel(t, &fakeSession)
+ actualOut, err := c.SendInteractive(testCase.events)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running Channel SendInteractive, error: %s",
+ testName,
+ err,
+ )
+ }
- events := []*channel.SendInteractiveEvent{
- {
- ChannelInput: "clear logging",
- ChannelResponse: "[confirm]",
- HideInput: false,
- },
- {
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
}
- actual, promptErr := c.SendInteractive(events, nil, 0)
+}
- if promptErr != nil {
- t.Fatalf("error sending input to mock channel: %v", promptErr)
+func TestSendInteractive(t *testing.T) {
+ cases := map[string]*sendInteractiveTestCase{
+ "send-interactive-simple": {
+ description: "simple send interactive input test",
+ events: []*channel.SendInteractiveEvent{
+ {
+ ChannelInput: "clear logging",
+ ChannelResponse: "[confirm]",
+ HideInput: false,
+ },
+ {
+ ChannelInput: "",
+ ChannelResponse: "",
+ HideInput: false,
+ },
+ },
+ completePatterns: nil,
+ payloadFile: "send-interactive-simple.txt",
+ },
}
- if diff := cmp.Diff(actual, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
+ for testName, testCase := range cases {
+ f := testSendInteractive(testName, testCase)
+
+ t.Run(testName, f)
}
}
diff --git a/channel/test-fixtures/auth-simple.txt b/channel/test-fixtures/auth-simple.txt
new file mode 100644
index 0000000..13a99df
--- /dev/null
+++ b/channel/test-fixtures/auth-simple.txt
@@ -0,0 +1,2 @@
+(vrnetlab@c3560) Password:
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/auth-telnet-simple.txt b/channel/test-fixtures/auth-telnet-simple.txt
new file mode 100644
index 0000000..b5c4de0
--- /dev/null
+++ b/channel/test-fixtures/auth-telnet-simple.txt
@@ -0,0 +1,10 @@
+Connected to c3560.
+Escape character is '^]'.
+
+
+User Access Verification
+
+Username: Kerberos: No default realm defined for Kerberos!
+some-username
+Password:
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/auth-telnet-two-attempts.txt b/channel/test-fixtures/auth-telnet-two-attempts.txt
new file mode 100644
index 0000000..ce8e979
--- /dev/null
+++ b/channel/test-fixtures/auth-telnet-two-attempts.txt
@@ -0,0 +1,13 @@
+Connected to c3560.
+Escape character is '^]'.
+
+User Access Verification
+
+Username: Kerberos: No default realm defined for Kerberos!
+some-username
+Password:
+% Login invalid
+
+Username: some-username
+Password:
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/auth-two-attempts.txt b/channel/test-fixtures/auth-two-attempts.txt
new file mode 100644
index 0000000..eaa9350
--- /dev/null
+++ b/channel/test-fixtures/auth-two-attempts.txt
@@ -0,0 +1,3 @@
+(vrnetlab@c3560) Password:
+(vrnetlab@c3560) Password:
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/get-prompt-simple.txt b/channel/test-fixtures/get-prompt-simple.txt
new file mode 100644
index 0000000..b456d2f
--- /dev/null
+++ b/channel/test-fixtures/get-prompt-simple.txt
@@ -0,0 +1 @@
+C3560#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/auth-simple-in.txt b/channel/test-fixtures/golden/auth-simple-in.txt
new file mode 100644
index 0000000..dd27780
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-simple-in.txt
@@ -0,0 +1,2 @@
+some-password
+
diff --git a/channel/test-fixtures/golden/auth-simple-out.txt b/channel/test-fixtures/golden/auth-simple-out.txt
new file mode 100644
index 0000000..7ec5bac
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-simple-out.txt
@@ -0,0 +1,2 @@
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/auth-telnet-simple-in.txt b/channel/test-fixtures/golden/auth-telnet-simple-in.txt
new file mode 100644
index 0000000..02fd6ab
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-telnet-simple-in.txt
@@ -0,0 +1,5 @@
+some-username
+
+
+some-password
+
diff --git a/channel/test-fixtures/golden/auth-telnet-simple-out.txt b/channel/test-fixtures/golden/auth-telnet-simple-out.txt
new file mode 100644
index 0000000..7ec5bac
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-telnet-simple-out.txt
@@ -0,0 +1,2 @@
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/auth-telnet-two-attempts-in.txt b/channel/test-fixtures/golden/auth-telnet-two-attempts-in.txt
new file mode 100644
index 0000000..222c0c5
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-telnet-two-attempts-in.txt
@@ -0,0 +1,11 @@
+some-username
+
+
+some-password
+
+
+some-username
+
+
+some-password
+
diff --git a/channel/test-fixtures/golden/auth-telnet-two-attempts-out.txt b/channel/test-fixtures/golden/auth-telnet-two-attempts-out.txt
new file mode 100644
index 0000000..7ec5bac
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-telnet-two-attempts-out.txt
@@ -0,0 +1,2 @@
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/auth-two-attempts-in.txt b/channel/test-fixtures/golden/auth-two-attempts-in.txt
new file mode 100644
index 0000000..dcac0d2
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-two-attempts-in.txt
@@ -0,0 +1,5 @@
+some-password
+
+
+some-password
+
diff --git a/channel/test-fixtures/golden/auth-two-attempts-out.txt b/channel/test-fixtures/golden/auth-two-attempts-out.txt
new file mode 100644
index 0000000..7ec5bac
--- /dev/null
+++ b/channel/test-fixtures/golden/auth-two-attempts-out.txt
@@ -0,0 +1,2 @@
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/get-prompt-simple-in.txt b/channel/test-fixtures/golden/get-prompt-simple-in.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/channel/test-fixtures/golden/get-prompt-simple-in.txt
@@ -0,0 +1 @@
+
diff --git a/channel/test-fixtures/golden/get-prompt-simple-out.txt b/channel/test-fixtures/golden/get-prompt-simple-out.txt
new file mode 100644
index 0000000..b456d2f
--- /dev/null
+++ b/channel/test-fixtures/golden/get-prompt-simple-out.txt
@@ -0,0 +1 @@
+C3560#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/send-input-simple-in.txt b/channel/test-fixtures/golden/send-input-simple-in.txt
new file mode 100644
index 0000000..8dbe38b
--- /dev/null
+++ b/channel/test-fixtures/golden/send-input-simple-in.txt
@@ -0,0 +1,2 @@
+show run int vlan1
+
diff --git a/channel/test-fixtures/golden/send-input-simple-out.txt b/channel/test-fixtures/golden/send-input-simple-out.txt
new file mode 100644
index 0000000..b048c25
--- /dev/null
+++ b/channel/test-fixtures/golden/send-input-simple-out.txt
@@ -0,0 +1,9 @@
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/send-input-simple-strip-prompt-in.txt b/channel/test-fixtures/golden/send-input-simple-strip-prompt-in.txt
new file mode 100644
index 0000000..8dbe38b
--- /dev/null
+++ b/channel/test-fixtures/golden/send-input-simple-strip-prompt-in.txt
@@ -0,0 +1,2 @@
+show run int vlan1
+
diff --git a/channel/test-fixtures/golden/send-input-simple-strip-prompt-out.txt b/channel/test-fixtures/golden/send-input-simple-strip-prompt-out.txt
new file mode 100644
index 0000000..ad32e91
--- /dev/null
+++ b/channel/test-fixtures/golden/send-input-simple-strip-prompt-out.txt
@@ -0,0 +1,7 @@
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/channel/test-fixtures/golden/send-interactive-simple-in.txt b/channel/test-fixtures/golden/send-interactive-simple-in.txt
new file mode 100644
index 0000000..53c0210
--- /dev/null
+++ b/channel/test-fixtures/golden/send-interactive-simple-in.txt
@@ -0,0 +1,5 @@
+clear logging
+
+
+
+
diff --git a/channel/test-fixtures/golden/send-interactive-simple-out.txt b/channel/test-fixtures/golden/send-interactive-simple-out.txt
new file mode 100644
index 0000000..62de6d5
--- /dev/null
+++ b/channel/test-fixtures/golden/send-interactive-simple-out.txt
@@ -0,0 +1,3 @@
+C3560CX#clear logging
+Clear logging buffer [confirm]
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/send-input-simple.txt b/channel/test-fixtures/send-input-simple.txt
new file mode 100644
index 0000000..68f2f76
--- /dev/null
+++ b/channel/test-fixtures/send-input-simple.txt
@@ -0,0 +1,10 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/channel/test-fixtures/send-interactive-simple.txt b/channel/test-fixtures/send-interactive-simple.txt
new file mode 100644
index 0000000..62de6d5
--- /dev/null
+++ b/channel/test-fixtures/send-interactive-simple.txt
@@ -0,0 +1,3 @@
+C3560CX#clear logging
+Clear logging buffer [confirm]
+C3560CX#
\ No newline at end of file
diff --git a/channel/write.go b/channel/write.go
new file mode 100644
index 0000000..44bd2b1
--- /dev/null
+++ b/channel/write.go
@@ -0,0 +1,28 @@
+package channel
+
+// Write writes the given bytes b to the channel.
+func (c *Channel) Write(b []byte, r bool) error {
+ lm := string(b)
+ if r {
+ lm = redacted
+ }
+
+ c.l.Debugf("channel write %#v", lm)
+
+ return c.t.Write(b)
+}
+
+// WriteReturn writes the channel ReturnChar to the channel.
+func (c *Channel) WriteReturn() error {
+ return c.Write(c.ReturnChar, false)
+}
+
+// WriteAndReturn writes the given bytes b and then sends the channel ReturnChar.
+func (c *Channel) WriteAndReturn(b []byte, r bool) error {
+ err := c.Write(b, r)
+ if err != nil {
+ return err
+ }
+
+ return c.WriteReturn()
+}
diff --git a/driver/base/base.go b/driver/base/base.go
deleted file mode 100644
index a353df0..0000000
--- a/driver/base/base.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package base
-
-import (
- "fmt"
- "regexp"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/transport"
-)
-
-// PrivilegeLevel struct defining a single privilege level -- used only for "network" level drivers.
-type PrivilegeLevel struct {
- Pattern string
- PatternRe *regexp.Regexp
- PatternNotContains []string
- Name string
- PreviousPriv string
- Deescalate string
- Escalate string
- EscalateAuth bool
- EscalatePrompt string
-}
-
-// Driver primary/base driver struct.
-type Driver struct {
- Host string
-
- AuthUsername string
- AuthPassword string
- AuthSecondary string
- AuthPrivateKey string
- AuthPrivateKeyPassphrase string
- AuthStrictKey bool
- AuthBypass bool
-
- SSHConfigFile string
- SSHKnownHostsFile string
-
- TransportType string
- Transport *transport.Transport
-
- Channel *channel.Channel
-
- FailedWhenContains []string
-
- PrivilegeLevels map[string]*PrivilegeLevel
- DefaultDesiredPriv string
-
- NetconfEcho *bool
- NetconfForceSelfClosingTags *bool
-}
-
-// Open opens the connection.
-func (d *Driver) Open() error {
- logging.LogDebug(
- d.FormatLogMessage(
- "info",
- fmt.Sprintf(
- "opening connection to '%s' on port '%d'",
- d.Host,
- d.Transport.BaseTransportArgs.Port,
- ),
- ),
- )
-
- err := d.Transport.Open()
- if err != nil {
- return err
- }
-
- var authErr error
- if d.TransportType == transport.SystemTransportName && !d.AuthBypass {
- _, authErr = d.Channel.AuthenticateSSH(d.AuthPassword, d.AuthPrivateKeyPassphrase)
- } else if d.TransportType == transport.TelnetTransportName && !d.AuthBypass {
- _, authErr = d.Channel.AuthenticateTelnet(d.AuthUsername, d.AuthPassword)
- }
-
- if authErr != nil {
- logging.LogError(
- d.FormatLogMessage("error", "authentication failed, connection not opened"),
- )
-
- return authErr
- }
-
- logging.LogDebug(d.FormatLogMessage("info", "connection to device opened successfully"))
-
- return nil
-}
-
-// Close closes the connection.
-func (d *Driver) Close() error {
- logging.LogDebug(
- d.FormatLogMessage(
- "info",
- fmt.Sprintf(
- "closing connection to '%s' on port '%d'",
- d.Host,
- d.Transport.BaseTransportArgs.Port,
- ),
- ),
- )
-
- err := d.Transport.Close()
- if err != nil {
- logging.LogError("failed closing transport")
-
- return err
- }
-
- logging.LogDebug(d.FormatLogMessage("info", "connection to device closed successfully"))
-
- return nil
-}
-
-// FormatLogMessage formats log message payload, adding contextual info about the host.
-func (d *Driver) FormatLogMessage(level, msg string) string {
- return logging.FormatLogMessage(level, d.Host, d.Transport.BaseTransportArgs.Port, msg)
-}
diff --git a/driver/base/getprompt.go b/driver/base/getprompt.go
deleted file mode 100644
index 6049ca5..0000000
--- a/driver/base/getprompt.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package base
-
-// GetPrompt fetch device prompt.
-func (d *Driver) GetPrompt() (string, error) {
- return d.Channel.GetPrompt()
-}
diff --git a/driver/base/helper.go b/driver/base/helper.go
deleted file mode 100644
index d3623bf..0000000
--- a/driver/base/helper.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package base
-
-// ParseSendOptions convenience function to parse and set defaults for `SendOption`s.
-func (d *Driver) ParseSendOptions(
- o []SendOption,
-) *SendOptions {
- finalOpts := &SendOptions{
- StripPrompt: DefaultSendOptionsStripPrompt,
- FailedWhenContains: d.FailedWhenContains,
- StopOnFailed: DefaultSendOptionsStopOnFailed,
- TimeoutOps: DefaultSendOptionsTimeoutOps,
- Eager: DefaultSendOptionsEager,
- }
-
- if len(o) > 0 && o[0] != nil {
- for _, option := range o {
- option(finalOpts)
- }
- }
-
- return finalOpts
-}
diff --git a/driver/base/new.go b/driver/base/new.go
deleted file mode 100644
index bd78727..0000000
--- a/driver/base/new.go
+++ /dev/null
@@ -1,118 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package base
-
-import (
- "errors"
- "time"
-
- "github.com/scrapli/scrapligo/transport"
-)
-
-// NewDriver create a new instance of `Driver`, accepts a host and variadic of options to modify
-// the driver behavior.
-func NewDriver( //nolint:funlen
- host string,
- options ...Option,
-) (*Driver, error) {
- d := &Driver{
- Host: host,
- AuthStrictKey: true,
- TransportType: transport.SystemTransportName,
- FailedWhenContains: []string{},
- PrivilegeLevels: map[string]*PrivilegeLevel{},
- DefaultDesiredPriv: "",
- }
-
- for _, option := range options {
- err := option(d)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- baseTransportArgs := &transport.BaseTransportArgs{
- Host: d.Host,
- Port: 22,
- AuthUsername: d.AuthUsername,
- TimeoutSocket: 30 * time.Second,
- TimeoutTransport: 45 * time.Second,
- PtyHeight: 80,
- PtyWidth: 256,
- }
-
- if d.Transport == nil {
- switch d.TransportType {
- case transport.SystemTransportName:
- systemTransportArgs := &transport.SystemTransportArgs{
- AuthPrivateKey: d.AuthPrivateKey,
- AuthStrictKey: d.AuthStrictKey,
- SSHConfigFile: d.SSHConfigFile,
- SSHKnownHostsFile: d.SSHKnownHostsFile,
- }
- tImpl := &transport.System{
- SystemTransportArgs: systemTransportArgs,
- }
- t := &transport.Transport{
- Impl: tImpl,
- BaseTransportArgs: baseTransportArgs,
- }
- d.Transport = t
- case transport.StandardTransportName:
- standardTransportArgs := &transport.StandardTransportArgs{
- AuthPassword: d.AuthPassword,
- AuthPrivateKey: d.AuthPrivateKey,
- AuthStrictKey: d.AuthStrictKey,
- SSHConfigFile: d.SSHConfigFile,
- SSHKnownHostsFile: d.SSHKnownHostsFile,
- }
- tImpl := &transport.Standard{
- StandardTransportArgs: standardTransportArgs,
- }
- t := &transport.Transport{
- Impl: tImpl,
- BaseTransportArgs: baseTransportArgs,
- }
- d.Transport = t
- case transport.TelnetTransportName:
- telnetTransportArgs := &transport.TelnetTransportArgs{}
- tImpl := &transport.Telnet{
- TelnetTransportArgs: telnetTransportArgs,
- }
- t := &transport.Transport{
- Impl: tImpl,
- BaseTransportArgs: baseTransportArgs,
- }
- d.Transport = t
- default:
- return nil, transport.ErrUnknownTransport
- }
- }
-
- for _, option := range options {
- err := option(d.Transport)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- c, err := NewChannel(d.Transport, options...)
- if err != nil {
- return nil, err
- }
-
- d.Channel = c
-
- return d, nil
-}
diff --git a/driver/base/new_common.go b/driver/base/new_common.go
deleted file mode 100644
index 3ce171c..0000000
--- a/driver/base/new_common.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package base
-
-import (
- "errors"
- "regexp"
- "time"
-
- "github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/transport"
-)
-
-func NewChannel(
- t *transport.Transport,
- options ...Option,
-) (*channel.Channel, error) {
- c := &channel.Channel{
- Transport: t,
- CommsPromptPattern: regexp.MustCompile(`(?im)^[a-z0-9.\-@()/:]{1,48}[#>$]\s*$`),
- CommsReturnChar: "\n",
- CommsPromptSearchDepth: 1000,
- TimeoutOps: 60 * time.Second,
- Host: t.BaseTransportArgs.Host,
- Port: t.BaseTransportArgs.Port,
- }
-
- for _, option := range options {
- err := option(c)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- return c, nil
-}
diff --git a/driver/base/new_windows.go b/driver/base/new_windows.go
deleted file mode 100644
index 4310d5a..0000000
--- a/driver/base/new_windows.go
+++ /dev/null
@@ -1,103 +0,0 @@
-//go:build windows
-// +build windows
-
-package base
-
-import (
- "errors"
- "time"
-
- "github.com/scrapli/scrapligo/transport"
-)
-
-// NewDriver create a new instance of `Driver`, accepts a host and variadic of options to modify
-// the driver behavior.
-func NewDriver(
- host string,
- options ...Option,
-) (*Driver, error) {
- d := &Driver{
- Host: host,
- AuthStrictKey: true,
- TransportType: transport.StandardTransportName,
- FailedWhenContains: []string{},
- PrivilegeLevels: map[string]*PrivilegeLevel{},
- DefaultDesiredPriv: "",
- }
-
- for _, option := range options {
- err := option(d)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- baseTransportArgs := &transport.BaseTransportArgs{
- Host: d.Host,
- Port: 22,
- AuthUsername: d.AuthUsername,
- TimeoutSocket: 30 * time.Second,
- TimeoutTransport: 45 * time.Second,
- PtyHeight: 80,
- PtyWidth: 256,
- }
-
- if d.Transport == nil {
- switch d.TransportType {
- case transport.StandardTransportName:
- standardTransportArgs := &transport.StandardTransportArgs{
- AuthPassword: d.AuthPassword,
- AuthPrivateKey: d.AuthPrivateKey,
- AuthStrictKey: d.AuthStrictKey,
- SSHConfigFile: d.SSHConfigFile,
- SSHKnownHostsFile: d.SSHKnownHostsFile,
- }
- tImpl := &transport.Standard{
- StandardTransportArgs: standardTransportArgs,
- }
- t := &transport.Transport{
- Impl: tImpl,
- BaseTransportArgs: baseTransportArgs,
- }
- d.Transport = t
- case transport.TelnetTransportName:
- telnetTransportArgs := &transport.TelnetTransportArgs{}
- tImpl := &transport.Telnet{
- TelnetTransportArgs: telnetTransportArgs,
- }
- t := &transport.Transport{
- Impl: tImpl,
- BaseTransportArgs: baseTransportArgs,
- }
- d.Transport = t
- default:
- return nil, transport.ErrUnknownTransport
- }
- }
-
- for _, option := range options {
- err := option(d.Transport)
-
- if err != nil {
- if errors.Is(err, ErrIgnoredOption) {
- continue
- } else {
- return nil, err
- }
- }
- }
-
- c, err := NewChannel(d.Transport, options...)
- if err != nil {
- return nil, err
- }
-
- d.Channel = c
-
- return d, nil
-}
diff --git a/driver/base/options.go b/driver/base/options.go
deleted file mode 100644
index 75ca7fc..0000000
--- a/driver/base/options.go
+++ /dev/null
@@ -1,509 +0,0 @@
-package base
-
-import (
- "errors"
- "io"
- "regexp"
- "strings"
- "time"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/channel"
-
- "github.com/scrapli/scrapligo/transport"
-)
-
-var ErrIgnoredOption = errors.New("option ignored, for different instance type")
-
-// Option function to set driver options.
-type Option func(interface{}) error
-
-// WithAuthUsername provide a string username to use for driver authentication.
-func WithAuthUsername(username string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthUsername = username
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithAuthPassword provide a string password to use for driver authentication.
-func WithAuthPassword(password string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthPassword = password
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithAuthSecondary provide a string "secondary" (or "enable") password to use for driver
-// authentication. Only applicable for "network" level drivers.
-func WithAuthSecondary(secondary string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthSecondary = secondary
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithAuthPrivateKey provide a string path to a private key to use for driver authentication,
-// optionally provide a string to use for passphrase for given private key.
-func WithAuthPrivateKey(privateKey string, privateKeyPassphrase ...string) Option {
- pkPassphrase := []string{""}
- if len(privateKeyPassphrase) > 0 {
- pkPassphrase = privateKeyPassphrase
- }
-
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthPrivateKey = privateKey
- d.AuthPrivateKeyPassphrase = strings.Join(pkPassphrase, "")
-
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithAuthBypass provide bool indicating if auth should be "bypassed" -- only applicable for system
-// and telnet transports.
-func WithAuthBypass(bypass bool) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthBypass = bypass
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithAuthStrictKey provide bool indicating if strict key checking should be enforced.
-func WithAuthStrictKey(stricktKey bool) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.AuthStrictKey = stricktKey
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// SSH file related options
-
-// WithSSHConfigFile provide string path to ssh config file.
-func WithSSHConfigFile(sshConfigFile string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- resolvedSSHConfigFile, err := util.ResolveFilePath(sshConfigFile)
- if err != nil {
- return err
- }
-
- d.SSHConfigFile = resolvedSSHConfigFile
-
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithSSHKnownHostsFile provide string path to ssh known hosts file.
-func WithSSHKnownHostsFile(sshKnownHostsFile string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- resolvedSSHKnownHostsFile, err := util.ResolveFilePath(sshKnownHostsFile)
- if err != nil {
- return err
- }
-
- d.SSHKnownHostsFile = resolvedSSHKnownHostsFile
-
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// Network driver options
-
-// WithFailedWhenContains provide a custom slice of strings to use to check if an output is failed
-// -- only applicable to network drivers.
-func WithFailedWhenContains(failedWhenContains []string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.FailedWhenContains = failedWhenContains
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithPrivilegeLevels provide custom privilege levels to use -- only applicable to network drivers.
-func WithPrivilegeLevels(privilegeLevels map[string]*PrivilegeLevel) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.PrivilegeLevels = privilegeLevels
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithDefaultDesiredPriv provide custom default preferred privilege level to use -- only applicable
-// to network drivers.
-func WithDefaultDesiredPriv(defaultDesiredPriv string) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.DefaultDesiredPriv = defaultDesiredPriv
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// Netconf driver options
-
-// WithNetconfServerEcho provide custom default preferred privilege level to use -- only applicable
-// for netconf.
-func WithNetconfServerEcho(echo bool) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.NetconfEcho = &echo
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithNetconfForceSelfClosingTags forces empty tags to the form -- only applicable for
-// netconf, and *probably* only for junos.
-func WithNetconfForceSelfClosingTags(forceSelfClosingTags bool) Option {
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.NetconfForceSelfClosingTags = &forceSelfClosingTags
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// Send command/config options
-
-const (
- // DefaultSendOptionsStripPrompt default to stripping prompt.
- DefaultSendOptionsStripPrompt = true
- // DefaultSendOptionsStopOnFailed default to *not* stopping on failures.
- DefaultSendOptionsStopOnFailed = false
- // DefaultSendOptionsTimeoutOps default to relying on the drivers timeout ops attribute.
- DefaultSendOptionsTimeoutOps = -1.0
- // DefaultSendOptionsEager default to *not* eager mode.
- DefaultSendOptionsEager = false
-)
-
-// SendOptions struct for send operation options.
-type SendOptions struct {
- StripPrompt bool
- FailedWhenContains []string
- StopOnFailed bool
- TimeoutOps time.Duration
- Eager bool
- DesiredPrivilegeLevel string
- InteractionCompletePatterns []string
-}
-
-// SendOption func to set send options.
-type SendOption func(*SendOptions)
-
-// WithSendStripPrompt bool indicating if you would like the hostname/device prompt stripped out of
-// output from a send operation.
-func WithSendStripPrompt(stripPrompt bool) SendOption {
- return func(o *SendOptions) {
- o.StripPrompt = stripPrompt
- }
-}
-
-// WithSendFailedWhenContains slice of strings that overrides the drivers `FailedWhenContains` list
-// for a given send operation.
-func WithSendFailedWhenContains(failedWhenContains []string) SendOption {
- return func(o *SendOptions) {
- o.FailedWhenContains = failedWhenContains
- }
-}
-
-// WithSendStopOnFailed bool indicating if multi command/config operations should stop at first sign
-// of failure (based on FailedWhenContains list).
-func WithSendStopOnFailed(stopOnFailed bool) SendOption {
- return func(o *SendOptions) {
- o.StopOnFailed = stopOnFailed
- }
-}
-
-// WithSendTimeoutOps duration to use for timeout of a given send operation.
-func WithSendTimeoutOps(timeoutOps time.Duration) SendOption {
- return func(o *SendOptions) {
- o.TimeoutOps = timeoutOps
- }
-}
-
-// WithSendEager bool indicating if send operation should operate in `eager` mode -- generally only
-// used for netconf operations.
-func WithSendEager(eager bool) SendOption {
- return func(o *SendOptions) {
- o.Eager = eager
- }
-}
-
-// WithDesiredPrivilegeLevel provide a desired privilege level for the send operation to work in.
-func WithDesiredPrivilegeLevel(privilegeLevel string) SendOption {
- // ignored for command(s) operations, only applicable for interactive/config operations
- return func(o *SendOptions) {
- o.DesiredPrivilegeLevel = privilegeLevel
- }
-}
-
-// WithInteractionCompletePatterns provide a list of patterns which, when seen, indicate a
-// `SendInteractive` "session" is complete. Only used for `SendInteractive`, otherwise ignored.
-func WithInteractionCompletePatterns(interactionCompletePatterns []string) SendOption {
- // ignored for command(s) operations, only applicable for interactive/config operations
- return func(o *SendOptions) {
- o.InteractionCompletePatterns = interactionCompletePatterns
- }
-}
-
-// WithPort modifies the default (22) port value of a driver/transport.
-func WithPort(port int) Option {
- return func(o interface{}) error {
- t, ok := o.(*transport.Transport)
-
- if ok {
- t.BaseTransportArgs.Port = port
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithTimeoutSocket provide duration to use for socket timeout.
-func WithTimeoutSocket(timeout time.Duration) Option {
- return func(o interface{}) error {
- t, ok := o.(*transport.Transport)
-
- if ok {
- t.BaseTransportArgs.TimeoutSocket = timeout
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithTimeoutTransport provide duration to use for transport timeout.
-func WithTimeoutTransport(timeout time.Duration) Option {
- return func(o interface{}) error {
- t, ok := o.(*transport.Transport)
-
- if ok {
- t.BaseTransportArgs.TimeoutTransport = timeout
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithTransportType provide string name of type of transport to use.
-func WithTransportType(transportType string) Option {
- var finalTransport string
-
- switch transportType {
- case transport.SystemTransportName:
- finalTransport = transport.SystemTransportName
- case transport.StandardTransportName:
- finalTransport = transport.StandardTransportName
- case transport.TelnetTransportName:
- finalTransport = transport.TelnetTransportName
- default:
- return func(o interface{}) error {
- return transport.ErrUnknownTransport
- }
- }
-
- return func(o interface{}) error {
- d, ok := o.(*Driver)
-
- if ok {
- d.TransportType = finalTransport
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithTransportPtySize provide pty width/height to use.
-func WithTransportPtySize(w, h int) Option {
- return func(o interface{}) error {
- t, ok := o.(*transport.Transport)
-
- if ok {
- t.BaseTransportArgs.PtyWidth = w
- t.BaseTransportArgs.PtyHeight = h
-
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithTimeoutOps provide duration to use for "operation" timeout.
-func WithTimeoutOps(timeout time.Duration) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.TimeoutOps = timeout
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// Comms related options
-
-// WithCommsPromptPattern provide string regex pattern to use for prompt pattern, typically not
-// necessary if using a network level driver.
-func WithCommsPromptPattern(pattern string) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.CommsPromptPattern = regexp.MustCompile(pattern)
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithCommsReturnChar provide string to use as the return character, typically can be left default.
-func WithCommsReturnChar(char string) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.CommsReturnChar = char
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// ChannelLog option
-
-// WithChannelLog provide an io.Writer object to write channel log data to.
-func WithChannelLog(log io.Writer) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.ChannelLog = log
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// ChannelAuth options
-
-// WithChannelAuthUsernamePattern overrides the default in channel username regexp.
-func WithChannelAuthUsernamePattern(p *regexp.Regexp) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.AuthUsernamePattern = p
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithChannelAuthPasswordPattern overrides the default in channel password regexp.
-func WithChannelAuthPasswordPattern(p *regexp.Regexp) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.AuthPasswordPattern = p
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
-
-// WithChannelAuthPassphrasePattern overrides the default in channel username regexp.
-func WithChannelAuthPassphrasePattern(p *regexp.Regexp) Option {
- return func(o interface{}) error {
- c, ok := o.(*channel.Channel)
-
- if ok {
- c.AuthUsernamePattern = p
- return nil
- }
-
- return ErrIgnoredOption
- }
-}
diff --git a/driver/base/readcallback.go b/driver/base/readcallback.go
deleted file mode 100644
index 1003a41..0000000
--- a/driver/base/readcallback.go
+++ /dev/null
@@ -1,347 +0,0 @@
-package base
-
-import (
- "bytes"
- "errors"
- "fmt"
- "regexp"
- "time"
-)
-
-var ErrMustSetContains = errors.New("must set Contains or ContainsRe")
-var ErrCallbackAlreadyTriggered = errors.New("callback set to 'OnlyOnce', but already triggered")
-var ErrCallbackTimeout = errors.New("callback timeout")
-
-type ReadCallbackOption func(callback *ReadCallback) error
-
-func WithCallbackContains(contains string) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.Contains = contains
-
- return nil
- }
-}
-
-func WithCallbackNotContains(notContains string) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.NotContains = notContains
-
- return nil
- }
-}
-
-func WithCallbackContainsRe(contains string) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.ContainsRe = contains
-
- return nil
- }
-}
-
-func WithCallbackCaseInsensitive(i bool) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.CaseInsensitive = i
-
- return nil
- }
-}
-
-func WithCallbackMultiline(m bool) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.MultiLine = m
-
- return nil
- }
-}
-
-func WithCallbackResetOutput(reset bool) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.ResetOutput = reset
-
- return nil
- }
-}
-
-func WithCallbackOnlyOnce(o bool) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.OnlyOnce = o
-
- return nil
- }
-}
-
-func WithCallbackNextTimeout(t time.Duration) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.NextTimeout = t
-
- return nil
- }
-}
-
-func WithCallbackNextReadDelay(t time.Duration) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.NextReadDelay = t
-
- return nil
- }
-}
-
-func WithCallbackComplete(complete bool) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.Complete = complete
-
- return nil
- }
-}
-
-func WithCallbackName(name string) ReadCallbackOption {
- return func(r *ReadCallback) error {
- r.Name = name
-
- return nil
- }
-}
-
-func NewReadCallback(
- callback func(*Driver, string) error,
- options ...ReadCallbackOption,
-) (*ReadCallback, error) {
- rc := &ReadCallback{
- Callback: callback,
- Contains: "",
- containsBytes: nil,
- ContainsRe: "",
- containsReCompiled: nil,
- CaseInsensitive: true,
- MultiLine: true,
- ResetOutput: true,
- OnlyOnce: false,
- NextTimeout: 0,
- NextReadDelay: 0,
- triggered: false,
- Complete: false,
- Name: "",
- }
-
- for _, option := range options {
- err := option(rc)
- if err != nil {
- return nil, err
- }
- }
-
- if rc.Contains == "" && rc.ContainsRe == "" {
- return nil, ErrMustSetContains
- }
-
- return rc, nil
-}
-
-type ReadCallback struct {
- Callback func(*Driver, string) error
- Contains string
- containsBytes []byte
- NotContains string
- notContainsBytes []byte
- ContainsRe string
- containsReCompiled *regexp.Regexp
- CaseInsensitive bool
- MultiLine bool
- // ResetOutput bool indicating if the output should be reset or not after callback execution.
- ResetOutput bool
- // OnlyOnce bool indicating if this callback should be executed only one time.
- OnlyOnce bool
- // NextTimout timeout value to use for the subsequent read loop - ignored if Complete is true.
- NextTimeout time.Duration
- // NextReadDelay is time to use for sleeps between reads for hte subsequent read loop.
- NextReadDelay time.Duration
- triggered bool
- Complete bool
- Name string
-}
-
-func (r *ReadCallback) contains() []byte {
- if len(r.containsBytes) == 0 {
- r.containsBytes = []byte(r.Contains)
-
- if r.CaseInsensitive {
- r.containsBytes = bytes.ToLower(r.containsBytes)
- }
- }
-
- return r.containsBytes
-}
-
-func (r *ReadCallback) notContains() []byte {
- if len(r.notContainsBytes) == 0 {
- r.notContainsBytes = []byte(r.NotContains)
-
- if r.CaseInsensitive {
- r.notContainsBytes = bytes.ToLower(r.notContainsBytes)
- }
- }
-
- return r.notContainsBytes
-}
-
-func (r *ReadCallback) containsRe() *regexp.Regexp {
- if r.containsReCompiled == nil {
- flags := ""
-
- if r.CaseInsensitive && r.MultiLine {
- flags = "(?im)"
- } else if r.CaseInsensitive {
- flags = "(?i)"
- } else if r.MultiLine {
- flags = "(?m)"
- }
-
- r.containsReCompiled = regexp.MustCompile(fmt.Sprintf(`%s%s`, flags, r.ContainsRe))
- }
-
- return r.containsReCompiled
-}
-
-func (r *ReadCallback) check(o []byte) bool {
- if r.CaseInsensitive {
- o = bytes.ToLower(o)
- }
-
- if (r.Contains != "" && bytes.Contains(o, r.contains())) &&
- !(r.NotContains != "" && !bytes.Contains(o, r.notContains())) {
- return true
- }
-
- if (r.ContainsRe != "" && r.containsRe().Match(o)) &&
- !(r.NotContains != "" && !bytes.Contains(o, r.notContains())) {
- return true
- }
-
- return false
-}
-
-type readCallbackResult struct {
- i int
- callbacks []*ReadCallback
- output []byte
- err error
-}
-
-func (d *Driver) executeCallback(
- i int,
- callbacks []*ReadCallback,
- output []byte,
- timeout,
- readDelay time.Duration) error {
- callback := callbacks[i]
-
- if callback.OnlyOnce {
- if callback.triggered {
- return ErrCallbackAlreadyTriggered
- }
-
- callback.triggered = true
- }
-
- err := callback.Callback(d, string(output))
- if err != nil {
- return err
- }
-
- if callback.Complete {
- return nil
- }
-
- if callback.ResetOutput {
- output = []byte{}
- }
-
- nextTimeout := timeout
- if callback.NextTimeout != 0 {
- nextTimeout = callback.NextTimeout
- }
-
- nextReadDelay := readDelay
- if callback.NextReadDelay != 0 {
- nextReadDelay = callback.NextReadDelay
- }
-
- return d.readWithCallbacks(callbacks, output, nextTimeout, nextReadDelay)
-}
-
-func (d *Driver) readWithCallbacks(
- callbacks []*ReadCallback,
- output []byte,
- timeout,
- readDelay time.Duration,
-) error {
- c := make(chan *readCallbackResult)
-
- go func() {
- defer close(c)
-
- for {
- newOutput, err := d.Channel.Read()
- if err != nil {
- c <- &readCallbackResult{
- err: err,
- }
-
- return
- }
-
- output = append(output, newOutput...)
-
- for i, callback := range callbacks {
- if callback.check(output) {
- c <- &readCallbackResult{
- i: i,
- callbacks: callbacks,
- output: output,
- err: nil,
- }
-
- return
- }
- }
-
- time.Sleep(readDelay)
- }
- }()
-
- timer := time.NewTimer(timeout)
-
- select {
- case r := <-c:
- if r.err != nil {
- return r.err
- }
-
- return d.executeCallback(r.i, r.callbacks, r.output, timeout, readDelay)
- case <-timer.C:
- return ErrCallbackTimeout
- }
-}
-
-func (d *Driver) ReadWithCallbacks(
- callbacks []*ReadCallback,
- input string,
- timeout,
- readDelay time.Duration,
-) error {
- if input != "" {
- err := d.Channel.WriteAndReturn([]byte(input), false)
- if err != nil {
- return err
- }
- }
-
- origTransportTimeout := d.Transport.BaseTransportArgs.TimeoutTransport
- d.Transport.BaseTransportArgs.TimeoutTransport = 0
-
- r := d.readWithCallbacks(callbacks, []byte{}, timeout, readDelay)
-
- d.Transport.BaseTransportArgs.TimeoutTransport = origTransportTimeout
-
- return r
-}
diff --git a/driver/base/response.go b/driver/base/response.go
deleted file mode 100644
index fe62495..0000000
--- a/driver/base/response.go
+++ /dev/null
@@ -1,188 +0,0 @@
-package base
-
-import (
- "errors"
- "fmt"
- "io"
- "net/http"
- "os"
- "strings"
- "time"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
- "github.com/sirikothe/gotextfsm"
-)
-
-// ErrFailedOpeningTemplate error for failure to open a textfsm template.
-var ErrFailedOpeningTemplate = errors.New("failed opening provided path to textfsm template")
-
-// ErrFailedParsingTemplate error for failure of parsing a textfsm template.
-var ErrFailedParsingTemplate = errors.New("failed parsing textfsm template")
-
-// OperationError is an error object returned when a scrapli operation completes "successfully" --
-// as in does not have an EOF/timeout or otherwise unrecoverable error -- but contains output in the
-// device's response indicating that an input was bad/invalid or device failed to process it at
-// that time.
-type OperationError struct {
- Input string
- Output string
- ErrorString string
-}
-
-// Error returns an error string for the OperationError object.
-func (e *OperationError) Error() string {
- return fmt.Sprintf(
- "operation error from input '%s'. indicated error '%s'",
- e.Input,
- e.ErrorString,
- )
-}
-
-// Response is a response object that gets returned from scrapli send operations.
-type Response struct {
- Host string
- Port int
- ChannelInput string
- RawResult []byte
- Result string
- StartTime time.Time
- EndTime time.Time
- ElapsedTime float64
- FailedWhenContains []string
- // Failed returns an error if any of the `FailedWhenContains` substrings are seen in the output
- // returned from the device. This error indicates that the operation has completed successfully,
- // but that an input was bad/invalid or device failed to process it at that time
- Failed error
-}
-
-// NewResponse creates a new response object.
-func NewResponse(
- host string,
- port int,
- channelInput string,
- failedWhenContains []string,
-) *Response {
- r := &Response{
- Host: host,
- Port: port,
- ChannelInput: channelInput,
- RawResult: nil,
- Result: "",
- StartTime: time.Now(),
- EndTime: time.Time{},
- ElapsedTime: 0,
- FailedWhenContains: failedWhenContains,
- }
-
- return r
-}
-
-// Record records a response from an operation.
-func (r *Response) Record(rawResult []byte, result string) {
- r.EndTime = time.Now()
- r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
-
- r.RawResult = rawResult
- r.Result = result
-
- s := util.StrContainsAnySubStr(r.Result, r.FailedWhenContains)
- if len(s) > 0 {
- r.Failed = &OperationError{
- Input: r.ChannelInput,
- Output: r.Result,
- ErrorString: s,
- }
- }
-}
-
-// TextFsmParse parses recorded output w/ a provided textfsm template.
-// the argument is interpreted as URL or filesystem path, for example:
-// response.TextFsmParse("http://example.com/textfsm.template") or
-// response.TextFsmParse("./local/textfsm.template").
-func (r *Response) TextFsmParse(path string) ([]map[string]interface{}, error) {
- var t []byte
-
- switch {
- case strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://"):
- resp, err := http.Get(path) // nolint:gosec,noctx
- if err != nil {
- logging.LogError(
- r.FormatLogMessage(
- "error",
- fmt.Sprintf("Failed downloading template, error: %s\n", err.Error()),
- ),
- )
-
- return []map[string]interface{}{}, ErrFailedOpeningTemplate
- }
-
- defer resp.Body.Close()
-
- t, err = io.ReadAll(resp.Body)
- if err != nil {
- logging.LogError(
- r.FormatLogMessage(
- "error",
- fmt.Sprintf("Failed reading downloaded template, error: %s\n", err.Error()),
- ),
- )
-
- return []map[string]interface{}{}, ErrFailedOpeningTemplate
- }
-
- default: // fall-through to local filesystem
- var err error
-
- t, err = os.ReadFile(path)
- if err != nil {
- logging.LogError(
- r.FormatLogMessage(
- "error",
- fmt.Sprintf("Failed opening provided template, error: %s\n", err.Error()),
- ),
- )
-
- return []map[string]interface{}{}, ErrFailedOpeningTemplate
- }
- }
-
- fsm := gotextfsm.TextFSM{}
-
- err := fsm.ParseString(string(t))
- if err != nil {
- logging.LogError(
- r.FormatLogMessage(
- "error",
- fmt.Sprintf("Failed parsing provided template, gotextfsm error: %s\n", err.Error()),
- ),
- )
-
- return []map[string]interface{}{}, ErrFailedParsingTemplate
- }
-
- parser := gotextfsm.ParserOutput{}
-
- err = parser.ParseTextString(r.Result, fsm, true)
- if err != nil {
- logging.LogError(
- r.FormatLogMessage(
- "error",
- fmt.Sprintf(
- "Error while parsing device output, gotextfsm error: %s\n",
- err.Error(),
- ),
- ),
- )
-
- return []map[string]interface{}{}, err
- }
-
- return parser.Dict, nil
-}
-
-// FormatLogMessage formats log message payload, adding contextual info about the host.
-func (r *Response) FormatLogMessage(level, msg string) string {
- return logging.FormatLogMessage(level, r.Host, r.Port, msg)
-}
diff --git a/driver/base/response_test.go b/driver/base/response_test.go
deleted file mode 100644
index ea8b71a..0000000
--- a/driver/base/response_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package base_test
-
-import (
- "os"
- "testing"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/google/go-cmp/cmp"
-)
-
-func TestTextFsmParse(t *testing.T) {
- r := base.NewResponse("localhost", 22, "show version", []string{})
-
- f, err := os.ReadFile(
- "../../test_data/driver/network/expected/cisco_iosxe_show_version_textfsm",
- )
- if err != nil {
- t.Fatalf("failed opening channel output file")
- }
-
- r.Record(f, string(f))
-
- textfsmOutput, parseErr := r.TextFsmParse(
- "../../test_data/driver/base/response/cisco_ios_show_version.textfsm",
- )
- if parseErr != nil {
- t.Fatalf("failed opening textfsm template file")
- }
-
- expected := []map[string]interface{}{
- {
- "CONFIG_REGISTER": string("0x2102"),
- "HARDWARE": []string{"CSR1000V"},
- "HOSTNAME": string("csr1000v"),
- "MAC": []string{},
- "RELOAD_REASON": string("reload"),
- "ROMMON": string("IOS-XE"),
- "RUNNING_IMAGE": string("packages.conf"),
- "SERIAL": []string{"9MVVU09YZFH"},
- "UPTIME": string("1 hour, 31 minutes"),
- "VERSION": string("16.12.3"),
- },
- }
-
- if diff := cmp.Diff(textfsmOutput, expected); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
-}
diff --git a/driver/base/sendcommand.go b/driver/base/sendcommand.go
deleted file mode 100644
index f6a9520..0000000
--- a/driver/base/sendcommand.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package base
-
-import (
- "time"
-)
-
-// FullSendCommand same as `SendCommand` but requiring explicit options.
-func (d *Driver) FullSendCommand(
- c string,
- failedWhenContains []string,
- stripPrompt, eager bool,
- timeoutOps time.Duration,
-) (*Response, error) {
- r := NewResponse(d.Host, d.Transport.BaseTransportArgs.Port, c, failedWhenContains)
-
- rawResult, err := d.Channel.SendInput(c, stripPrompt, eager, timeoutOps)
-
- r.Record(rawResult, string(rawResult))
-
- return r, err
-}
-
-// SendCommand send a command to a device, accepts a string command and variadic of `SendOption`s.
-func (d *Driver) SendCommand(c string, o ...SendOption) (*Response, error) {
- finalOpts := d.ParseSendOptions(o)
-
- return d.FullSendCommand(
- c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
-}
diff --git a/driver/base/sendcommands.go b/driver/base/sendcommands.go
deleted file mode 100644
index 1994891..0000000
--- a/driver/base/sendcommands.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package base
-
-import (
- "time"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-// FullSendCommands same as `SendCommands` but requiring explicit options.
-func (d *Driver) FullSendCommands(
- c []string,
- failedWhenContains []string,
- stripPrompt, stopOnFailed, eager bool,
- timeoutOps time.Duration,
-) (*MultiResponse, error) {
- mr := NewMultiResponse(d.Host)
-
- for _, command := range c[:len(c)-1] {
- r, err := d.FullSendCommand(
- command,
- failedWhenContains,
- stripPrompt,
- eager,
- timeoutOps,
- )
-
- mr.AppendResponse(r)
-
- if err != nil {
- return mr, err
- }
-
- if stopOnFailed && r.Failed != nil {
- logging.LogDebug(
- d.FormatLogMessage(
- "info",
- "encountered failed command, and stop on failed is true, discontinuing send"+
- " commands operation",
- ),
- )
-
- return mr, err
- }
- }
-
- r, err := d.FullSendCommand(
- c[len(c)-1],
- failedWhenContains,
- stripPrompt,
- eager,
- timeoutOps,
- )
- mr.AppendResponse(r)
-
- return mr, err
-}
-
-// SendCommands send commands to a device, accepts a string command and variadic of `SendOption`s.
-func (d *Driver) SendCommands(
- c []string,
- o ...SendOption,
-) (*MultiResponse, error) {
- finalOpts := d.ParseSendOptions(o)
-
- return d.FullSendCommands(
- c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.StopOnFailed,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
-}
-
-// SendCommandsFromFile send commands from a file to a device, accepts a string command and variadic
-// of `SendOption`s.
-func (d *Driver) SendCommandsFromFile(
- f string,
- o ...SendOption,
-) (*MultiResponse, error) {
- finalOpts := d.ParseSendOptions(o)
-
- c, err := util.LoadFileLines(f)
- if err != nil {
- return nil, err
- }
-
- return d.FullSendCommands(
- c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.StopOnFailed,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
-}
diff --git a/driver/base/sendinteractive.go b/driver/base/sendinteractive.go
deleted file mode 100644
index 026b380..0000000
--- a/driver/base/sendinteractive.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package base
-
-import (
- "strings"
- "time"
-
- "github.com/scrapli/scrapligo/channel"
-)
-
-// FullSendInteractive same as `SendInteractive` but requiring explicit options.
-func (d *Driver) FullSendInteractive(
- events []*channel.SendInteractiveEvent,
- interactionCompletePatterns,
- failedWhenContains []string,
- timeoutOps time.Duration,
- joinedEventInputs string,
-) (*Response, error) {
- r := NewResponse(
- d.Host,
- d.Transport.BaseTransportArgs.Port,
- joinedEventInputs,
- failedWhenContains,
- )
-
- rawResult, err := d.Channel.SendInteractive(events, interactionCompletePatterns, timeoutOps)
-
- r.Record(rawResult, string(rawResult))
-
- return r, err
-}
-
-// SendInteractive send interactive commands to a device, accepts a slice of `SendInteractiveEvent`
-// and variadic of `SendOption`s.
-func (d *Driver) SendInteractive(
- events []*channel.SendInteractiveEvent,
- o ...SendOption,
-) (*Response, error) {
- finalOpts := d.ParseSendOptions(o)
- joinedEventInputs := JoinEventInputs(events)
-
- return d.FullSendInteractive(
- events,
- finalOpts.InteractionCompletePatterns,
- finalOpts.FailedWhenContains,
- finalOpts.TimeoutOps,
- joinedEventInputs,
- )
-}
-
-// JoinEventInputs convenience function to join inputs from a `SendInteractive` method.
-func JoinEventInputs(events []*channel.SendInteractiveEvent) string {
- eventInputs := make([]string, len(events))
-
- for _, event := range events {
- eventInputs = append(eventInputs, event.ChannelInput)
- }
-
- joinedEventInputs := strings.Join(eventInputs, ", ")
-
- return joinedEventInputs
-}
diff --git a/driver/core/aristaeos.go b/driver/core/aristaeos.go
deleted file mode 100644
index 6ed04bf..0000000
--- a/driver/core/aristaeos.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package core
-
-import (
- "strings"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewEOSDriver return a driver setup for operation with EOS devices.
-func NewEOSDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^[\w.\-@()/: ]{1,63}>\s?$`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "privilege_exec": {
- Pattern: `(?im)^[\w.\-@()/: ]{1,63}#\s?$`,
- PatternNotContains: []string{"(config"},
- Name: privExecPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "disable",
- Escalate: "enable",
- EscalateAuth: true,
- EscalatePrompt: `(?im)^[pP]assword:\s?$`,
- },
- "configuration": {
- Pattern: `(?im)^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$`,
- PatternNotContains: []string{"(config-s-"},
- Name: configPrivLevel,
- PreviousPriv: privExecPrivLevel,
- Deescalate: "end",
- Escalate: "configure terminal",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- }
- defaultFailedWhenContains := []string{
- "% Ambiguous command",
- "% Error",
- "% Incomplete command",
- "% Invalid input",
- "% Cannot commit",
- "% Unavailable command",
- }
-
- const defaultDefaultDesiredPriv = privExecPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- EOSOnOpen,
- EOSOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- d.Augments["abortConfig"] = EOSAbortConfig
-
- return d, nil
-}
-
-// EOSOnOpen default on open callable for EOS.
-func EOSOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal width 32767", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// EOSOnClose default on close callable for EOS.
-func EOSOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// EOSAbortConfig abort EOS configuration session.
-func EOSAbortConfig(d *network.Driver) (*base.Response, error) {
- if strings.Contains(d.CurrentPriv, "config\\-s") {
- _, err := d.Channel.SendInput("abort", false, false, -1)
- d.CurrentPriv = privExecPrivLevel
-
- return nil, err
- }
-
- return nil, nil
-}
diff --git a/driver/core/ciscoiosxe.go b/driver/core/ciscoiosxe.go
deleted file mode 100644
index 138bba5..0000000
--- a/driver/core/ciscoiosxe.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewIOSXEDriver return a driver setup for operation with IOSXE devices.
-func NewIOSXEDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}>$`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "privilege_exec": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}#$`,
- Name: privExecPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "disable",
- Escalate: "enable",
- EscalateAuth: true,
- EscalatePrompt: `(?im)^(?:enable\s){0,1}password:\s?$`,
- },
- "configuration": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$`,
- PatternNotContains: []string{"tcl)"},
- Name: configPrivLevel,
- PreviousPriv: privExecPrivLevel,
- Deescalate: "end",
- Escalate: "configure terminal",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "tclsh": {
- Pattern: `(?im)^([\w.\-@/+>:]+\(tcl\)[>#]|\+>)$`,
- Name: "tclsh",
- PreviousPriv: privExecPrivLevel,
- Deescalate: "tclquit",
- Escalate: "tclsh",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- }
-
- defaultFailedWhenContains := []string{
- "% Ambiguous command",
- "% Incomplete command",
- "% Invalid input detected",
- "% Unknown command",
- }
-
- const defaultDefaultDesiredPriv = privExecPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- IOSXEOnOpen,
- IOSXEOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- return d, nil
-}
-
-// IOSXEOnOpen default on open callable for IOSXE.
-func IOSXEOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal width 512", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// IOSXEOnClose default on close callable for IOSXE.
-func IOSXEOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/driver/core/ciscoiosxr.go b/driver/core/ciscoiosxr.go
deleted file mode 100644
index 71d2f92..0000000
--- a/driver/core/ciscoiosxr.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewIOSXRDriver return a driver setup for operation with IOSXR devices.
-func NewIOSXRDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "privilege_exec": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}#\s?$`,
- Name: privExecPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$`,
- Name: configPrivLevel,
- PreviousPriv: privExecPrivLevel,
- Deescalate: "end",
- Escalate: "configure terminal",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration_exclusive": {
- Pattern: `(?im)^[\w.\-@/:]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$`,
- Name: "configuration_exclusive",
- PreviousPriv: privExecPrivLevel,
- Deescalate: "end",
- Escalate: "configure exclusive",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- }
-
- defaultFailedWhenContains := []string{
- "% Ambiguous command",
- "% Incomplete command",
- "% Invalid input detected",
- "% Unknown command",
- }
-
- const defaultDefaultDesiredPriv = privExecPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- IOSXROnOpen,
- IOSXROnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- d.Augments["abortConfig"] = IOSXRAbortConfig
-
- return d, nil
-}
-
-// IOSXROnOpen default on open callable for IOSXR.
-func IOSXROnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal width 512", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// IOSXROnClose default on close callable for IOSXR.
-func IOSXROnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// IOSXRAbortConfig abort IOSXR configuration session.
-func IOSXRAbortConfig(d *network.Driver) (*base.Response, error) {
- _, err := d.Channel.SendInput("abort", false, false, -1)
-
- d.CurrentPriv = privExecPrivLevel
-
- return nil, err
-}
diff --git a/driver/core/cisconxos.go b/driver/core/cisconxos.go
deleted file mode 100644
index 0e262a7..0000000
--- a/driver/core/cisconxos.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package core
-
-import (
- "strings"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewNXOSDriver return a driver setup for operation with NXOS devices.
-func NewNXOSDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^[\w.\-]{1,63}>\s?$`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "privilege_exec": {
- Pattern: `(?im)^[\w.\-]{1,63}#\s?$`,
- PatternNotContains: []string{"-tcl"},
- Name: privExecPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "disable",
- Escalate: "enable",
- EscalateAuth: true,
- EscalatePrompt: `(?im)^[pP]assword:\s?$`,
- },
- "configuration": {
- Pattern: `(?im)^[\w.\-]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$`,
- PatternNotContains: []string{"config-tcl", "config-s"},
- Name: configPrivLevel,
- PreviousPriv: privExecPrivLevel,
- Deescalate: "end",
- Escalate: "configure terminal",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "tclsh": {
- Pattern: `(?im)(^[\w.\-@/:]{1,63}\-tcl#\s?$)|` +
- `(^[\w.\-@/:]{1,63}\(config\-tcl\)#\s?$)|(^>\s?$)`,
- Name: "tclsh",
- PreviousPriv: privExecPrivLevel,
- Deescalate: "tclquit",
- Escalate: "tclsh",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- }
-
- defaultFailedWhenContains := []string{
- "% Ambiguous command",
- "% Incomplete command",
- "% Invalid input detected",
- "% Unknown command",
- }
-
- const defaultDefaultDesiredPriv = privExecPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- NXOSOnOpen,
- NXOSOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- d.Augments["abortConfig"] = NXOSAbortConfig
-
- return d, nil
-}
-
-// NXOSOnOpen default on open callable for NXOS.
-func NXOSOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal width 511", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// NXOSOnClose default on close callable for NXOS.
-func NXOSOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// NXOSAbortConfig abort NXOS configuration session.
-func NXOSAbortConfig(d *network.Driver) (*base.Response, error) {
- if strings.Contains(d.CurrentPriv, "config\\-s") {
- _, err := d.Channel.SendInput("abort", false, false, -1)
- d.CurrentPriv = privExecPrivLevel
-
- return nil, err
- }
-
- return nil, nil
-}
diff --git a/driver/core/constants.go b/driver/core/constants.go
deleted file mode 100644
index 8b20791..0000000
--- a/driver/core/constants.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package core
-
-const (
- execPrivLevel = "exec"
- privExecPrivLevel = "privilege_exec"
- configPrivLevel = "configuration"
-)
diff --git a/driver/core/factory.go b/driver/core/factory.go
deleted file mode 100644
index f6dff38..0000000
--- a/driver/core/factory.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package core
-
-import (
- "errors"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// ErrUnknownPlatform raised when user provides an unknown platform... duh.
-var ErrUnknownPlatform = errors.New("unknown platform provided")
-
-// SupportedPlatforms pseudo constant providing slice of all core platform types.
-func SupportedPlatforms() []string {
- return []string{"cisco_iosxe",
- "cisco_iosxr",
- "cisco_nxos",
- "arista_eos",
- "juniper_junos",
- "nokia_sros",
- "nokia_sros_classic",
- "paloalto_panos",
- }
-}
-
-// NewCoreDriver return a new core driver for a given platform.
-var NewCoreDriver = newCoreDriver //nolint:gochecknoglobals
-
-func newCoreDriver(
- host,
- platform string,
- options ...base.Option,
-) (*network.Driver, error) {
- switch platform {
- case "cisco_iosxe":
- return NewIOSXEDriver(host, options...)
- case "cisco_iosxr":
- return NewIOSXRDriver(host, options...)
- case "cisco_nxos":
- return NewNXOSDriver(host, options...)
- case "arista_eos":
- return NewEOSDriver(host, options...)
- case "juniper_junos":
- return NewJUNOSDriver(host, options...)
- case "nokia_sros":
- return NewSROSDriver(host, options...)
- case "nokia_sros_classic":
- return NewSROSClassicDriver(host, options...)
- case "paloalto_panos":
- return NewPanOSDriver(host, options...)
- }
-
- return nil, ErrUnknownPlatform
-}
diff --git a/driver/core/juniperjunos.go b/driver/core/juniperjunos.go
deleted file mode 100644
index 28b33ff..0000000
--- a/driver/core/juniperjunos.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewJUNOSDriver return a driver setup for operation with Junos devices.
-func NewJUNOSDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^({\w+:\d}\n){0,1}[\w\-@()/:]{1,63}>\s?$`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration": {
- Pattern: `(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$`,
- Name: configPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "exit configuration-mode",
- Escalate: "configure",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration_exclusive": {
- Pattern: `(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$`,
- Name: "configuration_exclusive",
- PreviousPriv: execPrivLevel,
- Deescalate: "exit configuration-mode",
- Escalate: "configure exclusive",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration_private": {
- Pattern: `(?im)^({\w+:\d}\[edit\]\n){0,1}[\w\-@()/:]{1,63}#\s?$`,
- Name: "configuration_private",
- PreviousPriv: execPrivLevel,
- Deescalate: "exit configuration-mode",
- Escalate: "configure exclusive",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "shell": {
- Pattern: `(?im)^.*[%$]\s?$`,
- PatternNotContains: []string{"root"},
- Name: "shell",
- PreviousPriv: execPrivLevel,
- Deescalate: "exit",
- Escalate: "start shell",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "root_shell": {
- Pattern: `(?im)^.*root@[[:ascii:]]*?:?[[:ascii:]]*?[%#]\s?$`,
- Name: "root_shell",
- PreviousPriv: execPrivLevel,
- Deescalate: "exit",
- Escalate: "start shell user root",
- EscalateAuth: true,
- EscalatePrompt: `(?im)^[pP]assword:\s?$`,
- },
- }
-
- defaultFailedWhenContains := []string{
- "is ambiguous",
- "No valid completions",
- "unknown command",
- "syntax error",
- }
-
- const defaultDefaultDesiredPriv = execPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- JUNOSOnOpen,
- JUNOSOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- d.Augments["abortConfig"] = JUNOSAbortConfig
-
- return d, nil
-}
-
-// JUNOSOnOpen default on open callable for Junos.
-func JUNOSOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("set cli screen-length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("set cli screen-width 511", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("set cli complete-on-space off", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// JUNOSOnClose default on close callable for Junos.
-func JUNOSOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// JUNOSAbortConfig abort Junos configuration session.
-func JUNOSAbortConfig(d *network.Driver) (*base.Response, error) {
- _, _ = d.Channel.SendInput("rollback 0", false, false, -1)
- _, err := d.Channel.SendInput("exit", false, false, -1)
- d.CurrentPriv = privExecPrivLevel
-
- return nil, err
-}
diff --git a/driver/core/nokiasros.go b/driver/core/nokiasros.go
deleted file mode 100644
index c2fe246..0000000
--- a/driver/core/nokiasros.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewSROSDriver returns a driver setup for operation with Nokia SR OS devices.
-func NewSROSDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^\[.*\]\n[abcd]:\S+@\S+#\s?$`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: ``,
- },
- // configuration privilege level maps to the exclusive config mode on SR OS
- "configuration": {
- Pattern: `(?im)^\*?\(ex\)\[/?\]\n[abcd]:\S+@\S+#\s?$`,
- Name: configPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "quit-config",
- Escalate: "edit-config exclusive",
- EscalateAuth: false,
- EscalatePrompt: ``,
- },
- "configuration-with-path": {
- Pattern: `(?im)^\*?\(ex\)\[\S{2,}.+\]\n[abcd]:\S+@\S+#\s?$`,
- Name: "configuration-with-path",
- PreviousPriv: configPrivLevel,
- Deescalate: "exit all",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: ``,
- },
- }
-
- defaultFailedWhenContains := []string{
- "CRITICAL:",
- "MAJOR:",
- "MINOR:",
- }
-
- const defaultDefaultDesiredPriv = execPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- SROSOnOpen,
- SROSOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- d.Augments["abortConfig"] = SROSAbortConfig
-
- return d, nil
-}
-
-// SROSOnOpen is a default on open callable for SR OS.
-func SROSOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- if _, err = d.SendCommand("environment console width 512", nil); err != nil {
- return err
- }
-
- if _, err = d.SendCommand("environment more false", nil); err != nil {
- return err
- }
-
- _, err = d.SendCommand("environment command-completion space false", nil)
-
- return err
-}
-
-// SROSOnClose is a default on close callable for SR OS.
-func SROSOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("logout"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// SROSAbortConfig aborts SR OS configuration session.
-func SROSAbortConfig(d *network.Driver) (*base.Response, error) {
- if _, err := d.Channel.SendInput("discard /", false, false, -1); err != nil {
- return nil, err
- }
-
- if _, err := d.Channel.SendInput("exit", false, false, -1); err != nil {
- return nil, err
- }
-
- _, err := d.Channel.SendInput("quit-config", false, false, -1)
-
- d.CurrentPriv = privExecPrivLevel
-
- return nil, err
-}
diff --git a/driver/core/nokiasros_classic.go b/driver/core/nokiasros_classic.go
deleted file mode 100644
index 70a2d5b..0000000
--- a/driver/core/nokiasros_classic.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewSROSClassicDriver returns a driver setup for operation
-// with Nokia SR OS devices running in classic configuration mode.
-func NewSROSClassicDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "configuration": {
- Pattern: `(?im)^\*?[abcd]:\S+#\s*$`,
- Name: configPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: ``,
- },
- }
-
- defaultFailedWhenContains := []string{
- "CRITICAL:",
- "MAJOR:",
- "MINOR:",
- "Error:",
- }
-
- const defaultDefaultDesiredPriv = configPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- SROSClassicOnOpen,
- SROSClassicOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- return d, nil
-}
-
-// SROSClassicOnOpen is a default on open callable for SR OS classic.
-func SROSClassicOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("environment no more", nil)
-
- return err
-}
-
-// SROSClassicOnClose is a default on close callable for SR OS classic.
-func SROSClassicOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("logout"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/driver/core/paloaltopanos.go b/driver/core/paloaltopanos.go
deleted file mode 100644
index 3c84cc1..0000000
--- a/driver/core/paloaltopanos.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package core
-
-import (
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-// NewPanOSDriver return a driver setup for operation with Palo Alto PanOS devices.
-func NewPanOSDriver(
- host string,
- options ...base.Option,
-) (*network.Driver, error) {
- defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
- "exec": {
- Pattern: `(?im)^[\w\._-]+@[\w\.\(\)_-]+>\s?`,
- Name: execPrivLevel,
- PreviousPriv: "",
- Deescalate: "",
- Escalate: "",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- "configuration": {
- Pattern: `(?im)^[\w\._-]+@[\w\.\(\)_-]+#\s?$`,
- Name: configPrivLevel,
- PreviousPriv: execPrivLevel,
- Deescalate: "exit",
- Escalate: "configure",
- EscalateAuth: false,
- EscalatePrompt: "",
- },
- }
- defaultFailedWhenContains := []string{
- "Unknown command:",
- "Invalid Syntax.",
- "Validation Error:",
- }
-
- const defaultDefaultDesiredPriv = execPrivLevel
-
- d, err := network.NewNetworkDriver(
- host,
- defaultPrivilegeLevels,
- defaultDefaultDesiredPriv,
- defaultFailedWhenContains,
- PanOSOnOpen,
- PanOSOnClose,
- options...)
-
- if err != nil {
- return nil, err
- }
-
- return d, nil
-}
-
-// PanOSOnOpen default on open callable for PanOS.
-func PanOSOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("set cli scripting-mode on", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("set cli pager off", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// PanOSOnClose default on close callable for PanOS.
-func PanOSOnClose(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- err = d.Channel.Write([]byte("exit"), false)
- if err != nil {
- return err
- }
-
- err = d.Channel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/driver/generic/driver.go b/driver/generic/driver.go
new file mode 100644
index 0000000..8d8e189
--- /dev/null
+++ b/driver/generic/driver.go
@@ -0,0 +1,135 @@
+package generic
+
+import (
+ "errors"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// NewDriver returns an instance of Driver for the provided host with the given options set. Any
+// options in the driver/options package may be passed to this function -- those options may be
+// applied at the network.Driver, generic.Driver, channel.Channel, or Transport depending on the
+// specific option.
+func NewDriver(host string, opts ...util.Option) (*Driver, error) {
+ d := &Driver{
+ Logger: nil,
+ TransportType: transport.DefaultTransport,
+ Transport: nil,
+ Channel: nil,
+ FailedWhenContains: make([]string, 0),
+ OnOpen: nil,
+ OnClose: nil,
+ }
+
+ var err error
+
+ for _, option := range opts {
+ err = option(d)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ if d.Logger == nil {
+ // set a default logging instance w/ no assigned loggers (a noop basically)
+ var l *logging.Instance
+
+ l, err = logging.NewInstance()
+ if err != nil {
+ return nil, err
+ }
+
+ d.Logger = l
+ }
+
+ d.Transport, err = transport.NewTransport(d.Logger, host, d.TransportType, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ d.Channel, err = channel.NewChannel(d.Logger, d.Transport, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return d, nil
+}
+
+// Driver is the primary user interface for scrapligo. All connections to devices revolve around
+// this object. The Driver has attributes for a Channel (channel.Channel) and Transport -- these
+// objects are responsible for sending and receiving data from the device over the transport layer,
+// and the Driver itself provides friendly methods such as SendCommand used to interact with the
+// target device.
+type Driver struct {
+ Logger *logging.Instance
+
+ TransportType string
+ Transport *transport.Transport
+
+ Channel *channel.Channel
+
+ FailedWhenContains []string
+
+ OnOpen func(d *Driver) error
+ OnClose func(d *Driver) error
+}
+
+// Open opens the underlying channel.Channel and Transport objects. This should be called prior to
+// executing any other Driver methods.
+func (d *Driver) Open() error {
+ d.Logger.Debugf(
+ "opening connection to host '%s' on port '%d'",
+ d.Transport.Args.Host,
+ d.Transport.Args.Port,
+ )
+
+ err := d.Channel.Open()
+ if err != nil {
+ return err
+ }
+
+ if d.OnOpen != nil {
+ d.Logger.Debug("generic driver OnOpen set, executing")
+
+ err = d.OnOpen(d)
+ if err != nil {
+ d.Logger.Criticalf("error executing generic driver OnOpen, error: %s", err)
+
+ return err
+ }
+ }
+
+ d.Logger.Info("connection opened successfully")
+
+ return nil
+}
+
+// Close closes the underlying channel.Channel and Transport objects.
+func (d *Driver) Close() error {
+ d.Logger.Debugf(
+ "closing connection to host '%s' on port '%d'",
+ d.Transport.Args.Host,
+ d.Transport.Args.Port,
+ )
+
+ if d.OnClose != nil {
+ err := d.OnClose(d)
+ if err != nil {
+ d.Logger.Criticalf("error executing generic driver OnClose, error: %s", err)
+ }
+ }
+
+ err := d.Channel.Close()
+ if err != nil {
+ return err
+ }
+
+ d.Logger.Info("connection closed successfully")
+
+ return nil
+}
diff --git a/driver/generic/driver_test.go b/driver/generic/driver_test.go
new file mode 100644
index 0000000..7edfda3
--- /dev/null
+++ b/driver/generic/driver_test.go
@@ -0,0 +1,101 @@
+package generic_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/scrapli/scrapligo/transport"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func resolveFile(t *testing.T, f string) string {
+ f, err := filepath.Abs(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return f
+}
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualIn []byte, actualOut string) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+ goldenIn := filepath.Join("test-fixtures", "golden", testName+"-in.txt")
+
+ err := os.WriteFile(goldenOut, []byte(actualOut), 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.WriteFile(goldenIn, actualIn, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func prepareDriver(
+ t *testing.T,
+ testName,
+ payloadFile string,
+) (*generic.Driver, *transport.File) {
+ d, err := generic.NewDriver(
+ "dummy",
+ options.WithTransportType(transport.FileTransport),
+ options.WithFileTransportFile(resolveFile(t, payloadFile)),
+ options.WithTransportReadSize(1),
+ options.WithReadDelay(0),
+ )
+ if err != nil {
+ t.Errorf("%s: encountered error creating generic Driver, error: %s", testName, err)
+ }
+
+ err = d.Channel.Open()
+ if err != nil {
+ t.Errorf("%s: encountered error opening Channel, error: %s", testName, err)
+ }
+
+ fileTransportObj, ok := d.Transport.Impl.(*transport.File)
+ if !ok {
+ t.Fatal("transport implementation is not Transport File")
+ }
+
+ return d, fileTransportObj
+}
diff --git a/driver/generic/getprompt.go b/driver/generic/getprompt.go
new file mode 100644
index 0000000..483cd20
--- /dev/null
+++ b/driver/generic/getprompt.go
@@ -0,0 +1,11 @@
+package generic
+
+// GetPrompt returns a string containing the current "prompt" of the connected ssh/telnet server.
+func (d *Driver) GetPrompt() (string, error) {
+ b, err := d.Channel.GetPrompt()
+ if err != nil {
+ return "", err
+ }
+
+ return string(b), nil
+}
diff --git a/driver/generic/getprompt_test.go b/driver/generic/getprompt_test.go
new file mode 100644
index 0000000..fd83312
--- /dev/null
+++ b/driver/generic/getprompt_test.go
@@ -0,0 +1,70 @@
+package generic_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testGetPrompt(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ actualOut, err := d.GetPrompt()
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestGetPrompt(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "get-prompt-simple": {
+ Description: "simple get prompt test",
+ PayloadFile: "get-prompt-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testGetPrompt(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/generic/operation.go b/driver/generic/operation.go
new file mode 100644
index 0000000..7818d37
--- /dev/null
+++ b/driver/generic/operation.go
@@ -0,0 +1,37 @@
+package generic
+
+import (
+ "errors"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ defaultStopOnFailed = false
+)
+
+// OperationOptions is a struct containing "operation" options for the generic driver.
+type OperationOptions struct {
+ FailedWhenContains []string
+ StopOnFailed bool
+}
+
+// NewOperation returns a new OperationOptions object with the defaults set and any provided options
+// applied.
+func NewOperation(options ...util.Option) (*OperationOptions, error) {
+ o := &OperationOptions{
+ FailedWhenContains: []string{},
+ StopOnFailed: defaultStopOnFailed,
+ }
+
+ for _, option := range options {
+ err := option(o)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return o, nil
+}
diff --git a/driver/generic/sendcommand.go b/driver/generic/sendcommand.go
new file mode 100644
index 0000000..6339649
--- /dev/null
+++ b/driver/generic/sendcommand.go
@@ -0,0 +1,40 @@
+package generic
+
+import (
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func (d *Driver) sendCommand(
+ command string,
+ driverOpts *OperationOptions,
+ opts ...util.Option,
+) (*response.Response, error) {
+ d.Logger.Infof("SendCommand requested, sending '%s'", command)
+
+ r := response.NewResponse(
+ command,
+ d.Transport.GetHost(),
+ d.Transport.GetPort(),
+ driverOpts.FailedWhenContains,
+ )
+
+ b, err := d.Channel.SendInput(command, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ r.Record(b)
+
+ return r, nil
+}
+
+// SendCommand sends the input string to the device and returns a response.Response object.
+func (d *Driver) SendCommand(command string, opts ...util.Option) (*response.Response, error) {
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendCommand(command, op, opts...)
+}
diff --git a/driver/generic/sendcommand_test.go b/driver/generic/sendcommand_test.go
new file mode 100644
index 0000000..0ff93e4
--- /dev/null
+++ b/driver/generic/sendcommand_test.go
@@ -0,0 +1,85 @@
+package generic_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type sendCommandTestCase struct {
+ description string
+ command string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+}
+
+func testSendCommand(testName string, testCase *sendCommandTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendCommand(testCase.command)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendCommand(t *testing.T) {
+ cases := map[string]*sendCommandTestCase{
+ "send-command-simple": {
+ description: "simple send command test",
+ command: "show run int vlan1",
+ payloadFile: "send-command-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendCommand(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/generic/sendcommands.go b/driver/generic/sendcommands.go
new file mode 100644
index 0000000..fff51b1
--- /dev/null
+++ b/driver/generic/sendcommands.go
@@ -0,0 +1,74 @@
+package generic
+
+import (
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// SendCommands sends the input strings to the device and returns a response.MultiResponse object.
+func (d *Driver) SendCommands(
+ commands []string,
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
+ d.Logger.Infof("SendCommands requested, sending '%s'", commands)
+
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ m := response.NewMultiResponse(d.Transport.GetHost())
+
+ for _, input := range commands[:len(commands)-1] {
+ var r *response.Response
+
+ r, err = d.sendCommand(
+ input,
+ op,
+ opts...,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ m.AppendResponse(r)
+
+ if op.StopOnFailed && r.Failed != nil {
+ d.Logger.Info(
+ "encountered failed command, and stop on failed is true, discontinuing send commands operation",
+ )
+
+ return m, err
+ }
+ }
+
+ r, err := d.sendCommand(
+ commands[len(commands)-1],
+ op,
+ opts...,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ m.AppendResponse(r)
+
+ return m, nil
+}
+
+// SendCommandsFromFile sends each line in the provided file f as a command to the device and
+// returns a response.MultiResponse object.
+func (d *Driver) SendCommandsFromFile(
+ f string,
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
+ d.Logger.Infof("SendCommandsFromFile requested, loading commands from file '%s'", f)
+
+ inputs, err := util.LoadFileLines(f)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.SendCommands(inputs, opts...)
+}
diff --git a/driver/generic/sendcommands_test.go b/driver/generic/sendcommands_test.go
new file mode 100644
index 0000000..114514d
--- /dev/null
+++ b/driver/generic/sendcommands_test.go
@@ -0,0 +1,164 @@
+package generic_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type sendCommandsTestCase struct {
+ description string
+ commands []string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+}
+
+func testSendCommands(testName string, testCase *sendCommandsTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendCommands(testCase.commands)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendCommands(t *testing.T) {
+ cases := map[string]*sendCommandsTestCase{
+ "send-commands-simple": {
+ description: "simple send commands test",
+ commands: []string{"show run int vlan1", "show run int vlan1"},
+ payloadFile: "send-commands-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendCommands(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+type sendCommandsFromFileTestCase struct {
+ description string
+ f string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+}
+
+func testSendCommandsFromFile(
+ testName string,
+ testCase *sendCommandsFromFileTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendCommandsFromFile(resolveFile(t, testCase.f))
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendCommandsFromFile(t *testing.T) {
+ cases := map[string]*sendCommandsFromFileTestCase{
+ "send-commands-from-file-simple": {
+ description: "simple send commands test",
+ f: "send-commands-from-file-simple-inputs.txt",
+ payloadFile: "send-commands-from-file-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendCommandsFromFile(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/generic/sendinteractive.go b/driver/generic/sendinteractive.go
new file mode 100644
index 0000000..8612d49
--- /dev/null
+++ b/driver/generic/sendinteractive.go
@@ -0,0 +1,50 @@
+package generic
+
+import (
+ "strings"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func joinInputEvents(events []*channel.SendInteractiveEvent) string {
+ inputs := make([]string, len(events))
+
+ for i, event := range events {
+ inputs[i] = event.ChannelInput
+ }
+
+ return strings.Join(inputs, ", ")
+}
+
+// SendInteractive sends a slice of channel.SendInteractiveEvent to the device. This method wraps
+// the channel level method and returns a response.Response instead of simply bytes.
+func (d *Driver) SendInteractive(
+ events []*channel.SendInteractiveEvent,
+ opts ...util.Option,
+) (*response.Response, error) {
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ r := response.NewResponse(
+ joinInputEvents(events),
+ d.Transport.GetHost(),
+ d.Transport.GetPort(),
+ op.FailedWhenContains,
+ )
+
+ b, err := d.Channel.SendInteractive(
+ events,
+ opts...,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ r.Record(b)
+
+ return r, nil
+}
diff --git a/driver/generic/sendinteractive_test.go b/driver/generic/sendinteractive_test.go
new file mode 100644
index 0000000..d0330d1
--- /dev/null
+++ b/driver/generic/sendinteractive_test.go
@@ -0,0 +1,97 @@
+package generic_test
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/scrapli/scrapligo/channel"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type sendInteractiveTestCase struct {
+ description string
+ events []*channel.SendInteractiveEvent
+ completePatterns []*regexp.Regexp
+ payloadFile string
+}
+
+func testSendInteractive(testName string, testCase *sendInteractiveTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendInteractive(testCase.events)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendInteractive(t *testing.T) {
+ cases := map[string]*sendInteractiveTestCase{
+ "send-interactive-simple": {
+ description: "simple send interactive test",
+ events: []*channel.SendInteractiveEvent{
+ {
+ ChannelInput: "clear logging",
+ ChannelResponse: "[confirm]",
+ HideInput: false,
+ },
+ {
+ ChannelInput: "",
+ ChannelResponse: "",
+ HideInput: false,
+ },
+ },
+ completePatterns: nil,
+ payloadFile: "send-interactive-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendInteractive(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/generic/sendwithcallbacks.go b/driver/generic/sendwithcallbacks.go
new file mode 100644
index 0000000..572e965
--- /dev/null
+++ b/driver/generic/sendwithcallbacks.go
@@ -0,0 +1,241 @@
+package generic
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/response"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// NewCallback returns a Callback object with provided options applied.
+func NewCallback(
+ callback func(*Driver, string) error,
+ opts ...util.Option,
+) (*Callback, error) {
+ c := &Callback{
+ Callback: callback,
+ Contains: "",
+ containsBytes: nil,
+ ContainsRe: nil,
+ Insensitive: true,
+ ResetOutput: true,
+ Once: false,
+ NextTimeout: 0,
+ triggered: false,
+ Complete: false,
+ Name: "",
+ }
+
+ for _, option := range opts {
+ err := option(c)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if c.Contains == "" && c.ContainsRe == nil {
+ return nil, fmt.Errorf("%w: must provide contains or contains regex", util.ErrBadOption)
+ }
+
+ return c, nil
+}
+
+// Callback represents not only a callback function, but what triggers that callback function. This
+// object is only used in conjunction with the SendWithCallbacks method.
+type Callback struct {
+ Callback func(*Driver, string) error
+ Contains string
+ containsBytes []byte
+ NotContains string
+ notContainsBytes []byte
+ ContainsRe *regexp.Regexp
+ Insensitive bool
+ // ResetOutput bool indicating if the output should be reset or not after callback execution.
+ ResetOutput bool
+ // Once bool indicating if this callback should be executed only one time.
+ Once bool
+ // NextTimout timeout value to use for the subsequent read loop - ignored if Complete is true.
+ NextTimeout time.Duration
+ triggered bool
+ Complete bool
+ Name string
+}
+
+func (c *Callback) contains() []byte {
+ if len(c.containsBytes) == 0 {
+ c.containsBytes = []byte(c.Contains)
+
+ if c.Insensitive {
+ c.containsBytes = bytes.ToLower(c.containsBytes)
+ }
+ }
+
+ return c.containsBytes
+}
+
+func (c *Callback) notContains() []byte {
+ if len(c.notContainsBytes) == 0 {
+ c.notContainsBytes = []byte(c.NotContains)
+
+ if c.Insensitive {
+ c.notContainsBytes = bytes.ToLower(c.notContainsBytes)
+ }
+ }
+
+ return c.notContainsBytes
+}
+
+func (c *Callback) check(b []byte) bool {
+ if c.Insensitive {
+ b = bytes.ToLower(b)
+ }
+
+ if (c.Contains != "" && bytes.Contains(b, c.contains())) &&
+ !(c.NotContains != "" && !bytes.Contains(b, c.notContains())) {
+ return true
+ }
+
+ if (c.ContainsRe != nil && c.ContainsRe.Match(b)) &&
+ !(c.NotContains != "" && !bytes.Contains(b, c.notContains())) {
+ return true
+ }
+
+ return false
+}
+
+type callbackResult struct {
+ i int
+ callbacks []*Callback
+ b []byte
+ fb []byte
+ err error
+}
+
+func (d *Driver) executeCallback(
+ i int,
+ callbacks []*Callback,
+ b, fb []byte,
+ t time.Duration,
+) ([]byte, error) {
+ cb := callbacks[i]
+
+ if cb.Once {
+ if cb.triggered {
+ return nil, fmt.Errorf(
+ "%w: callback once set, and callback already triggered",
+ util.ErrOperationError,
+ )
+ }
+
+ cb.triggered = true
+ }
+
+ if cb.Callback != nil {
+ // you might not want to set a callback on the "done" stage, so we skip executing if
+ // callback is nil
+ err := cb.Callback(d, string(b))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if cb.Complete {
+ return fb, nil
+ }
+
+ if cb.ResetOutput {
+ b = nil
+ }
+
+ nt := t
+ if cb.NextTimeout != 0 {
+ nt = cb.NextTimeout
+ }
+
+ return d.handleCallbacks(callbacks, b, fb, nt)
+}
+
+func (d *Driver) handleCallbacks(
+ callbacks []*Callback,
+ b, fb []byte,
+ timeout time.Duration,
+) ([]byte, error) {
+ c := make(chan *callbackResult)
+
+ go func() {
+ defer close(c)
+
+ for {
+ rb, err := d.Channel.Read()
+ if err != nil {
+ c <- &callbackResult{
+ err: err,
+ }
+
+ return
+ }
+
+ b = append(b, rb...)
+ fb = append(fb, rb...)
+
+ for i, cb := range callbacks {
+ if cb.check(b) {
+ c <- &callbackResult{
+ i: i,
+ callbacks: callbacks,
+ b: b,
+ fb: fb,
+ err: nil,
+ }
+
+ return
+ }
+ }
+ }
+ }()
+
+ timer := time.NewTimer(timeout)
+
+ select {
+ case r := <-c:
+ if r.err != nil {
+ return nil, r.err
+ }
+
+ return d.executeCallback(r.i, r.callbacks, r.b, r.fb, timeout)
+ case <-timer.C:
+ return nil, fmt.Errorf("%w: timeout handling callbacks", util.ErrTimeoutError)
+ }
+}
+
+// SendWithCallbacks sends some input and responds to the output of that input based on the list of
+// callbacks provided. This method can be looked at as a more advanced SendInteractive.
+func (d *Driver) SendWithCallbacks(
+ input string,
+ callbacks []*Callback,
+ timeout time.Duration,
+) (*response.Response, error) {
+ d.Logger.Info("SendWithCallbacks requested")
+
+ r := response.NewResponse(input, d.Transport.GetHost(), d.Transport.GetPort(), nil)
+
+ if input != "" {
+ err := d.Channel.WriteAndReturn([]byte(input), false)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ b, err := d.handleCallbacks(callbacks, nil, nil, timeout)
+ if err != nil {
+ return nil, err
+ }
+
+ r.Record(b)
+
+ return r, nil
+}
diff --git a/driver/generic/sendwithcallbacks_test.go b/driver/generic/sendwithcallbacks_test.go
new file mode 100644
index 0000000..80702d8
--- /dev/null
+++ b/driver/generic/sendwithcallbacks_test.go
@@ -0,0 +1,100 @@
+package generic_test
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type sendWithCallbacksTestCase struct {
+ description string
+ payloadFile string
+ initialInput string
+ callbacks []*generic.Callback
+}
+
+func testSendWithCallbacks(
+ testName string,
+ testCase *sendWithCallbacksTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendWithCallbacks(testCase.initialInput, testCase.callbacks, 1*time.Second)
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running generic Driver SendWithCallbacks, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf(
+ "%s: response object indicates failure, this shouldn't happenf or send with callbacks",
+ testName,
+ )
+ }
+
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, "")
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+ }
+}
+
+func TestSendWithCallbacks(t *testing.T) {
+ cases := map[string]*sendWithCallbacksTestCase{
+ "send-with-callbacks-simple": {
+ description: "simple send with callbacks test",
+ payloadFile: "send-with-callbacks-simple.txt",
+ initialInput: "",
+ callbacks: []*generic.Callback{
+ {
+ Callback: func(d *generic.Driver, s string) error {
+ _, err := d.Channel.SendInput("configure terminal")
+
+ return err
+ },
+ Contains: "C3560CX#",
+ Name: "callback-one",
+ ResetOutput: true,
+ },
+ {
+ Callback: func(d *generic.Driver, s string) error {
+ return d.Channel.WriteAndReturn([]byte("show version"), false)
+ },
+ ContainsRe: regexp.MustCompile(`(?im)^c3560cx\(config\)#`),
+ Name: "callback-two",
+ Complete: true,
+ },
+ },
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendWithCallbacks(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/generic/test-fixtures/get-prompt-simple.txt b/driver/generic/test-fixtures/get-prompt-simple.txt
new file mode 100644
index 0000000..b456d2f
--- /dev/null
+++ b/driver/generic/test-fixtures/get-prompt-simple.txt
@@ -0,0 +1 @@
+C3560#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/get-prompt-simple-in.txt b/driver/generic/test-fixtures/golden/get-prompt-simple-in.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/get-prompt-simple-in.txt
@@ -0,0 +1 @@
+
diff --git a/driver/generic/test-fixtures/golden/get-prompt-simple-out.txt b/driver/generic/test-fixtures/golden/get-prompt-simple-out.txt
new file mode 100644
index 0000000..b456d2f
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/get-prompt-simple-out.txt
@@ -0,0 +1 @@
+C3560#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/send-command-simple-in.txt b/driver/generic/test-fixtures/golden/send-command-simple-in.txt
new file mode 100644
index 0000000..8dbe38b
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-command-simple-in.txt
@@ -0,0 +1,2 @@
+show run int vlan1
+
diff --git a/driver/generic/test-fixtures/golden/send-command-simple-out.txt b/driver/generic/test-fixtures/golden/send-command-simple-out.txt
new file mode 100644
index 0000000..ad32e91
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-command-simple-out.txt
@@ -0,0 +1,7 @@
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/send-commands-from-file-simple-in.txt b/driver/generic/test-fixtures/golden/send-commands-from-file-simple-in.txt
new file mode 100644
index 0000000..d11da26
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-commands-from-file-simple-in.txt
@@ -0,0 +1,5 @@
+show run int vlan1
+
+
+show run int vlan1
+
diff --git a/driver/generic/test-fixtures/golden/send-commands-from-file-simple-out.txt b/driver/generic/test-fixtures/golden/send-commands-from-file-simple-out.txt
new file mode 100644
index 0000000..11d1a23
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-commands-from-file-simple-out.txt
@@ -0,0 +1,16 @@
+
+
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/send-commands-simple-in.txt b/driver/generic/test-fixtures/golden/send-commands-simple-in.txt
new file mode 100644
index 0000000..d11da26
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-commands-simple-in.txt
@@ -0,0 +1,5 @@
+show run int vlan1
+
+
+show run int vlan1
+
diff --git a/driver/generic/test-fixtures/golden/send-commands-simple-out.txt b/driver/generic/test-fixtures/golden/send-commands-simple-out.txt
new file mode 100644
index 0000000..11d1a23
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-commands-simple-out.txt
@@ -0,0 +1,16 @@
+
+
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/send-interactive-simple-in.txt b/driver/generic/test-fixtures/golden/send-interactive-simple-in.txt
new file mode 100644
index 0000000..53c0210
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-interactive-simple-in.txt
@@ -0,0 +1,5 @@
+clear logging
+
+
+
+
diff --git a/driver/generic/test-fixtures/golden/send-interactive-simple-out.txt b/driver/generic/test-fixtures/golden/send-interactive-simple-out.txt
new file mode 100644
index 0000000..62de6d5
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-interactive-simple-out.txt
@@ -0,0 +1,3 @@
+C3560CX#clear logging
+Clear logging buffer [confirm]
+C3560CX#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/golden/send-with-callbacks-simple-in.txt b/driver/generic/test-fixtures/golden/send-with-callbacks-simple-in.txt
new file mode 100644
index 0000000..2de0a72
--- /dev/null
+++ b/driver/generic/test-fixtures/golden/send-with-callbacks-simple-in.txt
@@ -0,0 +1,5 @@
+configure terminal
+
+
+show version
+
diff --git a/driver/generic/test-fixtures/golden/send-with-callbacks-simple-out.txt b/driver/generic/test-fixtures/golden/send-with-callbacks-simple-out.txt
new file mode 100644
index 0000000..e69de29
diff --git a/driver/generic/test-fixtures/send-command-simple.txt b/driver/generic/test-fixtures/send-command-simple.txt
new file mode 100644
index 0000000..68f2f76
--- /dev/null
+++ b/driver/generic/test-fixtures/send-command-simple.txt
@@ -0,0 +1,10 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/send-commands-from-file-simple-inputs.txt b/driver/generic/test-fixtures/send-commands-from-file-simple-inputs.txt
new file mode 100644
index 0000000..9ea914c
--- /dev/null
+++ b/driver/generic/test-fixtures/send-commands-from-file-simple-inputs.txt
@@ -0,0 +1,2 @@
+show run int vlan1
+show run int vlan1
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/send-commands-from-file-simple.txt b/driver/generic/test-fixtures/send-commands-from-file-simple.txt
new file mode 100644
index 0000000..badaaad
--- /dev/null
+++ b/driver/generic/test-fixtures/send-commands-from-file-simple.txt
@@ -0,0 +1,19 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/send-commands-simple.txt b/driver/generic/test-fixtures/send-commands-simple.txt
new file mode 100644
index 0000000..badaaad
--- /dev/null
+++ b/driver/generic/test-fixtures/send-commands-simple.txt
@@ -0,0 +1,19 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/send-interactive-simple.txt b/driver/generic/test-fixtures/send-interactive-simple.txt
new file mode 100644
index 0000000..62de6d5
--- /dev/null
+++ b/driver/generic/test-fixtures/send-interactive-simple.txt
@@ -0,0 +1,3 @@
+C3560CX#clear logging
+Clear logging buffer [confirm]
+C3560CX#
\ No newline at end of file
diff --git a/driver/generic/test-fixtures/send-with-callbacks-simple.txt b/driver/generic/test-fixtures/send-with-callbacks-simple.txt
new file mode 100644
index 0000000..6dce75d
--- /dev/null
+++ b/driver/generic/test-fixtures/send-with-callbacks-simple.txt
@@ -0,0 +1,7 @@
+C3560CX#configure terminal
+Enter configuration commands, one per line. End with CNTL/Z.
+C3560CX(config)#show version
+ ^
+% Invalid input detected at '^' marker.
+
+C3560CX(config)#
\ No newline at end of file
diff --git a/driver/netconf/capabilities.go b/driver/netconf/capabilities.go
new file mode 100644
index 0000000..805783e
--- /dev/null
+++ b/driver/netconf/capabilities.go
@@ -0,0 +1,102 @@
+package netconf
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// ServerHasCapability returns true if the server supports capability s, otherwise false.
+func (d *Driver) ServerHasCapability(s string) bool {
+ for _, serverCapability := range d.serverCapabilities {
+ if serverCapability == s {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (d *Driver) processServerCapabilities() error {
+ b, err := d.Channel.ReadUntilPrompt()
+ if err != nil {
+ return err
+ }
+
+ ncPatterns := getNetconfPatterns()
+
+ serverHelloMatch := ncPatterns.hello.Match(b)
+
+ if !serverHelloMatch {
+ return fmt.Errorf("%w: did not find server hello", util.ErrNetconfError)
+ }
+
+ // rather than deal w/ xml like scrapli python does, just regex the caps out
+ serverCapabilitiesMatches := ncPatterns.capability.FindAllSubmatch(b, -1)
+
+ d.serverCapabilities = make([]string, 1)
+ for _, match := range serverCapabilitiesMatches {
+ d.serverCapabilities = append(d.serverCapabilities, string(match[1]))
+ }
+
+ return nil
+}
+
+func (d *Driver) determineVersion() error {
+ if d.ServerHasCapability(v1Dot1Cap) {
+ d.SelectedVersion = V1Dot1
+ } else if d.ServerHasCapability(v1Dot0Cap) {
+ d.SelectedVersion = V1Dot0
+ } else {
+ return fmt.Errorf("%w: capabilities exchange failed", util.ErrNetconfError)
+ }
+
+ switch d.PreferredVersion {
+ case V1Dot0:
+ if !d.ServerHasCapability(v1Dot0Cap) {
+ d.SelectedVersion = V1Dot0
+ } else {
+ return fmt.Errorf(
+ "%w: user requested netconf version 1.0, but server does not support this capability",
+ util.ErrNetconfError,
+ )
+ }
+ case V1Dot1:
+ if !d.ServerHasCapability(v1Dot1Cap) {
+ d.SelectedVersion = V1Dot1
+ } else {
+ return fmt.Errorf(
+ "%w: user requested netconf version 1.1, but server does not support this capability",
+ util.ErrNetconfError)
+ }
+ }
+
+ ncPatterns := getNetconfPatterns()
+
+ switch d.SelectedVersion {
+ case V1Dot0:
+ d.Channel.PromptPattern = ncPatterns.v1Dot0Delim
+ case V1Dot1:
+ d.Channel.PromptPattern = ncPatterns.v1Dot1Delim
+ }
+
+ return nil
+}
+
+func (d *Driver) sendClientCapabilities() error {
+ var caps []byte
+
+ switch d.SelectedVersion {
+ case V1Dot0:
+ caps = []byte(v1Dot0Caps)
+ case V1Dot1:
+ caps = []byte(v1Dot1Caps)
+ }
+
+ err := d.Channel.WriteAndReturn(caps, false)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/driver/netconf/commit.go b/driver/netconf/commit.go
new file mode 100644
index 0000000..a79d3ad
--- /dev/null
+++ b/driver/netconf/commit.go
@@ -0,0 +1,59 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type commit struct {
+ XMLName xml.Name `xml:"commit"`
+}
+
+func (d *Driver) buildCommitElem() *message {
+ commitElem := &commit{
+ XMLName: xml.Name{},
+ }
+
+ netconfInput := d.buildPayload(commitElem)
+
+ return netconfInput
+}
+
+// Commit executes a commit rpc against the NETCONF server.
+func (d *Driver) Commit() (*response.NetconfResponse, error) {
+ d.Logger.Info("Commit RPC requested")
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildCommitElem(), op)
+}
+
+type discard struct {
+ XMLName xml.Name `xml:"discard-changes"`
+}
+
+func (d *Driver) buildDiscardElem() *message {
+ discardElem := &discard{
+ XMLName: xml.Name{},
+ }
+
+ netconfInput := d.buildPayload(discardElem)
+
+ return netconfInput
+}
+
+// Discard executes a discard rpc against the NETCONF server.
+func (d *Driver) Discard() (*response.NetconfResponse, error) {
+ d.Logger.Info("Discard RPC requested")
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildDiscardElem(), op)
+}
diff --git a/driver/netconf/commit_test.go b/driver/netconf/commit_test.go
new file mode 100644
index 0000000..3318e7c
--- /dev/null
+++ b/driver/netconf/commit_test.go
@@ -0,0 +1,88 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testCommitDiscard(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ commitr, err := d.Commit()
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver Commit, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if commitr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ discardr, err := d.Discard()
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver Discard, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if discardr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := commitr.Result + "\n" + discardr.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestCommitDiscard(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "commit-discard-simple": {
+ Description: "simple commit/discard test",
+ PayloadFile: "commit-discard-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testCommitDiscard(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/netconf/copyconfig.go b/driver/netconf/copyconfig.go
new file mode 100644
index 0000000..9792037
--- /dev/null
+++ b/driver/netconf/copyconfig.go
@@ -0,0 +1,41 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type copyConfig struct {
+ XMLName xml.Name `xml:"copy-config"`
+ Target *targetT `xml:""`
+ Source *sourceT `xml:""`
+}
+
+func (d *Driver) buildCopyConfigElem(
+ source,
+ target string,
+) *message {
+ copyConfigElem := ©Config{
+ XMLName: xml.Name{},
+ Target: d.buildTargetElem(target),
+ Source: d.buildSourceElem(source),
+ }
+
+ netconfInput := d.buildPayload(copyConfigElem)
+
+ return netconfInput
+}
+
+// CopyConfig executes a copy config RPC against the NETCONF server copying the source to the
+// target datastore.
+func (d *Driver) CopyConfig(source, target string) (*response.NetconfResponse, error) {
+ d.Logger.Infof("CopyConfig RPC requested, source '%s'/target '%s'", source, target)
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildCopyConfigElem(source, target), op)
+}
diff --git a/driver/netconf/copyconfig_test.go b/driver/netconf/copyconfig_test.go
new file mode 100644
index 0000000..5325d38
--- /dev/null
+++ b/driver/netconf/copyconfig_test.go
@@ -0,0 +1,74 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testCopyConfig(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.CopyConfig("running", "running")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver CopyConfig, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestCopyConfig(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "copy-config-simple": {
+ Description: "simple copy config test",
+ PayloadFile: "copy-config-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testCopyConfig(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/netconf/deleteconfig.go b/driver/netconf/deleteconfig.go
new file mode 100644
index 0000000..e15ae07
--- /dev/null
+++ b/driver/netconf/deleteconfig.go
@@ -0,0 +1,38 @@
+package netconf //nolint: dupl
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type deleteConfig struct {
+ XMLName xml.Name `xml:"delete-config"`
+ Target *targetT `xml:""`
+}
+
+func (d *Driver) buildDeleteConfigElem(
+ target string,
+) *message {
+ deleteConfigElem := &deleteConfig{
+ XMLName: xml.Name{},
+ Target: d.buildTargetElem(target),
+ }
+
+ netconfInput := d.buildPayload(deleteConfigElem)
+
+ return netconfInput
+}
+
+// DeleteConfig executes the delete-config RPC against the NETCONF server deleting the target
+// datastore.
+func (d *Driver) DeleteConfig(target string) (*response.NetconfResponse, error) {
+ d.Logger.Infof("DeleteConfig RPC requested, target '%s'", target)
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildDeleteConfigElem(target), op)
+}
diff --git a/driver/netconf/deleteconfig_test.go b/driver/netconf/deleteconfig_test.go
new file mode 100644
index 0000000..f73f0c8
--- /dev/null
+++ b/driver/netconf/deleteconfig_test.go
@@ -0,0 +1,74 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testDeleteConfig(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.DeleteConfig("startup")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver DeleteConfig, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestDeleteConfig(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "delete-config-simple": {
+ Description: "simple delete config test",
+ PayloadFile: "delete-config-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testDeleteConfig(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/netconf/driver.go b/driver/netconf/driver.go
new file mode 100644
index 0000000..4a91635
--- /dev/null
+++ b/driver/netconf/driver.go
@@ -0,0 +1,300 @@
+package netconf
+
+import (
+ "encoding/xml"
+ "errors"
+ "regexp"
+ "sync"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/logging"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ // V1Dot0 is a constant for the NETCONF 1.0 version string.
+ V1Dot0 = "1.0"
+ v1Dot0Delim = `]]>]]>`
+ v1Dot0Cap = "urn:ietf:params:netconf:base:1.0"
+ v1Dot0Caps = "" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " urn:ietf:params:netconf:base:1.0 \n" +
+ " \n" +
+ " ]]>]]>"
+
+ // V1Dot1 is a constant for the NETCONF 1.1 version string.
+ V1Dot1 = "1.1"
+ v1Dot1Delim = `(?m)^##$`
+ v1Dot1Cap = "urn:ietf:params:netconf:base:1.1"
+ v1Dot1Caps = "" +
+ "\n" +
+ "\n" +
+ " \n" +
+ " urn:ietf:params:netconf:base:1.1 \n" +
+ " \n" +
+ " ]]>]]>"
+
+ helloPattern = `(?is)(<(\w+:)?hello.*(\w+:)?hello>)`
+ capabilityPattern = `(?i)(?:<(?:\w+:)?capability>)(.*?)(?:(?:\w+:)?capability>)`
+
+ messageIDPattern = `(?i)(?:message-id="(\d+)")`
+ subscriptionIDPattern = `(?i)(\d+)`
+
+ emptyTagPattern = `<(\w+)>\w+>`
+
+ defaultNamespace = "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"
+
+ xmlHeader = ""
+)
+
+type netconfPatterns struct {
+ v1Dot0Delim *regexp.Regexp
+ v1Dot1Delim *regexp.Regexp
+ hello *regexp.Regexp
+ capability *regexp.Regexp
+ messageID *regexp.Regexp
+ subscriptionID *regexp.Regexp
+ emptyTags *regexp.Regexp
+}
+
+var (
+ netconfPatternsInstance *netconfPatterns //nolint:gochecknoglobals
+ netconfPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
+)
+
+func getNetconfPatterns() *netconfPatterns {
+ netconfPatternsInstanceOnce.Do(func() {
+ netconfPatternsInstance = &netconfPatterns{
+ v1Dot0Delim: regexp.MustCompile(v1Dot0Delim),
+ v1Dot1Delim: regexp.MustCompile(v1Dot1Delim),
+ hello: regexp.MustCompile(helloPattern),
+ capability: regexp.MustCompile(capabilityPattern),
+ messageID: regexp.MustCompile(messageIDPattern),
+ subscriptionID: regexp.MustCompile(subscriptionIDPattern),
+ emptyTags: regexp.MustCompile(emptyTagPattern),
+ }
+ })
+
+ return netconfPatternsInstance
+}
+
+func withNetconfConnection(b bool) func(interface{}) error {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if ok {
+ a.NetconfConnection = b
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
+
+// NewDriver returns an instance of Driver for the provided host with the given options set. Any
+// options in the driver/options package may be passed to this function -- those options may be
+// applied at the network.Driver, generic.Driver, channel.Channel, or Transport depending on the
+// specific option.
+func NewDriver(
+ host string,
+ opts ...util.Option,
+) (*Driver, error) {
+ opts = append(opts, withNetconfConnection(true))
+
+ // create the generic driver just to yoink the transport and channel out of it, by doing this
+ // all the "normal" options get applied, then we just take the parts we care about. we very much
+ // do *not* want the "normal" driver here because then users may use things like GetPrompt and
+ // the like that would break netconf-y things.
+ gd, err := generic.NewDriver(host, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ d := &Driver{
+ TransportType: gd.TransportType,
+ Transport: gd.Transport,
+ Channel: gd.Channel,
+
+ messageID: 101,
+
+ messages: map[int][]byte{},
+ messagesLock: &sync.Mutex{},
+
+ subscriptions: map[int][][]byte{},
+ subscriptionsLock: &sync.Mutex{},
+
+ errs: make(chan error),
+ done: make(chan bool),
+ }
+
+ for _, option := range opts {
+ err = option(d)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ if d.Logger == nil {
+ // set a default logging instance w/ no assigned loggers (a noop basically)
+ var l *logging.Instance
+
+ l, err = logging.NewInstance()
+ if err != nil {
+ return nil, err
+ }
+
+ d.Logger = l
+ }
+
+ ncPatterns := getNetconfPatterns()
+
+ d.Channel.PromptPattern = ncPatterns.v1Dot0Delim
+
+ return d, nil
+}
+
+// Driver embeds generic.Driver and adds "netconf" centric functionality.
+type Driver struct {
+ Logger *logging.Instance
+
+ TransportType string
+ Transport *transport.Transport
+
+ Channel *channel.Channel
+
+ PreferredVersion string
+ SelectedVersion string
+
+ ForceSelfClosingTags bool
+
+ serverCapabilities []string
+ ServerEcho *bool
+
+ messageID int
+
+ messages map[int][]byte
+ messagesLock *sync.Mutex
+
+ subscriptions map[int][][]byte
+ subscriptionsLock *sync.Mutex
+
+ errs chan error
+ done chan bool
+}
+
+// Open opens the underlying generic.Driver, and by extension the channel.Channel and Transport
+// objects. This should be called prior to executing any RPC methods of the Driver.
+func (d *Driver) Open() error {
+ d.Logger.Debugf(
+ "opening connection to host '%s' on port '%d'",
+ d.Transport.Args.Host,
+ d.Transport.Args.Port,
+ )
+
+ err := d.Channel.Open()
+ if err != nil {
+ return err
+ }
+
+ err = d.processServerCapabilities()
+ if err != nil {
+ return err
+ }
+
+ err = d.determineVersion()
+ if err != nil {
+ return err
+ }
+
+ err = d.sendClientCapabilities()
+ if err != nil {
+ return err
+ }
+
+ go d.read()
+
+ return nil
+}
+
+// Close closes the underlying channel.Channel and Transport objects.
+func (d *Driver) Close() error {
+ d.Logger.Debugf(
+ "closing connection to host '%s' on port '%d'",
+ d.Transport.Args.Host,
+ d.Transport.Args.Port,
+ )
+
+ d.done <- true
+
+ err := d.Transport.Close(true)
+ if err != nil {
+ return err
+ }
+
+ d.Logger.Info("connection closed successfully")
+
+ return nil
+}
+
+func (d *Driver) buildPayload(payload interface{}) *message {
+ baseElem := &message{
+ XMLName: xml.Name{},
+ Namespace: "urn:ietf:params:xml:ns:netconf:base:1.0",
+ MessageID: d.messageID,
+ Payload: payload,
+ }
+
+ d.messageID++
+
+ return baseElem
+}
+
+func (d *Driver) storeMessage(i int, b []byte) {
+ d.messagesLock.Lock()
+ defer d.messagesLock.Unlock()
+
+ d.messages[i] = b
+}
+
+func (d *Driver) getMessage(i int) []byte {
+ d.messagesLock.Lock()
+ defer d.messagesLock.Unlock()
+
+ data := d.messages[i]
+
+ // no point keeping this in memory -- especially as some messages may be huge! we can also
+ // safely delete the key in the map as we should not be getting another message for the same
+ // id ever again (unlike with subscriptions).
+ delete(d.messages, i)
+
+ return data
+}
+
+func (d *Driver) storeSubscriptionMessage(i int, b []byte) {
+ d.subscriptionsLock.Lock()
+ defer d.subscriptionsLock.Unlock()
+
+ d.subscriptions[i] = append(d.subscriptions[i], b)
+}
+
+// GetSubscriptionMessages fetches any messages that have been received for the given subscription
+// id i.
+func (d *Driver) GetSubscriptionMessages(i int) [][]byte {
+ d.subscriptionsLock.Lock()
+ defer d.subscriptionsLock.Unlock()
+
+ m := d.subscriptions[i]
+
+ d.subscriptions[i] = nil
+
+ return m
+}
diff --git a/driver/netconf/driver_test.go b/driver/netconf/driver_test.go
new file mode 100644
index 0000000..1540d81
--- /dev/null
+++ b/driver/netconf/driver_test.go
@@ -0,0 +1,281 @@
+package netconf_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+
+ "github.com/scrapli/scrapligo/platform"
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func resolveFile(t *testing.T, f string) string {
+ f, err := filepath.Abs(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return f
+}
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualIn []byte, actualOut string) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+ goldenIn := filepath.Join("test-fixtures", "golden", testName+"-in.txt")
+
+ err := os.WriteFile(goldenOut, []byte(actualOut), 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.WriteFile(goldenIn, actualIn, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func prepareDriver(
+ t *testing.T,
+ testName,
+ payloadFile string,
+) (*netconf.Driver, *transport.File) {
+ d, err := netconf.NewDriver(
+ "dummy",
+ options.WithTransportType(transport.FileTransport),
+ options.WithFileTransportFile(resolveFile(t, payloadFile)),
+ // options.WithTransportReadSize(1),
+ options.WithReadDelay(0),
+ )
+ if err != nil {
+ t.Fatalf("%s: encountered error creating network Driver, error: %s", testName, err)
+ }
+
+ err = d.Open()
+ if err != nil {
+ t.Fatalf("%s: encountered error opening netconf Driver, error: %s", testName, err)
+ }
+
+ fileTransportObj, ok := d.Transport.Impl.(*transport.File)
+ if !ok {
+ t.Fatalf("transport implementation is not Transport File")
+ }
+
+ return d, fileTransportObj
+}
+
+func writeGoldenFunctional(t *testing.T, testName, actualOut string) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+
+ err := os.WriteFile(goldenOut, []byte(actualOut), 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func getNetconfTransportNames() []string {
+ ncTransportNames := make([]string, 0)
+
+ for _, transportName := range transport.GetTransportNames() {
+ if transportName == transport.TelnetTransport {
+ continue
+ }
+
+ ncTransportNames = append(ncTransportNames, transportName)
+ }
+
+ return ncTransportNames
+}
+
+func getNetconfPlatformNames() []string {
+ ncPlatformNames := make([]string, 0)
+
+ for _, platformName := range platform.GetPlatformNames() {
+ if platformName == platform.NokiaSrl {
+ continue
+ }
+
+ ncPlatformNames = append(ncPlatformNames, platformName)
+ }
+
+ return ncPlatformNames
+}
+
+func getFunctionalHostIPPortLinuxOrRemote(
+ t *testing.T,
+ platformName string,
+) (host string, port int) {
+ switch platformName {
+ case platform.CiscoIosxe:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_IOSXE_HOST", "172.20.20.11")
+
+ return host, 830
+ case platform.CiscoIosxr:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_IOSXR_HOST", "172.20.20.12")
+
+ return host, 830
+ case platform.CiscoNxos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_NXOS_HOST", "172.20.20.13")
+
+ return host, 830
+ case platform.AristaEos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_ARISTA_EOS_HOST", "172.20.20.14")
+
+ return host, 830
+ case platform.JuniperJunos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_JUNIPER_JUNOS_HOST", "172.20.20.15")
+
+ return host, 830
+ }
+
+ t.Fatalf("failed finding platform host/port info")
+
+ return "", 0
+}
+
+func getFunctionalHostIPPort(t *testing.T, platformName string) (host string, port int) {
+ osType := runtime.GOOS
+
+ remoteOverride := util.GetEnvIntOrDefault("SCRAPLIGO_NO_HOST_FWD", 0)
+
+ if osType == "linux" || remoteOverride != 0 {
+ return getFunctionalHostIPPortLinuxOrRemote(t, platformName)
+ }
+
+ // otherwise we are running on darwin w/ local boxen w/ nat setup
+
+ host = "localhost"
+
+ switch platformName {
+ case platform.CiscoIosxe:
+ return host, 21830
+ case platform.CiscoIosxr:
+ return host, 22830
+ case platform.CiscoNxos:
+ return host, 23830
+ case platform.AristaEos:
+ return host, 24830
+ case platform.JuniperJunos:
+ return host, 25830
+ }
+
+ t.Fatalf("failed finding platform host/port info")
+
+ return "", 0
+}
+
+func getFunctionalHostUserPass(t *testing.T, platformName string) (user, pass string) {
+ user = util.Admin
+ pass = util.Admin
+
+ switch platformName {
+ case platform.CiscoIosxe:
+ return user, pass
+ case platform.CiscoIosxr:
+ return "clab", "clab@123"
+ case platform.CiscoNxos:
+ return user, pass
+ case platform.AristaEos:
+ return user, pass
+ case platform.JuniperJunos:
+ return user, "admin@123"
+ }
+
+ t.Fatalf("failed finding platform user/pass info")
+
+ return "", ""
+}
+
+func prepareFunctionalDriver(
+ t *testing.T,
+ testName, platformName, transportName string,
+) *netconf.Driver {
+ host, port := getFunctionalHostIPPort(t, platformName)
+ user, pass := getFunctionalHostUserPass(t, platformName)
+
+ d, err := netconf.NewDriver(
+ host,
+ options.WithPort(port),
+ options.WithAuthUsername(user),
+ options.WithAuthPassword(pass),
+ options.WithTransportType(transportName),
+ options.WithAuthNoStrictKey(),
+ )
+ if err != nil {
+ t.Fatalf("%s: encountered error creating netconf driver, error: %s", testName, err)
+ }
+
+ if platformName == platform.JuniperJunos {
+ d.ForceSelfClosingTags = true
+ }
+
+ err = d.Open()
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error opening netconf driver, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ return d
+}
+
+func interTestSleep() {
+ if len(strings.Split(*platforms, ",")) == 1 {
+ // when only running against a single platform, back to back tests tend to cause some issues
+ // so basically stagger things out so the device doesn't choke. we stagger more for netconf
+ // than ssh/telnet as the xr box in particular seems to not appreciate this!
+ time.Sleep(1 * time.Second)
+
+ return
+ }
+
+ if *transports == util.All {
+ // when we run w/ all transports we do one transport after another, so similar to above
+ // we just want to stagger things a bit.
+ time.Sleep(1 * time.Second)
+
+ return
+ }
+}
diff --git a/driver/netconf/editconfig.go b/driver/netconf/editconfig.go
new file mode 100644
index 0000000..174a24f
--- /dev/null
+++ b/driver/netconf/editconfig.go
@@ -0,0 +1,39 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type editConfig struct {
+ XMLName xml.Name `xml:"edit-config"`
+ Target *targetT `xml:""`
+ Payload string `xml:",innerxml"`
+}
+
+func (d *Driver) buildEditConfigElem(
+ target, config string,
+) *message {
+ editConfigElem := &editConfig{
+ XMLName: xml.Name{},
+ Target: d.buildTargetElem(target),
+ Payload: config,
+ }
+
+ netconfInput := d.buildPayload(editConfigElem)
+
+ return netconfInput
+}
+
+// EditConfig executes the edit-config RPC pushing the provided config against the target datastore.
+func (d *Driver) EditConfig(target, config string) (*response.NetconfResponse, error) {
+ d.Logger.Infof("EditConfig RPC requested, target '%s'", target)
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildEditConfigElem(target, config), op)
+}
diff --git a/driver/netconf/editconfig_test.go b/driver/netconf/editconfig_test.go
new file mode 100644
index 0000000..42ef508
--- /dev/null
+++ b/driver/netconf/editconfig_test.go
@@ -0,0 +1,77 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testEditConfig(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.EditConfig(
+ "candidate",
+ "\n \n 80 \n true \n \n 200 \n \n \n ", //nolint: lll
+ )
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver Get, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestEditConfig(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "edit-config-simple": {
+ Description: "simple edit config test",
+ PayloadFile: "edit-config-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testEditConfig(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/netconf/elements.go b/driver/netconf/elements.go
new file mode 100644
index 0000000..54831e4
--- /dev/null
+++ b/driver/netconf/elements.go
@@ -0,0 +1,118 @@
+package netconf
+
+import (
+ "encoding/xml"
+ "fmt"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ reportAll = "report-all"
+ reportAllTagged = "report-all-tagged"
+ trim = "trim"
+ explicit = "explicit"
+
+ // FilterSubtree is a constant representing the subtree filter type.
+ FilterSubtree = "subtree"
+
+ // FilterXpath is a constant representing the xpath filter type.
+ FilterXpath = "xpath"
+)
+
+type sourceElement struct {
+ XMLName xml.Name
+}
+
+type sourceT struct {
+ XMLName xml.Name `xml:"source"`
+ Source *sourceElement `xml:""`
+}
+
+func (d *Driver) buildSourceElem(source string) *sourceT {
+ sourceElem := &sourceT{
+ XMLName: xml.Name{},
+ Source: &sourceElement{XMLName: xml.Name{Local: source}},
+ }
+
+ return sourceElem
+}
+
+type targetElement struct {
+ XMLName xml.Name
+}
+
+type targetT struct {
+ XMLName xml.Name `xml:"target"`
+ Source *targetElement `xml:""`
+}
+
+func (d *Driver) buildTargetElem(target string) *targetT {
+ targetElem := &targetT{
+ XMLName: xml.Name{},
+ Source: &targetElement{XMLName: xml.Name{Local: target}},
+ }
+
+ return targetElem
+}
+
+type defaultType struct {
+ XMLName xml.Name `xml:"with-defaults"`
+ Namespace string `xml:"xmlns,attr"`
+ Type string `xml:",innerxml"`
+}
+
+func (d *Driver) buildDefaultsElem(defaultsType string) (*defaultType, error) {
+ if defaultsType == "" {
+ return nil, nil
+ }
+
+ switch defaultsType {
+ case reportAll, reportAllTagged, trim, explicit:
+ default:
+ return nil, fmt.Errorf("%w: unknown default type '%s'", util.ErrNetconfError, defaultsType)
+ }
+
+ return &defaultType{
+ XMLName: xml.Name{},
+ Namespace: defaultNamespace,
+ Type: defaultsType,
+ }, nil
+}
+
+type filterT struct {
+ XMLName xml.Name `xml:"filter"`
+ Type string `xml:"type,attr"`
+ Select string `xml:"select,attr,omitempty"`
+ Payload string `xml:",innerxml"`
+}
+
+func (d *Driver) buildFilterElem(filter, filterType string) (*filterT, error) {
+ if filter == "" || filterType == "" {
+ return nil, nil
+ }
+
+ var f *filterT
+
+ var err error
+
+ switch filterType {
+ case FilterSubtree:
+ f = &filterT{
+ XMLName: xml.Name{},
+ Type: filterType,
+ Select: "",
+ Payload: filter,
+ }
+ case FilterXpath:
+ f = &filterT{
+ XMLName: xml.Name{},
+ Type: filterType,
+ Select: filter,
+ }
+ default:
+ err = fmt.Errorf("%w: unknown filter type '%s'", util.ErrNetconfError, filterType)
+ }
+
+ return f, err
+}
diff --git a/driver/netconf/get.go b/driver/netconf/get.go
new file mode 100644
index 0000000..8e6dd4a
--- /dev/null
+++ b/driver/netconf/get.go
@@ -0,0 +1,49 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+type get struct {
+ XMLName xml.Name `xml:"get"`
+ Source *sourceT `xml:""`
+ Filter *filterT `xml:""`
+}
+
+func (d *Driver) buildGetElem(
+ filter, filterType string,
+) (*message, error) {
+ filterElem, err := d.buildFilterElem(filter, filterType)
+ if err != nil {
+ return nil, err
+ }
+
+ getElem := &get{
+ XMLName: xml.Name{},
+ Filter: filterElem,
+ }
+
+ netconfInput := d.buildPayload(getElem)
+
+ return netconfInput, nil
+}
+
+// Get executes a get RPC against the NETCONF server.
+func (d *Driver) Get(filter string, opts ...util.Option) (*response.NetconfResponse, error) {
+ d.Logger.Info("Get RPC requested")
+
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ m, err := d.buildGetElem(filter, op.FilterType)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(m, op)
+}
diff --git a/driver/netconf/get_test.go b/driver/netconf/get_test.go
new file mode 100644
index 0000000..e7eb78c
--- /dev/null
+++ b/driver/netconf/get_test.go
@@ -0,0 +1,76 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testGet(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.Get(
+ "\n ", //nolint: lll
+ )
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver Get, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestGet(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "get-simple": {
+ Description: "simple get test",
+ PayloadFile: "get-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testGet(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/netconf/getconfig.go b/driver/netconf/getconfig.go
new file mode 100644
index 0000000..83bc112
--- /dev/null
+++ b/driver/netconf/getconfig.go
@@ -0,0 +1,57 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+type getConfig struct {
+ XMLName xml.Name `xml:"get-config"`
+ Source *sourceT `xml:""`
+ Filter *filterT `xml:""`
+ Defaults *defaultType `xml:""`
+}
+
+func (d *Driver) buildGetConfigElem(
+ source, filter, filterType, defaultType string,
+) (*message, error) {
+ filterElem, err := d.buildFilterElem(filter, filterType)
+ if err != nil {
+ return nil, err
+ }
+
+ defaultsElem, err := d.buildDefaultsElem(defaultType)
+ if err != nil {
+ return nil, err
+ }
+
+ getConfigElem := &getConfig{
+ XMLName: xml.Name{},
+ Source: d.buildSourceElem(source),
+ Filter: filterElem,
+ Defaults: defaultsElem,
+ }
+
+ netconfInput := d.buildPayload(getConfigElem)
+
+ return netconfInput, nil
+}
+
+// GetConfig executes a get-config RPC against the NETCONF server.
+func (d *Driver) GetConfig(source string, opts ...util.Option) (*response.NetconfResponse, error) {
+ d.Logger.Infof("GetConfig RPC requested, source '%s'", source)
+
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ m, err := d.buildGetConfigElem(source, op.Filter, op.FilterType, op.DefaultType)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(m, op)
+}
diff --git a/driver/netconf/getconfig_test.go b/driver/netconf/getconfig_test.go
new file mode 100644
index 0000000..f9c05ac
--- /dev/null
+++ b/driver/netconf/getconfig_test.go
@@ -0,0 +1,180 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testGetConfig(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.GetConfig("running")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver getConfig, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestGetConfig(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "getconfig-simple": {
+ Description: "simple getconfig test",
+ PayloadFile: "getconfig-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testGetConfig(testName, testCase)
+ t.Run(testName, f)
+ }
+}
+
+func testGetConfigFunctional(
+ testName, platformName, transportName string,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
+
+ r, err := d.GetConfig("running")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver getConfig, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
+
+ actualOut := r.Result
+
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-%s-%s", testName, platformName, transportName),
+ actualOut,
+ )
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
+
+ expectedOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-%s-%s-out.txt", testName, platformName, transportName),
+ )
+
+ if !cmp.Equal(
+ cleanF(actualOut),
+ cleanF(string(expectedOut)),
+ ) {
+ t.Logf(
+ "%s: actual and expected outputs do *not* match, however we did not get "+
+ "a failed operation and devices sometimes have empty elements and out of "+
+ "order elements causing comparison to be difficult, this is "+
+ "*probably* not an issue, but you should take a look at the output to confirm!",
+ testName,
+ )
+ }
+ }
+}
+
+func TestGetConfigFunctional(t *testing.T) {
+ cases := map[string]*struct {
+ description string
+ }{
+ "functional-getconfig-simple": {
+ description: "simple get config test",
+ },
+ }
+
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
+
+ for testName := range cases {
+ for _, platformName := range getNetconfPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
+
+ continue
+ }
+
+ for _, transportName := range getNetconfTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
+
+ continue
+ }
+
+ f := testGetConfigFunctional(testName, platformName, transportName)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
+
+ interTestSleep()
+ }
+ }
+ }
+}
diff --git a/driver/netconf/lock.go b/driver/netconf/lock.go
new file mode 100644
index 0000000..be36169
--- /dev/null
+++ b/driver/netconf/lock.go
@@ -0,0 +1,63 @@
+package netconf
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type lock struct {
+ XMLName xml.Name `xml:"lock"`
+ Target *targetT `xml:""`
+}
+
+func (d *Driver) buildLockElem(target string) *message {
+ lockElem := &lock{
+ XMLName: xml.Name{},
+ Target: d.buildTargetElem(target),
+ }
+
+ netconfInput := d.buildPayload(lockElem)
+
+ return netconfInput
+}
+
+// Lock executes the lock rpc for the target datastore against the NETCONF server.
+func (d *Driver) Lock(target string) (*response.NetconfResponse, error) {
+ d.Logger.Info("Lock RPC requested")
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildLockElem(target), op)
+}
+
+type unlock struct {
+ XMLName xml.Name `xml:"unlock"`
+ Target *targetT `xml:""`
+}
+
+func (d *Driver) buildUnlockElem(target string) *message {
+ unlockElem := &unlock{
+ XMLName: xml.Name{},
+ Target: d.buildTargetElem(target),
+ }
+
+ netconfInput := d.buildPayload(unlockElem)
+
+ return netconfInput
+}
+
+// Unlock executes unlock rpc for the target datastore against the NETCONF server.
+func (d *Driver) Unlock(target string) (*response.NetconfResponse, error) {
+ d.Logger.Info("Unlock RPC requested")
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildUnlockElem(target), op)
+}
diff --git a/driver/netconf/lock_test.go b/driver/netconf/lock_test.go
new file mode 100644
index 0000000..b5c8cd2
--- /dev/null
+++ b/driver/netconf/lock_test.go
@@ -0,0 +1,241 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/scrapli/scrapligo/platform"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testLockUnlock(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ lockr, err := d.Lock("running")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver lock, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if lockr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ unlockr, err := d.Unlock("running")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver unlock, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if unlockr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := lockr.Result + "\n" + unlockr.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestLockUnlock(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "lock-unlock-simple": {
+ Description: "simple lock/unlock test",
+ PayloadFile: "lock-unlock-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testLockUnlock(testName, testCase)
+ t.Run(testName, f)
+ }
+}
+
+func testLockUnlockFunctional( //nolint: funlen
+ testName, platformName, transportName string,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
+
+ var target string
+
+ switch platformName {
+ case platform.CiscoIosxe, platform.CiscoNxos, platform.AristaEos:
+ target = "running"
+ default:
+ target = "candidate"
+ }
+
+ lockr, err := d.Lock(target)
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver lock, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if lockr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ unlockr, err := d.Unlock(target)
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver unlock, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if unlockr.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
+
+ actualLockOut := lockr.Result
+ actualUnlockOut := unlockr.Result
+
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-lock-%s-%s", testName, platformName, transportName),
+ actualLockOut,
+ )
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-unlock-%s-%s", testName, platformName, transportName),
+ actualUnlockOut,
+ )
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
+
+ expectedLockOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-lock-%s-%s-out.txt", testName, platformName, transportName),
+ )
+
+ expectedUnlockOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-unlock-%s-%s-out.txt", testName, platformName, transportName),
+ )
+
+ if !cmp.Equal(
+ util.GetCleanFunc(platformName)(actualLockOut),
+ cleanF(string(expectedLockOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualLockOut,
+ expectedLockOut,
+ )
+ }
+
+ if !cmp.Equal(
+ util.GetCleanFunc(platformName)(actualUnlockOut),
+ cleanF(string(expectedUnlockOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualUnlockOut,
+ expectedUnlockOut,
+ )
+ }
+ }
+}
+
+func TestLockUnlockFunctional(t *testing.T) {
+ cases := map[string]*struct {
+ description string
+ }{
+ "functional-lock-unlock-simple": {
+ description: "simple lock and unlock test",
+ },
+ }
+
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
+
+ for testName := range cases {
+ for _, platformName := range getNetconfPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
+
+ continue
+ }
+
+ for _, transportName := range getNetconfTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
+
+ continue
+ }
+
+ f := testLockUnlockFunctional(testName, platformName, transportName)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
+
+ interTestSleep()
+ }
+ }
+ }
+}
diff --git a/driver/netconf/message.go b/driver/netconf/message.go
new file mode 100644
index 0000000..323ef70
--- /dev/null
+++ b/driver/netconf/message.go
@@ -0,0 +1,32 @@
+package netconf
+
+import (
+ "encoding/xml"
+ "fmt"
+)
+
+type message struct {
+ XMLName xml.Name `xml:"rpc"`
+ Namespace string `xml:"xmlns,attr"`
+ MessageID int `xml:"message-id,attr"`
+ Payload interface{} `xml:",innerxml"`
+}
+
+func (m *message) serialize(v string) ([]byte, error) {
+ message, err := xml.Marshal(m)
+ if err != nil {
+ return nil, err
+ }
+
+ message = append([]byte(xmlHeader), message...)
+
+ switch v {
+ case V1Dot0:
+ message = append(message, []byte(v1Dot0Delim)...)
+ case V1Dot1:
+ message = append([]byte(fmt.Sprintf("#%d\n", len(message))), message...)
+ message = append(message, []byte("\n##")...)
+ }
+
+ return message, nil
+}
diff --git a/driver/netconf/operation.go b/driver/netconf/operation.go
new file mode 100644
index 0000000..f77ad26
--- /dev/null
+++ b/driver/netconf/operation.go
@@ -0,0 +1,42 @@
+package netconf
+
+import (
+ "errors"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ defaultFilterType = "subtree"
+ defaultTimeout = -1
+)
+
+// OperationOptions is a struct containing "operation" options that are relevant to the Netconf
+// Driver.
+type OperationOptions struct {
+ Filter string
+ FilterType string
+ DefaultType string
+ Timeout time.Duration
+}
+
+// NewOperation returns a new OperationOptions object with the defaults set and any provided options
+// applied.
+func NewOperation(options ...util.Option) (*OperationOptions, error) {
+ o := &OperationOptions{
+ FilterType: defaultFilterType,
+ Timeout: defaultTimeout,
+ }
+
+ for _, option := range options {
+ err := option(o)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return o, nil
+}
diff --git a/driver/netconf/read.go b/driver/netconf/read.go
new file mode 100644
index 0000000..61d5a28
--- /dev/null
+++ b/driver/netconf/read.go
@@ -0,0 +1,90 @@
+package netconf
+
+import (
+ "bytes"
+ "strconv"
+ "time"
+)
+
+const (
+ idOrSubMatchLen = 2
+ endRPCSplitLen = 2
+)
+
+func getID(match [][]byte) int {
+ if len(match) != idOrSubMatchLen {
+ return 0
+ }
+
+ id, _ := strconv.Atoi(string(match[1]))
+
+ return id
+}
+
+func (d *Driver) read() {
+ var b []byte
+
+ patterns := getNetconfPatterns()
+
+ for {
+ select {
+ case <-d.done:
+ return
+ default:
+ }
+
+ rb, err := d.Channel.Read()
+ if err != nil {
+ d.errs <- err
+ }
+
+ b = append(b, rb...)
+
+ if d.Channel.PromptPattern.Match(b) { //nolint: nestif
+ if bytes.Contains(b, []byte("")) {
+ // we read past the input, yay this is good, but we don't care that much, we just
+ // need to reset the buffer... *but* because there is a small read delay in channel
+ // we can sometimes already have read past the prompt/end of the original rpc. This
+ // isn't an issue in "normal" SSH operations where we don't send return until we
+ // read the input off the session, but obviously can break things here, so we'll
+ // use regex to split on the delim and then get only the bits after the delim and
+ // update b to be just that part.
+ var ss []string
+
+ switch d.SelectedVersion {
+ case V1Dot0:
+ ss = patterns.v1Dot0Delim.Split(string(b), endRPCSplitLen)
+ case V1Dot1:
+ ss = patterns.v1Dot1Delim.Split(string(b), endRPCSplitLen)
+ }
+
+ b = []byte(ss[1])
+ } else if d.Channel.PromptPattern.Match(b) {
+ var messageID int
+ var subID int
+
+ messageID = getID(patterns.messageID.FindSubmatch(b))
+
+ if bytes.Contains(b, []byte("")) {
+ subID = getID(patterns.subscriptionID.FindSubmatch(b))
+ }
+
+ if messageID != 0 {
+ d.Logger.Debugf("Received message response for message ID '%d', storing", messageID)
+
+ d.storeMessage(messageID, b)
+ }
+
+ if subID != 0 {
+ d.Logger.Debugf("Received message response for subscription ID '%d', storing", subID)
+
+ d.storeSubscriptionMessage(subID, b)
+ }
+
+ b = nil
+ }
+ }
+
+ time.Sleep(d.Channel.ReadDelay)
+ }
+}
diff --git a/driver/netconf/rpc.go b/driver/netconf/rpc.go
new file mode 100644
index 0000000..e0fef07
--- /dev/null
+++ b/driver/netconf/rpc.go
@@ -0,0 +1,116 @@
+package netconf
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func (d *Driver) buildRPCElem(
+ filter string,
+) *message {
+ netconfInput := d.buildPayload(filter)
+
+ return netconfInput
+}
+
+// RPC executes a "bare" RPC against the NETCONF server.
+func (d *Driver) RPC(opts ...util.Option) (*response.NetconfResponse, error) {
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildRPCElem(op.Filter), op)
+}
+
+func forceSelfClosingTags(b []byte) []byte {
+ ncPatterns := getNetconfPatterns()
+
+ emptyTagIdxs := ncPatterns.emptyTags.FindAllSubmatchIndex(b, -1)
+
+ var nb []byte
+
+ for _, idx := range emptyTagIdxs {
+ // get everything in b up till the first of the submatch indexes (this is the start of an
+ // "empty" tag), then get the name of the tag and put it in a self-closing
+ // tag.
+ nb = append(b[0:idx[0]], fmt.Sprintf("<%s/>", b[idx[2]:idx[3]])...) //nolint: gocritic
+
+ // finally, append everything *after* the submatch indexes
+ nb = append(nb, b[len(b)-(len(b)-idx[1]):]...)
+ }
+
+ return nb
+}
+
+func (d *Driver) sendRPC(
+ m *message,
+ op *OperationOptions,
+) (*response.NetconfResponse, error) {
+ b, err := m.serialize(d.SelectedVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ if d.ForceSelfClosingTags {
+ d.Logger.Debug("ForceSelfClosingTags is true, enforcing...")
+
+ b = forceSelfClosingTags(b)
+ }
+
+ d.Logger.Debugf("sending finalized rpc payload:\n%s", string(b))
+
+ r := response.NewNetconfResponse(
+ b,
+ d.Transport.GetHost(),
+ d.Transport.GetPort(),
+ d.SelectedVersion,
+ )
+
+ err = d.Channel.WriteAndReturn(b, false)
+ if err != nil {
+ return nil, err
+ }
+
+ if d.SelectedVersion == V1Dot1 {
+ err = d.Channel.WriteReturn()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ done := make(chan []byte)
+
+ go func() {
+ var data []byte
+
+ for {
+ data = d.getMessage(m.MessageID)
+ if data != nil {
+ break
+ }
+
+ time.Sleep(5 * time.Microsecond) //nolint: gomnd
+ }
+
+ done <- data
+ }()
+
+ timer := time.NewTimer(d.Channel.GetTimeout(op.Timeout))
+
+ select {
+ case err = <-d.errs:
+ return nil, err
+ case <-timer.C:
+ d.Logger.Critical("channel timeout sending input to device")
+
+ return nil, fmt.Errorf("%w: channel timeout sending input to device", util.ErrTimeoutError)
+ case data := <-done:
+ r.Record(data)
+ }
+
+ return r, nil
+}
diff --git a/driver/netconf/subscription.go b/driver/netconf/subscription.go
new file mode 100644
index 0000000..792bec7
--- /dev/null
+++ b/driver/netconf/subscription.go
@@ -0,0 +1,51 @@
+package netconf
+
+import (
+ "encoding/xml"
+ "strconv"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type establishSubscription struct {
+ XMLName xml.Name `xml:"establish-subscription"`
+ NamespaceNotification string `xml:"xmlns,attr"`
+ NamespaceYANGPush string `xml:"xmlns:yp,attr"`
+ Stream string `xml:"stream"`
+ Filter string `xml:"yp:xpath-filter"`
+ Period int `xml:"yp:period"`
+}
+
+// EstablishPeriodicSubscription is a BETA method to establish a NETCONF subscription. Seriously,
+// don't trust that this won't change, just pretend it doesn't exist for now or something!
+func (d *Driver) EstablishPeriodicSubscription(
+ xpath string,
+ period int,
+) (*response.NetconfResponse, error) {
+ establishElem := &establishSubscription{
+ XMLName: xml.Name{},
+ NamespaceNotification: "urn:ietf:params:xml:ns:yang:ietf-event-notifications",
+ NamespaceYANGPush: "urn:ietf:params:xml:ns:yang:ietf-yang-push",
+ Stream: "yp:yang-push",
+ Filter: xpath,
+ Period: period,
+ }
+
+ m := d.buildPayload(establishElem)
+
+ r, err := d.sendRPC(m, &OperationOptions{})
+ if err != nil {
+ return nil, err
+ }
+
+ patterns := getNetconfPatterns()
+
+ match := patterns.subscriptionID.FindSubmatch(r.RawResult)
+ subID, _ := strconv.Atoi(string(match[1]))
+
+ d.subscriptions[subID] = make([][]byte, 0)
+
+ r.SubscriptionID = subID
+
+ return r, nil
+}
diff --git a/driver/netconf/test-fixtures/commit-discard-simple.txt b/driver/netconf/test-fixtures/commit-discard-simple.txt
new file mode 100644
index 0000000..dd1e3f0
--- /dev/null
+++ b/driver/netconf/test-fixtures/commit-discard-simple.txt
@@ -0,0 +1,50 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#131
+
+##
+
+
+#119
+
+
+
+
+
+##
+#149
+
+##
+
+
+#119
+
+
+
+
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/copy-config-simple.txt b/driver/netconf/test-fixtures/copy-config-simple.txt
new file mode 100644
index 0000000..5945b9c
--- /dev/null
+++ b/driver/netconf/test-fixtures/copy-config-simple.txt
@@ -0,0 +1,34 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#213
+
+##
+
+
+#97
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/delete-config-simple.txt b/driver/netconf/test-fixtures/delete-config-simple.txt
new file mode 100644
index 0000000..0584ee4
--- /dev/null
+++ b/driver/netconf/test-fixtures/delete-config-simple.txt
@@ -0,0 +1,34 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#181
+
+##
+
+
+#97
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/edit-config-simple.txt b/driver/netconf/test-fixtures/edit-config-simple.txt
new file mode 100644
index 0000000..d1c9822
--- /dev/null
+++ b/driver/netconf/test-fixtures/edit-config-simple.txt
@@ -0,0 +1,46 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#453
+
+
+ 80
+ true
+
+ 200
+
+
+
+##
+
+
+#119
+
+
+
+
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/get-simple.txt b/driver/netconf/test-fixtures/get-simple.txt
new file mode 100644
index 0000000..e1e95ed
--- /dev/null
+++ b/driver/netconf/test-fixtures/get-simple.txt
@@ -0,0 +1,47 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#249
+
+
+##
+
+
+#293
+
+
+
+
+
+
+
+
+
+
+
+
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/getconfig-simple.txt b/driver/netconf/test-fixtures/getconfig-simple.txt
new file mode 100644
index 0000000..dc7ba56
--- /dev/null
+++ b/driver/netconf/test-fixtures/getconfig-simple.txt
@@ -0,0 +1,37 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#175
+
+##
+
+
+#121
+
+
+#832
+true deny deny deny true admin PRIV15 permit-all * * permit default default-vrf [read-only] static 1
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/commit-discard-simple-in.txt b/driver/netconf/test-fixtures/golden/commit-discard-simple-in.txt
new file mode 100644
index 0000000..e253d4b
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/commit-discard-simple-in.txt
@@ -0,0 +1,21 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#131
+
+##
+
+
+
+
+#149
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/commit-discard-simple-out.txt b/driver/netconf/test-fixtures/golden/commit-discard-simple-out.txt
new file mode 100644
index 0000000..6001ace
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/commit-discard-simple-out.txt
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/copy-config-simple-in.txt b/driver/netconf/test-fixtures/golden/copy-config-simple-in.txt
new file mode 100644
index 0000000..22aea61
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/copy-config-simple-in.txt
@@ -0,0 +1,14 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#213
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/copy-config-simple-out.txt b/driver/netconf/test-fixtures/golden/copy-config-simple-out.txt
new file mode 100644
index 0000000..7e6dd12
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/copy-config-simple-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/delete-config-simple-in.txt b/driver/netconf/test-fixtures/golden/delete-config-simple-in.txt
new file mode 100644
index 0000000..e50f4e6
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/delete-config-simple-in.txt
@@ -0,0 +1,14 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#181
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/delete-config-simple-out.txt b/driver/netconf/test-fixtures/golden/delete-config-simple-out.txt
new file mode 100644
index 0000000..7e6dd12
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/delete-config-simple-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/edit-config-simple-in.txt b/driver/netconf/test-fixtures/golden/edit-config-simple-in.txt
new file mode 100644
index 0000000..2194232
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/edit-config-simple-in.txt
@@ -0,0 +1,22 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#453
+
+
+ 80
+ true
+
+ 200
+
+
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/edit-config-simple-out.txt b/driver/netconf/test-fixtures/golden/edit-config-simple-out.txt
new file mode 100644
index 0000000..8eae6a9
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/edit-config-simple-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-standard-out.txt
new file mode 100644
index 0000000..1372648
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-standard-out.txt
@@ -0,0 +1 @@
+action-none false mapControlPlane copp-system-tc3to5 matchConditionAny copp-system-nat matchConditionAny copp-system-rsvp matchConditionAny copp-system-mtu matchConditionAny copp-system-ipv6nd matchConditionAny copp-system-mod matchConditionAny copp-system-l2forward-includes-cpu matchConditionAny copp-system-selfip matchConditionAny copp-system-ipmcmiss matchConditionAny copp-system-mac-learn matchConditionAny copp-system-ptp matchConditionAny copp-system-l3lpmoverflow matchConditionAny copp-system-arp-inspect matchConditionAny copp-system-l3ttl0 matchConditionAny copp-system-cfm-snoop matchConditionAny copp-system-mpls-ttl01 matchConditionAny copp-system-sflow matchConditionAny copp-system-l3rsvd matchConditionAny copp-system-ipmcrsvd matchConditionAny copp-system-dot1x-mba matchConditionAny copp-system-l2rsvd matchConditionAny copp-system-OspfIsis matchConditionAny copp-system-mirroring matchConditionAny copp-system-pim matchConditionAny copp-system-protocol-snoop matchConditionAny copp-system-vrrp matchConditionAny copp-system-vxlan-vtep-learn matchConditionAny copp-system-mpls-label-miss matchConditionAny copp-system-bgp matchConditionAny copp-system-PimPtp matchConditionAny copp-system-arp matchConditionAny copp-system-iplocking matchConditionAny copp-system-arpresolver matchConditionAny copp-system-ptp-snoop matchConditionAny copp-system-bpdu matchConditionAny copp-system-igmp matchConditionAny copp-system-ipmc matchConditionAny copp-system-selfip-tc6to7 matchConditionAny copp-system-l2broadcast matchConditionAny copp-system-cfm matchConditionAny copp-system-lacp matchConditionAny copp-system-vxlan-encapsulation matchConditionAny copp-system-lldp matchConditionAny copp-system-default-snoop matchConditionAny copp-system-bfd matchConditionAny copp-system-ldp matchConditionAny copp-system-ipbroadcast matchConditionAny copp-system-mpls-arp-suppress matchConditionAny copp-system-mlag matchConditionAny copp-system-ipunicast matchConditionAny copp-system-acllog matchConditionAny copp-system-mvrp matchConditionAny copp-system-drop matchConditionAny copp-system-l3destmiss matchConditionAny copp-system-multicastsnoop matchConditionAny copp-system-l3-high-pri matchConditionAny copp-system-bfd-ptp matchConditionAny copp-system-acllog-sflow matchConditionAny copp-system-dot1x-eapol-data matchConditionAny copp-system-glean matchConditionAny copp-system-unicastarp matchConditionAny copp-system-l3-exception matchConditionAny copp-system-mcast matchConditionAny copp-system-tc6to7 matchConditionAny copp-system-cvx-heartbeat matchConditionAny copp-system-egress-acllog matchConditionAny copp-system-l3slowpath matchConditionAny copp-system-cvx matchConditionAny copp-system-l3-low-pri matchConditionAny copp-system-l3ttl1 matchConditionAny copp-system-linklocal matchConditionAny copp-system-dot1x-vxlan matchConditionAny copp-system-l2ucast matchConditionAny mapPdp cvx matchBuiltIn cvx matchConditionAny nat-miss matchBuiltIn nat-miss matchConditionAny ip-broadcast matchBuiltIn ip-broadcast matchConditionAny unicast-route-miss matchBuiltIn unicast-route-miss matchConditionAny vxlan-encapsulation matchBuiltIn vxlan-encapsulation matchConditionAny igmp matchBuiltIn igmp matchConditionAny mac-source-miss matchBuiltIn mac-source-miss matchConditionAny self-icmp matchBuiltIn self-icmp matchConditionAny natTcpFlags matchBuiltIn natTcpFlags matchConditionAny isis matchBuiltIn isis matchConditionAny link-local-multicast matchBuiltIn link-local-multicast matchConditionAny vxlan-vtep-learn matchBuiltIn vxlan-vtep-learn matchConditionAny layer3-control matchBuiltIn layer3-control matchConditionAny arp matchBuiltIn arp matchConditionAny self-ip-low-priority matchBuiltIn self-ip-low-priority matchConditionAny arp-needed matchBuiltIn arp-needed matchConditionAny dhcp matchBuiltIn dhcp matchConditionAny multicast-route-miss matchBuiltIn multicast-route-miss matchConditionAny layer2-control matchBuiltIn layer2-control matchConditionAny mstp matchBuiltIn mstp matchConditionAny ptp matchBuiltIn ptp matchConditionAny dot1xMBA matchBuiltIn dot1xMBA matchConditionAny ttl-exception matchBuiltIn ttl-exception matchConditionAny pvst matchBuiltIn pvst matchConditionAny mpls-route-miss matchBuiltIn mpls-route-miss matchConditionAny mvrp matchBuiltIn mvrp matchConditionAny layer3-slow-path matchBuiltIn layer3-slow-path matchConditionAny lacp matchBuiltIn lacp matchConditionAny self-ip-all matchBuiltIn self-ip-all matchConditionAny lldp matchBuiltIn lldp matchConditionAny bfd matchBuiltIn bfd matchConditionAny mlag-control matchBuiltIn mlag-control matchConditionAny self-ip-high-priority matchBuiltIn self-ip-high-priority matchConditionAny pim matchBuiltIn pim matchConditionAny bpdu matchBuiltIn bpdu matchConditionAny vrrp matchBuiltIn vrrp matchConditionAny ipv6-nd matchBuiltIn ipv6-nd matchConditionAny ospf matchBuiltIn ospf matchConditionAny unicast-route-overflow matchBuiltIn unicast-route-overflow matchConditionAny cfm matchBuiltIn cfm matchConditionAny routed-ip-options matchBuiltIn routed-ip-options matchConditionAny unicast-rpf-failure matchBuiltIn unicast-rpf-failure matchConditionAny self-bgp matchBuiltIn self-bgp matchConditionAny layer2-broadcast matchBuiltIn layer2-broadcast matchConditionAny mlag-control-heartbeat matchBuiltIn mlag-control-heartbeat matchConditionAny mapQos mapControlPlane copp-system-policy copp-system-tc3to5 actionSetBandwidth actionSetShape copp-system-l3ttl1 actionSetShape actionSetBandwidth copp-system-mlag actionSetShape actionSetBandwidth copp-system-linklocal actionSetShape actionSetBandwidth copp-system-l3rsvd actionSetShape actionSetBandwidth copp-system-ptp actionSetShape actionSetBandwidth copp-system-l3-low-pri actionSetBandwidth actionSetShape copp-system-PimPtp actionSetShape actionSetBandwidth copp-system-cfm-snoop actionSetShape actionSetBandwidth copp-system-mod actionSetBandwidth actionSetShape copp-system-cvx-heartbeat actionSetBandwidth actionSetShape copp-system-vrrp actionSetShape actionSetBandwidth copp-system-l2rsvd actionSetShape actionSetBandwidth copp-system-ipmcmiss actionSetShape actionSetBandwidth copp-system-arpresolver actionSetShape actionSetBandwidth copp-system-ipmcrsvd actionSetShape actionSetBandwidth copp-system-ipbroadcast actionSetBandwidth actionSetShape copp-system-acllog actionSetShape actionSetBandwidth copp-system-multicastsnoop actionSetBandwidth actionSetShape copp-system-dot1x-vxlan actionSetShape actionSetBandwidth copp-system-ptp-snoop actionSetShape actionSetBandwidth copp-system-dot1x-mba actionSetBandwidth actionSetShape copp-system-mirroring actionSetBandwidth actionSetShape copp-system-l2forward-includes-cpu actionSetBandwidth actionSetShape copp-system-dot1x-eapol-data actionSetShape actionSetBandwidth copp-system-lacp actionSetShape actionSetBandwidth copp-system-arp actionSetShape actionSetBandwidth copp-system-bfd actionSetBandwidth actionSetShape copp-system-l3lpmoverflow actionSetShape actionSetBandwidth copp-system-cfm actionSetShape actionSetBandwidth copp-system-bgp actionSetShape actionSetBandwidth copp-system-lldp actionSetShape actionSetBandwidth copp-system-mpls-ttl01 actionSetBandwidth actionSetShape copp-system-selfip-tc6to7 actionSetBandwidth actionSetShape copp-system-l3-high-pri actionSetShape actionSetBandwidth copp-system-ipmc actionSetShape actionSetBandwidth copp-system-l2ucast actionSetShape actionSetBandwidth copp-system-ipv6nd actionSetShape actionSetBandwidth copp-system-OspfIsis actionSetShape actionSetBandwidth copp-system-vxlan-vtep-learn actionSetShape actionSetBandwidth copp-system-selfip actionSetShape actionSetBandwidth copp-system-l3destmiss actionSetShape actionSetBandwidth copp-system-vxlan-encapsulation actionSetShape actionSetBandwidth copp-system-rsvp actionSetShape actionSetBandwidth copp-system-default-snoop actionSetShape actionSetBandwidth copp-system-bpdu actionSetBandwidth actionSetShape copp-system-mvrp actionSetShape actionSetBandwidth copp-system-iplocking actionSetBandwidth actionSetShape copp-system-bfd-ptp actionSetBandwidth actionSetShape copp-system-protocol-snoop actionSetShape actionSetBandwidth copp-system-cvx actionSetShape actionSetBandwidth copp-system-igmp actionSetShape actionSetBandwidth copp-system-mac-learn actionSetShape actionSetBandwidth copp-system-mpls-arp-suppress actionSetShape actionSetBandwidth copp-system-ipunicast actionSetShape actionSetBandwidth copp-system-pim actionSetShape actionSetBandwidth copp-system-egress-acllog actionSetShape actionSetBandwidth copp-system-ldp actionSetShape actionSetBandwidth copp-system-arp-inspect actionSetShape actionSetBandwidth copp-system-nat actionSetShape actionSetBandwidth copp-system-mtu actionSetShape actionSetBandwidth copp-system-l3-exception actionSetShape actionSetBandwidth copp-system-l3slowpath actionSetBandwidth actionSetShape copp-system-l2broadcast actionSetShape actionSetBandwidth copp-system-mcast actionSetBandwidth actionSetShape copp-system-glean actionSetShape actionSetBandwidth copp-system-l3ttl0 actionSetShape actionSetBandwidth copp-system-drop actionSetShape actionSetBandwidth copp-system-unicastarp actionSetShape actionSetBandwidth copp-system-acllog-sflow actionSetShape actionSetBandwidth copp-system-tc6to7 actionSetShape actionSetBandwidth copp-system-mpls-label-miss actionSetShape actionSetBandwidth copp-system-sflow actionSetShape actionSetBandwidth copp-system-default actionSetShape actionSetBandwidth matchIpAccessGroup 36 53 10 29 42 2 3 70 73 44 17 37 49 4 41 1 9 22 57 50 61 71 7 25 28 60 23 43 55 12 32 46 47 52 66 56 59 14 21 24 8 26 15 72 48 27 64 31 35 39 20 65 69 34 45 11 6 30 13 67 51 18 5 62 33 68 16 40 38 63 19 54 58 mapPdp mapQos input copp-system-policy mapControlPlane default true ARISTA_DEFAULT_PROFILE ARISTA_DEFAULT_PROFILE 0 0 CPU1 CPU1 CPU7 CPU7 CPU4 CPU4 CPU3 CPU3 CPU12 CPU12 CPU11 CPU11 CPU9 CPU9 CPU6 CPU6 CPU5 CPU5 CPU0 CPU0 CPU15 CPU15 CPU10 CPU10 CPU13 CPU13 CPU2 CPU2 CPU8 CPU8 CPU14 CPU14 Chassis Chassis SwitchAsic0 SwitchAsic0 Ethernet2 Ethernet2 SwitchAsic0 SwitchAsic0 Ethernet2 Ethernet2 Ethernet1 Ethernet1 Ethernet1 Ethernet1 Port1 Port1 Port2 Port2 Aboot Aboot EOS EOS chassis-hostName chassis-hostName Management0 0 Management0 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 172.20.20.14 172.20.20.14 24 172.20.20.1 172.20.20.1 2001:172:20:20::14 2001:172:20:20::14 64 Ethernet1 tacocat 0 Ethernet1 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 tacocat Ethernet2 0 Ethernet2 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 30 default BEST Arista Networks EOS version 4.28.0F-26924507.4280F (engineering build) running on an Arista cEOSLab ceos Ethernet2 Ethernet2 Ethernet1 Ethernet1 Management0 Management0 default 00:00:00:00:00:00 true oc-types:IPV4 default oc-ni-types:DEFAULT_INSTANCE static static 16 99999 dynamic dynamic 100000 362143 l2evpn l2evpn 1036288 1048575 srlb srlb 965536 1031071 l2evpnSharedEs l2evpnSharedEs 1031072 1032095 bgp-sr bgp-sr 900000 965535 isis-sr isis-sr 900000 965535 10000 true oc-pol-types:DIRECTLY_CONNECTED DIRECTLY_CONNECTED oc-pol-types:DIRECTLY_CONNECTED DIRECTLY_CONNECTED oc-pol-types:STATIC STATIC oc-pol-types:STATIC STATIC 0.0.0.0/0 0.0.0.0/0 AUTO_1_172-20-20-1 AUTO_1_172-20-20-1 1 172.20.20.1 oc-pol-types:BGP BGP oc-bgp-types:IPV4_UNICAST oc-bgp-types:IPV4_UNICAST oc-bgp-types:IPV6_UNICAST oc-bgp-types:IPV6_UNICAST oc-pol-types:BGP BGP isis-sr MPLS isis-sr isis-sr srlb MPLS srlb srlb oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4 oc-types:IPV4 oc-pol-types:DIRECTLY_CONNECTED
oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6 oc-types:IPV6 oc-pol-types:DIRECTLY_CONNECTED
oc-pol-types:STATIC oc-types:IPV4 oc-types:IPV4 oc-pol-types:STATIC
oc-pol-types:STATIC oc-types:IPV6 oc-types:IPV6 oc-pol-types:STATIC
1 default 1 oc-aaa-types:LOCAL admin $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/ oc-aaa-types:SYSTEM_ROLE_ADMIN admin 3 5 5 UTC ceos default true false default default 6030 false 10 50 0
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-system-out.txt
new file mode 100644
index 0000000..4b4aca1
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-arista_eos-system-out.txt
@@ -0,0 +1 @@
+action-none false mapControlPlane copp-system-tc3to5 matchConditionAny copp-system-nat matchConditionAny copp-system-rsvp matchConditionAny copp-system-mtu matchConditionAny copp-system-ipv6nd matchConditionAny copp-system-mod matchConditionAny copp-system-l2forward-includes-cpu matchConditionAny copp-system-selfip matchConditionAny copp-system-ipmcmiss matchConditionAny copp-system-mac-learn matchConditionAny copp-system-ptp matchConditionAny copp-system-l3lpmoverflow matchConditionAny copp-system-arp-inspect matchConditionAny copp-system-l3ttl0 matchConditionAny copp-system-cfm-snoop matchConditionAny copp-system-mpls-ttl01 matchConditionAny copp-system-sflow matchConditionAny copp-system-l3rsvd matchConditionAny copp-system-ipmcrsvd matchConditionAny copp-system-dot1x-mba matchConditionAny copp-system-l2rsvd matchConditionAny copp-system-OspfIsis matchConditionAny copp-system-mirroring matchConditionAny copp-system-pim matchConditionAny copp-system-protocol-snoop matchConditionAny copp-system-vrrp matchConditionAny copp-system-vxlan-vtep-learn matchConditionAny copp-system-mpls-label-miss matchConditionAny copp-system-bgp matchConditionAny copp-system-PimPtp matchConditionAny copp-system-arp matchConditionAny copp-system-iplocking matchConditionAny copp-system-arpresolver matchConditionAny copp-system-ptp-snoop matchConditionAny copp-system-bpdu matchConditionAny copp-system-igmp matchConditionAny copp-system-ipmc matchConditionAny copp-system-selfip-tc6to7 matchConditionAny copp-system-l2broadcast matchConditionAny copp-system-cfm matchConditionAny copp-system-lacp matchConditionAny copp-system-vxlan-encapsulation matchConditionAny copp-system-lldp matchConditionAny copp-system-default-snoop matchConditionAny copp-system-bfd matchConditionAny copp-system-ldp matchConditionAny copp-system-ipbroadcast matchConditionAny copp-system-mpls-arp-suppress matchConditionAny copp-system-mlag matchConditionAny copp-system-ipunicast matchConditionAny copp-system-acllog matchConditionAny copp-system-mvrp matchConditionAny copp-system-drop matchConditionAny copp-system-l3destmiss matchConditionAny copp-system-multicastsnoop matchConditionAny copp-system-l3-high-pri matchConditionAny copp-system-bfd-ptp matchConditionAny copp-system-acllog-sflow matchConditionAny copp-system-dot1x-eapol-data matchConditionAny copp-system-glean matchConditionAny copp-system-unicastarp matchConditionAny copp-system-l3-exception matchConditionAny copp-system-mcast matchConditionAny copp-system-tc6to7 matchConditionAny copp-system-cvx-heartbeat matchConditionAny copp-system-egress-acllog matchConditionAny copp-system-l3slowpath matchConditionAny copp-system-cvx matchConditionAny copp-system-l3-low-pri matchConditionAny copp-system-l3ttl1 matchConditionAny copp-system-linklocal matchConditionAny copp-system-dot1x-vxlan matchConditionAny copp-system-l2ucast matchConditionAny mapPdp cvx matchBuiltIn cvx matchConditionAny nat-miss matchBuiltIn nat-miss matchConditionAny ip-broadcast matchBuiltIn ip-broadcast matchConditionAny unicast-route-miss matchBuiltIn unicast-route-miss matchConditionAny vxlan-encapsulation matchBuiltIn vxlan-encapsulation matchConditionAny igmp matchBuiltIn igmp matchConditionAny mac-source-miss matchBuiltIn mac-source-miss matchConditionAny self-icmp matchBuiltIn self-icmp matchConditionAny natTcpFlags matchBuiltIn natTcpFlags matchConditionAny isis matchBuiltIn isis matchConditionAny link-local-multicast matchBuiltIn link-local-multicast matchConditionAny vxlan-vtep-learn matchBuiltIn vxlan-vtep-learn matchConditionAny layer3-control matchBuiltIn layer3-control matchConditionAny arp matchBuiltIn arp matchConditionAny self-ip-low-priority matchBuiltIn self-ip-low-priority matchConditionAny arp-needed matchBuiltIn arp-needed matchConditionAny dhcp matchBuiltIn dhcp matchConditionAny multicast-route-miss matchBuiltIn multicast-route-miss matchConditionAny layer2-control matchBuiltIn layer2-control matchConditionAny mstp matchBuiltIn mstp matchConditionAny ptp matchBuiltIn ptp matchConditionAny dot1xMBA matchBuiltIn dot1xMBA matchConditionAny ttl-exception matchBuiltIn ttl-exception matchConditionAny pvst matchBuiltIn pvst matchConditionAny mpls-route-miss matchBuiltIn mpls-route-miss matchConditionAny mvrp matchBuiltIn mvrp matchConditionAny layer3-slow-path matchBuiltIn layer3-slow-path matchConditionAny lacp matchBuiltIn lacp matchConditionAny self-ip-all matchBuiltIn self-ip-all matchConditionAny lldp matchBuiltIn lldp matchConditionAny bfd matchBuiltIn bfd matchConditionAny mlag-control matchBuiltIn mlag-control matchConditionAny self-ip-high-priority matchBuiltIn self-ip-high-priority matchConditionAny pim matchBuiltIn pim matchConditionAny bpdu matchBuiltIn bpdu matchConditionAny vrrp matchBuiltIn vrrp matchConditionAny ipv6-nd matchBuiltIn ipv6-nd matchConditionAny ospf matchBuiltIn ospf matchConditionAny unicast-route-overflow matchBuiltIn unicast-route-overflow matchConditionAny cfm matchBuiltIn cfm matchConditionAny routed-ip-options matchBuiltIn routed-ip-options matchConditionAny unicast-rpf-failure matchBuiltIn unicast-rpf-failure matchConditionAny self-bgp matchBuiltIn self-bgp matchConditionAny layer2-broadcast matchBuiltIn layer2-broadcast matchConditionAny mlag-control-heartbeat matchBuiltIn mlag-control-heartbeat matchConditionAny mapQos mapControlPlane copp-system-policy copp-system-tc3to5 actionSetBandwidth actionSetShape copp-system-l3ttl1 actionSetShape actionSetBandwidth copp-system-mlag actionSetShape actionSetBandwidth copp-system-linklocal actionSetShape actionSetBandwidth copp-system-l3rsvd actionSetShape actionSetBandwidth copp-system-ptp actionSetShape actionSetBandwidth copp-system-l3-low-pri actionSetBandwidth actionSetShape copp-system-PimPtp actionSetShape actionSetBandwidth copp-system-cfm-snoop actionSetShape actionSetBandwidth copp-system-mod actionSetBandwidth actionSetShape copp-system-cvx-heartbeat actionSetBandwidth actionSetShape copp-system-vrrp actionSetShape actionSetBandwidth copp-system-l2rsvd actionSetShape actionSetBandwidth copp-system-ipmcmiss actionSetShape actionSetBandwidth copp-system-arpresolver actionSetShape actionSetBandwidth copp-system-ipmcrsvd actionSetShape actionSetBandwidth copp-system-ipbroadcast actionSetBandwidth actionSetShape copp-system-acllog actionSetShape actionSetBandwidth copp-system-multicastsnoop actionSetBandwidth actionSetShape copp-system-dot1x-vxlan actionSetShape actionSetBandwidth copp-system-ptp-snoop actionSetShape actionSetBandwidth copp-system-dot1x-mba actionSetBandwidth actionSetShape copp-system-mirroring actionSetBandwidth actionSetShape copp-system-l2forward-includes-cpu actionSetBandwidth actionSetShape copp-system-dot1x-eapol-data actionSetShape actionSetBandwidth copp-system-lacp actionSetShape actionSetBandwidth copp-system-arp actionSetShape actionSetBandwidth copp-system-bfd actionSetBandwidth actionSetShape copp-system-l3lpmoverflow actionSetShape actionSetBandwidth copp-system-cfm actionSetShape actionSetBandwidth copp-system-bgp actionSetShape actionSetBandwidth copp-system-lldp actionSetShape actionSetBandwidth copp-system-mpls-ttl01 actionSetBandwidth actionSetShape copp-system-selfip-tc6to7 actionSetBandwidth actionSetShape copp-system-l3-high-pri actionSetShape actionSetBandwidth copp-system-ipmc actionSetShape actionSetBandwidth copp-system-l2ucast actionSetShape actionSetBandwidth copp-system-ipv6nd actionSetShape actionSetBandwidth copp-system-OspfIsis actionSetShape actionSetBandwidth copp-system-vxlan-vtep-learn actionSetShape actionSetBandwidth copp-system-selfip actionSetShape actionSetBandwidth copp-system-l3destmiss actionSetShape actionSetBandwidth copp-system-vxlan-encapsulation actionSetShape actionSetBandwidth copp-system-rsvp actionSetShape actionSetBandwidth copp-system-default-snoop actionSetShape actionSetBandwidth copp-system-bpdu actionSetBandwidth actionSetShape copp-system-mvrp actionSetShape actionSetBandwidth copp-system-iplocking actionSetBandwidth actionSetShape copp-system-bfd-ptp actionSetBandwidth actionSetShape copp-system-protocol-snoop actionSetShape actionSetBandwidth copp-system-cvx actionSetShape actionSetBandwidth copp-system-igmp actionSetShape actionSetBandwidth copp-system-mac-learn actionSetShape actionSetBandwidth copp-system-mpls-arp-suppress actionSetShape actionSetBandwidth copp-system-ipunicast actionSetShape actionSetBandwidth copp-system-pim actionSetShape actionSetBandwidth copp-system-egress-acllog actionSetShape actionSetBandwidth copp-system-ldp actionSetShape actionSetBandwidth copp-system-arp-inspect actionSetShape actionSetBandwidth copp-system-nat actionSetShape actionSetBandwidth copp-system-mtu actionSetShape actionSetBandwidth copp-system-l3-exception actionSetShape actionSetBandwidth copp-system-l3slowpath actionSetBandwidth actionSetShape copp-system-l2broadcast actionSetShape actionSetBandwidth copp-system-mcast actionSetBandwidth actionSetShape copp-system-glean actionSetShape actionSetBandwidth copp-system-l3ttl0 actionSetShape actionSetBandwidth copp-system-drop actionSetShape actionSetBandwidth copp-system-unicastarp actionSetShape actionSetBandwidth copp-system-acllog-sflow actionSetShape actionSetBandwidth copp-system-tc6to7 actionSetShape actionSetBandwidth copp-system-mpls-label-miss actionSetShape actionSetBandwidth copp-system-sflow actionSetShape actionSetBandwidth copp-system-default actionSetShape actionSetBandwidth matchIpAccessGroup 36 53 10 29 42 2 3 70 73 44 17 37 49 4 41 1 9 22 57 50 61 71 7 25 28 60 23 43 55 12 32 46 47 52 66 56 59 14 21 24 8 26 15 72 48 27 64 31 35 39 20 65 69 34 45 11 6 30 13 67 51 18 5 62 33 68 16 40 38 63 19 54 58 mapPdp mapQos input copp-system-policy mapControlPlane default true ARISTA_DEFAULT_PROFILE ARISTA_DEFAULT_PROFILE 0 0 CPU1 CPU1 CPU7 CPU7 CPU4 CPU4 CPU3 CPU3 CPU12 CPU12 CPU11 CPU11 CPU9 CPU9 CPU6 CPU6 CPU5 CPU5 CPU0 CPU0 CPU15 CPU15 CPU10 CPU10 CPU13 CPU13 CPU2 CPU2 CPU8 CPU8 CPU14 CPU14 Chassis Chassis SwitchAsic0 SwitchAsic0 Ethernet2 Ethernet2 SwitchAsic0 SwitchAsic0 Ethernet2 Ethernet2 Ethernet1 Ethernet1 Ethernet1 Ethernet1 Port1 Port1 Port2 Port2 Aboot Aboot EOS EOS chassis-hostName chassis-hostName Management0 0 Management0 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 172.20.20.14 172.20.20.14 24 172.20.20.1 172.20.20.1 2001:172:20:20::14 2001:172:20:20::14 64 Ethernet1 tacocat 0 Ethernet1 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 tacocat Ethernet2 0 Ethernet2 ianaift:ethernetCsmacd false false false false 00:00:00:00:00:00 SPEED_UNKNOWN false 0 30 default BEST Arista Networks EOS version 4.28.0F-26924507.4280F (engineering build) running on an Arista cEOSLab ceos Ethernet2 Ethernet2 Ethernet1 Ethernet1 Management0 Management0 default 00:00:00:00:00:00 true oc-types:IPV4 default oc-ni-types:DEFAULT_INSTANCE static static 16 99999 dynamic dynamic 100000 362143 l2evpn l2evpn 1036288 1048575 srlb srlb 965536 1031071 l2evpnSharedEs l2evpnSharedEs 1031072 1032095 bgp-sr bgp-sr 900000 965535 isis-sr isis-sr 900000 965535 10000 true oc-pol-types:DIRECTLY_CONNECTED DIRECTLY_CONNECTED oc-pol-types:DIRECTLY_CONNECTED DIRECTLY_CONNECTED oc-pol-types:STATIC STATIC oc-pol-types:STATIC STATIC 0.0.0.0/0 0.0.0.0/0 AUTO_1_172-20-20-1 AUTO_1_172-20-20-1 1 172.20.20.1 oc-pol-types:BGP BGP oc-bgp-types:IPV4_UNICAST oc-bgp-types:IPV4_UNICAST oc-bgp-types:IPV6_UNICAST oc-bgp-types:IPV6_UNICAST oc-pol-types:BGP BGP isis-sr MPLS isis-sr isis-sr srlb MPLS srlb srlb oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4 oc-types:IPV4 oc-pol-types:DIRECTLY_CONNECTED
oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6 oc-types:IPV6 oc-pol-types:DIRECTLY_CONNECTED
oc-pol-types:STATIC oc-types:IPV4 oc-types:IPV4 oc-pol-types:STATIC
oc-pol-types:STATIC oc-types:IPV6 oc-types:IPV6 oc-pol-types:STATIC
1 default 1 oc-aaa-types:LOCAL admin $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/ oc-aaa-types:SYSTEM_ROLE_ADMIN admin 3 5 5 UTC ceos default true false default default 6030 false 10 50 0
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..2f64c91
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-standard-out.txt
@@ -0,0 +1 @@
+16.12 72329 sch-smart-licensing@cisco.com CiscoTAC-1 true serial true vr-csr 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc admin 15 0 admin boxen 15 0 b0x3N-b0x3N boxen.box nd 2147483647 false meraki-fqdn-dns false true 1 10.0.0.15 255.255.255.0 false false true 10 false false true 2 false false true 3 false false true 4 false false true 5 false false true 6 false false true 7 false false true 8 false false true 9 false false true authenticated SLA-TrustPoint 01 ca TP-self-signed-2238708418 01 self-signed SLA-TrustPoint crl TP-self-signed-2238708418 none cn=IOS-Self-Signed-Certificate-2238708418 CSR1000V 9MM1GU3SD2V 0 1 0 1 0 2 4 minimal false false false false true private meraki-fqdn-dns ACL_IPV4 meraki-fqdn-dns ACL_IPV4 GigabitEthernet1 GigabitEthernet1 ianaift:ethernetCsmacd true 0 0 true 10.0.0.15 10.0.0.15 24 false 52:54:00:12:34:56 true true GigabitEthernet10 GigabitEthernet10 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:ca:d9:09 true true GigabitEthernet2 GigabitEthernet2 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:7d:ca:01 true true GigabitEthernet3 GigabitEthernet3 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:13:1f:02 true true GigabitEthernet4 GigabitEthernet4 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:f6:a7:03 true true GigabitEthernet5 GigabitEthernet5 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:5b:dc:04 true true GigabitEthernet6 GigabitEthernet6 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:a0:a2:05 true true GigabitEthernet7 GigabitEthernet7 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:70:f2:06 true true GigabitEthernet8 GigabitEthernet8 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:28:12:07 true true GigabitEthernet9 GigabitEthernet9 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:eb:de:08 true true false GigabitEthernet1 GigabitEthernet1 true GigabitEthernet10 GigabitEthernet10 true GigabitEthernet2 GigabitEthernet2 true GigabitEthernet3 GigabitEthernet3 true GigabitEthernet4 GigabitEthernet4 true GigabitEthernet5 GigabitEthernet5 true GigabitEthernet6 GigabitEthernet6 true GigabitEthernet7 GigabitEthernet7 true GigabitEthernet8 GigabitEthernet8 true GigabitEthernet9 GigabitEthernet9 true default default oc-ni-types:DEFAULT_INSTANCE default-vrf [read-only] oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4 oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4
oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6 oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6
oc-pol-types:STATIC oc-types:IPV4 oc-pol-types:STATIC oc-types:IPV4
oc-pol-types:STATIC oc-types:IPV6 oc-pol-types:STATIC oc-types:IPV6
oc-pol-types:STATIC DEFAULT oc-pol-types:STATIC DEFAULT oc-pol-types:DIRECTLY_CONNECTED DEFAULT oc-pol-types:DIRECTLY_CONNECTED DEFAULT GigabitEthernet1 ianaift:ethernetCsmacd true 10.0.0.15 255.255.255.0 GigabitEthernet10 ianaift:ethernetCsmacd false GigabitEthernet2 ianaift:ethernetCsmacd false GigabitEthernet3 ianaift:ethernetCsmacd false GigabitEthernet4 ianaift:ethernetCsmacd false GigabitEthernet5 ianaift:ethernetCsmacd false GigabitEthernet6 ianaift:ethernetCsmacd false GigabitEthernet7 ianaift:ethernetCsmacd false GigabitEthernet8 ianaift:ethernetCsmacd false GigabitEthernet9 ianaift:ethernetCsmacd false true deny deny deny true admin PRIV15 permit-all * * permit default default-vrf [read-only] static 1
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-system-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..2f64c91
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxe-system-out.txt
@@ -0,0 +1 @@
+16.12 72329 sch-smart-licensing@cisco.com CiscoTAC-1 true serial true vr-csr 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc admin 15 0 admin boxen 15 0 b0x3N-b0x3N boxen.box nd 2147483647 false meraki-fqdn-dns false true 1 10.0.0.15 255.255.255.0 false false true 10 false false true 2 false false true 3 false false true 4 false false true 5 false false true 6 false false true 7 false false true 8 false false true 9 false false true authenticated SLA-TrustPoint 01 ca TP-self-signed-2238708418 01 self-signed SLA-TrustPoint crl TP-self-signed-2238708418 none cn=IOS-Self-Signed-Certificate-2238708418 CSR1000V 9MM1GU3SD2V 0 1 0 1 0 2 4 minimal false false false false true private meraki-fqdn-dns ACL_IPV4 meraki-fqdn-dns ACL_IPV4 GigabitEthernet1 GigabitEthernet1 ianaift:ethernetCsmacd true 0 0 true 10.0.0.15 10.0.0.15 24 false 52:54:00:12:34:56 true true GigabitEthernet10 GigabitEthernet10 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:ca:d9:09 true true GigabitEthernet2 GigabitEthernet2 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:7d:ca:01 true true GigabitEthernet3 GigabitEthernet3 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:13:1f:02 true true GigabitEthernet4 GigabitEthernet4 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:f6:a7:03 true true GigabitEthernet5 GigabitEthernet5 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:5b:dc:04 true true GigabitEthernet6 GigabitEthernet6 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:a0:a2:05 true true GigabitEthernet7 GigabitEthernet7 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:70:f2:06 true true GigabitEthernet8 GigabitEthernet8 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:28:12:07 true true GigabitEthernet9 GigabitEthernet9 ianaift:ethernetCsmacd false 0 0 false false 52:54:00:eb:de:08 true true false GigabitEthernet1 GigabitEthernet1 true GigabitEthernet10 GigabitEthernet10 true GigabitEthernet2 GigabitEthernet2 true GigabitEthernet3 GigabitEthernet3 true GigabitEthernet4 GigabitEthernet4 true GigabitEthernet5 GigabitEthernet5 true GigabitEthernet6 GigabitEthernet6 true GigabitEthernet7 GigabitEthernet7 true GigabitEthernet8 GigabitEthernet8 true GigabitEthernet9 GigabitEthernet9 true default default oc-ni-types:DEFAULT_INSTANCE default-vrf [read-only] oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4 oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV4
oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6 oc-pol-types:DIRECTLY_CONNECTED oc-types:IPV6
oc-pol-types:STATIC oc-types:IPV4 oc-pol-types:STATIC oc-types:IPV4
oc-pol-types:STATIC oc-types:IPV6 oc-pol-types:STATIC oc-types:IPV6
oc-pol-types:STATIC DEFAULT oc-pol-types:STATIC DEFAULT oc-pol-types:DIRECTLY_CONNECTED DEFAULT oc-pol-types:DIRECTLY_CONNECTED DEFAULT GigabitEthernet1 ianaift:ethernetCsmacd true 10.0.0.15 255.255.255.0 GigabitEthernet10 ianaift:ethernetCsmacd false GigabitEthernet2 ianaift:ethernetCsmacd false GigabitEthernet3 ianaift:ethernetCsmacd false GigabitEthernet4 ianaift:ethernetCsmacd false GigabitEthernet5 ianaift:ethernetCsmacd false GigabitEthernet6 ianaift:ethernetCsmacd false GigabitEthernet7 ianaift:ethernetCsmacd false GigabitEthernet8 ianaift:ethernetCsmacd false GigabitEthernet9 ianaift:ethernetCsmacd false true deny deny deny true admin PRIV15 permit-all * * permit default default-vrf [read-only] static 1
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..f4b2da8
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-standard-out.txt
@@ -0,0 +1,176 @@
+
+
+
+
+ vr-xrv9k
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ boxen
+
+
+ root-lr
+
+
+ cisco-support
+
+
+ $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+
+
+ 1
+ clab
+
+
+ root-lr
+
+
+ cisco-support
+
+
+ 1511070D060A7A767B
+
+
+
+
+
+ act
+ MgmtEth0/RP0/CPU0/0
+
+
+
+ 10.0.0.15
+ 255.255.255.0
+
+
+
+
+
+ act
+ GigabitEthernet0/0/0/0
+
+
+
+ act
+ GigabitEthernet0/0/0/1
+
+
+
+ act
+ GigabitEthernet0/0/0/2
+
+
+
+ act
+ GigabitEthernet0/0/0/3
+
+
+
+ act
+ GigabitEthernet0/0/0/4
+
+
+
+ act
+ GigabitEthernet0/0/0/5
+
+
+
+ act
+ GigabitEthernet0/0/0/6
+
+
+
+ act
+ GigabitEthernet0/0/0/7
+
+
+
+ act
+ GigabitEthernet0/0/0/8
+
+
+
+ act
+ GigabitEthernet0/0/0/9
+
+
+
+ act
+ GigabitEthernet0/0/0/10
+
+
+
+ act
+ GigabitEthernet0/0/0/11
+
+
+
+ act
+ GigabitEthernet0/0/0/12
+
+
+
+ act
+ GigabitEthernet0/0/0/13
+
+
+
+ act
+ GigabitEthernet0/0/0/14
+
+
+
+ act
+ GigabitEthernet0/0/0/15
+
+
+
+
+
+ true
+
+
+ CiscoTAC-1
+
+
+
+ email
+ false
+
+
+ http
+ true
+
+
+
+
+
+
+
+ 600
+
+
+
+ default
+
+
+
+
+
+
+ 57400
+
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-system-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..f4b2da8
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_iosxr-system-out.txt
@@ -0,0 +1,176 @@
+
+
+
+
+ vr-xrv9k
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ boxen
+
+
+ root-lr
+
+
+ cisco-support
+
+
+ $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+
+
+ 1
+ clab
+
+
+ root-lr
+
+
+ cisco-support
+
+
+ 1511070D060A7A767B
+
+
+
+
+
+ act
+ MgmtEth0/RP0/CPU0/0
+
+
+
+ 10.0.0.15
+ 255.255.255.0
+
+
+
+
+
+ act
+ GigabitEthernet0/0/0/0
+
+
+
+ act
+ GigabitEthernet0/0/0/1
+
+
+
+ act
+ GigabitEthernet0/0/0/2
+
+
+
+ act
+ GigabitEthernet0/0/0/3
+
+
+
+ act
+ GigabitEthernet0/0/0/4
+
+
+
+ act
+ GigabitEthernet0/0/0/5
+
+
+
+ act
+ GigabitEthernet0/0/0/6
+
+
+
+ act
+ GigabitEthernet0/0/0/7
+
+
+
+ act
+ GigabitEthernet0/0/0/8
+
+
+
+ act
+ GigabitEthernet0/0/0/9
+
+
+
+ act
+ GigabitEthernet0/0/0/10
+
+
+
+ act
+ GigabitEthernet0/0/0/11
+
+
+
+ act
+ GigabitEthernet0/0/0/12
+
+
+
+ act
+ GigabitEthernet0/0/0/13
+
+
+
+ act
+ GigabitEthernet0/0/0/14
+
+
+
+ act
+ GigabitEthernet0/0/0/15
+
+
+
+
+
+ true
+
+
+ CiscoTAC-1
+
+
+
+ email
+ false
+
+
+ http
+ true
+
+
+
+
+
+
+
+ 600
+
+
+
+ default
+
+
+
+
+
+
+ 57400
+
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..812c4e0
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-standard-out.txt
@@ -0,0 +1,15055 @@
+
+
+
+
+ enabled
+
+
+ enabled
+
+ error
+ enabled
+ disabled
+ disabled
+ 174080
+ 1
+
+
+ management
+
+
+ mgmt0
+ enabled
+ disabled
+ disabled
+ enabled
+ enabled
+ enabled
+ disabled
+ disabled
+ disabled
+ 1500
+
+
+
+
+ default
+
+
+ 600
+ 250
+ error
+ 180
+ disabled
+ 200
+ 0
+ 1500
+
+
+
+
+
+ vlan-1
+ active
+ active
+ mac
+ mdst-flood
+ bridge,route
+ 1
+ CE
+ disable
+
+
+ enable
+
+
+
+ enable
+ bootflash:/nxos.9.2.4.bin
+ bootflash:/nxos.9.2.4.bin
+
+ disable
+
+
+
+ enabled
+ none
+ 180
+
+
+ eth1/27
+ enabled
+
+
+ eth1/115
+ enabled
+
+
+ eth1/15
+ enabled
+
+
+ eth1/100
+ enabled
+
+
+ eth1/105
+ enabled
+
+
+ eth1/16
+ enabled
+
+
+ eth1/46
+ enabled
+
+
+ eth1/56
+ enabled
+
+
+ eth1/25
+ enabled
+
+
+ eth1/122
+ enabled
+
+
+ eth1/81
+ enabled
+
+
+ eth1/88
+ enabled
+
+
+ eth1/65
+ enabled
+
+
+ eth1/76
+ enabled
+
+
+ eth1/127
+ enabled
+
+
+ eth1/41
+ enabled
+
+
+ eth1/30
+ enabled
+
+
+ eth1/128
+ enabled
+
+
+ mgmt0
+ enabled
+
+
+ eth1/91
+ enabled
+
+
+ eth1/52
+ enabled
+
+
+ eth1/71
+ enabled
+
+
+ eth1/78
+ enabled
+
+
+ eth1/79
+ enabled
+
+
+ eth1/33
+ enabled
+
+
+ eth1/99
+ enabled
+
+
+ eth1/61
+ enabled
+
+
+ eth1/26
+ enabled
+
+
+ eth1/62
+ enabled
+
+
+ eth1/114
+ enabled
+
+
+ eth1/124
+ enabled
+
+
+ eth1/22
+ enabled
+
+
+ eth1/50
+ enabled
+
+
+ eth1/103
+ enabled
+
+
+ eth1/2
+ enabled
+
+
+ eth1/107
+ enabled
+
+
+ eth1/45
+ enabled
+
+
+ eth1/72
+ enabled
+
+
+ eth1/74
+ enabled
+
+
+ eth1/86
+ enabled
+
+
+ eth1/96
+ enabled
+
+
+ eth1/70
+ enabled
+
+
+ eth1/40
+ enabled
+
+
+ eth1/10
+ enabled
+
+
+ eth1/5
+ enabled
+
+
+ eth1/95
+ enabled
+
+
+ eth1/18
+ enabled
+
+
+ eth1/55
+ enabled
+
+
+ eth1/63
+ enabled
+
+
+ eth1/111
+ enabled
+
+
+ eth1/117
+ enabled
+
+
+ eth1/90
+ enabled
+
+
+ eth1/23
+ enabled
+
+
+ eth1/75
+ enabled
+
+
+ eth1/106
+ enabled
+
+
+ eth1/38
+ enabled
+
+
+ eth1/69
+ enabled
+
+
+ eth1/7
+ enabled
+
+
+ eth1/67
+ enabled
+
+
+ eth1/43
+ enabled
+
+
+ eth1/11
+ enabled
+
+
+ eth1/110
+ enabled
+
+
+ eth1/37
+ enabled
+
+
+ eth1/51
+ enabled
+
+
+ eth1/42
+ enabled
+
+
+ eth1/49
+ enabled
+
+
+ eth1/57
+ enabled
+
+
+ eth1/8
+ enabled
+
+
+ eth1/53
+ enabled
+
+
+ eth1/113
+ enabled
+
+
+ eth1/68
+ enabled
+
+
+ eth1/6
+ enabled
+
+
+ eth1/20
+ enabled
+
+
+ eth1/84
+ enabled
+
+
+ eth1/123
+ enabled
+
+
+ eth1/109
+ enabled
+
+
+ eth1/58
+ enabled
+
+
+ eth1/4
+ enabled
+
+
+ eth1/73
+ enabled
+
+
+ eth1/34
+ enabled
+
+
+ eth1/60
+ enabled
+
+
+ eth1/112
+ enabled
+
+
+ eth1/66
+ enabled
+
+
+ eth1/24
+ enabled
+
+
+ eth1/89
+ enabled
+
+
+ eth1/97
+ enabled
+
+
+ eth1/36
+ enabled
+
+
+ eth1/102
+ enabled
+
+
+ eth1/121
+ enabled
+
+
+ eth1/44
+ enabled
+
+
+ eth1/12
+ enabled
+
+
+ eth1/59
+ enabled
+
+
+ eth1/119
+ enabled
+
+
+ eth1/17
+ enabled
+
+
+ eth1/21
+ enabled
+
+
+ eth1/31
+ enabled
+
+
+ eth1/32
+ enabled
+
+
+ eth1/14
+ enabled
+
+
+ eth1/108
+ enabled
+
+
+ eth1/101
+ enabled
+
+
+ eth1/13
+ enabled
+
+
+ eth1/64
+ enabled
+
+
+ eth1/125
+ enabled
+
+
+ eth1/47
+ enabled
+
+
+ eth1/54
+ enabled
+
+
+ eth1/19
+ enabled
+
+
+ eth1/87
+ enabled
+
+
+ eth1/77
+ enabled
+
+
+ eth1/1
+ enabled
+
+
+ eth1/126
+ enabled
+
+
+ eth1/104
+ enabled
+
+
+ eth1/3
+ enabled
+
+
+ eth1/98
+ enabled
+
+
+ eth1/82
+ enabled
+
+
+ eth1/29
+ enabled
+
+
+ eth1/28
+ enabled
+
+
+ eth1/80
+ enabled
+
+
+ eth1/116
+ enabled
+
+
+ eth1/9
+ enabled
+
+
+ eth1/35
+ enabled
+
+
+ eth1/118
+ enabled
+
+
+ eth1/120
+ enabled
+
+
+ eth1/93
+ enabled
+
+
+ eth1/85
+ enabled
+
+
+ eth1/94
+ enabled
+
+
+ eth1/39
+ enabled
+
+
+ eth1/48
+ enabled
+
+
+ eth1/83
+ enabled
+
+
+ eth1/92
+ enabled
+
+
+ 60
+ v2
+
+
+
+ enabled
+
+ enabled
+ enabled
+ disabled
+ disabled
+ disabled
+
+
+
+ enabled
+ disabled
+ 24hours
+ ntp
+ 1
+
+
+
+
+ eth1/11
+ topology/pod-1/paths-0/pathep-[eth1/11]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/11']
+
+
+
+ eth1/64
+ topology/pod-1/paths-0/pathep-[eth1/64]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/64']
+
+
+
+ eth1/12
+ topology/pod-1/paths-0/pathep-[eth1/12]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/12']
+
+
+
+ eth1/53
+ topology/pod-1/paths-0/pathep-[eth1/53]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/53']
+
+
+
+ eth1/59
+ topology/pod-1/paths-0/pathep-[eth1/59]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/59']
+
+
+
+ eth1/93
+ topology/pod-1/paths-0/pathep-[eth1/93]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/93']
+
+
+
+ eth1/46
+ topology/pod-1/paths-0/pathep-[eth1/46]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/46']
+
+
+
+ eth1/24
+ topology/pod-1/paths-0/pathep-[eth1/24]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/24']
+
+
+
+ eth1/120
+ topology/pod-1/paths-0/pathep-[eth1/120]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/120']
+
+
+
+ eth1/66
+ topology/pod-1/paths-0/pathep-[eth1/66]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/66']
+
+
+
+ eth1/122
+ topology/pod-1/paths-0/pathep-[eth1/122]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/122']
+
+
+
+ eth1/45
+ topology/pod-1/paths-0/pathep-[eth1/45]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/45']
+
+
+
+ eth1/123
+ topology/pod-1/paths-0/pathep-[eth1/123]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/123']
+
+
+
+ eth1/73
+ topology/pod-1/paths-0/pathep-[eth1/73]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/73']
+
+
+
+ eth1/128
+ topology/pod-1/paths-0/pathep-[eth1/128]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/128']
+
+
+
+ eth1/32
+ topology/pod-1/paths-0/pathep-[eth1/32]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/32']
+
+
+
+ eth1/126
+ topology/pod-1/paths-0/pathep-[eth1/126]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/126']
+
+
+
+ eth1/95
+ topology/pod-1/paths-0/pathep-[eth1/95]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/95']
+
+
+
+ eth1/81
+ topology/pod-1/paths-0/pathep-[eth1/81]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/81']
+
+
+
+ eth1/7
+ topology/pod-1/paths-0/pathep-[eth1/7]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/7']
+
+
+
+ eth1/23
+ topology/pod-1/paths-0/pathep-[eth1/23]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/23']
+
+
+
+ eth1/76
+ topology/pod-1/paths-0/pathep-[eth1/76]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/76']
+
+
+
+ eth1/71
+ topology/pod-1/paths-0/pathep-[eth1/71]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/71']
+
+
+
+ eth1/55
+ topology/pod-1/paths-0/pathep-[eth1/55]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/55']
+
+
+
+ eth1/9
+ topology/pod-1/paths-0/pathep-[eth1/9]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/9']
+
+
+
+ eth1/121
+ topology/pod-1/paths-0/pathep-[eth1/121]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/121']
+
+
+
+ eth1/88
+ topology/pod-1/paths-0/pathep-[eth1/88]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/88']
+
+
+
+ eth1/102
+ topology/pod-1/paths-0/pathep-[eth1/102]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/102']
+
+
+
+ eth1/107
+ topology/pod-1/paths-0/pathep-[eth1/107]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/107']
+
+
+
+ eth1/108
+ topology/pod-1/paths-0/pathep-[eth1/108]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/108']
+
+
+
+ eth1/17
+ topology/pod-1/paths-0/pathep-[eth1/17]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/17']
+
+
+
+ eth1/5
+ topology/pod-1/paths-0/pathep-[eth1/5]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/5']
+
+
+
+ eth1/48
+ topology/pod-1/paths-0/pathep-[eth1/48]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/48']
+
+
+
+ eth1/61
+ topology/pod-1/paths-0/pathep-[eth1/61]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/61']
+
+
+
+ eth1/72
+ topology/pod-1/paths-0/pathep-[eth1/72]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/72']
+
+
+
+ eth1/43
+ topology/pod-1/paths-0/pathep-[eth1/43]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/43']
+
+
+
+ eth1/105
+ topology/pod-1/paths-0/pathep-[eth1/105]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/105']
+
+
+
+ eth1/33
+ topology/pod-1/paths-0/pathep-[eth1/33]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/33']
+
+
+
+ eth1/54
+ topology/pod-1/paths-0/pathep-[eth1/54]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/54']
+
+
+
+ eth1/67
+ topology/pod-1/paths-0/pathep-[eth1/67]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/67']
+
+
+
+ eth1/28
+ topology/pod-1/paths-0/pathep-[eth1/28]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/28']
+
+
+
+ eth1/21
+ topology/pod-1/paths-0/pathep-[eth1/21]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/21']
+
+
+
+ eth1/62
+ topology/pod-1/paths-0/pathep-[eth1/62]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/62']
+
+
+
+ eth1/22
+ topology/pod-1/paths-0/pathep-[eth1/22]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/22']
+
+
+
+ eth1/42
+ topology/pod-1/paths-0/pathep-[eth1/42]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/42']
+
+
+
+ eth1/91
+ topology/pod-1/paths-0/pathep-[eth1/91]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/91']
+
+
+
+ eth1/34
+ topology/pod-1/paths-0/pathep-[eth1/34]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/34']
+
+
+
+ eth1/103
+ topology/pod-1/paths-0/pathep-[eth1/103]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/103']
+
+
+
+ eth1/35
+ topology/pod-1/paths-0/pathep-[eth1/35]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/35']
+
+
+
+ eth1/90
+ topology/pod-1/paths-0/pathep-[eth1/90]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/90']
+
+
+
+ eth1/58
+ topology/pod-1/paths-0/pathep-[eth1/58]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/58']
+
+
+
+ eth1/60
+ topology/pod-1/paths-0/pathep-[eth1/60]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/60']
+
+
+
+ eth1/96
+ topology/pod-1/paths-0/pathep-[eth1/96]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/96']
+
+
+
+ eth1/20
+ topology/pod-1/paths-0/pathep-[eth1/20]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/20']
+
+
+
+ eth1/127
+ topology/pod-1/paths-0/pathep-[eth1/127]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/127']
+
+
+
+ eth1/56
+ topology/pod-1/paths-0/pathep-[eth1/56]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/56']
+
+
+
+ eth1/79
+ topology/pod-1/paths-0/pathep-[eth1/79]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/79']
+
+
+
+ eth1/31
+ topology/pod-1/paths-0/pathep-[eth1/31]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/31']
+
+
+
+ eth1/82
+ topology/pod-1/paths-0/pathep-[eth1/82]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/82']
+
+
+
+ eth1/4
+ topology/pod-1/paths-0/pathep-[eth1/4]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/4']
+
+
+
+ eth1/25
+ topology/pod-1/paths-0/pathep-[eth1/25]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/25']
+
+
+
+ eth1/92
+ topology/pod-1/paths-0/pathep-[eth1/92]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/92']
+
+
+
+ eth1/112
+ topology/pod-1/paths-0/pathep-[eth1/112]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/112']
+
+
+
+ eth1/69
+ topology/pod-1/paths-0/pathep-[eth1/69]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/69']
+
+
+
+ eth1/86
+ topology/pod-1/paths-0/pathep-[eth1/86]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/86']
+
+
+
+ eth1/113
+ topology/pod-1/paths-0/pathep-[eth1/113]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/113']
+
+
+
+ eth1/51
+ topology/pod-1/paths-0/pathep-[eth1/51]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/51']
+
+
+
+ eth1/63
+ topology/pod-1/paths-0/pathep-[eth1/63]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/63']
+
+
+
+ eth1/115
+ topology/pod-1/paths-0/pathep-[eth1/115]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/115']
+
+
+
+ eth1/18
+ topology/pod-1/paths-0/pathep-[eth1/18]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/18']
+
+
+
+ eth1/65
+ topology/pod-1/paths-0/pathep-[eth1/65]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/65']
+
+
+
+ eth1/109
+ topology/pod-1/paths-0/pathep-[eth1/109]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/109']
+
+
+
+ eth1/70
+ topology/pod-1/paths-0/pathep-[eth1/70]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/70']
+
+
+
+ eth1/114
+ topology/pod-1/paths-0/pathep-[eth1/114]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/114']
+
+
+
+ eth1/125
+ topology/pod-1/paths-0/pathep-[eth1/125]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/125']
+
+
+
+ eth1/117
+ topology/pod-1/paths-0/pathep-[eth1/117]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/117']
+
+
+
+ eth1/101
+ topology/pod-1/paths-0/pathep-[eth1/101]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/101']
+
+
+
+ eth1/29
+ topology/pod-1/paths-0/pathep-[eth1/29]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/29']
+
+
+
+ eth1/68
+ topology/pod-1/paths-0/pathep-[eth1/68]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/68']
+
+
+
+ eth1/83
+ topology/pod-1/paths-0/pathep-[eth1/83]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/83']
+
+
+
+ eth1/44
+ topology/pod-1/paths-0/pathep-[eth1/44]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/44']
+
+
+
+ eth1/110
+ topology/pod-1/paths-0/pathep-[eth1/110]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/110']
+
+
+
+ eth1/36
+ topology/pod-1/paths-0/pathep-[eth1/36]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/36']
+
+
+
+ eth1/14
+ topology/pod-1/paths-0/pathep-[eth1/14]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/14']
+
+
+
+ eth1/47
+ topology/pod-1/paths-0/pathep-[eth1/47]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/47']
+
+
+
+ eth1/85
+ topology/pod-1/paths-0/pathep-[eth1/85]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/85']
+
+
+
+ eth1/52
+ topology/pod-1/paths-0/pathep-[eth1/52]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/52']
+
+
+
+ eth1/84
+ topology/pod-1/paths-0/pathep-[eth1/84]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/84']
+
+
+
+ eth1/38
+ topology/pod-1/paths-0/pathep-[eth1/38]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/38']
+
+
+
+ eth1/100
+ topology/pod-1/paths-0/pathep-[eth1/100]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/100']
+
+
+
+ eth1/57
+ topology/pod-1/paths-0/pathep-[eth1/57]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/57']
+
+
+
+ eth1/13
+ topology/pod-1/paths-0/pathep-[eth1/13]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/13']
+
+
+
+ eth1/111
+ topology/pod-1/paths-0/pathep-[eth1/111]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/111']
+
+
+
+ eth1/26
+ topology/pod-1/paths-0/pathep-[eth1/26]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/26']
+
+
+
+ eth1/3
+ topology/pod-1/paths-0/pathep-[eth1/3]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/3']
+
+
+
+ eth1/19
+ topology/pod-1/paths-0/pathep-[eth1/19]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/19']
+
+
+
+ eth1/119
+ topology/pod-1/paths-0/pathep-[eth1/119]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/119']
+
+
+
+ eth1/89
+ topology/pod-1/paths-0/pathep-[eth1/89]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/89']
+
+
+
+ eth1/77
+ topology/pod-1/paths-0/pathep-[eth1/77]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/77']
+
+
+
+ eth1/104
+ topology/pod-1/paths-0/pathep-[eth1/104]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/104']
+
+
+
+ eth1/116
+ topology/pod-1/paths-0/pathep-[eth1/116]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/116']
+
+
+
+ eth1/37
+ topology/pod-1/paths-0/pathep-[eth1/37]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/37']
+
+
+
+ eth1/16
+ topology/pod-1/paths-0/pathep-[eth1/16]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/16']
+
+
+
+ eth1/97
+ topology/pod-1/paths-0/pathep-[eth1/97]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/97']
+
+
+
+ eth1/39
+ topology/pod-1/paths-0/pathep-[eth1/39]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/39']
+
+
+
+ eth1/94
+ topology/pod-1/paths-0/pathep-[eth1/94]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/94']
+
+
+
+ eth1/78
+ topology/pod-1/paths-0/pathep-[eth1/78]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/78']
+
+
+
+ eth1/98
+ topology/pod-1/paths-0/pathep-[eth1/98]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/98']
+
+
+
+ eth1/40
+ topology/pod-1/paths-0/pathep-[eth1/40]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/40']
+
+
+
+ eth1/124
+ topology/pod-1/paths-0/pathep-[eth1/124]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/124']
+
+
+
+ eth1/41
+ topology/pod-1/paths-0/pathep-[eth1/41]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/41']
+
+
+
+ eth1/49
+ topology/pod-1/paths-0/pathep-[eth1/49]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/49']
+
+
+
+ eth1/27
+ topology/pod-1/paths-0/pathep-[eth1/27]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/27']
+
+
+
+ eth1/80
+ topology/pod-1/paths-0/pathep-[eth1/80]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/80']
+
+
+
+ eth1/15
+ topology/pod-1/paths-0/pathep-[eth1/15]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/15']
+
+
+
+ eth1/106
+ topology/pod-1/paths-0/pathep-[eth1/106]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/106']
+
+
+
+ eth1/6
+ topology/pod-1/paths-0/pathep-[eth1/6]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/6']
+
+
+
+ eth1/30
+ topology/pod-1/paths-0/pathep-[eth1/30]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/30']
+
+
+
+ eth1/50
+ topology/pod-1/paths-0/pathep-[eth1/50]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/50']
+
+
+
+ eth1/118
+ topology/pod-1/paths-0/pathep-[eth1/118]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/118']
+
+
+
+ eth1/10
+ topology/pod-1/paths-0/pathep-[eth1/10]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/10']
+
+
+
+ eth1/74
+ topology/pod-1/paths-0/pathep-[eth1/74]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/74']
+
+
+
+ eth1/1
+ topology/pod-1/paths-0/pathep-[eth1/1]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/1']
+
+
+
+ eth1/87
+ topology/pod-1/paths-0/pathep-[eth1/87]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/87']
+
+
+
+ eth1/2
+ topology/pod-1/paths-0/pathep-[eth1/2]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/2']
+
+
+
+ eth1/8
+ topology/pod-1/paths-0/pathep-[eth1/8]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/8']
+
+
+
+ eth1/99
+ topology/pod-1/paths-0/pathep-[eth1/99]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/99']
+
+
+
+ eth1/75
+ topology/pod-1/paths-0/pathep-[eth1/75]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/75']
+
+
+
+
+
+ enabled
+
+ strict
+
+
+
+ enabled
+
+
+ enabled
+
+
+ default
+
+
+
+
+
+
+
+
+ 5
+ 5
+ enabled
+ true
+
+
+
+ event-stp-inconsist-vpc-peerlink
+ enabled
+ true
+ false
+
+
+ event-link-flap
+ enabled
+ true
+ false
+
+
+ event-bpduguard
+ enabled
+ true
+ false
+
+
+ event-udld
+ enabled
+ true
+ false
+
+
+ event-storm-ctrl
+ enabled
+ true
+ false
+
+
+ event-set-port-state-failed
+ enabled
+ true
+ false
+
+
+ event-loopback
+ enabled
+ true
+ false
+
+
+ event-psec-violation
+ enabled
+ true
+ false
+
+
+ event-sec-violation
+ enabled
+ true
+ false
+
+
+ enabled
+ 300
+
+ default
+ linkStatusDefault,linkStatusEnable,trunkStatusEnable
+ 5
+ up
+ Layer2
+ 9216
+ false
+
+
+
+
+ enabled
+
+
+ enabled
+
+
+ enabled
+
+
+
+ enabled
+
+
+ enabled
+
+
+ enabled
+
+
+
+ default
+ admin-up
+ 0
+
+
+ default
+
+
+
+
+ management
+ admin-up
+ 0
+
+
+ management
+
+
+
+
+ /System/mgmt-items/MgmtAddr-list[addr='mgmt0']
+
+
+
+
+
+ no
+ none
+ none
+
+
+
+
+ eth1/71
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/71
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/124
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/124
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/112
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/112
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/67
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/67
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/79
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/79
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/33
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/33
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/43
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/43
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/4
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/4
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/99
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/99
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/84
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/84
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/109
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/109
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/17
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/17
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/10
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/10
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/108
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/108
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/89
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/89
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/73
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/73
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/106
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/106
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/32
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/32
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/22
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/22
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/61
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/61
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/30
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/30
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/116
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/116
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/54
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/54
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/119
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/119
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/111
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/111
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/92
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/92
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/78
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/78
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/50
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/50
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/115
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/115
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/72
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/72
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/6
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/6
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/38
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/38
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/65
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/65
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/5
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/5
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/97
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/97
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/36
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/36
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/120
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/120
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/100
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/100
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/90
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/90
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/81
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/81
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/31
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/31
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/28
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/28
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/93
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/93
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/2
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/2
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/66
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/66
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/105
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/105
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/58
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/58
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/88
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/88
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/94
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/94
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/104
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/104
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/87
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/87
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/62
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/62
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/37
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/37
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/77
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/77
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/46
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/46
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/40
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/40
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/101
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/101
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/125
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/125
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/123
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/123
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/118
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/118
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/7
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/7
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/12
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/12
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/60
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/60
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/56
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/56
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/63
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/63
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/110
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/110
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/14
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/14
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/23
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/23
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/41
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/41
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/19
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/19
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/103
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/103
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/15
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/15
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/80
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/80
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/113
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/113
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/35
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/35
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/86
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/86
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/96
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/96
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/70
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/70
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/18
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/18
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/98
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/98
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/95
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/95
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/82
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/82
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/59
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/59
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/13
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/13
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/9
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/9
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/27
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/27
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/49
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/49
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/44
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/44
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/16
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/16
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/76
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/76
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/51
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/51
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/11
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/11
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/39
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/39
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/83
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/83
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/107
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/107
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/57
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/57
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/1
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/1
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/126
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/126
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/47
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/47
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/85
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/85
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/45
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/45
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/24
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/24
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/55
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/55
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/42
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/42
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/25
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/25
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/26
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/26
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/91
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/91
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/114
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/114
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/20
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/20
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/69
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/69
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/68
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/68
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/48
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/48
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/121
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/121
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/21
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/21
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/34
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/34
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/53
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/53
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/3
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/3
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/74
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/74
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/122
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/122
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/102
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/102
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/8
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/8
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/64
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/64
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/75
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/75
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/52
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/52
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/127
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/127
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/29
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/29
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/128
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/128
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/117
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/117
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+
+
+
+
+
+
+
+ eth1/26
+
+
+ eth1/36
+
+
+ eth1/102
+
+
+ eth1/6
+
+
+ eth1/47
+
+
+ eth1/57
+
+
+ eth1/115
+
+
+ eth1/96
+
+
+ eth1/5
+
+
+ eth1/101
+
+
+ eth1/83
+
+
+ eth1/49
+
+
+ eth1/20
+
+
+ eth1/52
+
+
+ eth1/13
+
+
+ eth1/72
+
+
+ eth1/90
+
+
+ eth1/44
+
+
+ eth1/68
+
+
+ eth1/34
+
+
+ eth1/123
+
+
+ eth1/75
+
+
+ eth1/65
+
+
+ eth1/71
+
+
+ eth1/81
+
+
+ eth1/24
+
+
+ eth1/12
+
+
+ eth1/64
+
+
+ eth1/10
+
+
+ eth1/92
+
+
+ eth1/126
+
+
+ eth1/62
+
+
+ eth1/4
+
+
+ eth1/128
+
+
+ eth1/9
+
+
+ eth1/85
+
+
+ eth1/7
+
+
+ eth1/111
+
+
+ eth1/55
+
+
+ eth1/98
+
+
+ eth1/113
+
+
+ eth1/63
+
+
+ eth1/110
+
+
+ eth1/120
+
+
+ eth1/108
+
+
+ eth1/124
+
+
+ eth1/1
+
+
+ eth1/97
+
+
+ eth1/122
+
+
+ eth1/48
+
+
+ eth1/46
+
+
+ eth1/77
+
+
+ eth1/21
+
+
+ eth1/60
+
+
+ eth1/50
+
+
+ eth1/80
+
+
+ eth1/16
+
+
+ eth1/94
+
+
+ eth1/82
+
+
+ eth1/17
+
+
+ eth1/89
+
+
+ eth1/45
+
+
+ eth1/32
+
+
+ eth1/119
+
+
+ eth1/118
+
+
+ eth1/23
+
+
+ eth1/127
+
+
+ eth1/41
+
+
+ eth1/105
+
+
+ eth1/11
+
+
+ eth1/100
+
+
+ eth1/37
+
+
+ eth1/51
+
+
+ eth1/58
+
+
+ eth1/56
+
+
+ eth1/107
+
+
+ eth1/14
+
+
+ eth1/95
+
+
+ eth1/78
+
+
+ eth1/104
+
+
+ eth1/31
+
+
+ eth1/42
+
+
+ eth1/79
+
+
+ eth1/2
+
+
+ eth1/93
+
+
+ eth1/33
+
+
+ eth1/61
+
+
+ eth1/106
+
+
+ eth1/73
+
+
+ eth1/22
+
+
+ eth1/76
+
+
+ eth1/28
+
+
+ eth1/91
+
+
+ eth1/35
+
+
+ eth1/19
+
+
+ eth1/117
+
+
+ eth1/87
+
+
+ eth1/125
+
+
+ eth1/114
+
+
+ eth1/8
+
+
+ eth1/39
+
+
+ eth1/54
+
+
+ eth1/74
+
+
+ eth1/15
+
+
+ eth1/29
+
+
+ eth1/67
+
+
+ eth1/116
+
+
+ eth1/38
+
+
+ eth1/30
+
+
+ eth1/27
+
+
+ eth1/40
+
+
+ eth1/112
+
+
+ eth1/69
+
+
+ eth1/109
+
+
+ eth1/88
+
+
+ eth1/3
+
+
+ eth1/103
+
+
+ eth1/43
+
+
+ eth1/84
+
+
+ eth1/18
+
+
+ eth1/121
+
+
+ eth1/25
+
+
+ eth1/53
+
+
+ eth1/70
+
+
+ eth1/99
+
+
+ eth1/86
+
+
+ eth1/66
+
+
+ eth1/59
+
+
+
+
+
+
+ 0
+ 0
+ 2022-06-30T22:35:37.525+00:00
+ 0
+
+ enabled
+
+
+ enabled
+
+ disabled
+ enabled
+
+
+ management
+ disabled
+
+
+ mgmt0
+
+
+ 10.0.0.15/24
+ 1
+ 0
+ primary
+
+
+ enabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+
+
+
+ disabled
+ 0
+ CRC16
+ error
+ enabled
+
+
+
+ enabled
+
+ disabled
+ enabled
+
+
+ management
+
+
+ mgmt0
+ enabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+
+
+
+ disabled
+ disabled
+ disabled
+
+
+
+ enable
+
+
+ enabled
+
+ 32768
+ primary
+ enabled
+
+
+ eth1/91
+ enabled
+ 32768
+ normal
+
+
+ eth1/28
+ enabled
+ 32768
+ normal
+
+
+ eth1/106
+ enabled
+ 32768
+ normal
+
+
+ eth1/4
+ enabled
+ 32768
+ normal
+
+
+ eth1/25
+ enabled
+ 32768
+ normal
+
+
+ eth1/6
+ enabled
+ 32768
+ normal
+
+
+ eth1/120
+ enabled
+ 32768
+ normal
+
+
+ eth1/35
+ enabled
+ 32768
+ normal
+
+
+ eth1/30
+ enabled
+ 32768
+ normal
+
+
+ eth1/5
+ enabled
+ 32768
+ normal
+
+
+ eth1/119
+ enabled
+ 32768
+ normal
+
+
+ eth1/81
+ enabled
+ 32768
+ normal
+
+
+ eth1/31
+ enabled
+ 32768
+ normal
+
+
+ eth1/84
+ enabled
+ 32768
+ normal
+
+
+ eth1/56
+ enabled
+ 32768
+ normal
+
+
+ eth1/61
+ enabled
+ 32768
+ normal
+
+
+ eth1/66
+ enabled
+ 32768
+ normal
+
+
+ eth1/118
+ enabled
+ 32768
+ normal
+
+
+ eth1/128
+ enabled
+ 32768
+ normal
+
+
+ eth1/116
+ enabled
+ 32768
+ normal
+
+
+ eth1/22
+ enabled
+ 32768
+ normal
+
+
+ eth1/7
+ enabled
+ 32768
+ normal
+
+
+ eth1/51
+ enabled
+ 32768
+ normal
+
+
+ eth1/9
+ enabled
+ 32768
+ normal
+
+
+ eth1/92
+ enabled
+ 32768
+ normal
+
+
+ eth1/90
+ enabled
+ 32768
+ normal
+
+
+ eth1/73
+ enabled
+ 32768
+ normal
+
+
+ eth1/19
+ enabled
+ 32768
+ normal
+
+
+ eth1/93
+ enabled
+ 32768
+ normal
+
+
+ eth1/64
+ enabled
+ 32768
+ normal
+
+
+ eth1/115
+ enabled
+ 32768
+ normal
+
+
+ eth1/78
+ enabled
+ 32768
+ normal
+
+
+ eth1/114
+ enabled
+ 32768
+ normal
+
+
+ eth1/121
+ enabled
+ 32768
+ normal
+
+
+ eth1/83
+ enabled
+ 32768
+ normal
+
+
+ eth1/75
+ enabled
+ 32768
+ normal
+
+
+ eth1/112
+ enabled
+ 32768
+ normal
+
+
+ eth1/3
+ enabled
+ 32768
+ normal
+
+
+ eth1/41
+ enabled
+ 32768
+ normal
+
+
+ eth1/122
+ enabled
+ 32768
+ normal
+
+
+ eth1/33
+ enabled
+ 32768
+ normal
+
+
+ eth1/50
+ enabled
+ 32768
+ normal
+
+
+ eth1/124
+ enabled
+ 32768
+ normal
+
+
+ eth1/59
+ enabled
+ 32768
+ normal
+
+
+ eth1/21
+ enabled
+ 32768
+ normal
+
+
+ eth1/109
+ enabled
+ 32768
+ normal
+
+
+ eth1/29
+ enabled
+ 32768
+ normal
+
+
+ eth1/23
+ enabled
+ 32768
+ normal
+
+
+ eth1/55
+ enabled
+ 32768
+ normal
+
+
+ eth1/107
+ enabled
+ 32768
+ normal
+
+
+ eth1/89
+ enabled
+ 32768
+ normal
+
+
+ eth1/94
+ enabled
+ 32768
+ normal
+
+
+ eth1/44
+ enabled
+ 32768
+ normal
+
+
+ eth1/77
+ enabled
+ 32768
+ normal
+
+
+ eth1/117
+ enabled
+ 32768
+ normal
+
+
+ eth1/47
+ enabled
+ 32768
+ normal
+
+
+ eth1/42
+ enabled
+ 32768
+ normal
+
+
+ eth1/67
+ enabled
+ 32768
+ normal
+
+
+ eth1/102
+ enabled
+ 32768
+ normal
+
+
+ eth1/26
+ enabled
+ 32768
+ normal
+
+
+ eth1/76
+ enabled
+ 32768
+ normal
+
+
+ eth1/18
+ enabled
+ 32768
+ normal
+
+
+ eth1/62
+ enabled
+ 32768
+ normal
+
+
+ eth1/71
+ enabled
+ 32768
+ normal
+
+
+ eth1/95
+ enabled
+ 32768
+ normal
+
+
+ eth1/85
+ enabled
+ 32768
+ normal
+
+
+ eth1/60
+ enabled
+ 32768
+ normal
+
+
+ eth1/8
+ enabled
+ 32768
+ normal
+
+
+ eth1/97
+ enabled
+ 32768
+ normal
+
+
+ eth1/39
+ enabled
+ 32768
+ normal
+
+
+ eth1/38
+ enabled
+ 32768
+ normal
+
+
+ eth1/79
+ enabled
+ 32768
+ normal
+
+
+ eth1/17
+ enabled
+ 32768
+ normal
+
+
+ eth1/96
+ enabled
+ 32768
+ normal
+
+
+ eth1/105
+ enabled
+ 32768
+ normal
+
+
+ eth1/70
+ enabled
+ 32768
+ normal
+
+
+ eth1/110
+ enabled
+ 32768
+ normal
+
+
+ eth1/45
+ enabled
+ 32768
+ normal
+
+
+ eth1/127
+ enabled
+ 32768
+ normal
+
+
+ eth1/87
+ enabled
+ 32768
+ normal
+
+
+ eth1/125
+ enabled
+ 32768
+ normal
+
+
+ eth1/88
+ enabled
+ 32768
+ normal
+
+
+ eth1/15
+ enabled
+ 32768
+ normal
+
+
+ eth1/101
+ enabled
+ 32768
+ normal
+
+
+ eth1/111
+ enabled
+ 32768
+ normal
+
+
+ eth1/34
+ enabled
+ 32768
+ normal
+
+
+ eth1/113
+ enabled
+ 32768
+ normal
+
+
+ eth1/68
+ enabled
+ 32768
+ normal
+
+
+ eth1/57
+ enabled
+ 32768
+ normal
+
+
+ eth1/54
+ enabled
+ 32768
+ normal
+
+
+ eth1/13
+ enabled
+ 32768
+ normal
+
+
+ eth1/2
+ enabled
+ 32768
+ normal
+
+
+ eth1/53
+ enabled
+ 32768
+ normal
+
+
+ eth1/98
+ enabled
+ 32768
+ normal
+
+
+ eth1/48
+ enabled
+ 32768
+ normal
+
+
+ eth1/65
+ enabled
+ 32768
+ normal
+
+
+ eth1/11
+ enabled
+ 32768
+ normal
+
+
+ eth1/72
+ enabled
+ 32768
+ normal
+
+
+ eth1/12
+ enabled
+ 32768
+ normal
+
+
+ eth1/126
+ enabled
+ 32768
+ normal
+
+
+ eth1/103
+ enabled
+ 32768
+ normal
+
+
+ eth1/36
+ enabled
+ 32768
+ normal
+
+
+ eth1/80
+ enabled
+ 32768
+ normal
+
+
+ eth1/52
+ enabled
+ 32768
+ normal
+
+
+ eth1/82
+ enabled
+ 32768
+ normal
+
+
+ eth1/10
+ enabled
+ 32768
+ normal
+
+
+ eth1/20
+ enabled
+ 32768
+ normal
+
+
+ eth1/1
+ enabled
+ 32768
+ normal
+
+
+ eth1/27
+ enabled
+ 32768
+ normal
+
+
+ eth1/74
+ enabled
+ 32768
+ normal
+
+
+ eth1/16
+ enabled
+ 32768
+ normal
+
+
+ eth1/14
+ enabled
+ 32768
+ normal
+
+
+ eth1/24
+ enabled
+ 32768
+ normal
+
+
+ eth1/99
+ enabled
+ 32768
+ normal
+
+
+ eth1/108
+ enabled
+ 32768
+ normal
+
+
+ eth1/43
+ enabled
+ 32768
+ normal
+
+
+ eth1/104
+ enabled
+ 32768
+ normal
+
+
+ eth1/32
+ enabled
+ 32768
+ normal
+
+
+ eth1/69
+ enabled
+ 32768
+ normal
+
+
+ eth1/86
+ enabled
+ 32768
+ normal
+
+
+ eth1/37
+ enabled
+ 32768
+ normal
+
+
+ eth1/40
+ enabled
+ 32768
+ normal
+
+
+ eth1/46
+ enabled
+ 32768
+ normal
+
+
+ eth1/63
+ enabled
+ 32768
+ normal
+
+
+ eth1/49
+ enabled
+ 32768
+ normal
+
+
+ eth1/58
+ enabled
+ 32768
+ normal
+
+
+ eth1/100
+ enabled
+ 32768
+ normal
+
+
+ eth1/123
+ enabled
+ 32768
+ normal
+
+
+
+
+
+ 1800
+
+
+ enabled
+
+ enabled
+
+
+
+
+ mgmt0
+ up
+ on
+ auto
+ 1500
+
+ l3Inst
+ /System/inst-items/Inst-list[mode='management']
+
+ enable
+ auto
+
+
+ vr-n9kv
+
+ enabled
+
+ none
+ enabled
+ 1380
+ 174080
+ 1
+ 250
+ 180
+ 5
+ disabled
+
+
+
+
+ enabled
+
+
+
+ -1
+ 443
+ false
+ TLSv1.1 TLSv1.2
+
+
+
+ ps-rdn
+
+
+
+ enabled
+
+
+ enabled
+
+
+ 0
+
+
+
+ vlan-1
+
+
+
+
+ None
+ Disable
+ 0
+ 4294967295
+
+
+
+
+ eth1/116
+
+
+ eth1/53
+
+
+ eth1/62
+
+
+ eth1/48
+
+
+ eth1/50
+
+
+ eth1/60
+
+
+ eth1/4
+
+
+ eth1/95
+
+
+ eth1/126
+
+
+ eth1/80
+
+
+ eth1/76
+
+
+ eth1/21
+
+
+ eth1/43
+
+
+ eth1/127
+
+
+ eth1/26
+
+
+ eth1/82
+
+
+ eth1/85
+
+
+ eth1/86
+
+
+ eth1/49
+
+
+ eth1/125
+
+
+ eth1/15
+
+
+ eth1/121
+
+
+ eth1/38
+
+
+ eth1/73
+
+
+ eth1/98
+
+
+ eth1/24
+
+
+ eth1/35
+
+
+ eth1/91
+
+
+ eth1/56
+
+
+ eth1/33
+
+
+ eth1/117
+
+
+ eth1/34
+
+
+ eth1/61
+
+
+ eth1/74
+
+
+ eth1/30
+
+
+ eth1/57
+
+
+ eth1/94
+
+
+ eth1/128
+
+
+ eth1/32
+
+
+ eth1/107
+
+
+ eth1/54
+
+
+ eth1/87
+
+
+ eth1/106
+
+
+ eth1/90
+
+
+ eth1/42
+
+
+ eth1/10
+
+
+ eth1/96
+
+
+ eth1/123
+
+
+ eth1/1
+
+
+ eth1/83
+
+
+ eth1/114
+
+
+ eth1/7
+
+
+ eth1/104
+
+
+ eth1/100
+
+
+ eth1/11
+
+
+ eth1/55
+
+
+ eth1/2
+
+
+ eth1/46
+
+
+ eth1/27
+
+
+ eth1/67
+
+
+ eth1/31
+
+
+ eth1/29
+
+
+ eth1/3
+
+
+ eth1/81
+
+
+ eth1/108
+
+
+ eth1/93
+
+
+ eth1/39
+
+
+ eth1/78
+
+
+ eth1/12
+
+
+ eth1/5
+
+
+ eth1/18
+
+
+ eth1/115
+
+
+ eth1/28
+
+
+ eth1/124
+
+
+ eth1/97
+
+
+ eth1/120
+
+
+ eth1/71
+
+
+ eth1/89
+
+
+ eth1/70
+
+
+ eth1/22
+
+
+ eth1/72
+
+
+ eth1/59
+
+
+ eth1/92
+
+
+ eth1/75
+
+
+ eth1/44
+
+
+ eth1/112
+
+
+ eth1/79
+
+
+ eth1/101
+
+
+ eth1/13
+
+
+ eth1/40
+
+
+ eth1/17
+
+
+ eth1/47
+
+
+ eth1/113
+
+
+ eth1/37
+
+
+ eth1/64
+
+
+ eth1/8
+
+
+ eth1/23
+
+
+ eth1/9
+
+
+ eth1/99
+
+
+ eth1/20
+
+
+ eth1/51
+
+
+ eth1/84
+
+
+ eth1/122
+
+
+ eth1/110
+
+
+ eth1/88
+
+
+ eth1/65
+
+
+ eth1/102
+
+
+ eth1/52
+
+
+ eth1/103
+
+
+ eth1/14
+
+
+ eth1/45
+
+
+ eth1/68
+
+
+ eth1/118
+
+
+ eth1/63
+
+
+ eth1/66
+
+
+ eth1/25
+
+
+ eth1/105
+
+
+ eth1/41
+
+
+ eth1/119
+
+
+ eth1/111
+
+
+ eth1/109
+
+
+ eth1/58
+
+
+ eth1/77
+
+
+ eth1/69
+
+
+ eth1/16
+
+
+ eth1/36
+
+
+ eth1/6
+
+
+ eth1/19
+
+
+
+
+ Disable
+ 1000000
+ 2000000
+ 3
+
+ 4096
+ 0
+ PC_LB_ALGO_DLB
+
+ 100
+ enable
+
+ Default
+ Disable
+ PSTAT_DISABLE
+ all
+ DEFAULT
+ DEFAULT
+ DEFAULT
+
+ 768
+ 0
+ 512
+
+ disabled
+
+
+ enabled
+
+
+ enabled
+
+ disabled
+
+
+ boxen
+ md5
+
+
+ network-admin
+
+
+ false
+ false
+ des
+ 0
+
+
+ admin
+ md5
+
+
+ network-admin
+
+
+ false
+ false
+ des
+ 0
+
+
+ default
+
+
+
+ 2
+ CRITICAL(2)
+ no
+ PMON@CRITICAL
+
+
+ 5
+ INFORMATION(5)
+ no
+ PMON@INFO
+
+
+ 4
+ WARNING(4)
+ no
+ PMON@WARNING
+
+
+ 1
+ FATAL(1)
+ no
+ PMON@FATAL
+
+
+ 3
+ ERROR(3)
+ no
+ PMON@ERROR
+
+
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ disable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ enable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+ 0
+
+
+
+ disable
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+ disable
+
+
+
+ no
+ no
+
+
+
+ enabled
+
+ enabled
+ enabled
+ normal
+ enabled
+
+
+ eth1/111
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/111
+ 128
+
+
+ eth1/36
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/36
+ 128
+
+
+ eth1/60
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/60
+ 128
+
+
+ eth1/110
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/110
+ 128
+
+
+ eth1/97
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/97
+ 128
+
+
+ eth1/37
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/37
+ 128
+
+
+ eth1/13
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/13
+ 128
+
+
+ eth1/107
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/107
+ 128
+
+
+ eth1/15
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/15
+ 128
+
+
+ eth1/103
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/103
+ 128
+
+
+ eth1/12
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/12
+ 128
+
+
+ eth1/96
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/96
+ 128
+
+
+ eth1/124
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/124
+ 128
+
+
+ eth1/101
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/101
+ 128
+
+
+ eth1/90
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/90
+ 128
+
+
+ eth1/69
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/69
+ 128
+
+
+ eth1/120
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/120
+ 128
+
+
+ eth1/72
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/72
+ 128
+
+
+ eth1/113
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/113
+ 128
+
+
+ eth1/22
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/22
+ 128
+
+
+ eth1/29
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/29
+ 128
+
+
+ eth1/33
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/33
+ 128
+
+
+ eth1/78
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/78
+ 128
+
+
+ eth1/25
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/25
+ 128
+
+
+ eth1/65
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/65
+ 128
+
+
+ eth1/44
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/44
+ 128
+
+
+ eth1/59
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/59
+ 128
+
+
+ eth1/40
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/40
+ 128
+
+
+ eth1/117
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/117
+ 128
+
+
+ eth1/104
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/104
+ 128
+
+
+ eth1/42
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/42
+ 128
+
+
+ eth1/61
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/61
+ 128
+
+
+ eth1/86
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/86
+ 128
+
+
+ eth1/49
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/49
+ 128
+
+
+ eth1/21
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/21
+ 128
+
+
+ eth1/4
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/4
+ 128
+
+
+ eth1/87
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/87
+ 128
+
+
+ eth1/95
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/95
+ 128
+
+
+ eth1/9
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/9
+ 128
+
+
+ eth1/19
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/19
+ 128
+
+
+ eth1/14
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/14
+ 128
+
+
+ eth1/53
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/53
+ 128
+
+
+ eth1/46
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/46
+ 128
+
+
+ eth1/10
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/10
+ 128
+
+
+ eth1/126
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/126
+ 128
+
+
+ eth1/62
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/62
+ 128
+
+
+ eth1/17
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/17
+ 128
+
+
+ eth1/50
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/50
+ 128
+
+
+ eth1/84
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/84
+ 128
+
+
+ eth1/32
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/32
+ 128
+
+
+ eth1/35
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/35
+ 128
+
+
+ eth1/70
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/70
+ 128
+
+
+ eth1/81
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/81
+ 128
+
+
+ eth1/67
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/67
+ 128
+
+
+ eth1/48
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/48
+ 128
+
+
+ eth1/31
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/31
+ 128
+
+
+ eth1/75
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/75
+ 128
+
+
+ eth1/43
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/43
+ 128
+
+
+ eth1/119
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/119
+ 128
+
+
+ eth1/123
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/123
+ 128
+
+
+ eth1/8
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/8
+ 128
+
+
+ eth1/76
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/76
+ 128
+
+
+ eth1/58
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/58
+ 128
+
+
+ eth1/28
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/28
+ 128
+
+
+ eth1/98
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/98
+ 128
+
+
+ eth1/105
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/105
+ 128
+
+
+ eth1/30
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/30
+ 128
+
+
+ eth1/66
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/66
+ 128
+
+
+ eth1/127
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/127
+ 128
+
+
+ eth1/7
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/7
+ 128
+
+
+ eth1/73
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/73
+ 128
+
+
+ eth1/27
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/27
+ 128
+
+
+ eth1/100
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/100
+ 128
+
+
+ eth1/18
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/18
+ 128
+
+
+ eth1/20
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/20
+ 128
+
+
+ eth1/23
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/23
+ 128
+
+
+ eth1/54
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/54
+ 128
+
+
+ eth1/45
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/45
+ 128
+
+
+ eth1/106
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/106
+ 128
+
+
+ eth1/91
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/91
+ 128
+
+
+ eth1/116
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/116
+ 128
+
+
+ eth1/1
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/1
+ 128
+
+
+ eth1/63
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/63
+ 128
+
+
+ eth1/57
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/57
+ 128
+
+
+ eth1/79
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/79
+ 128
+
+
+ eth1/85
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/85
+ 128
+
+
+ eth1/125
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/125
+ 128
+
+
+ eth1/122
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/122
+ 128
+
+
+ eth1/6
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/6
+ 128
+
+
+ eth1/74
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/74
+ 128
+
+
+ eth1/16
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/16
+ 128
+
+
+ eth1/51
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/51
+ 128
+
+
+ eth1/80
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/80
+ 128
+
+
+ eth1/3
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/3
+ 128
+
+
+ eth1/115
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/115
+ 128
+
+
+ eth1/38
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/38
+ 128
+
+
+ eth1/108
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/108
+ 128
+
+
+ eth1/121
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/121
+ 128
+
+
+ eth1/92
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/92
+ 128
+
+
+ eth1/88
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/88
+ 128
+
+
+ eth1/68
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/68
+ 128
+
+
+ eth1/71
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/71
+ 128
+
+
+ eth1/94
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/94
+ 128
+
+
+ eth1/93
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/93
+ 128
+
+
+ eth1/41
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/41
+ 128
+
+
+ eth1/52
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/52
+ 128
+
+
+ eth1/102
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/102
+ 128
+
+
+ eth1/2
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/2
+ 128
+
+
+ eth1/11
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/11
+ 128
+
+
+ eth1/128
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/128
+ 128
+
+
+ eth1/89
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/89
+ 128
+
+
+ eth1/83
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/83
+ 128
+
+
+ eth1/26
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/26
+ 128
+
+
+ eth1/24
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/24
+ 128
+
+
+ eth1/34
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/34
+ 128
+
+
+ eth1/118
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/118
+ 128
+
+
+ eth1/55
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/55
+ 128
+
+
+ eth1/109
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/109
+ 128
+
+
+ eth1/99
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/99
+ 128
+
+
+ eth1/64
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/64
+ 128
+
+
+ eth1/112
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/112
+ 128
+
+
+ eth1/77
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/77
+ 128
+
+
+ eth1/47
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/47
+ 128
+
+
+ eth1/114
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/114
+ 128
+
+
+ eth1/5
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/5
+ 128
+
+
+ eth1/56
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/56
+ 128
+
+
+ eth1/39
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/39
+ 128
+
+
+ eth1/82
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/82
+ 128
+
+
+ 1024
+ disabled
+ pvrst
+
+ disabled
+ 15
+ 2
+ 20
+ 20
+
+
+ 0
+ enabled
+ 2
+ 32768
+ primary
+ disabled
+ none
+ 1-4094
+
+
+ 0
+ enabled
+
+ short
+
+
+ 1
+ enabled
+ 2
+ 15
+ 2
+ 20
+ 32768
+ disabled
+ none
+
+
+
+
+
+ false
+ never
+ never
+ N9K-9000v
+
+
+ enabled
+ disabled
+
+ disabled
+ critical
+ disabled
+ 8
+
+
+
+
+ enabled
+
+
+
+
+ pap
+ false
+ yes
+ no
+ no
+ local
+
+
+
+ config
+ false
+ true
+
+
+ exec
+ false
+ true
+
+
+ assign-default-role
+
+ false
+ true
+ local
+
+
+ pap
+ false
+ yes
+ yes
+ no
+ local
+
+
+
+ config
+ false
+ true
+
+
+ exec
+ false
+ true
+
+
+ Error
+
+ false
+ local
+
+
+ false
+ local
+
+ no
+ no
+
+
+
+ all
+
+
+ rootep
+
+
+ 24
+ login,logout,refresh
+ 1200
+ 600
+
+
+ 127
+ 8
+ yes
+ no
+
+ 2
+ enable
+ 48
+ 15
+ 5
+ 24
+
+
+ 0
+ 0
+ Error
+
+
+ radius
+ 0
+ default
+
+
+ 1
+ 5
+
+
+
+ dev-ops
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-11
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-12
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ network-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-6
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ nxdb-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ priv-1
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-7
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ vdc-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-9
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-4
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-14
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ network-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ vdc-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ priv-5
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-15
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-8
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-3
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ nxdb-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-13
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-0
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-10
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-2
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ yes
+
+
+ admin
+ active
+ no
+ no
+ never
+ no
+ 0
+
+
+ all
+
+
+ network-admin
+ noDataPriv
+
+
+
+
+
+
+ boxen
+ active
+ no
+ no
+ never
+ no
+ 0
+
+
+ all
+
+
+ network-admin
+ noDataPriv
+
+
+
+
+
+
+
+
+
+
+ default
+ 0
+ false
+
+
+
+
+ enabled
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..e5481b3
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-cisco_nxos-system-out.txt
@@ -0,0 +1,15052 @@
+
+
+
+
+ enabled
+
+
+ enabled
+
+ error
+ enabled
+ disabled
+ disabled
+ 174080
+ 1
+
+
+ management
+
+
+ mgmt0
+ enabled
+ disabled
+ disabled
+ enabled
+ enabled
+ enabled
+ disabled
+ disabled
+ disabled
+ 1500
+
+
+
+
+ 600
+ 250
+ error
+ 180
+ disabled
+ 200
+ 0
+ 1500
+
+
+
+
+
+ vlan-1
+ active
+ active
+ mac
+ mdst-flood
+ bridge,route
+ 1
+ CE
+ disable
+
+
+ enable
+
+
+
+ enable
+ bootflash:/nxos.9.2.4.bin
+ bootflash:/nxos.9.2.4.bin
+
+ disable
+
+
+
+ enabled
+ none
+ 180
+
+
+ eth1/27
+ enabled
+
+
+ eth1/115
+ enabled
+
+
+ eth1/15
+ enabled
+
+
+ eth1/100
+ enabled
+
+
+ eth1/105
+ enabled
+
+
+ eth1/16
+ enabled
+
+
+ eth1/46
+ enabled
+
+
+ eth1/56
+ enabled
+
+
+ eth1/25
+ enabled
+
+
+ eth1/122
+ enabled
+
+
+ eth1/81
+ enabled
+
+
+ eth1/88
+ enabled
+
+
+ eth1/65
+ enabled
+
+
+ eth1/76
+ enabled
+
+
+ eth1/127
+ enabled
+
+
+ eth1/41
+ enabled
+
+
+ eth1/30
+ enabled
+
+
+ eth1/128
+ enabled
+
+
+ mgmt0
+ enabled
+
+
+ eth1/91
+ enabled
+
+
+ eth1/52
+ enabled
+
+
+ eth1/71
+ enabled
+
+
+ eth1/78
+ enabled
+
+
+ eth1/79
+ enabled
+
+
+ eth1/33
+ enabled
+
+
+ eth1/99
+ enabled
+
+
+ eth1/61
+ enabled
+
+
+ eth1/26
+ enabled
+
+
+ eth1/62
+ enabled
+
+
+ eth1/114
+ enabled
+
+
+ eth1/124
+ enabled
+
+
+ eth1/22
+ enabled
+
+
+ eth1/50
+ enabled
+
+
+ eth1/103
+ enabled
+
+
+ eth1/2
+ enabled
+
+
+ eth1/107
+ enabled
+
+
+ eth1/45
+ enabled
+
+
+ eth1/72
+ enabled
+
+
+ eth1/74
+ enabled
+
+
+ eth1/86
+ enabled
+
+
+ eth1/96
+ enabled
+
+
+ eth1/70
+ enabled
+
+
+ eth1/40
+ enabled
+
+
+ eth1/10
+ enabled
+
+
+ eth1/5
+ enabled
+
+
+ eth1/95
+ enabled
+
+
+ eth1/18
+ enabled
+
+
+ eth1/55
+ enabled
+
+
+ eth1/63
+ enabled
+
+
+ eth1/111
+ enabled
+
+
+ eth1/117
+ enabled
+
+
+ eth1/90
+ enabled
+
+
+ eth1/23
+ enabled
+
+
+ eth1/75
+ enabled
+
+
+ eth1/106
+ enabled
+
+
+ eth1/38
+ enabled
+
+
+ eth1/69
+ enabled
+
+
+ eth1/7
+ enabled
+
+
+ eth1/67
+ enabled
+
+
+ eth1/43
+ enabled
+
+
+ eth1/11
+ enabled
+
+
+ eth1/110
+ enabled
+
+
+ eth1/37
+ enabled
+
+
+ eth1/51
+ enabled
+
+
+ eth1/42
+ enabled
+
+
+ eth1/49
+ enabled
+
+
+ eth1/57
+ enabled
+
+
+ eth1/8
+ enabled
+
+
+ eth1/53
+ enabled
+
+
+ eth1/113
+ enabled
+
+
+ eth1/68
+ enabled
+
+
+ eth1/6
+ enabled
+
+
+ eth1/20
+ enabled
+
+
+ eth1/84
+ enabled
+
+
+ eth1/123
+ enabled
+
+
+ eth1/109
+ enabled
+
+
+ eth1/58
+ enabled
+
+
+ eth1/4
+ enabled
+
+
+ eth1/73
+ enabled
+
+
+ eth1/34
+ enabled
+
+
+ eth1/60
+ enabled
+
+
+ eth1/112
+ enabled
+
+
+ eth1/66
+ enabled
+
+
+ eth1/24
+ enabled
+
+
+ eth1/89
+ enabled
+
+
+ eth1/97
+ enabled
+
+
+ eth1/36
+ enabled
+
+
+ eth1/102
+ enabled
+
+
+ eth1/121
+ enabled
+
+
+ eth1/44
+ enabled
+
+
+ eth1/12
+ enabled
+
+
+ eth1/59
+ enabled
+
+
+ eth1/119
+ enabled
+
+
+ eth1/17
+ enabled
+
+
+ eth1/21
+ enabled
+
+
+ eth1/31
+ enabled
+
+
+ eth1/32
+ enabled
+
+
+ eth1/14
+ enabled
+
+
+ eth1/108
+ enabled
+
+
+ eth1/101
+ enabled
+
+
+ eth1/13
+ enabled
+
+
+ eth1/64
+ enabled
+
+
+ eth1/125
+ enabled
+
+
+ eth1/47
+ enabled
+
+
+ eth1/54
+ enabled
+
+
+ eth1/19
+ enabled
+
+
+ eth1/87
+ enabled
+
+
+ eth1/77
+ enabled
+
+
+ eth1/1
+ enabled
+
+
+ eth1/126
+ enabled
+
+
+ eth1/104
+ enabled
+
+
+ eth1/3
+ enabled
+
+
+ eth1/98
+ enabled
+
+
+ eth1/82
+ enabled
+
+
+ eth1/29
+ enabled
+
+
+ eth1/28
+ enabled
+
+
+ eth1/80
+ enabled
+
+
+ eth1/116
+ enabled
+
+
+ eth1/9
+ enabled
+
+
+ eth1/35
+ enabled
+
+
+ eth1/118
+ enabled
+
+
+ eth1/120
+ enabled
+
+
+ eth1/93
+ enabled
+
+
+ eth1/85
+ enabled
+
+
+ eth1/94
+ enabled
+
+
+ eth1/39
+ enabled
+
+
+ eth1/48
+ enabled
+
+
+ eth1/83
+ enabled
+
+
+ eth1/92
+ enabled
+
+
+ 60
+ v2
+
+
+
+ enabled
+
+ enabled
+ enabled
+ disabled
+ disabled
+ disabled
+
+
+
+ enabled
+ disabled
+ 24hours
+ ntp
+ 1
+
+
+
+
+ eth1/11
+ topology/pod-1/paths-0/pathep-[eth1/11]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/11']
+
+
+
+ eth1/64
+ topology/pod-1/paths-0/pathep-[eth1/64]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/64']
+
+
+
+ eth1/12
+ topology/pod-1/paths-0/pathep-[eth1/12]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/12']
+
+
+
+ eth1/53
+ topology/pod-1/paths-0/pathep-[eth1/53]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/53']
+
+
+
+ eth1/59
+ topology/pod-1/paths-0/pathep-[eth1/59]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/59']
+
+
+
+ eth1/93
+ topology/pod-1/paths-0/pathep-[eth1/93]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/93']
+
+
+
+ eth1/46
+ topology/pod-1/paths-0/pathep-[eth1/46]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/46']
+
+
+
+ eth1/24
+ topology/pod-1/paths-0/pathep-[eth1/24]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/24']
+
+
+
+ eth1/120
+ topology/pod-1/paths-0/pathep-[eth1/120]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/120']
+
+
+
+ eth1/66
+ topology/pod-1/paths-0/pathep-[eth1/66]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/66']
+
+
+
+ eth1/122
+ topology/pod-1/paths-0/pathep-[eth1/122]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/122']
+
+
+
+ eth1/45
+ topology/pod-1/paths-0/pathep-[eth1/45]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/45']
+
+
+
+ eth1/123
+ topology/pod-1/paths-0/pathep-[eth1/123]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/123']
+
+
+
+ eth1/73
+ topology/pod-1/paths-0/pathep-[eth1/73]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/73']
+
+
+
+ eth1/128
+ topology/pod-1/paths-0/pathep-[eth1/128]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/128']
+
+
+
+ eth1/32
+ topology/pod-1/paths-0/pathep-[eth1/32]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/32']
+
+
+
+ eth1/126
+ topology/pod-1/paths-0/pathep-[eth1/126]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/126']
+
+
+
+ eth1/95
+ topology/pod-1/paths-0/pathep-[eth1/95]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/95']
+
+
+
+ eth1/81
+ topology/pod-1/paths-0/pathep-[eth1/81]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/81']
+
+
+
+ eth1/7
+ topology/pod-1/paths-0/pathep-[eth1/7]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/7']
+
+
+
+ eth1/23
+ topology/pod-1/paths-0/pathep-[eth1/23]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/23']
+
+
+
+ eth1/76
+ topology/pod-1/paths-0/pathep-[eth1/76]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/76']
+
+
+
+ eth1/71
+ topology/pod-1/paths-0/pathep-[eth1/71]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/71']
+
+
+
+ eth1/55
+ topology/pod-1/paths-0/pathep-[eth1/55]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/55']
+
+
+
+ eth1/9
+ topology/pod-1/paths-0/pathep-[eth1/9]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/9']
+
+
+
+ eth1/121
+ topology/pod-1/paths-0/pathep-[eth1/121]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/121']
+
+
+
+ eth1/88
+ topology/pod-1/paths-0/pathep-[eth1/88]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/88']
+
+
+
+ eth1/102
+ topology/pod-1/paths-0/pathep-[eth1/102]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/102']
+
+
+
+ eth1/107
+ topology/pod-1/paths-0/pathep-[eth1/107]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/107']
+
+
+
+ eth1/108
+ topology/pod-1/paths-0/pathep-[eth1/108]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/108']
+
+
+
+ eth1/17
+ topology/pod-1/paths-0/pathep-[eth1/17]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/17']
+
+
+
+ eth1/5
+ topology/pod-1/paths-0/pathep-[eth1/5]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/5']
+
+
+
+ eth1/48
+ topology/pod-1/paths-0/pathep-[eth1/48]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/48']
+
+
+
+ eth1/61
+ topology/pod-1/paths-0/pathep-[eth1/61]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/61']
+
+
+
+ eth1/72
+ topology/pod-1/paths-0/pathep-[eth1/72]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/72']
+
+
+
+ eth1/43
+ topology/pod-1/paths-0/pathep-[eth1/43]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/43']
+
+
+
+ eth1/105
+ topology/pod-1/paths-0/pathep-[eth1/105]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/105']
+
+
+
+ eth1/33
+ topology/pod-1/paths-0/pathep-[eth1/33]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/33']
+
+
+
+ eth1/54
+ topology/pod-1/paths-0/pathep-[eth1/54]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/54']
+
+
+
+ eth1/67
+ topology/pod-1/paths-0/pathep-[eth1/67]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/67']
+
+
+
+ eth1/28
+ topology/pod-1/paths-0/pathep-[eth1/28]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/28']
+
+
+
+ eth1/21
+ topology/pod-1/paths-0/pathep-[eth1/21]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/21']
+
+
+
+ eth1/62
+ topology/pod-1/paths-0/pathep-[eth1/62]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/62']
+
+
+
+ eth1/22
+ topology/pod-1/paths-0/pathep-[eth1/22]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/22']
+
+
+
+ eth1/42
+ topology/pod-1/paths-0/pathep-[eth1/42]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/42']
+
+
+
+ eth1/91
+ topology/pod-1/paths-0/pathep-[eth1/91]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/91']
+
+
+
+ eth1/34
+ topology/pod-1/paths-0/pathep-[eth1/34]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/34']
+
+
+
+ eth1/103
+ topology/pod-1/paths-0/pathep-[eth1/103]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/103']
+
+
+
+ eth1/35
+ topology/pod-1/paths-0/pathep-[eth1/35]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/35']
+
+
+
+ eth1/90
+ topology/pod-1/paths-0/pathep-[eth1/90]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/90']
+
+
+
+ eth1/58
+ topology/pod-1/paths-0/pathep-[eth1/58]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/58']
+
+
+
+ eth1/60
+ topology/pod-1/paths-0/pathep-[eth1/60]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/60']
+
+
+
+ eth1/96
+ topology/pod-1/paths-0/pathep-[eth1/96]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/96']
+
+
+
+ eth1/20
+ topology/pod-1/paths-0/pathep-[eth1/20]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/20']
+
+
+
+ eth1/127
+ topology/pod-1/paths-0/pathep-[eth1/127]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/127']
+
+
+
+ eth1/56
+ topology/pod-1/paths-0/pathep-[eth1/56]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/56']
+
+
+
+ eth1/79
+ topology/pod-1/paths-0/pathep-[eth1/79]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/79']
+
+
+
+ eth1/31
+ topology/pod-1/paths-0/pathep-[eth1/31]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/31']
+
+
+
+ eth1/82
+ topology/pod-1/paths-0/pathep-[eth1/82]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/82']
+
+
+
+ eth1/4
+ topology/pod-1/paths-0/pathep-[eth1/4]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/4']
+
+
+
+ eth1/25
+ topology/pod-1/paths-0/pathep-[eth1/25]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/25']
+
+
+
+ eth1/92
+ topology/pod-1/paths-0/pathep-[eth1/92]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/92']
+
+
+
+ eth1/112
+ topology/pod-1/paths-0/pathep-[eth1/112]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/112']
+
+
+
+ eth1/69
+ topology/pod-1/paths-0/pathep-[eth1/69]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/69']
+
+
+
+ eth1/86
+ topology/pod-1/paths-0/pathep-[eth1/86]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/86']
+
+
+
+ eth1/113
+ topology/pod-1/paths-0/pathep-[eth1/113]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/113']
+
+
+
+ eth1/51
+ topology/pod-1/paths-0/pathep-[eth1/51]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/51']
+
+
+
+ eth1/63
+ topology/pod-1/paths-0/pathep-[eth1/63]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/63']
+
+
+
+ eth1/115
+ topology/pod-1/paths-0/pathep-[eth1/115]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/115']
+
+
+
+ eth1/18
+ topology/pod-1/paths-0/pathep-[eth1/18]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/18']
+
+
+
+ eth1/65
+ topology/pod-1/paths-0/pathep-[eth1/65]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/65']
+
+
+
+ eth1/109
+ topology/pod-1/paths-0/pathep-[eth1/109]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/109']
+
+
+
+ eth1/70
+ topology/pod-1/paths-0/pathep-[eth1/70]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/70']
+
+
+
+ eth1/114
+ topology/pod-1/paths-0/pathep-[eth1/114]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/114']
+
+
+
+ eth1/125
+ topology/pod-1/paths-0/pathep-[eth1/125]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/125']
+
+
+
+ eth1/117
+ topology/pod-1/paths-0/pathep-[eth1/117]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/117']
+
+
+
+ eth1/101
+ topology/pod-1/paths-0/pathep-[eth1/101]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/101']
+
+
+
+ eth1/29
+ topology/pod-1/paths-0/pathep-[eth1/29]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/29']
+
+
+
+ eth1/68
+ topology/pod-1/paths-0/pathep-[eth1/68]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/68']
+
+
+
+ eth1/83
+ topology/pod-1/paths-0/pathep-[eth1/83]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/83']
+
+
+
+ eth1/44
+ topology/pod-1/paths-0/pathep-[eth1/44]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/44']
+
+
+
+ eth1/110
+ topology/pod-1/paths-0/pathep-[eth1/110]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/110']
+
+
+
+ eth1/36
+ topology/pod-1/paths-0/pathep-[eth1/36]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/36']
+
+
+
+ eth1/14
+ topology/pod-1/paths-0/pathep-[eth1/14]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/14']
+
+
+
+ eth1/47
+ topology/pod-1/paths-0/pathep-[eth1/47]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/47']
+
+
+
+ eth1/85
+ topology/pod-1/paths-0/pathep-[eth1/85]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/85']
+
+
+
+ eth1/52
+ topology/pod-1/paths-0/pathep-[eth1/52]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/52']
+
+
+
+ eth1/84
+ topology/pod-1/paths-0/pathep-[eth1/84]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/84']
+
+
+
+ eth1/38
+ topology/pod-1/paths-0/pathep-[eth1/38]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/38']
+
+
+
+ eth1/100
+ topology/pod-1/paths-0/pathep-[eth1/100]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/100']
+
+
+
+ eth1/57
+ topology/pod-1/paths-0/pathep-[eth1/57]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/57']
+
+
+
+ eth1/13
+ topology/pod-1/paths-0/pathep-[eth1/13]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/13']
+
+
+
+ eth1/111
+ topology/pod-1/paths-0/pathep-[eth1/111]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/111']
+
+
+
+ eth1/26
+ topology/pod-1/paths-0/pathep-[eth1/26]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/26']
+
+
+
+ eth1/3
+ topology/pod-1/paths-0/pathep-[eth1/3]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/3']
+
+
+
+ eth1/19
+ topology/pod-1/paths-0/pathep-[eth1/19]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/19']
+
+
+
+ eth1/119
+ topology/pod-1/paths-0/pathep-[eth1/119]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/119']
+
+
+
+ eth1/89
+ topology/pod-1/paths-0/pathep-[eth1/89]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/89']
+
+
+
+ eth1/77
+ topology/pod-1/paths-0/pathep-[eth1/77]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/77']
+
+
+
+ eth1/104
+ topology/pod-1/paths-0/pathep-[eth1/104]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/104']
+
+
+
+ eth1/116
+ topology/pod-1/paths-0/pathep-[eth1/116]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/116']
+
+
+
+ eth1/37
+ topology/pod-1/paths-0/pathep-[eth1/37]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/37']
+
+
+
+ eth1/16
+ topology/pod-1/paths-0/pathep-[eth1/16]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/16']
+
+
+
+ eth1/97
+ topology/pod-1/paths-0/pathep-[eth1/97]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/97']
+
+
+
+ eth1/39
+ topology/pod-1/paths-0/pathep-[eth1/39]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/39']
+
+
+
+ eth1/94
+ topology/pod-1/paths-0/pathep-[eth1/94]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/94']
+
+
+
+ eth1/78
+ topology/pod-1/paths-0/pathep-[eth1/78]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/78']
+
+
+
+ eth1/98
+ topology/pod-1/paths-0/pathep-[eth1/98]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/98']
+
+
+
+ eth1/40
+ topology/pod-1/paths-0/pathep-[eth1/40]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/40']
+
+
+
+ eth1/124
+ topology/pod-1/paths-0/pathep-[eth1/124]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/124']
+
+
+
+ eth1/41
+ topology/pod-1/paths-0/pathep-[eth1/41]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/41']
+
+
+
+ eth1/49
+ topology/pod-1/paths-0/pathep-[eth1/49]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/49']
+
+
+
+ eth1/27
+ topology/pod-1/paths-0/pathep-[eth1/27]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/27']
+
+
+
+ eth1/80
+ topology/pod-1/paths-0/pathep-[eth1/80]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/80']
+
+
+
+ eth1/15
+ topology/pod-1/paths-0/pathep-[eth1/15]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/15']
+
+
+
+ eth1/106
+ topology/pod-1/paths-0/pathep-[eth1/106]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/106']
+
+
+
+ eth1/6
+ topology/pod-1/paths-0/pathep-[eth1/6]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/6']
+
+
+
+ eth1/30
+ topology/pod-1/paths-0/pathep-[eth1/30]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/30']
+
+
+
+ eth1/50
+ topology/pod-1/paths-0/pathep-[eth1/50]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/50']
+
+
+
+ eth1/118
+ topology/pod-1/paths-0/pathep-[eth1/118]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/118']
+
+
+
+ eth1/10
+ topology/pod-1/paths-0/pathep-[eth1/10]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/10']
+
+
+
+ eth1/74
+ topology/pod-1/paths-0/pathep-[eth1/74]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/74']
+
+
+
+ eth1/1
+ topology/pod-1/paths-0/pathep-[eth1/1]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/1']
+
+
+
+ eth1/87
+ topology/pod-1/paths-0/pathep-[eth1/87]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/87']
+
+
+
+ eth1/2
+ topology/pod-1/paths-0/pathep-[eth1/2]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/2']
+
+
+
+ eth1/8
+ topology/pod-1/paths-0/pathep-[eth1/8]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/8']
+
+
+
+ eth1/99
+ topology/pod-1/paths-0/pathep-[eth1/99]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/99']
+
+
+
+ eth1/75
+ topology/pod-1/paths-0/pathep-[eth1/75]
+
+ /System/intf-items/phys-items/PhysIf-list[id='eth1/75']
+
+
+
+
+
+ enabled
+
+ strict
+
+
+
+ enabled
+
+
+ enabled
+
+
+ default
+
+
+
+
+
+
+
+
+ 5
+ 5
+ enabled
+ true
+
+
+
+ event-stp-inconsist-vpc-peerlink
+ enabled
+ true
+ false
+
+
+ event-link-flap
+ enabled
+ true
+ false
+
+
+ event-bpduguard
+ enabled
+ true
+ false
+
+
+ event-udld
+ enabled
+ true
+ false
+
+
+ event-storm-ctrl
+ enabled
+ true
+ false
+
+
+ event-set-port-state-failed
+ enabled
+ true
+ false
+
+
+ event-loopback
+ enabled
+ true
+ false
+
+
+ event-psec-violation
+ enabled
+ true
+ false
+
+
+ event-sec-violation
+ enabled
+ true
+ false
+
+
+ enabled
+ 300
+
+ default
+ linkStatusDefault,linkStatusEnable,trunkStatusEnable
+ 5
+ up
+ Layer2
+ 9216
+ false
+
+
+
+
+ enabled
+
+
+ enabled
+
+
+ enabled
+
+
+
+ enabled
+
+
+ enabled
+
+
+ enabled
+
+
+
+ default
+ admin-up
+ 0
+
+
+ default
+
+
+
+
+ management
+ admin-up
+ 0
+
+
+ management
+
+
+
+
+ /System/mgmt-items/MgmtAddr-list[addr='mgmt0']
+
+
+
+
+
+ no
+ none
+ none
+
+
+
+
+ eth1/71
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/71
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/124
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/124
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/112
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/112
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/67
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/67
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/79
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/79
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/33
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/33
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/43
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/43
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/4
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/4
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/99
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/99
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/84
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/84
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/109
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/109
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/17
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/17
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/10
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/10
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/108
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/108
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/89
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/89
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/73
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/73
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/106
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/106
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/32
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/32
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/22
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/22
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/61
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/61
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/30
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/30
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/116
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/116
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/54
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/54
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/119
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/119
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/111
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/111
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/92
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/92
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/78
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/78
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/50
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/50
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/115
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/115
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/72
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/72
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/6
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/6
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/38
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/38
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/65
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/65
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/5
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/5
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/97
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/97
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/36
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/36
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/120
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/120
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/100
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/100
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/90
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/90
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/81
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/81
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/31
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/31
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/28
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/28
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/93
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/93
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/2
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/2
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/66
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/66
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/105
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/105
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/58
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/58
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/88
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/88
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/94
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/94
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/104
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/104
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/87
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/87
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/62
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/62
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/37
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/37
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/77
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/77
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/46
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/46
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/40
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/40
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/101
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/101
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/125
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/125
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/123
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/123
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/118
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/118
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/7
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/7
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/12
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/12
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/60
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/60
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/56
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/56
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/63
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/63
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/110
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/110
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/14
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/14
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/23
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/23
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/41
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/41
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/19
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/19
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/103
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/103
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/15
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/15
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/80
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/80
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/113
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/113
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/35
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/35
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/86
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/86
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/96
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/96
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/70
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/70
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/18
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/18
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/98
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/98
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/95
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/95
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/82
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/82
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/59
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/59
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/13
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/13
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/9
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/9
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/27
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/27
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/49
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/49
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/44
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/44
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/16
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/16
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/76
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/76
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/51
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/51
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/11
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/11
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/39
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/39
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/83
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/83
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/107
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/107
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/57
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/57
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/1
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/1
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/126
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/126
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/47
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/47
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/85
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/85
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/45
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/45
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/24
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/24
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/55
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/55
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/42
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/42
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/25
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/25
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/26
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/26
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/91
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/91
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/114
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/114
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/20
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/20
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/69
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/69
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/68
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/68
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/48
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/48
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/121
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/121
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/21
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/21
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/34
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/34
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/53
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/53
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/3
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/3
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/74
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/74
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/122
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/122
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/102
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/102
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/8
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/8
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/64
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/64
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/75
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/75
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/52
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/52
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/127
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/127
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/29
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/29
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/128
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/128
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+ eth1/117
+ auto
+ vlan-1
+ up
+ on
+ off
+ 0
+
+ 1
+ 0x8100
+ auto
+
+ variable
+ aggressive
+ not-applicable
+
+ 4294967295
+ Layer2
+ 100
+ 0
+ default
+ enable
+
+ 30
+ 300
+ 0
+
+ auto
+ broadcast
+ access
+ 1500
+ vlan-1
+ 0
+ 0
+ disable
+
+ disable
+ enable
+ eth1/117
+ no
+ disable
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ 100.0
+ 4294967295
+ enable
+ disable
+
+ leaf
+ not-applicable
+ enable
+ not-a-span-dest
+ auto
+ auto
+
+ 4294967295
+ 100.0
+ 100.0
+ 4294967295
+ all
+
+ default
+ 1-4094
+ discovery
+ -1
+ -1
+ 0
+ none
+
+
+
+
+
+
+
+
+
+ eth1/26
+
+
+ eth1/36
+
+
+ eth1/102
+
+
+ eth1/6
+
+
+ eth1/47
+
+
+ eth1/57
+
+
+ eth1/115
+
+
+ eth1/96
+
+
+ eth1/5
+
+
+ eth1/101
+
+
+ eth1/83
+
+
+ eth1/49
+
+
+ eth1/20
+
+
+ eth1/52
+
+
+ eth1/13
+
+
+ eth1/72
+
+
+ eth1/90
+
+
+ eth1/44
+
+
+ eth1/68
+
+
+ eth1/34
+
+
+ eth1/123
+
+
+ eth1/75
+
+
+ eth1/65
+
+
+ eth1/71
+
+
+ eth1/81
+
+
+ eth1/24
+
+
+ eth1/12
+
+
+ eth1/64
+
+
+ eth1/10
+
+
+ eth1/92
+
+
+ eth1/126
+
+
+ eth1/62
+
+
+ eth1/4
+
+
+ eth1/128
+
+
+ eth1/9
+
+
+ eth1/85
+
+
+ eth1/7
+
+
+ eth1/111
+
+
+ eth1/55
+
+
+ eth1/98
+
+
+ eth1/113
+
+
+ eth1/63
+
+
+ eth1/110
+
+
+ eth1/120
+
+
+ eth1/108
+
+
+ eth1/124
+
+
+ eth1/1
+
+
+ eth1/97
+
+
+ eth1/122
+
+
+ eth1/48
+
+
+ eth1/46
+
+
+ eth1/77
+
+
+ eth1/21
+
+
+ eth1/60
+
+
+ eth1/50
+
+
+ eth1/80
+
+
+ eth1/16
+
+
+ eth1/94
+
+
+ eth1/82
+
+
+ eth1/17
+
+
+ eth1/89
+
+
+ eth1/45
+
+
+ eth1/32
+
+
+ eth1/119
+
+
+ eth1/118
+
+
+ eth1/23
+
+
+ eth1/127
+
+
+ eth1/41
+
+
+ eth1/105
+
+
+ eth1/11
+
+
+ eth1/100
+
+
+ eth1/37
+
+
+ eth1/51
+
+
+ eth1/58
+
+
+ eth1/56
+
+
+ eth1/107
+
+
+ eth1/14
+
+
+ eth1/95
+
+
+ eth1/78
+
+
+ eth1/104
+
+
+ eth1/31
+
+
+ eth1/42
+
+
+ eth1/79
+
+
+ eth1/2
+
+
+ eth1/93
+
+
+ eth1/33
+
+
+ eth1/61
+
+
+ eth1/106
+
+
+ eth1/73
+
+
+ eth1/22
+
+
+ eth1/76
+
+
+ eth1/28
+
+
+ eth1/91
+
+
+ eth1/35
+
+
+ eth1/19
+
+
+ eth1/117
+
+
+ eth1/87
+
+
+ eth1/125
+
+
+ eth1/114
+
+
+ eth1/8
+
+
+ eth1/39
+
+
+ eth1/54
+
+
+ eth1/74
+
+
+ eth1/15
+
+
+ eth1/29
+
+
+ eth1/67
+
+
+ eth1/116
+
+
+ eth1/38
+
+
+ eth1/30
+
+
+ eth1/27
+
+
+ eth1/40
+
+
+ eth1/112
+
+
+ eth1/69
+
+
+ eth1/109
+
+
+ eth1/88
+
+
+ eth1/3
+
+
+ eth1/103
+
+
+ eth1/43
+
+
+ eth1/84
+
+
+ eth1/18
+
+
+ eth1/121
+
+
+ eth1/25
+
+
+ eth1/53
+
+
+ eth1/70
+
+
+ eth1/99
+
+
+ eth1/86
+
+
+ eth1/66
+
+
+ eth1/59
+
+
+
+
+
+
+ 0
+ 0
+ 2022-06-30T22:35:37.525+00:00
+ 0
+
+ enabled
+
+
+ enabled
+
+ disabled
+ enabled
+
+
+ management
+ disabled
+
+
+ mgmt0
+
+
+ 10.0.0.15/24
+ 1
+ 0
+ primary
+
+
+ enabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+
+
+
+ disabled
+ 0
+ CRC16
+ error
+ enabled
+
+
+
+ enabled
+
+ disabled
+ enabled
+
+
+ management
+
+
+ mgmt0
+ enabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+
+
+
+ disabled
+ disabled
+ disabled
+
+
+
+ enable
+
+
+ enabled
+
+ 32768
+ primary
+ enabled
+
+
+ eth1/91
+ enabled
+ 32768
+ normal
+
+
+ eth1/28
+ enabled
+ 32768
+ normal
+
+
+ eth1/106
+ enabled
+ 32768
+ normal
+
+
+ eth1/4
+ enabled
+ 32768
+ normal
+
+
+ eth1/25
+ enabled
+ 32768
+ normal
+
+
+ eth1/6
+ enabled
+ 32768
+ normal
+
+
+ eth1/120
+ enabled
+ 32768
+ normal
+
+
+ eth1/35
+ enabled
+ 32768
+ normal
+
+
+ eth1/30
+ enabled
+ 32768
+ normal
+
+
+ eth1/5
+ enabled
+ 32768
+ normal
+
+
+ eth1/119
+ enabled
+ 32768
+ normal
+
+
+ eth1/81
+ enabled
+ 32768
+ normal
+
+
+ eth1/31
+ enabled
+ 32768
+ normal
+
+
+ eth1/84
+ enabled
+ 32768
+ normal
+
+
+ eth1/56
+ enabled
+ 32768
+ normal
+
+
+ eth1/61
+ enabled
+ 32768
+ normal
+
+
+ eth1/66
+ enabled
+ 32768
+ normal
+
+
+ eth1/118
+ enabled
+ 32768
+ normal
+
+
+ eth1/128
+ enabled
+ 32768
+ normal
+
+
+ eth1/116
+ enabled
+ 32768
+ normal
+
+
+ eth1/22
+ enabled
+ 32768
+ normal
+
+
+ eth1/7
+ enabled
+ 32768
+ normal
+
+
+ eth1/51
+ enabled
+ 32768
+ normal
+
+
+ eth1/9
+ enabled
+ 32768
+ normal
+
+
+ eth1/92
+ enabled
+ 32768
+ normal
+
+
+ eth1/90
+ enabled
+ 32768
+ normal
+
+
+ eth1/73
+ enabled
+ 32768
+ normal
+
+
+ eth1/19
+ enabled
+ 32768
+ normal
+
+
+ eth1/93
+ enabled
+ 32768
+ normal
+
+
+ eth1/64
+ enabled
+ 32768
+ normal
+
+
+ eth1/115
+ enabled
+ 32768
+ normal
+
+
+ eth1/78
+ enabled
+ 32768
+ normal
+
+
+ eth1/114
+ enabled
+ 32768
+ normal
+
+
+ eth1/121
+ enabled
+ 32768
+ normal
+
+
+ eth1/83
+ enabled
+ 32768
+ normal
+
+
+ eth1/75
+ enabled
+ 32768
+ normal
+
+
+ eth1/112
+ enabled
+ 32768
+ normal
+
+
+ eth1/3
+ enabled
+ 32768
+ normal
+
+
+ eth1/41
+ enabled
+ 32768
+ normal
+
+
+ eth1/122
+ enabled
+ 32768
+ normal
+
+
+ eth1/33
+ enabled
+ 32768
+ normal
+
+
+ eth1/50
+ enabled
+ 32768
+ normal
+
+
+ eth1/124
+ enabled
+ 32768
+ normal
+
+
+ eth1/59
+ enabled
+ 32768
+ normal
+
+
+ eth1/21
+ enabled
+ 32768
+ normal
+
+
+ eth1/109
+ enabled
+ 32768
+ normal
+
+
+ eth1/29
+ enabled
+ 32768
+ normal
+
+
+ eth1/23
+ enabled
+ 32768
+ normal
+
+
+ eth1/55
+ enabled
+ 32768
+ normal
+
+
+ eth1/107
+ enabled
+ 32768
+ normal
+
+
+ eth1/89
+ enabled
+ 32768
+ normal
+
+
+ eth1/94
+ enabled
+ 32768
+ normal
+
+
+ eth1/44
+ enabled
+ 32768
+ normal
+
+
+ eth1/77
+ enabled
+ 32768
+ normal
+
+
+ eth1/117
+ enabled
+ 32768
+ normal
+
+
+ eth1/47
+ enabled
+ 32768
+ normal
+
+
+ eth1/42
+ enabled
+ 32768
+ normal
+
+
+ eth1/67
+ enabled
+ 32768
+ normal
+
+
+ eth1/102
+ enabled
+ 32768
+ normal
+
+
+ eth1/26
+ enabled
+ 32768
+ normal
+
+
+ eth1/76
+ enabled
+ 32768
+ normal
+
+
+ eth1/18
+ enabled
+ 32768
+ normal
+
+
+ eth1/62
+ enabled
+ 32768
+ normal
+
+
+ eth1/71
+ enabled
+ 32768
+ normal
+
+
+ eth1/95
+ enabled
+ 32768
+ normal
+
+
+ eth1/85
+ enabled
+ 32768
+ normal
+
+
+ eth1/60
+ enabled
+ 32768
+ normal
+
+
+ eth1/8
+ enabled
+ 32768
+ normal
+
+
+ eth1/97
+ enabled
+ 32768
+ normal
+
+
+ eth1/39
+ enabled
+ 32768
+ normal
+
+
+ eth1/38
+ enabled
+ 32768
+ normal
+
+
+ eth1/79
+ enabled
+ 32768
+ normal
+
+
+ eth1/17
+ enabled
+ 32768
+ normal
+
+
+ eth1/96
+ enabled
+ 32768
+ normal
+
+
+ eth1/105
+ enabled
+ 32768
+ normal
+
+
+ eth1/70
+ enabled
+ 32768
+ normal
+
+
+ eth1/110
+ enabled
+ 32768
+ normal
+
+
+ eth1/45
+ enabled
+ 32768
+ normal
+
+
+ eth1/127
+ enabled
+ 32768
+ normal
+
+
+ eth1/87
+ enabled
+ 32768
+ normal
+
+
+ eth1/125
+ enabled
+ 32768
+ normal
+
+
+ eth1/88
+ enabled
+ 32768
+ normal
+
+
+ eth1/15
+ enabled
+ 32768
+ normal
+
+
+ eth1/101
+ enabled
+ 32768
+ normal
+
+
+ eth1/111
+ enabled
+ 32768
+ normal
+
+
+ eth1/34
+ enabled
+ 32768
+ normal
+
+
+ eth1/113
+ enabled
+ 32768
+ normal
+
+
+ eth1/68
+ enabled
+ 32768
+ normal
+
+
+ eth1/57
+ enabled
+ 32768
+ normal
+
+
+ eth1/54
+ enabled
+ 32768
+ normal
+
+
+ eth1/13
+ enabled
+ 32768
+ normal
+
+
+ eth1/2
+ enabled
+ 32768
+ normal
+
+
+ eth1/53
+ enabled
+ 32768
+ normal
+
+
+ eth1/98
+ enabled
+ 32768
+ normal
+
+
+ eth1/48
+ enabled
+ 32768
+ normal
+
+
+ eth1/65
+ enabled
+ 32768
+ normal
+
+
+ eth1/11
+ enabled
+ 32768
+ normal
+
+
+ eth1/72
+ enabled
+ 32768
+ normal
+
+
+ eth1/12
+ enabled
+ 32768
+ normal
+
+
+ eth1/126
+ enabled
+ 32768
+ normal
+
+
+ eth1/103
+ enabled
+ 32768
+ normal
+
+
+ eth1/36
+ enabled
+ 32768
+ normal
+
+
+ eth1/80
+ enabled
+ 32768
+ normal
+
+
+ eth1/52
+ enabled
+ 32768
+ normal
+
+
+ eth1/82
+ enabled
+ 32768
+ normal
+
+
+ eth1/10
+ enabled
+ 32768
+ normal
+
+
+ eth1/20
+ enabled
+ 32768
+ normal
+
+
+ eth1/1
+ enabled
+ 32768
+ normal
+
+
+ eth1/27
+ enabled
+ 32768
+ normal
+
+
+ eth1/74
+ enabled
+ 32768
+ normal
+
+
+ eth1/16
+ enabled
+ 32768
+ normal
+
+
+ eth1/14
+ enabled
+ 32768
+ normal
+
+
+ eth1/24
+ enabled
+ 32768
+ normal
+
+
+ eth1/99
+ enabled
+ 32768
+ normal
+
+
+ eth1/108
+ enabled
+ 32768
+ normal
+
+
+ eth1/43
+ enabled
+ 32768
+ normal
+
+
+ eth1/104
+ enabled
+ 32768
+ normal
+
+
+ eth1/32
+ enabled
+ 32768
+ normal
+
+
+ eth1/69
+ enabled
+ 32768
+ normal
+
+
+ eth1/86
+ enabled
+ 32768
+ normal
+
+
+ eth1/37
+ enabled
+ 32768
+ normal
+
+
+ eth1/40
+ enabled
+ 32768
+ normal
+
+
+ eth1/46
+ enabled
+ 32768
+ normal
+
+
+ eth1/63
+ enabled
+ 32768
+ normal
+
+
+ eth1/49
+ enabled
+ 32768
+ normal
+
+
+ eth1/58
+ enabled
+ 32768
+ normal
+
+
+ eth1/100
+ enabled
+ 32768
+ normal
+
+
+ eth1/123
+ enabled
+ 32768
+ normal
+
+
+
+
+
+ 1800
+
+
+ enabled
+
+ enabled
+
+
+
+
+ mgmt0
+ up
+ on
+ auto
+ 1500
+
+ l3Inst
+ /System/inst-items/Inst-list[mode='management']
+
+ enable
+ auto
+
+
+ vr-n9kv
+
+ enabled
+
+ none
+ enabled
+ 1380
+ 174080
+ 1
+ 250
+ 180
+ 5
+ disabled
+
+
+
+
+ enabled
+
+
+
+ -1
+ 443
+ false
+ TLSv1.1 TLSv1.2
+
+
+
+ ps-rdn
+
+
+
+ enabled
+
+
+ enabled
+
+
+ 0
+
+
+
+ vlan-1
+
+
+
+
+ None
+ Disable
+ 0
+ 4294967295
+
+
+
+
+ eth1/116
+
+
+ eth1/53
+
+
+ eth1/62
+
+
+ eth1/48
+
+
+ eth1/50
+
+
+ eth1/60
+
+
+ eth1/4
+
+
+ eth1/95
+
+
+ eth1/126
+
+
+ eth1/80
+
+
+ eth1/76
+
+
+ eth1/21
+
+
+ eth1/43
+
+
+ eth1/127
+
+
+ eth1/26
+
+
+ eth1/82
+
+
+ eth1/85
+
+
+ eth1/86
+
+
+ eth1/49
+
+
+ eth1/125
+
+
+ eth1/15
+
+
+ eth1/121
+
+
+ eth1/38
+
+
+ eth1/73
+
+
+ eth1/98
+
+
+ eth1/24
+
+
+ eth1/35
+
+
+ eth1/91
+
+
+ eth1/56
+
+
+ eth1/33
+
+
+ eth1/117
+
+
+ eth1/34
+
+
+ eth1/61
+
+
+ eth1/74
+
+
+ eth1/30
+
+
+ eth1/57
+
+
+ eth1/94
+
+
+ eth1/128
+
+
+ eth1/32
+
+
+ eth1/107
+
+
+ eth1/54
+
+
+ eth1/87
+
+
+ eth1/106
+
+
+ eth1/90
+
+
+ eth1/42
+
+
+ eth1/10
+
+
+ eth1/96
+
+
+ eth1/123
+
+
+ eth1/1
+
+
+ eth1/83
+
+
+ eth1/114
+
+
+ eth1/7
+
+
+ eth1/104
+
+
+ eth1/100
+
+
+ eth1/11
+
+
+ eth1/55
+
+
+ eth1/2
+
+
+ eth1/46
+
+
+ eth1/27
+
+
+ eth1/67
+
+
+ eth1/31
+
+
+ eth1/29
+
+
+ eth1/3
+
+
+ eth1/81
+
+
+ eth1/108
+
+
+ eth1/93
+
+
+ eth1/39
+
+
+ eth1/78
+
+
+ eth1/12
+
+
+ eth1/5
+
+
+ eth1/18
+
+
+ eth1/115
+
+
+ eth1/28
+
+
+ eth1/124
+
+
+ eth1/97
+
+
+ eth1/120
+
+
+ eth1/71
+
+
+ eth1/89
+
+
+ eth1/70
+
+
+ eth1/22
+
+
+ eth1/72
+
+
+ eth1/59
+
+
+ eth1/92
+
+
+ eth1/75
+
+
+ eth1/44
+
+
+ eth1/112
+
+
+ eth1/79
+
+
+ eth1/101
+
+
+ eth1/13
+
+
+ eth1/40
+
+
+ eth1/17
+
+
+ eth1/47
+
+
+ eth1/113
+
+
+ eth1/37
+
+
+ eth1/64
+
+
+ eth1/8
+
+
+ eth1/23
+
+
+ eth1/9
+
+
+ eth1/99
+
+
+ eth1/20
+
+
+ eth1/51
+
+
+ eth1/84
+
+
+ eth1/122
+
+
+ eth1/110
+
+
+ eth1/88
+
+
+ eth1/65
+
+
+ eth1/102
+
+
+ eth1/52
+
+
+ eth1/103
+
+
+ eth1/14
+
+
+ eth1/45
+
+
+ eth1/68
+
+
+ eth1/118
+
+
+ eth1/63
+
+
+ eth1/66
+
+
+ eth1/25
+
+
+ eth1/105
+
+
+ eth1/41
+
+
+ eth1/119
+
+
+ eth1/111
+
+
+ eth1/109
+
+
+ eth1/58
+
+
+ eth1/77
+
+
+ eth1/69
+
+
+ eth1/16
+
+
+ eth1/36
+
+
+ eth1/6
+
+
+ eth1/19
+
+
+
+
+ Disable
+ 1000000
+ 2000000
+ 3
+
+ 4096
+ 0
+ PC_LB_ALGO_DLB
+
+ 100
+ enable
+
+ Default
+ Disable
+ PSTAT_DISABLE
+ all
+ DEFAULT
+ DEFAULT
+ DEFAULT
+
+ 768
+ 0
+ 512
+
+ disabled
+
+
+ enabled
+
+
+ enabled
+
+ disabled
+
+
+ boxen
+ md5
+
+
+ network-admin
+
+
+ false
+ false
+ des
+ 0
+
+
+ admin
+ md5
+
+
+ network-admin
+
+
+ false
+ false
+ des
+ 0
+
+
+ default
+
+
+
+ 2
+ CRITICAL(2)
+ no
+ PMON@CRITICAL
+
+
+ 5
+ INFORMATION(5)
+ no
+ PMON@INFO
+
+
+ 4
+ WARNING(4)
+ no
+ PMON@WARNING
+
+
+ 1
+ FATAL(1)
+ no
+ PMON@FATAL
+
+
+ 3
+ ERROR(3)
+ no
+ PMON@ERROR
+
+
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ disable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ enable
+
+
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+ enable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+ 0
+
+
+
+ disable
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ disable
+
+
+
+
+ enable
+
+
+
+
+ disable
+
+
+ disable
+
+
+
+
+ disable
+
+
+ disable
+
+
+ disable
+
+
+
+ no
+ no
+
+
+
+ enabled
+
+ enabled
+ enabled
+ normal
+ enabled
+
+
+ eth1/111
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/111
+ 128
+
+
+ eth1/36
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/36
+ 128
+
+
+ eth1/60
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/60
+ 128
+
+
+ eth1/110
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/110
+ 128
+
+
+ eth1/97
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/97
+ 128
+
+
+ eth1/37
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/37
+ 128
+
+
+ eth1/13
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/13
+ 128
+
+
+ eth1/107
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/107
+ 128
+
+
+ eth1/15
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/15
+ 128
+
+
+ eth1/103
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/103
+ 128
+
+
+ eth1/12
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/12
+ 128
+
+
+ eth1/96
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/96
+ 128
+
+
+ eth1/124
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/124
+ 128
+
+
+ eth1/101
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/101
+ 128
+
+
+ eth1/90
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/90
+ 128
+
+
+ eth1/69
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/69
+ 128
+
+
+ eth1/120
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/120
+ 128
+
+
+ eth1/72
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/72
+ 128
+
+
+ eth1/113
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/113
+ 128
+
+
+ eth1/22
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/22
+ 128
+
+
+ eth1/29
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/29
+ 128
+
+
+ eth1/33
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/33
+ 128
+
+
+ eth1/78
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/78
+ 128
+
+
+ eth1/25
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/25
+ 128
+
+
+ eth1/65
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/65
+ 128
+
+
+ eth1/44
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/44
+ 128
+
+
+ eth1/59
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/59
+ 128
+
+
+ eth1/40
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/40
+ 128
+
+
+ eth1/117
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/117
+ 128
+
+
+ eth1/104
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/104
+ 128
+
+
+ eth1/42
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/42
+ 128
+
+
+ eth1/61
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/61
+ 128
+
+
+ eth1/86
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/86
+ 128
+
+
+ eth1/49
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/49
+ 128
+
+
+ eth1/21
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/21
+ 128
+
+
+ eth1/4
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/4
+ 128
+
+
+ eth1/87
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/87
+ 128
+
+
+ eth1/95
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/95
+ 128
+
+
+ eth1/9
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/9
+ 128
+
+
+ eth1/19
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/19
+ 128
+
+
+ eth1/14
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/14
+ 128
+
+
+ eth1/53
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/53
+ 128
+
+
+ eth1/46
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/46
+ 128
+
+
+ eth1/10
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/10
+ 128
+
+
+ eth1/126
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/126
+ 128
+
+
+ eth1/62
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/62
+ 128
+
+
+ eth1/17
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/17
+ 128
+
+
+ eth1/50
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/50
+ 128
+
+
+ eth1/84
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/84
+ 128
+
+
+ eth1/32
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/32
+ 128
+
+
+ eth1/35
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/35
+ 128
+
+
+ eth1/70
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/70
+ 128
+
+
+ eth1/81
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/81
+ 128
+
+
+ eth1/67
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/67
+ 128
+
+
+ eth1/48
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/48
+ 128
+
+
+ eth1/31
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/31
+ 128
+
+
+ eth1/75
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/75
+ 128
+
+
+ eth1/43
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/43
+ 128
+
+
+ eth1/119
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/119
+ 128
+
+
+ eth1/123
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/123
+ 128
+
+
+ eth1/8
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/8
+ 128
+
+
+ eth1/76
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/76
+ 128
+
+
+ eth1/58
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/58
+ 128
+
+
+ eth1/28
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/28
+ 128
+
+
+ eth1/98
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/98
+ 128
+
+
+ eth1/105
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/105
+ 128
+
+
+ eth1/30
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/30
+ 128
+
+
+ eth1/66
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/66
+ 128
+
+
+ eth1/127
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/127
+ 128
+
+
+ eth1/7
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/7
+ 128
+
+
+ eth1/73
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/73
+ 128
+
+
+ eth1/27
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/27
+ 128
+
+
+ eth1/100
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/100
+ 128
+
+
+ eth1/18
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/18
+ 128
+
+
+ eth1/20
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/20
+ 128
+
+
+ eth1/23
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/23
+ 128
+
+
+ eth1/54
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/54
+ 128
+
+
+ eth1/45
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/45
+ 128
+
+
+ eth1/106
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/106
+ 128
+
+
+ eth1/91
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/91
+ 128
+
+
+ eth1/116
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/116
+ 128
+
+
+ eth1/1
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/1
+ 128
+
+
+ eth1/63
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/63
+ 128
+
+
+ eth1/57
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/57
+ 128
+
+
+ eth1/79
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/79
+ 128
+
+
+ eth1/85
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/85
+ 128
+
+
+ eth1/125
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/125
+ 128
+
+
+ eth1/122
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/122
+ 128
+
+
+ eth1/6
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/6
+ 128
+
+
+ eth1/74
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/74
+ 128
+
+
+ eth1/16
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/16
+ 128
+
+
+ eth1/51
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/51
+ 128
+
+
+ eth1/80
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/80
+ 128
+
+
+ eth1/3
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/3
+ 128
+
+
+ eth1/115
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/115
+ 128
+
+
+ eth1/38
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/38
+ 128
+
+
+ eth1/108
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/108
+ 128
+
+
+ eth1/121
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/121
+ 128
+
+
+ eth1/92
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/92
+ 128
+
+
+ eth1/88
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/88
+ 128
+
+
+ eth1/68
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/68
+ 128
+
+
+ eth1/71
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/71
+ 128
+
+
+ eth1/94
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/94
+ 128
+
+
+ eth1/93
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/93
+ 128
+
+
+ eth1/41
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/41
+ 128
+
+
+ eth1/52
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/52
+ 128
+
+
+ eth1/102
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/102
+ 128
+
+
+ eth1/2
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/2
+ 128
+
+
+ eth1/11
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/11
+ 128
+
+
+ eth1/128
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/128
+ 128
+
+
+ eth1/89
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/89
+ 128
+
+
+ eth1/83
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/83
+ 128
+
+
+ eth1/26
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/26
+ 128
+
+
+ eth1/24
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/24
+ 128
+
+
+ eth1/34
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/34
+ 128
+
+
+ eth1/118
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/118
+ 128
+
+
+ eth1/55
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/55
+ 128
+
+
+ eth1/109
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/109
+ 128
+
+
+ eth1/99
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/99
+ 128
+
+
+ eth1/64
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/64
+ 128
+
+
+ eth1/112
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/112
+ 128
+
+
+ eth1/77
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/77
+ 128
+
+
+ eth1/47
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/47
+ 128
+
+
+ eth1/114
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/114
+ 128
+
+
+ eth1/5
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/5
+ 128
+
+
+ eth1/56
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/56
+ 128
+
+
+ eth1/39
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/39
+ 128
+
+
+ eth1/82
+ enabled
+ default
+ default
+ 0
+
+ default
+ auto
+ default
+ eth1/82
+ 128
+
+
+ 1024
+ disabled
+ pvrst
+
+ disabled
+ 15
+ 2
+ 20
+ 20
+
+
+ 0
+ enabled
+ 2
+ 32768
+ primary
+ disabled
+ none
+ 1-4094
+
+
+ 0
+ enabled
+
+ short
+
+
+ 1
+ enabled
+ 2
+ 15
+ 2
+ 20
+ 32768
+ disabled
+ none
+
+
+
+
+
+ false
+ never
+ never
+ N9K-9000v
+
+
+ enabled
+ disabled
+
+ disabled
+ critical
+ disabled
+ 8
+
+
+
+
+ enabled
+
+
+
+
+ pap
+ false
+ yes
+ no
+ no
+ local
+
+
+
+ config
+ false
+ true
+
+
+ exec
+ false
+ true
+
+
+ assign-default-role
+
+ false
+ true
+ local
+
+
+ pap
+ false
+ yes
+ yes
+ no
+ local
+
+
+
+ config
+ false
+ true
+
+
+ exec
+ false
+ true
+
+
+ Error
+
+ false
+ local
+
+
+ false
+ local
+
+ no
+ no
+
+
+
+ all
+
+
+ rootep
+
+
+ 24
+ login,logout,refresh
+ 1200
+ 600
+
+
+ 127
+ 8
+ yes
+ no
+
+ 2
+ enable
+ 48
+ 15
+ 5
+ 24
+
+
+ 0
+ 0
+ Error
+
+
+ radius
+ 0
+ default
+
+
+ 1
+ 5
+
+
+
+ dev-ops
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-11
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-12
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ network-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-6
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ nxdb-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ priv-1
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-7
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ vdc-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-9
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-4
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-14
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ network-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ vdc-operator
+ no
+ no
+ no
+ operator
+ no
+ readPriv
+
+
+ priv-5
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-15
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-8
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-3
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ nxdb-admin
+ no
+ no
+ no
+ admin
+ no
+ writePriv
+
+
+ priv-13
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-0
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-10
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ priv-2
+ no
+ no
+ no
+
+ no
+ noDataPriv
+
+
+ yes
+
+
+ admin
+ active
+ no
+ no
+ never
+ no
+ 0
+
+
+ all
+
+
+ network-admin
+ noDataPriv
+
+
+
+
+
+
+ boxen
+ active
+ no
+ no
+ never
+ no
+ 0
+
+
+ all
+
+
+ network-admin
+ noDataPriv
+
+
+
+
+
+
+
+
+
+
+ default
+ 0
+ false
+
+
+
+
+ enabled
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-standard-out.txt
new file mode 100644
index 0000000..6b87305
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-standard-out.txt
@@ -0,0 +1,164 @@
+
+
+
+ 17.3R2.10
+
+ vr-vqfx
+
+ $1$a$/nr2uXmtaTqW504hxM3Yw0
+
+
+
+ admin
+ 2001
+ super-user
+
+ $6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1.
+
+
+
+ boxen
+ 2000
+ super-user
+
+ $1$a$/nr2uXmtaTqW504hxM3Yw0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fxp0.0
+
+
+
+
+
+ *
+
+ any
+
+
+
+
+ messages
+
+ any
+
+
+
+ authorization
+
+
+
+
+ interactive-commands
+
+ interactive-commands
+
+
+
+
+
+
+
+ https://ae1.juniper.net/junos/key_retrieval
+
+
+
+
+
+
+
+ untrust-screen
+
+
+
+
+
+
+
+
+
+ 1024
+ 200
+ 1024
+ 2048
+ 2000
+ 20
+
+
+
+
+
+
+
+ trust
+ trust
+
+ default-permit
+
+ any
+ any
+ any
+
+
+
+
+
+
+
+
+ trust
+ untrust
+
+ default-permit
+
+ any
+ any
+ any
+
+
+
+
+
+
+
+
+
+
+ trust
+
+
+
+ untrust
+ untrust-screen
+
+
+
+
+
+ fxp0
+
+ 0
+
+
+
+ 10.0.0.15/24
+
+
+
+
+
+
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-system-out.txt
new file mode 100644
index 0000000..6b87305
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-getconfig-simple-juniper_junos-system-out.txt
@@ -0,0 +1,164 @@
+
+
+
+ 17.3R2.10
+
+ vr-vqfx
+
+ $1$a$/nr2uXmtaTqW504hxM3Yw0
+
+
+
+ admin
+ 2001
+ super-user
+
+ $6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1.
+
+
+
+ boxen
+ 2000
+ super-user
+
+ $1$a$/nr2uXmtaTqW504hxM3Yw0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fxp0.0
+
+
+
+
+
+ *
+
+ any
+
+
+
+
+ messages
+
+ any
+
+
+
+ authorization
+
+
+
+
+ interactive-commands
+
+ interactive-commands
+
+
+
+
+
+
+
+ https://ae1.juniper.net/junos/key_retrieval
+
+
+
+
+
+
+
+ untrust-screen
+
+
+
+
+
+
+
+
+
+ 1024
+ 200
+ 1024
+ 2048
+ 2000
+ 20
+
+
+
+
+
+
+
+ trust
+ trust
+
+ default-permit
+
+ any
+ any
+ any
+
+
+
+
+
+
+
+
+ trust
+ untrust
+
+ default-permit
+
+ any
+ any
+ any
+
+
+
+
+
+
+
+
+
+
+ trust
+
+
+
+ untrust
+ untrust-screen
+
+
+
+
+
+ fxp0
+
+ 0
+
+
+
+ 10.0.0.15/24
+
+
+
+
+
+
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-standard-out.txt
new file mode 100644
index 0000000..7e6dd12
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-standard-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-system-out.txt
new file mode 100644
index 0000000..7e6dd12
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-arista_eos-system-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..1b48447
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-standard-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..1b48447
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxe-system-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..8eae6a9
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-standard-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..8eae6a9
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_iosxr-system-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..c7abff4
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..c7abff4
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-cisco_nxos-system-out.txt
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-standard-out.txt
new file mode 100644
index 0000000..536a6fb
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-standard-out.txt
@@ -0,0 +1,4 @@
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-system-out.txt
new file mode 100644
index 0000000..536a6fb
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-lock-juniper_junos-system-out.txt
@@ -0,0 +1,4 @@
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-standard-out.txt
new file mode 100644
index 0000000..203c1c4
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-standard-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-system-out.txt
new file mode 100644
index 0000000..203c1c4
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-arista_eos-system-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..32dd93b
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-standard-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..32dd93b
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxe-system-out.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..f2e6b2a
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-standard-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..f2e6b2a
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_iosxr-system-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..151b7ab
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..151b7ab
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-cisco_nxos-system-out.txt
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-standard-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-standard-out.txt
new file mode 100644
index 0000000..e40e197
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-standard-out.txt
@@ -0,0 +1,4 @@
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-system-out.txt b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-system-out.txt
new file mode 100644
index 0000000..e40e197
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/functional-lock-unlock-simple-unlock-juniper_junos-system-out.txt
@@ -0,0 +1,4 @@
+
+
+
+]]>]]>
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/get-simple-in.txt b/driver/netconf/test-fixtures/golden/get-simple-in.txt
new file mode 100644
index 0000000..60e271b
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/get-simple-in.txt
@@ -0,0 +1,15 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#249
+
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/get-simple-out.txt b/driver/netconf/test-fixtures/golden/get-simple-out.txt
new file mode 100644
index 0000000..48e6eab
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/get-simple-out.txt
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/getconfig-simple-in.txt b/driver/netconf/test-fixtures/golden/getconfig-simple-in.txt
new file mode 100644
index 0000000..9d32206
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/getconfig-simple-in.txt
@@ -0,0 +1,14 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#175
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/getconfig-simple-out.txt b/driver/netconf/test-fixtures/golden/getconfig-simple-out.txt
new file mode 100644
index 0000000..27d3cfb
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/getconfig-simple-out.txt
@@ -0,0 +1 @@
+true deny deny deny true admin PRIV15 permit-all * * permit default default-vrf [read-only] static 1
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/lock-unlock-simple-in.txt b/driver/netconf/test-fixtures/golden/lock-unlock-simple-in.txt
new file mode 100644
index 0000000..3d342c8
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/lock-unlock-simple-in.txt
@@ -0,0 +1,21 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#163
+
+##
+
+
+
+
+#167
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/lock-unlock-simple-out.txt b/driver/netconf/test-fixtures/golden/lock-unlock-simple-out.txt
new file mode 100644
index 0000000..edb6480
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/lock-unlock-simple-out.txt
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/golden/validate-simple-in.txt b/driver/netconf/test-fixtures/golden/validate-simple-in.txt
new file mode 100644
index 0000000..6b625e1
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/validate-simple-in.txt
@@ -0,0 +1,14 @@
+
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+
+
+#175
+
+##
+
+
+
diff --git a/driver/netconf/test-fixtures/golden/validate-simple-out.txt b/driver/netconf/test-fixtures/golden/validate-simple-out.txt
new file mode 100644
index 0000000..8eae6a9
--- /dev/null
+++ b/driver/netconf/test-fixtures/golden/validate-simple-out.txt
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/lock-unlock-simple.txt b/driver/netconf/test-fixtures/lock-unlock-simple.txt
new file mode 100644
index 0000000..a616276
--- /dev/null
+++ b/driver/netconf/test-fixtures/lock-unlock-simple.txt
@@ -0,0 +1,49 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#175
+
+##
+
+
+#163
+
+##
+
+
+#132
+
+
+##
+#167
+
+##
+
+
+#132
+
+
+##
\ No newline at end of file
diff --git a/driver/netconf/test-fixtures/validate-simple.txt b/driver/netconf/test-fixtures/validate-simple.txt
new file mode 100644
index 0000000..6f9c7cf
--- /dev/null
+++ b/driver/netconf/test-fixtures/validate-simple.txt
@@ -0,0 +1,38 @@
+
+
+
+urn:ietf:params:netconf:base:1.0
+urn:ietf:params:netconf:base:1.1
+urn:ietf:params:netconf:capability:writable-running:1.0
+urn:ietf:params:netconf:capability:xpath:1.0
+urn:ietf:params:netconf:capability:validate:1.0
+urn:ietf:params:netconf:capability:validate:1.1
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:netconf:capability:notification:1.0
+urn:ietf:params:netconf:capability:interleave:1.0
+urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged
+urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=9ba76f016f7eb0d15cc6fb677efc3fee
+urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01
+urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01
+
+ urn:ietf:params:netconf:capability:notification:1.1
+
+
+25 ]]>]]>
+
+
+ urn:ietf:params:netconf:base:1.1
+
+ ]]>]]>
+#175
+
+##
+
+
+#119
+
+
+
+
+
+##
\ No newline at end of file
diff --git a/driver/netconf/validate.go b/driver/netconf/validate.go
new file mode 100644
index 0000000..723786d
--- /dev/null
+++ b/driver/netconf/validate.go
@@ -0,0 +1,35 @@
+package netconf //nolint: dupl
+
+import (
+ "encoding/xml"
+
+ "github.com/scrapli/scrapligo/response"
+)
+
+type validate struct {
+ XMLName xml.Name `xml:"validate"`
+ Source *sourceT `xml:""`
+}
+
+func (d *Driver) buildValidateElem(source string) *message {
+ validateElem := &validate{
+ XMLName: xml.Name{},
+ Source: d.buildSourceElem(source),
+ }
+
+ netconfInput := d.buildPayload(validateElem)
+
+ return netconfInput
+}
+
+// Validate executes validate RPC for the source datastore against the NETCONF server.
+func (d *Driver) Validate(source string) (*response.NetconfResponse, error) {
+ d.Logger.Infof("Validate RPC requested, source '%s'", source)
+
+ op, err := NewOperation()
+ if err != nil {
+ return nil, err
+ }
+
+ return d.sendRPC(d.buildValidateElem(source), op)
+}
diff --git a/driver/netconf/validate_test.go b/driver/netconf/validate_test.go
new file mode 100644
index 0000000..64523e7
--- /dev/null
+++ b/driver/netconf/validate_test.go
@@ -0,0 +1,74 @@
+package netconf_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testValidate(testName string, testCase *util.PayloadTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.PayloadFile)
+
+ r, err := d.Validate("candidate")
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running netconf Driver Validate, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestValidate(t *testing.T) {
+ cases := map[string]*util.PayloadTestCase{
+ "validate-simple": {
+ Description: "simple validate test",
+ PayloadFile: "validate-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testValidate(testName, testCase)
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/network/acquirepriv.go b/driver/network/acquirepriv.go
new file mode 100644
index 0000000..37a300a
--- /dev/null
+++ b/driver/network/acquirepriv.go
@@ -0,0 +1,225 @@
+package network
+
+import (
+ "fmt"
+ "regexp"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ noAction = "noAction"
+ escalateAction = "escalateAction"
+ deescalateAction = "deescalateAction"
+
+ unknownPriv = "UNKNOWN"
+)
+
+func (d *Driver) buildPrivChangeMap(current, target string, steps *[]string) []string {
+ var workingSteps []string
+
+ if steps != nil {
+ workingSteps = *steps
+ }
+
+ workingSteps = append(workingSteps, current)
+
+ if current == target {
+ return workingSteps
+ }
+
+ for priv := range d.privGraph[current] {
+ if !util.StringSliceContains(workingSteps, priv) {
+ newWorkingSteps := d.buildPrivChangeMap(priv, target, &workingSteps)
+ if len(newWorkingSteps) > 0 {
+ return newWorkingSteps
+ }
+ }
+ }
+
+ return nil
+}
+
+func (d *Driver) determineCurrentPriv(currentPrompt string) ([]string, error) {
+ var possiblePrivs []string
+
+ for _, priv := range d.PrivilegeLevels {
+ if util.StringContainsAny(currentPrompt, priv.NotContains) {
+ continue
+ }
+
+ if priv.patternRe.MatchString(currentPrompt) {
+ possiblePrivs = append(possiblePrivs, priv.Name)
+ }
+ }
+
+ if len(possiblePrivs) == 0 {
+ return nil, fmt.Errorf(
+ "%w: could not determine privilege level from prompt '%s'",
+ util.ErrPrivilegeError, currentPrompt,
+ )
+ }
+
+ return possiblePrivs, nil
+}
+
+func (d *Driver) processAcquirePriv(
+ target, currentPrompt string,
+) (action, nextPriv string, err error) {
+ possiblePrivs, err := d.determineCurrentPriv(currentPrompt)
+ if err != nil {
+ return "", "", err
+ }
+
+ var current string
+
+ switch {
+ case util.StringSliceContains(possiblePrivs, d.CurrentPriv):
+ current = d.CurrentPriv
+ case util.StringSliceContains(possiblePrivs, target):
+ current = d.PrivilegeLevels[target].Name
+ default:
+ current = possiblePrivs[0]
+ }
+
+ if current == target {
+ d.CurrentPriv = current
+
+ return noAction, current, nil
+ }
+
+ mapTo := d.buildPrivChangeMap(current, target, nil)
+
+ // at this point we basically dont *know* the privilege leve we are at (or we wont/cant after
+ // we do an escalation or deescalation, so we reset to the dummy priv level
+ d.CurrentPriv = unknownPriv
+
+ if d.PrivilegeLevels[mapTo[1]].PreviousPriv != current {
+ return deescalateAction, current, nil
+ }
+
+ return escalateAction, d.PrivilegeLevels[mapTo[1]].Name, nil
+}
+
+func (d *Driver) escalate(target string) error {
+ var err error
+
+ p := d.PrivilegeLevels[target]
+
+ if !p.EscalateAuth || d.AuthSecondary == "" {
+ if d.AuthSecondary == "" {
+ d.Logger.Info(
+ "no auth secondary set, but escalate target may require auth, trying with no password",
+ )
+ }
+
+ _, err = d.Driver.Channel.SendInput(p.Escalate)
+ } else {
+ events := []*channel.SendInteractiveEvent{
+ {
+ ChannelInput: p.Escalate,
+ ChannelResponse: p.EscalatePrompt,
+ HideInput: false,
+ },
+ {
+ ChannelInput: d.AuthSecondary,
+ ChannelResponse: p.Pattern,
+ HideInput: true,
+ },
+ }
+
+ _, err = d.Driver.Channel.SendInteractive(
+ events,
+ // can't import opoptions as we'll have recursive imports, so we'll just do this...
+ // options probably could be handled more nicely, but it seems nice to have them in a
+ // single place so users don't have to think about where to import which option from,
+ // if this is the price we pay for that then it seems ok.
+ func(o interface{}) error {
+ a, ok := o.(*channel.OperationOptions)
+
+ if ok {
+ a.CompletePatterns = []*regexp.Regexp{
+ d.PrivilegeLevels[p.PreviousPriv].patternRe,
+ p.patternRe,
+ }
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ },
+ )
+ }
+
+ return err
+}
+
+func (d *Driver) deescalate(target string) error {
+ p := d.PrivilegeLevels[target]
+
+ _, err := d.Driver.Channel.SendInput(p.Deescalate)
+
+ return err
+}
+
+// AcquirePriv acquires the privilege level target. This method will handle any escalation or
+// deescalation necessary to acquire the requested privilege level including any authentication
+// that may be required.
+func (d *Driver) AcquirePriv(target string) error {
+ d.Logger.Infof("AcquirePriv requested, target privilege level '%s'", target)
+
+ if _, ok := d.PrivilegeLevels[target]; !ok {
+ return fmt.Errorf(
+ "%w: requested target privilege level '%s' is not a valid privilege level",
+ util.ErrPrivilegeError,
+ target,
+ )
+ }
+
+ var count int
+
+ for {
+ currentPrompt, err := d.Driver.GetPrompt()
+ if err != nil {
+ return err
+ }
+
+ action, next, err := d.processAcquirePriv(
+ target,
+ currentPrompt,
+ )
+ if err != nil {
+ return err
+ }
+
+ switch action {
+ case noAction:
+ d.Logger.Debug("AcquirePriv determined no privilege action necessary")
+
+ return nil
+ case escalateAction:
+ d.Logger.Debug("AcquirePriv determined privilege escalation necessary")
+
+ err = d.escalate(next)
+ case deescalateAction:
+ d.Logger.Debug("AcquirePriv determined privilege de-escalation necessary")
+
+ err = d.deescalate(next)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ count++
+
+ if count > len(d.PrivilegeLevels)*2 {
+ return fmt.Errorf(
+ "%w: failed to acquire target privilege level '%s'",
+ util.ErrPrivilegeError,
+ target,
+ )
+ }
+ }
+}
diff --git a/driver/network/common_test.go b/driver/network/common_test.go
deleted file mode 100644
index b0d97ce..0000000
--- a/driver/network/common_test.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package network_test
-
-import (
- "testing"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/driver/core"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-func platformCommandMapShort() map[string]string {
- return map[string]string{
- "cisco_iosxe": "show run | i hostname",
- "cisco_iosxr": "show run | i MgmtEth0",
- "cisco_nxos": "show run | i scp-server",
- "arista_eos": "show run | i ZTP",
- "juniper_junos": "show configuration | match 10.0.0.15",
- "nokia_sros": "show version",
- "nokia_sros_classic": "show version",
- "paloalto_panos": "show clock",
- }
-}
-
-func platformCommandMapLong() map[string]string {
- return map[string]string{
- "cisco_iosxe": "show run",
- "cisco_iosxr": "show run",
- "cisco_nxos": "show run",
- "arista_eos": "show run",
- "juniper_junos": "show configuration",
- "nokia_sros": "show router interface",
- "nokia_sros_classic": "show router interface",
- "paloalto_panos": "show templates",
- }
-}
-
-func platformConfigsMap() map[string][]string {
- return map[string][]string{
- "cisco_iosxe": {"interface loopback0", "description tacocat", "no interface loopback0"},
- "cisco_iosxr": {
- "interface loopback0",
- "description tacocat",
- "no interface loopback0",
- "commit",
- },
- "cisco_nxos": {"interface loopback0", "description tacocat", "no interface loopback0"},
- "arista_eos": {"interface loopback0", "description tacocat", "no interface loopback0"},
- "juniper_junos": {
- "set interfaces fxp0.0 description tacocat",
- "delete interfaces fxp0.0 description tacocat",
- "commit",
- },
- "nokia_sros": {
- `configure router interface "system" description "@ntdvps"`,
- "configure system",
- "location wide_internet",
- "commit",
- },
- "nokia_sros_classic": {
- `configure router interface "system" description "@ntdvps"`,
- "configure system",
- "location wide_internet",
- },
- "paloalto_panos": {
- "set display-name BLAH",
- "commit",
- },
- }
-}
-
-type functionalTestHostConnData struct {
- Host string
- Port int
- TelnetPort int
-}
-
-func functionalTestHosts() map[string]*functionalTestHostConnData {
- return map[string]*functionalTestHostConnData{
- "cisco_iosxe": {
- Host: "localhost",
- Port: 21022,
- TelnetPort: 21023,
- },
- "cisco_iosxr": {
- Host: "localhost",
- Port: 23022,
- TelnetPort: 23023,
- },
- "cisco_nxos": {
- Host: "localhost",
- Port: 22022,
- TelnetPort: 22023,
- },
- "arista_eos": {
- Host: "localhost",
- Port: 24022,
- TelnetPort: 24023,
- },
- "juniper_junos": {
- Host: "localhost",
- Port: 25022,
- TelnetPort: 25023,
- },
- }
-}
-
-func newFunctionalTestDriver(
- t *testing.T,
- host, platform, transportName string,
- port int,
-) *network.Driver {
- d, driverErr := core.NewCoreDriver(
- host,
- platform,
- base.WithAuthUsername("boxen"),
- base.WithAuthPassword("b0x3N-b0x3N"),
- base.WithAuthSecondary("b0x3N-b0x3N"),
- base.WithPort(port),
- base.WithTransportType(transportName),
- base.WithAuthStrictKey(false),
- )
-
- if driverErr != nil {
- t.Fatalf("failed creating test device: %v", driverErr)
- }
-
- return d
-}
diff --git a/driver/network/driver.go b/driver/network/driver.go
new file mode 100644
index 0000000..846f121
--- /dev/null
+++ b/driver/network/driver.go
@@ -0,0 +1,103 @@
+package network
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// NewDriver returns an instance of Driver for the provided host with the given options set. Any
+// options in the driver/options package may be passed to this function -- those options may be
+// applied at the network.Driver, generic.Driver, channel.Channel, or Transport depending on the
+// specific option.
+func NewDriver(
+ host string,
+ opts ...util.Option,
+) (*Driver, error) {
+ gd, err := generic.NewDriver(host, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ d := &Driver{
+ Driver: gd,
+ AuthSecondary: "",
+ PrivilegeLevels: nil,
+ DefaultDesiredPriv: "",
+ OnOpen: nil,
+ OnClose: nil,
+ }
+
+ for _, option := range opts {
+ err = option(d)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ if d.DefaultDesiredPriv == "" || len(d.PrivilegeLevels) == 0 {
+ return nil, fmt.Errorf(
+ "%w: no default desired privilege level and/or privilege levels provided, "+
+ "these are required with 'netework' driver",
+ util.ErrBadOption,
+ )
+ }
+
+ d.UpdatePrivileges()
+
+ return d, nil
+}
+
+// Driver embeds generic.Driver and adds "network" centric functionality including privilege level
+// understanding and escalation/deescalation.
+type Driver struct {
+ *generic.Driver
+
+ AuthSecondary string
+
+ PrivilegeLevels map[string]*PrivilegeLevel
+ DefaultDesiredPriv string
+ CurrentPriv string
+ privGraph map[string]map[string]bool
+
+ OnOpen func(d *Driver) error
+ OnClose func(d *Driver) error
+}
+
+// Open opens the underlying generic.Driver, and by extension the channel.Channel and Transport
+// objects. This should be called prior to executing any SendX methods of the Driver.
+func (d *Driver) Open() error {
+ err := d.Driver.Open()
+ if err != nil {
+ return err
+ }
+
+ if d.OnOpen != nil {
+ err = d.OnOpen(d)
+ if err != nil {
+ d.Logger.Criticalf("error executing network driver OnOpen, error: %s", err)
+
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Close closes the underlying generic.Driver and by extension the channel.Channel and Transport
+// objects.
+func (d *Driver) Close() error {
+ if d.OnClose != nil {
+ err := d.OnClose(d)
+ if err != nil {
+ d.Logger.Criticalf("error running network on close, error: %s", err)
+ }
+ }
+
+ return d.Driver.Close()
+}
diff --git a/driver/network/driver_test.go b/driver/network/driver_test.go
new file mode 100644
index 0000000..8a17907
--- /dev/null
+++ b/driver/network/driver_test.go
@@ -0,0 +1,313 @@
+package network_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/scrapli/scrapligo/platform"
+ "github.com/scrapli/scrapligo/transport"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func resolveFile(t *testing.T, f string) string {
+ f, err := filepath.Abs(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return f
+}
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualIn []byte, actualOut string) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+ goldenIn := filepath.Join("test-fixtures", "golden", testName+"-in.txt")
+
+ err := os.WriteFile(goldenOut, []byte(actualOut), 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.WriteFile(goldenIn, actualIn, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func prepareDriver(
+ t *testing.T,
+ testName,
+ payloadFile string,
+) (*network.Driver, *transport.File) {
+ d, err := network.NewDriver(
+ "dummy",
+ options.WithTransportType(transport.FileTransport),
+ options.WithFileTransportFile(resolveFile(t, payloadFile)),
+ options.WithTransportReadSize(1),
+ options.WithReadDelay(0),
+ options.WithDefaultDesiredPriv("privilege-exec"),
+ options.WithPrivilegeLevels(map[string]*network.PrivilegeLevel{
+ "exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}>$`,
+ Name: "exec",
+ PreviousPriv: "",
+ Deescalate: "",
+ Escalate: "",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ "privilege-exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}#$`,
+ Name: "privilege-exec",
+ PreviousPriv: "exec",
+ Deescalate: "disable",
+ Escalate: "enable",
+ EscalateAuth: true,
+ EscalatePrompt: `(?im)^(?:enable\s){0,1}password:\s?$`,
+ },
+ "configuration": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$`,
+ NotContains: []string{"tcl)"},
+ Name: "configuration",
+ PreviousPriv: "privilege-exec",
+ Deescalate: "end",
+ Escalate: "configure terminal",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ }),
+ )
+ if err != nil {
+ t.Fatalf("%s: encountered error creating network Driver, error: %s", testName, err)
+ }
+
+ err = d.Channel.Open()
+ if err != nil {
+ t.Fatalf("%s: encountered error opening Channel, error: %s", testName, err)
+ }
+
+ fileTransportObj, ok := d.Transport.Impl.(*transport.File)
+ if !ok {
+ t.Fatalf("transport implementation is not Transport File")
+ }
+
+ return d, fileTransportObj
+}
+
+func writeGoldenFunctional(t *testing.T, testName, actualOut string) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+
+ err := os.WriteFile(goldenOut, []byte(actualOut), 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func getFunctionalHostIPPortLinuxOrRemote(
+ t *testing.T,
+ platformName string,
+) (host string, port int) {
+ switch platformName {
+ case platform.CiscoIosxe:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_IOSXE_HOST", "172.20.20.11")
+
+ return host, 22
+ case platform.CiscoIosxr:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_IOSXR_HOST", "172.20.20.12")
+
+ return host, 22
+ case platform.CiscoNxos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_CISCO_NXOS_HOST", "172.20.20.13")
+
+ return host, 22
+ case platform.AristaEos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_ARISTA_EOS_HOST", "172.20.20.14")
+
+ return host, 22
+ case platform.JuniperJunos:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_JUNIPER_JUNOS_HOST", "172.20.20.15")
+
+ return host, 22
+ case platform.NokiaSrl:
+ host = util.GetEnvStrOrDefault("SCRAPLIGO_NOKIA_SRL_HOST", "172.20.20.16")
+
+ return host, 22
+ }
+
+ t.Fatalf("failed finding platform host/port info")
+
+ return "", 0
+}
+
+func getFunctionalHostIPPort(t *testing.T, platformName string) (host string, port int) {
+ osType := runtime.GOOS
+
+ remoteOverride := util.GetEnvIntOrDefault("SCRAPLIGO_NO_HOST_FWD", 0)
+
+ if osType == "linux" || remoteOverride != 0 {
+ return getFunctionalHostIPPortLinuxOrRemote(t, platformName)
+ }
+
+ // otherwise we are running on darwin w/ local boxen w/ nat setup
+
+ host = "localhost"
+
+ switch platformName {
+ case platform.CiscoIosxe:
+ return host, 21022
+ case platform.CiscoIosxr:
+ return host, 22022
+ case platform.CiscoNxos:
+ return host, 23022
+ case platform.AristaEos:
+ return host, 24022
+ case platform.JuniperJunos:
+ return host, 25022
+ case platform.NokiaSrl:
+ return host, 26022
+ }
+
+ t.Fatalf("failed finding platform host/port info")
+
+ return "", 0
+}
+
+func getFunctionalHostUserPass(t *testing.T, platformName string) (user, pass string) {
+ user = util.Admin
+ pass = util.Admin
+
+ switch platformName {
+ case platform.CiscoIosxe:
+ return user, pass
+ case platform.CiscoIosxr:
+ return "clab", "clab@123"
+ case platform.CiscoNxos:
+ return user, pass
+ case platform.AristaEos:
+ return user, pass
+ case platform.JuniperJunos:
+ return user, "admin@123"
+ case platform.NokiaSrl:
+ return user, pass
+ }
+
+ t.Fatalf("failed finding platform user/pass info")
+
+ return "", ""
+}
+
+func prepareFunctionalDriver(
+ t *testing.T,
+ testName, platformName, transportName string,
+) *network.Driver {
+ host, port := getFunctionalHostIPPort(t, platformName)
+ user, pass := getFunctionalHostUserPass(t, platformName)
+
+ if transportName == transport.TelnetTransport {
+ if platformName != platform.CiscoIosxe {
+ t.Skip("only testing telnet on iosxe at the moment")
+ }
+
+ port++
+ }
+
+ p, err := platform.NewPlatform(
+ fmt.Sprintf("%s.yaml", platformName),
+ host,
+ options.WithPort(port),
+ options.WithAuthUsername(user),
+ options.WithAuthPassword(pass),
+ options.WithTransportType(transportName),
+ options.WithAuthNoStrictKey(),
+ // obviously only relevant for system transport, but will be ignored for others
+ // also should only be necessary for iosxe.
+ options.WithSystemTransportOpenArgs(
+ []string{
+ "-o",
+ "KexAlgorithms=+diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1",
+ },
+ ),
+ )
+ if err != nil {
+ t.Fatalf("%s: encountered error creating platform, error: %s", testName, err)
+ }
+
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error fetching network driver from platform, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ err = d.Open()
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error opening network driver, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ return d
+}
+
+func interTestSleep() {
+ if len(strings.Split(*platforms, ",")) == 1 {
+ // when only running against a single platform, back to back tests tend to cause some issues
+ // so basically stagger things out so the device doesn't choke.
+ time.Sleep(1 * time.Second)
+
+ return
+ }
+
+ if *transports == util.All {
+ // when we run w/ all transports we do one transport after another, so similar to above
+ // we just want to stagger things a bit.
+ time.Sleep(1 * time.Second)
+
+ return
+ }
+}
diff --git a/driver/network/network.go b/driver/network/network.go
deleted file mode 100644
index 74b07ea..0000000
--- a/driver/network/network.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package network
-
-import (
- "errors"
- "regexp"
- "strings"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
-)
-
-type privilegeAction string
-
-const (
- deescalateAction privilegeAction = "deescalateAction"
- escalateAction privilegeAction = "escalateAction"
- noAction privilegeAction = "noAction"
-)
-
-// ErrInvalidDesiredPriv error raised when user attempts to acquire an invalid privilege level.
-var ErrInvalidDesiredPriv = errors.New("invalid desired priv name")
-
-// ErrCouldNotDeterminePriv error raised when unable to determine the current privilege level.
-var ErrCouldNotDeterminePriv = errors.New("could not determine current privilege level")
-
-// Driver driver for the "network" layer -- adds privilege levels, on open/close, and augments to
-// the base driver it extends.
-type Driver struct {
- base.Driver
- OnOpen func(*Driver) error
- OnClose func(*Driver) error
- privGraph map[string]map[string]bool
- CurrentPriv string
- Augments map[string]func(d *Driver) (*base.Response, error)
-}
-
-// NewNetworkDriver returns a new driver of the network flavor.
-func NewNetworkDriver(
- host string,
- privilegeLevels map[string]*base.PrivilegeLevel,
- defaultDesiredPriv string,
- failedWhenContains []string,
- onOpen func(d *Driver) error,
- onClose func(d *Driver) error,
- options ...base.Option,
-) (*Driver, error) {
- newDriver, err := base.NewDriver(host, options...)
-
- if err != nil {
- return nil, err
- }
-
- d := &Driver{
- Driver: *newDriver,
- OnOpen: onOpen,
- OnClose: onClose,
- Augments: map[string]func(d *Driver) (*base.Response, error){},
- }
-
- if len(d.FailedWhenContains) == 0 {
- d.FailedWhenContains = failedWhenContains
- }
-
- if len(d.PrivilegeLevels) == 0 {
- d.PrivilegeLevels = privilegeLevels
- }
-
- if d.DefaultDesiredPriv == "" {
- d.DefaultDesiredPriv = defaultDesiredPriv
- }
-
- d.buildPrivGraph()
- d.generateJoinedCommsPromptPattern()
-
- return d, nil
-}
-
-// Open opens a connection; calls the base driver `open` method, but additionally executes the
-// `OnOpen` callable.
-func (d *Driver) Open() error {
- err := d.Driver.Open()
- if err != nil {
- return err
- }
-
- err = d.OnOpen(d)
-
- return err
-}
-
-// Close closes a connection; calls the base driver `close` method, but additionally executes the
-// `OnClose` callable.
-func (d *Driver) Close() error {
- if d.OnClose != nil {
- err := d.OnClose(d)
- if err != nil {
- logging.LogError(
- d.FormatLogMessage(
- "error",
- "encountered error on OnClose - continuing to close transport...",
- ),
- )
- }
- }
-
- err := d.Driver.Close()
-
- return err
-}
-
-func (d *Driver) generateJoinedCommsPromptPattern() {
- // handle setting up the "joined" priv pattern
- allPatterns := make([]string, 0, len(d.PrivilegeLevels))
- for _, pLevel := range d.PrivilegeLevels {
- allPatterns = append(allPatterns, pLevel.Pattern)
- }
-
- joinedPattern := strings.Join(allPatterns, "|")
-
- d.Channel.CommsPromptPattern = regexp.MustCompile(joinedPattern)
-}
diff --git a/driver/network/operation.go b/driver/network/operation.go
new file mode 100644
index 0000000..c1714d1
--- /dev/null
+++ b/driver/network/operation.go
@@ -0,0 +1,30 @@
+package network
+
+import (
+ "errors"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// OperationOptions is a struct containing "operation" options that are relevant to the network
+// Driver, for example providing a target privilege level for a SendInteractive operation.
+type OperationOptions struct {
+ PrivilegeLevel string
+}
+
+// NewOperation returns a new OperationOptions object with the defaults set and any provided options
+// applied.
+func NewOperation(options ...util.Option) (*OperationOptions, error) {
+ o := &OperationOptions{}
+
+ for _, option := range options {
+ err := option(o)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return o, nil
+}
diff --git a/driver/network/privilege.go b/driver/network/privilege.go
index acea36c..d13ee09 100644
--- a/driver/network/privilege.go
+++ b/driver/network/privilege.go
@@ -1,24 +1,34 @@
package network
import (
- "errors"
- "fmt"
"regexp"
"strings"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/channel"
)
-// ErrFailedToAcquirePriv raised when unable to acquire requested privilege level.
-var ErrFailedToAcquirePriv = errors.New("failed to acquire requested privilege level")
+// PrivilegeLevels is a type alias for the map of privilege levels that gets assigned to a network
+// Driver object.
+type PrivilegeLevels map[string]*PrivilegeLevel
+
+// PrivilegeLevel defines a privilege level, including a name, the pattern used to match a prompt
+// output to the privilege level, as well as information about how to escalate into and deescalate
+// out of this privilege level.
+type PrivilegeLevel struct {
+ Name string `yaml:"name"`
+ Pattern string `yaml:"pattern"`
+ patternRe *regexp.Regexp
+ NotContains []string `yaml:"not-contains"`
+ PreviousPriv string `yaml:"previous-priv"`
+ Deescalate string `yaml:"deescalate"`
+ Escalate string `yaml:"escalate"`
+ EscalateAuth bool `yaml:"escalate-auth"`
+ EscalatePrompt string `yaml:"escalate-prompt"`
+}
func (d *Driver) buildPrivGraph() {
d.privGraph = map[string]map[string]bool{}
for _, privLevel := range d.PrivilegeLevels {
- privLevel.PatternRe = regexp.MustCompile(privLevel.Pattern)
+ privLevel.patternRe = regexp.MustCompile(privLevel.Pattern)
d.privGraph[privLevel.Name] = map[string]bool{}
if privLevel.PreviousPriv != "" {
@@ -33,246 +43,24 @@ func (d *Driver) buildPrivGraph() {
}
}
-// UpdatePrivilegeLevels convenience method to build priv graph and create a new joined comms prompt
-// pattern.
-func (d *Driver) UpdatePrivilegeLevels() {
- d.buildPrivGraph()
- d.generateJoinedCommsPromptPattern()
-}
-
-func (d *Driver) escalate(escalatePriv string) error {
- var err error
-
- if !d.PrivilegeLevels[escalatePriv].EscalateAuth {
- _, err = d.Channel.SendInput(d.PrivilegeLevels[escalatePriv].Escalate, false, false, -1)
- } else {
- events := []*channel.SendInteractiveEvent{
- {
- ChannelInput: d.PrivilegeLevels[escalatePriv].Escalate,
- ChannelResponse: d.PrivilegeLevels[escalatePriv].EscalatePrompt,
- HideInput: false,
- },
- {
- ChannelInput: d.AuthSecondary,
- ChannelResponse: d.PrivilegeLevels[escalatePriv].Pattern,
- HideInput: true,
- },
- }
- _, err = d.Channel.SendInteractive(
- events,
- []string{
- // check for the current (previous priv) and escalate (desired priv) patterns,
- // either would indicate we are "good to go".
- d.PrivilegeLevels[d.PrivilegeLevels[escalatePriv].PreviousPriv].Pattern,
- d.PrivilegeLevels[escalatePriv].Pattern,
- },
- -1,
- )
- }
-
- return err
-}
-
-func (d *Driver) deescalate(currentPriv string) error {
- _, err := d.Channel.SendInput(d.PrivilegeLevels[currentPriv].Deescalate, false, false, -1)
- return err
-}
-
-func (d *Driver) determineCurrentPriv(currentPrompt string) ([]string, error) {
- var matchingPrivLevels []string
-
-PrivLevel:
- for privName, privData := range d.PrivilegeLevels {
- for _, notContains := range privData.PatternNotContains {
- if strings.Contains(currentPrompt, notContains) {
- continue PrivLevel
- }
- }
-
- promptMatch := privData.PatternRe.MatchString(currentPrompt)
-
- if promptMatch {
- matchingPrivLevels = append(matchingPrivLevels, privName)
- }
- }
-
- if len(matchingPrivLevels) == 0 {
- logging.LogError(
- d.FormatLogMessage(
- "error",
- fmt.Sprintf(
- "could not determine privilege level from provided prompt: %s\n",
- currentPrompt,
- ),
- ),
- )
-
- return []string{}, ErrCouldNotDeterminePriv
- }
-
- logging.LogDebug(
- d.FormatLogMessage(
- "error",
- fmt.Sprintf("determined current privilege level is one of: %s\n", matchingPrivLevels),
- ),
- )
-
- return matchingPrivLevels, nil
-}
-
-func strSliceContains(s []string, e string) bool {
- for _, a := range s {
- if a == e {
- return true
- }
- }
-
- return false
-}
-
-func (d *Driver) buildPrivChangeMap(
- currentPriv, desiredPriv string,
- privChangeMap *[]string,
-) []string {
- var workingPrivChangeMap []string
-
- if privChangeMap != nil {
- workingPrivChangeMap = *privChangeMap
- }
-
- workingPrivChangeMap = append(workingPrivChangeMap, currentPriv)
-
- if currentPriv == desiredPriv {
- return workingPrivChangeMap
- }
-
- for privName := range d.privGraph[currentPriv] {
- if !strSliceContains(workingPrivChangeMap, privName) {
- updatedPrivChangeMap := d.buildPrivChangeMap(
- privName,
- desiredPriv,
- &workingPrivChangeMap,
- )
- if len(updatedPrivChangeMap) > 0 {
- return updatedPrivChangeMap
- }
- }
- }
-
- return []string{}
-}
-
-func (d *Driver) processAcquirePriv(
- desiredPriv, currentPrompt string,
-) (privilegeAction, string, error) {
- logging.LogDebug(
- d.FormatLogMessage(
- "info",
- fmt.Sprintf("attempting to acquire '%s' privilege level", desiredPriv),
- ),
- )
-
- possibleCurrentPrivs, err := d.determineCurrentPriv(currentPrompt)
+func (d *Driver) buildJoinedPromptPattern() {
+ patterns := make([]string, 0)
- if err != nil {
- return noAction, "", err
+ for _, priv := range d.PrivilegeLevels {
+ patterns = append(patterns, priv.Pattern)
}
- var currentPriv string
-
- switch {
- case strSliceContains(possibleCurrentPrivs, d.CurrentPriv):
- currentPriv = d.PrivilegeLevels[d.CurrentPriv].Name
- case strSliceContains(possibleCurrentPrivs, desiredPriv):
- currentPriv = d.PrivilegeLevels[desiredPriv].Name
- default:
- currentPriv = possibleCurrentPrivs[0]
- }
-
- if currentPriv == desiredPriv {
- logging.LogDebug(
- d.FormatLogMessage(
- "debug",
- "determined current privilege level is target privilege level, no action needed",
- ),
- )
-
- d.CurrentPriv = desiredPriv
-
- return noAction, currentPriv, nil
- }
-
- mapToDesiredPriv := d.buildPrivChangeMap(currentPriv, desiredPriv, nil)
-
- // at this point we basically dont *know* the privilege leve we are at (or we wont/cant after
- // we do an escalation or deescalation, so we reset to the dummy priv level
- d.CurrentPriv = "UNKNOWN"
-
- if d.PrivilegeLevels[mapToDesiredPriv[1]].PreviousPriv != currentPriv {
- logging.LogDebug(d.FormatLogMessage("debug", "determined privilege deescalation necessary"))
+ joinedPattern := strings.Join(patterns, "|")
- return deescalateAction, currentPriv, nil
- }
-
- logging.LogDebug(d.FormatLogMessage("debug", "determined privilege escalation necessary"))
-
- return escalateAction, d.PrivilegeLevels[mapToDesiredPriv[1]].Name, nil
+ d.Driver.Channel.PromptPattern = regexp.MustCompile(joinedPattern)
}
-// AcquirePriv acquire a target privilege level.
-func (d *Driver) AcquirePriv(desiredPriv string) error {
- logging.LogDebug(
- d.FormatLogMessage(
- "debug",
- fmt.Sprintf("attempting to acquire privilege level: %s\n", desiredPriv),
- ),
- )
-
- if _, ok := d.PrivilegeLevels[desiredPriv]; !ok {
- return ErrInvalidDesiredPriv
- }
-
- privChangeCount := 0
-
- for {
- currentPrompt, err := d.GetPrompt()
- if err != nil {
- logging.LogError(
- d.FormatLogMessage(
- "error",
- fmt.Sprintf("failed fetching prompt, error: %s\n", err),
- ),
- )
-
- return err
- }
-
- privAction, targetPriv, err := d.processAcquirePriv(
- desiredPriv,
- currentPrompt,
- )
-
- switch {
- case err != nil:
- return err
- case privAction == noAction:
- return nil
- case privAction == escalateAction:
- err = d.escalate(targetPriv)
- if err != nil {
- return err
- }
- case privAction == deescalateAction:
- err = d.deescalate(targetPriv)
- if err != nil {
- return err
- }
- }
-
- privChangeCount++
-
- if privChangeCount > len(d.PrivilegeLevels)*2 {
- return ErrFailedToAcquirePriv
- }
- }
+// UpdatePrivileges refreshes the Driver's internal privilege map, the map that is used to determine
+// appropriate next steps during privilege escalation/deescalation. Any time a user modifies the
+// Driver PrivilegeLevels this method should be called as it will regenerate the base
+// channel.Channel prompt pattern to include all privilege level patterns in the PrivilegeLevels
+// map, thus ensuring we can always "find" a prompt.
+func (d *Driver) UpdatePrivileges() {
+ d.buildPrivGraph()
+ d.buildJoinedPromptPattern()
}
diff --git a/driver/network/sendcommand.go b/driver/network/sendcommand.go
index 86add29..39ad4e8 100644
--- a/driver/network/sendcommand.go
+++ b/driver/network/sendcommand.go
@@ -1,26 +1,27 @@
package network
import (
- "github.com/scrapli/scrapligo/driver/base"
-)
+ "fmt"
-// SendCommand basically the same as the base driver flavor, but acquires the
-// `DefaultDesiredPriv` prior to sending the command.
-func (d *Driver) SendCommand(c string, o ...base.SendOption) (*base.Response, error) {
- finalOpts := d.ParseSendOptions(o)
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
+)
+// SendCommand sends the command string to the device and returns a response.Response object. This
+// method will always ensure that the Driver CurrentPriv value is the DefaultDesiredPriv before
+// sending any command. If the privilege level is *not* the DefaultDesiredPriv (which is typically
+// "privilege-exec" or "exec"), AcquirePriv will be called with a target privilege level of
+// DefaultDesiredPriv.
+func (d *Driver) SendCommand(command string, opts ...util.Option) (*response.Response, error) {
if d.CurrentPriv != d.DefaultDesiredPriv {
err := d.AcquirePriv(d.DefaultDesiredPriv)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf(
+ "%w: failed acquiring default desired privilege level",
+ util.ErrPrivilegeError,
+ )
}
}
- return d.Driver.FullSendCommand(
- c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
+ return d.Driver.SendCommand(command, opts...)
}
diff --git a/driver/network/sendcommand_test.go b/driver/network/sendcommand_test.go
index 890c0de..bcaef96 100644
--- a/driver/network/sendcommand_test.go
+++ b/driver/network/sendcommand_test.go
@@ -1,171 +1,228 @@
package network_test
import (
+ "bytes"
"fmt"
- "os"
"testing"
- "github.com/scrapli/scrapligo/driver/network"
-
- "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+ "github.com/scrapli/scrapligo/platform"
"github.com/scrapli/scrapligo/transport"
- "github.com/scrapli/scrapligo/util/testhelper"
+ "github.com/google/go-cmp/cmp"
)
-func testSendCommand(
- d *network.Driver, command string,
- expectedOutput []byte, cleanFunc func(r string) string,
-) func(t *testing.T) {
+type sendCommandTestCase struct {
+ description string
+ command string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+}
+
+func testSendCommand(testName string, testCase *sendCommandTestCase) func(t *testing.T) {
return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
+ t.Logf("%s: starting", testName)
- r, cmdErr := d.SendCommand(command)
- if cmdErr != nil {
- t.Fatalf("failed sending command: %v", cmdErr)
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendCommand(testCase.command)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
}
if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
+ t.Fatalf("%s: response object indicates failure",
+ testName)
}
- if diff := cmp.Diff(cleanFunc(r.Result), string(expectedOutput)); diff != "" {
- t.Errorf("actual result and expected result do not match (-want +got):\n%s", diff)
- }
- }
-}
-
-func TestSendCommand(t *testing.T) {
- commandMap := platformCommandMapShort()
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
- for platform, command := range commandMap {
- sessionFile := fmt.Sprintf(
- "../../test_data/driver/network/sendcommand/%s_session_short",
- platform,
- )
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
- expectedFile := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_short_expected",
- platform,
- )
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
- expectedOutput, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
+ if !cmp.Equal(actualIn, expectedIn) {
t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFile,
- expectedErr,
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
)
}
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
-
- f := testSendCommand(
- d,
- command,
- expectedOutput,
- testhelper.GetCleanFunc(platform),
- )
-
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
}
}
-func testFunctionalSendCommandCommon(
- t *testing.T,
- command, expectedFile, platform, transportName string,
-) {
- if !testhelper.RunPlatform(platform) {
- t.Logf("skip; platform %s deselected for testing\n", platform)
- return
+func TestSendCommand(t *testing.T) {
+ cases := map[string]*sendCommandTestCase{
+ "send-command-simple": {
+ description: "simple send command test",
+ command: "show run int vlan1",
+ payloadFile: "send-command-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ "send-command-acquire-priv": {
+ description: "simple send command test plus acquire priv",
+ command: "show run int vlan1",
+ payloadFile: "send-command-acquire-priv.txt",
+ stripPrompt: false,
+ eager: false,
+ },
}
- hostConnData, ok := functionalTestHosts()[platform]
- if !ok {
- t.Logf("skip; no host connection data for platform type %s\n", platform)
- return
+ for testName, testCase := range cases {
+ f := testSendCommand(testName, testCase)
+ t.Run(testName, f)
}
+}
- expectedOutput, expectedErr := os.ReadFile(expectedFile)
- if expectedErr != nil {
- t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFile,
- expectedErr,
- )
+type sendCommandFunctionalTestcase struct {
+ description string
+ stripPrompt bool
+ eager bool
+}
+
+func getTestSendCommandFunctionalCommand(t *testing.T, testName, platformName string) string {
+ commands := map[string]string{
+ platform.CiscoIosxe: "show run",
+ platform.CiscoIosxr: "show run",
+ platform.CiscoNxos: "show run",
+ platform.AristaEos: "show run",
+ platform.JuniperJunos: "show configuration",
+ platform.NokiaSrl: "show interface all",
}
- port := hostConnData.Port
- if transportName == transport.TelnetTransportName {
- port = hostConnData.TelnetPort
+ c, ok := commands[platformName]
+ if !ok {
+ t.Skipf("%s: skipping platform '%s', no command in commands map", testName, platformName)
}
- d := newFunctionalTestDriver(
- t,
- hostConnData.Host,
- platform,
- transportName,
- port,
- )
-
- f := testSendCommand(
- d,
- command,
- expectedOutput,
- testhelper.GetCleanFunc(platform),
- )
-
- t.Run(fmt.Sprintf("Platform=%s;Transport=%s", platform, transportName), f)
+ return c
}
-func TestFunctionalSendCommandShort(t *testing.T) {
- if !*testhelper.Functional {
- t.Skip("skip: functional tests skipped unless the '-functional' flag is passed")
- }
+func testSendCommandFunctional(
+ testName, platformName, transportName string,
+ testCase *sendCommandFunctionalTestcase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
- commandMap := platformCommandMapShort()
+ r, err := d.SendCommand(
+ getTestSendCommandFunctionalCommand(t, testName, platformName),
+ )
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
+ }
- for _, transportName := range transport.SupportedTransports() {
- if !testhelper.RunTransport(transportName) {
- t.Logf("skip; transport %s deselected for testing\n", transportName)
- continue
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
}
- for platform, command := range commandMap {
- expectedFile := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_short_expected",
- platform,
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
+
+ actualOut := r.Result
+
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-%s-%s", testName, platformName, transportName),
+ actualOut,
)
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
+
+ expectedOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-%s-%s-out.txt", testName, platformName, transportName),
+ )
- testFunctionalSendCommandCommon(t, command, expectedFile, platform, transportName)
+ if !cmp.Equal(
+ cleanF(actualOut),
+ cleanF(string(expectedOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ cleanF(actualOut),
+ cleanF(string(expectedOut)),
+ )
}
}
}
-func TestFunctionalSendCommandLong(t *testing.T) {
- if !*testhelper.Functional {
- t.Skip("skip: functional tests skipped unless the '-functional' flag is passed")
+func TestSendCommandFunctional(t *testing.T) {
+ cases := map[string]*sendCommandFunctionalTestcase{
+ "functional-send-command-simple": {
+ description: "simple send command test",
+ stripPrompt: false,
+ eager: false,
+ },
}
- commandMap := platformCommandMapLong()
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
- for _, transportName := range transport.SupportedTransports() {
- if !testhelper.RunTransport(transportName) {
- t.Logf("skip; transport %s deselected for testing\n", transportName)
- continue
- }
+ for testName, testCase := range cases {
+ for _, platformName := range platform.GetPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
- for platform, command := range commandMap {
- expectedFile := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_long_expected",
- platform,
- )
+ continue
+ }
+
+ for _, transportName := range transport.GetTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
+
+ continue
+ }
+
+ f := testSendCommandFunctional(testName, platformName, transportName, testCase)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
- testFunctionalSendCommandCommon(t, command, expectedFile, platform, transportName)
+ interTestSleep()
+ }
}
}
}
diff --git a/driver/network/sendcommands.go b/driver/network/sendcommands.go
index a7aed44..3658621 100644
--- a/driver/network/sendcommands.go
+++ b/driver/network/sendcommands.go
@@ -1,45 +1,49 @@
package network
import (
- "github.com/scrapli/scrapligo/driver/base"
+ "fmt"
+
+ "github.com/scrapli/scrapligo/response"
"github.com/scrapli/scrapligo/util"
)
-// SendCommands basically the same as the base driver flavor, but acquires the
-// `DefaultDesiredPriv` prior to sending the command.
+// SendCommands sends the command strings to the device and returns a response.MultiResponse object.
+// This method will always ensure that the Driver CurrentPriv value is the DefaultDesiredPriv before
+// sending any command. If the privilege level is *not* the DefaultDesiredPriv (which is typically
+// "privilege-exec" or "exec"), AcquirePriv will be called with a target privilege level of
+// DefaultDesiredPriv.
func (d *Driver) SendCommands(
- c []string,
- o ...base.SendOption,
-) (*base.MultiResponse, error) {
- finalOpts := d.ParseSendOptions(o)
-
+ commands []string,
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
if d.CurrentPriv != d.DefaultDesiredPriv {
err := d.AcquirePriv(d.DefaultDesiredPriv)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf(
+ "%w: failed acquiring default desired privilege level",
+ util.ErrPrivilegeError,
+ )
}
}
- return d.Driver.FullSendCommands(
- c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.StopOnFailed,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
+ return d.Driver.SendCommands(commands, opts...)
}
-// SendCommandsFromFile basically the same as the base driver flavor, but acquires the
-// `DefaultDesiredPriv` prior to sending the command.
+// SendCommandsFromFile is a convenience wrapper to send each line of file f as a command via the
+// SendCommands method.
func (d *Driver) SendCommandsFromFile(
f string,
- o ...base.SendOption,
-) (*base.MultiResponse, error) {
- c, err := util.LoadFileLines(f)
- if err != nil {
- return nil, err
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
+ if d.CurrentPriv != d.DefaultDesiredPriv {
+ err := d.AcquirePriv(d.DefaultDesiredPriv)
+ if err != nil {
+ return nil, fmt.Errorf(
+ "%w: failed acquiring default desired privilege level",
+ util.ErrPrivilegeError,
+ )
+ }
}
- return d.SendCommands(c, o...)
+ return d.Driver.SendCommandsFromFile(f, opts...)
}
diff --git a/driver/network/sendcommands_test.go b/driver/network/sendcommands_test.go
index c79ca49..08eab52 100644
--- a/driver/network/sendcommands_test.go
+++ b/driver/network/sendcommands_test.go
@@ -1,297 +1,301 @@
package network_test
import (
+ "bytes"
"fmt"
- "os"
"testing"
- "github.com/scrapli/scrapligo/driver/base"
+ "github.com/scrapli/scrapligo/util"
+ "github.com/scrapli/scrapligo/platform"
"github.com/scrapli/scrapligo/transport"
"github.com/google/go-cmp/cmp"
- "github.com/scrapli/scrapligo/driver/core"
- "github.com/scrapli/scrapligo/driver/network"
- "github.com/scrapli/scrapligo/util/testhelper"
)
-func compareResults(
- t *testing.T,
- r *base.MultiResponse,
- expectedOutput [][]byte,
- cleanFunc func(r string) string,
-) {
- if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
- }
-
- rOne := r.Responses[0].Result
- rTwo := r.Responses[1].Result
-
- if diff := cmp.Diff(cleanFunc(rOne), string(expectedOutput[0])); diff != "" {
- t.Errorf("actual result one and expected result do not match (-want +got):\n%s", diff)
- }
-
- if diff := cmp.Diff(cleanFunc(rTwo), string(expectedOutput[1])); diff != "" {
- t.Errorf("actual result two and expected result do not match (-want +got):\n%s", diff)
- }
+type sendCommandsTestCase struct {
+ description string
+ commands []string
+ payloadFile string
+ stripPrompt bool
+ eager bool
}
-func testSendCommands(
- d *network.Driver, commands []string,
- expectedOutput [][]byte, cleanFunc func(r string) string,
-) func(t *testing.T) {
+func testSendCommands(testName string, testCase *sendCommandsTestCase) func(t *testing.T) {
return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendCommands(testCase.commands)
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error running generic Driver SendCommands, error: %s",
+ testName,
+ err,
+ )
}
- r, cmdErr := d.SendCommands(commands)
- if cmdErr != nil {
- t.Fatalf("failed sending commands: %v", cmdErr)
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
}
- compareResults(t, r, expectedOutput, cleanFunc)
- }
-}
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
-func testSendCommandsFromFile(
- d *network.Driver,
- commandsFile string,
- expectedOutput [][]byte,
- cleanFunc func(r string) string,
-) func(t *testing.T) {
- return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
}
- r, cmdErr := d.SendCommandsFromFile(commandsFile)
- if cmdErr != nil {
- t.Fatalf("failed sending commands: %v", cmdErr)
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
}
- compareResults(t, r, expectedOutput, cleanFunc)
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
}
}
-func testSendCommandsCommon(
- t *testing.T,
- platform string,
-) (d *network.Driver, expectedOutputs [][]byte) {
- sessionFile := fmt.Sprintf(
- "../../test_data/driver/network/sendcommands/%s_session",
- platform,
- )
-
- expectedFileOne := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_short_expected",
- platform,
- )
-
- expectedFileTwo := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_long_expected",
- platform,
- )
-
- expectedOutputOne, expectedErr := os.ReadFile(expectedFileOne)
- if expectedErr != nil {
- t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFileOne,
- expectedErr,
- )
+func TestSendCommands(t *testing.T) {
+ cases := map[string]*sendCommandsTestCase{
+ "send-commands-simple": {
+ description: "simple send commands test",
+ commands: []string{"show run int vlan1", "show run int vlan1"},
+ payloadFile: "send-commands-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
}
- expectedOutputTwo, expectedErr := os.ReadFile(expectedFileTwo)
- if expectedErr != nil {
- t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFileTwo,
- expectedErr,
- )
- }
+ for testName, testCase := range cases {
+ f := testSendCommands(testName, testCase)
- d = testhelper.CreatePatchedDriver(t, sessionFile, platform)
+ t.Run(testName, f)
+ }
+}
- return d, [][]byte{expectedOutputOne, expectedOutputTwo}
+type sendCommandsFromFileTestCase struct {
+ description string
+ f string
+ payloadFile string
+ stripPrompt bool
+ eager bool
}
-func TestSendCommands(t *testing.T) {
- commandMapShort := platformCommandMapShort()
- commandMapLong := platformCommandMapLong()
+func testSendCommandsFromFile(
+ testName string,
+ testCase *sendCommandsFromFileTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
- for _, platform := range core.SupportedPlatforms() {
- commandOne := commandMapShort[platform]
- commandTwo := commandMapLong[platform]
+ r, err := d.SendCommandsFromFile(resolveFile(t, testCase.f))
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
+ }
- d, expectedOutputs := testSendCommandsCommon(t, platform)
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
- f := testSendCommands(
- d,
- []string{commandOne, commandTwo},
- expectedOutputs,
- testhelper.GetCleanFunc(platform),
- )
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
- }
-}
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
-func TestSendCommandsFromFile(t *testing.T) {
- for _, platform := range core.SupportedPlatforms() {
- d, expectedOutputs := testSendCommandsCommon(t, platform)
-
- f := testSendCommandsFromFile(
- d,
- fmt.Sprintf(
- "../../test_data/driver/network/sendcommandsfromfile/%s_commands",
- platform,
- ),
- expectedOutputs,
- testhelper.GetCleanFunc(platform),
- )
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
}
}
-func testFunctionalSendCommandsCommon(
- t *testing.T,
- platform, transportName string,
-) (d *network.Driver, expectedOutputs [][]byte) {
- if !testhelper.RunPlatform(platform) {
- t.Logf("skip; platform %s deselected for testing\n", platform)
- return
+func TestSendCommandsFromFile(t *testing.T) {
+ cases := map[string]*sendCommandsFromFileTestCase{
+ "send-commands-from-file-simple": {
+ description: "simple send commands test",
+ f: "send-commands-from-file-simple-inputs.txt",
+ payloadFile: "send-commands-from-file-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
}
- hostConnData, ok := functionalTestHosts()[platform]
- if !ok {
- t.Logf("skip; no host connection data for platform type %s\n", platform)
- return
- }
+ for testName, testCase := range cases {
+ f := testSendCommandsFromFile(testName, testCase)
- expectedFileOne := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_short_expected",
- platform,
- )
-
- expectedFileTwo := fmt.Sprintf(
- "../../test_data/driver/network/expected/%s_long_expected",
- platform,
- )
-
- expectedOutputOne, expectedErr := os.ReadFile(expectedFileOne)
- if expectedErr != nil {
- t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFileOne,
- expectedErr,
- )
+ t.Run(testName, f)
}
+}
- expectedOutputTwo, expectedErr := os.ReadFile(expectedFileTwo)
- if expectedErr != nil {
- t.Fatalf(
- "failed opening expected output file '%s' err: %v",
- expectedFileTwo,
- expectedErr,
- )
- }
+type sendCommandsFunctionalTestcase struct {
+ description string
+ stripPrompt bool
+ eager bool
+}
- port := hostConnData.Port
- if transportName == transport.TelnetTransportName {
- port = hostConnData.TelnetPort
+func getTestSendCommandsFunctionalCommand(t *testing.T, testName, platformName string) []string {
+ commands := map[string][]string{
+ platform.CiscoIosxe: {"show run", "show ip interface brief"},
+ platform.CiscoIosxr: {"show run", "show interfaces brief"},
+ platform.CiscoNxos: {"show run", "show interface status"},
+ platform.AristaEos: {"show run", "show ip interface brief"},
+ platform.JuniperJunos: {"show configuration", "show version"},
+ platform.NokiaSrl: {"show interface all", "show platform environment"},
}
- d = newFunctionalTestDriver(
- t,
- hostConnData.Host,
- platform,
- transportName,
- port,
- )
+ c, ok := commands[platformName]
+ if !ok {
+ t.Skipf("%s: skipping platform '%s', no command in commands map", testName, platformName)
+ }
- return d, [][]byte{expectedOutputOne, expectedOutputTwo}
+ return c
}
-func TestFunctionalSendCommands(t *testing.T) {
- if !*testhelper.Functional {
- t.Skip("skip: functional tests skipped unless the '-functional' flag is passed")
- }
+func testSendCommandsFunctional(
+ testName, platformName, transportName string,
+ testCase *sendCommandsFunctionalTestcase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
- commandMapShort := platformCommandMapShort()
- commandMapLong := platformCommandMapLong()
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
- for _, transportName := range transport.SupportedTransports() {
- if !testhelper.RunTransport(transportName) {
- t.Logf("skip; transport %s deselected for testing\n", transportName)
- continue
+ r, err := d.SendCommands(
+ getTestSendCommandsFunctionalCommand(t, testName, platformName),
+ )
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
}
- for _, platform := range core.SupportedPlatforms() {
- if !testhelper.RunPlatform(platform) {
- t.Logf("skip; platform %s deselected for testing\n", platform)
- continue
- }
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
- commandOne := commandMapShort[platform]
- commandTwo := commandMapLong[platform]
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
- d, expectedOutputs := testFunctionalSendCommandsCommon(t, platform, transportName)
- if d == nil {
- // no connection data or some reason to skip
- continue
- }
+ actualOut := r.JoinedResult()
- f := testSendCommands(
- d,
- []string{commandOne, commandTwo},
- expectedOutputs,
- testhelper.GetCleanFunc(platform),
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-%s-%s", testName, platformName, transportName),
+ actualOut,
)
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
- t.Run(fmt.Sprintf("Platform=%s;Transport=%s", platform, transportName), f)
+ expectedOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-%s-%s-out.txt", testName, platformName, transportName),
+ )
+
+ if !cmp.Equal(
+ cleanF(actualOut),
+ cleanF(string(expectedOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ cleanF(actualOut),
+ cleanF(string(expectedOut)),
+ )
}
}
}
-func TestFunctionalSendCommandsFromFile(t *testing.T) {
- if !*testhelper.Functional {
- t.Skip("skip: functional tests skipped unless the '-functional' flag is passed")
+func TestSendCommandsFunctional(t *testing.T) {
+ cases := map[string]*sendCommandsFunctionalTestcase{
+ "functional-send-commands-simple": {
+ description: "simple send command test",
+ stripPrompt: false,
+ eager: false,
+ },
}
- for _, transportName := range transport.SupportedTransports() {
- if !testhelper.RunTransport(transportName) {
- t.Logf("skip; transport %s deselected for testing\n", transportName)
- continue
- }
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
- for _, platform := range core.SupportedPlatforms() {
- if !testhelper.RunPlatform(platform) {
- t.Logf("skip; platform %s deselected for testing\n", platform)
- continue
- }
+ for testName, testCase := range cases {
+ for _, platformName := range platform.GetPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
- d, expectedOutputs := testFunctionalSendCommandsCommon(t, platform, transportName)
- if d == nil {
- // no connection data or some reason to skip
continue
}
- f := testSendCommandsFromFile(
- d,
- fmt.Sprintf(
- "../../test_data/driver/network/sendcommandsfromfile/%s_commands",
- platform,
- ),
- expectedOutputs,
- testhelper.GetCleanFunc(platform),
- )
+ for _, transportName := range transport.GetTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
- t.Run(fmt.Sprintf("Platform=%s;Transport=%s", platform, transportName), f)
+ continue
+ }
+
+ f := testSendCommandsFunctional(testName, platformName, transportName, testCase)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
+
+ interTestSleep()
+ }
}
}
}
diff --git a/driver/network/sendconfig.go b/driver/network/sendconfig.go
index 3a696c9..0eb19c3 100644
--- a/driver/network/sendconfig.go
+++ b/driver/network/sendconfig.go
@@ -4,34 +4,37 @@ import (
"strings"
"time"
- "github.com/scrapli/scrapligo/driver/base"
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
)
-// SendConfig send configuration string to the device.
-func (d *Driver) SendConfig(c string, o ...base.SendOption) (*base.Response, error) {
- sc := strings.Split(c, "\n")
- m, err := d.SendConfigs(sc, o...)
+// SendConfig is a convenience wrapper around SendConfigs. This method accepts a config string which
+// is split on new lines and sent as configLines to SendConfigs. The resulting
+// response.MultiResponse is then collapsed into a single response.Response object.
+func (d *Driver) SendConfig(config string, opts ...util.Option) (*response.Response, error) {
+ configLines := strings.Split(config, "\n")
+ m, err := d.SendConfigs(configLines, opts...)
if err != nil {
return nil, err
}
- r := base.NewResponse(
- d.Host,
- d.Transport.BaseTransportArgs.Port,
- c,
+ r := response.NewResponse(
+ config,
+ d.Transport.GetHost(),
+ d.Transport.GetPort(),
m.Responses[0].FailedWhenContains,
)
- individualResponses := make([]string, len(sc))
- for _, response := range m.Responses {
- individualResponses = append(individualResponses, response.Result)
+ rOutputs := make([]string, len(m.Responses))
+ for i, resp := range m.Responses {
+ rOutputs[i] = resp.Result
}
r.StartTime = m.StartTime
r.EndTime = time.Now()
r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
- r.Result = strings.Join(individualResponses, "\n")
+ r.Result = strings.Join(rOutputs, "\n")
return r, nil
}
diff --git a/driver/network/sendconfig_test.go b/driver/network/sendconfig_test.go
index 9221599..a840e6d 100644
--- a/driver/network/sendconfig_test.go
+++ b/driver/network/sendconfig_test.go
@@ -1,44 +1,207 @@
package network_test
import (
+ "bytes"
"fmt"
"strings"
"testing"
- "github.com/scrapli/scrapligo/util/testhelper"
+ "github.com/scrapli/scrapligo/platform"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
- "github.com/scrapli/scrapligo/driver/network"
-
- "github.com/scrapli/scrapligo/driver/core"
+ "github.com/google/go-cmp/cmp"
)
-func testSendConfig(d *network.Driver, config string) func(t *testing.T) {
+type sendConfigTestCase struct {
+ description string
+ configs string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+ privLevel string
+}
+
+func testSendConfig(testName string, testCase *sendConfigTestCase) func(t *testing.T) {
return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
+ t.Logf("%s: starting", testName)
- r, cmdErr := d.SendConfig(config)
- if cmdErr != nil {
- t.Fatalf("failed sending config: %v", cmdErr)
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendConfig(testCase.configs)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
}
if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.Result
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
}
}
}
func TestSendConfig(t *testing.T) {
- configsMap := platformConfigsMap()
+ cases := map[string]*sendConfigTestCase{
+ "send-config-simple": {
+ description: "simple send config test",
+ configs: "interface loopback1\nno interface loopback1",
+ payloadFile: "send-config-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendConfig(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+type sendConfigFunctionalTestCase struct {
+ description string
+}
- for _, platform := range core.SupportedPlatforms() {
- sessionFile := fmt.Sprintf("../../test_data/driver/network/sendconfigs/%s", platform)
+func getTestSendConfigFunctionalConfig(t *testing.T, testName, platformName string) string {
+ cSlice := getTestSendConfigsFunctionalConfig(t, testName, platformName)
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
+ return strings.Join(cSlice, "\n")
+}
+
+func testSendConfigFunctional(
+ testName, platformName, transportName string,
+ testCase *sendConfigFunctionalTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
+
+ r, err := d.SendConfig(
+ getTestSendConfigFunctionalConfig(t, testName, platformName),
+ )
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendConfig, error: %s",
+ testName,
+ err,
+ )
+ }
- f := testSendConfig(d, strings.Join(configsMap[platform], "\n"))
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
+
+ actualOut := r.Result
+
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-%s-%s", testName, platformName, transportName),
+ actualOut,
+ )
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
+
+ expectedOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-%s-%s-out.txt", testName, platformName, transportName),
+ )
+
+ if !cmp.Equal(
+ util.GetCleanFunc(platformName)(actualOut),
+ cleanF(string(expectedOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendConfigFunctional(t *testing.T) {
+ cases := map[string]*sendConfigFunctionalTestCase{
+ "functional-send-config-simple": {
+ description: "simple send config test",
+ },
+ }
+
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
+
+ for testName, testCase := range cases {
+ for _, platformName := range platform.GetPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
+
+ continue
+ }
+
+ for _, transportName := range transport.GetTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
+
+ continue
+ }
+
+ f := testSendConfigFunctional(testName, platformName, transportName, testCase)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
+
+ interTestSleep()
+ }
+ }
}
}
diff --git a/driver/network/sendconfigs.go b/driver/network/sendconfigs.go
index 17f9ee1..3712f0e 100644
--- a/driver/network/sendconfigs.go
+++ b/driver/network/sendconfigs.go
@@ -1,62 +1,52 @@
package network
import (
- "errors"
-
+ "github.com/scrapli/scrapligo/response"
"github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/channel"
)
-// SendConfigs send configurations to the device.
-func (d *Driver) SendConfigs(c []string, o ...base.SendOption) (*base.MultiResponse, error) {
- finalOpts := d.ParseSendOptions(o)
+const (
+ defaultConfigurationPrivLevel = "configuration"
+)
- if finalOpts.DesiredPrivilegeLevel == "" {
- finalOpts.DesiredPrivilegeLevel = "configuration"
+// SendConfigs sends a list of configs to the device. This method will auto acquire the
+// "configuration" privilege level. If your device does *not* have a "configuration" privilege
+// level this will fail. If your device has multiple "flavors" of configuration level (i.e.
+// exclusive or private) you can acquire that target privilege level for the configuration option
+// by passing the opoptions.WithPrivilegeLevel with the requested privilege level set.
+func (d *Driver) SendConfigs(
+ configs []string,
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
}
- if d.CurrentPriv != finalOpts.DesiredPrivilegeLevel {
- err := d.AcquirePriv(finalOpts.DesiredPrivilegeLevel)
- if err != nil {
- return nil, err
- }
- }
+ targetPriv := op.PrivilegeLevel
- m, err := d.Driver.FullSendCommands(c,
- finalOpts.FailedWhenContains,
- finalOpts.StripPrompt,
- finalOpts.StopOnFailed,
- finalOpts.Eager,
- finalOpts.TimeoutOps,
- )
-
- if err != nil && !errors.Is(err, channel.ErrChannelTimeout) {
- // if we encountered an error we *probably* cant abort anyway unless its a timeout error
- // if its a timeout error we can at least try to keep going on, otherwise lets bail here
- return m, err
+ if targetPriv == "" {
+ targetPriv = defaultConfigurationPrivLevel
}
- if finalOpts.StopOnFailed && m.Failed != nil {
- if f, ok := d.Augments["abortConfig"]; ok {
- _, err = f(d)
- }
+ err = d.AcquirePriv(targetPriv)
+ if err != nil {
+ return nil, err
}
- return m, err
+ return d.Driver.SendCommands(configs, opts...)
}
-// SendConfigsFromFile send configurations from a file to the device.
+// SendConfigsFromFile is a convenience wrapper that sends the lines of file f as config lines via
+// SendConfigs.
func (d *Driver) SendConfigsFromFile(
f string,
- o ...base.SendOption,
-) (*base.MultiResponse, error) {
+ opts ...util.Option,
+) (*response.MultiResponse, error) {
c, err := util.LoadFileLines(f)
if err != nil {
return nil, err
}
- return d.SendConfigs(c, o...)
+ return d.SendConfigs(c, opts...)
}
diff --git a/driver/network/sendconfigs_test.go b/driver/network/sendconfigs_test.go
index ce40930..40a4d4e 100644
--- a/driver/network/sendconfigs_test.go
+++ b/driver/network/sendconfigs_test.go
@@ -1,76 +1,310 @@
package network_test
import (
+ "bytes"
"fmt"
"testing"
- "github.com/scrapli/scrapligo/util/testhelper"
+ "github.com/scrapli/scrapligo/platform"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
- "github.com/scrapli/scrapligo/driver/network"
-
- "github.com/scrapli/scrapligo/driver/core"
+ "github.com/google/go-cmp/cmp"
)
-func testSendConfigs(d *network.Driver, config []string) func(t *testing.T) {
+type sendConfigsTestCase struct {
+ description string
+ configs []string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+ privLevel string
+}
+
+func testSendConfigs(testName string, testCase *sendConfigsTestCase) func(t *testing.T) {
return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
- r, cmdErr := d.SendConfigs(config)
- if cmdErr != nil {
- t.Fatalf("failed sending config: %v", cmdErr)
+ r, err := d.SendConfigs(testCase.configs)
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
}
if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
}
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendConfigs(t *testing.T) {
+ cases := map[string]*sendConfigsTestCase{
+ "send-configs-simple": {
+ description: "simple send config test",
+ configs: []string{"interface loopback1", "no interface loopback1"},
+ payloadFile: "send-configs-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendConfigs(testName, testCase)
+
+ t.Run(testName, f)
}
}
-func testSendConfigsFromFile(d *network.Driver, config string) func(t *testing.T) {
+type sendConfigsFromFileTestCase struct {
+ description string
+ f string
+ payloadFile string
+ stripPrompt bool
+ eager bool
+}
+
+func testSendConfigsFromFile(
+ testName string,
+ testCase *sendConfigsFromFileTestCase,
+) func(t *testing.T) {
return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
+ t.Logf("%s: starting", testName)
- r, cmdErr := d.SendConfigsFromFile(config)
- if cmdErr != nil {
- t.Fatalf("failed sending config: %v", cmdErr)
+ d, fileTransportObj := prepareDriver(t, testName, testCase.payloadFile)
+
+ r, err := d.SendConfigsFromFile(resolveFile(t, testCase.f))
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running generic Driver GetPrompt, error: %s",
+ testName,
+ err,
+ )
}
if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ actualOut := r.JoinedResult()
+ actualIn := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualIn, actualOut)
+ }
+
+ expectedIn := readFile(t, fmt.Sprintf("golden/%s-in.txt", testName))
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualIn, expectedIn) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualIn,
+ expectedIn,
+ )
+ }
+
+ if !cmp.Equal(actualOut, string(expectedOut)) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
}
}
}
-func TestSendConfigs(t *testing.T) {
- configsMap := platformConfigsMap()
+func TestSendConfigsFromFile(t *testing.T) {
+ cases := map[string]*sendConfigsFromFileTestCase{
+ "send-configs-from-file-simple": {
+ description: "simple send commands test",
+ f: "send-configs-from-file-simple-inputs.txt",
+ payloadFile: "send-configs-from-file-simple.txt",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSendConfigsFromFile(testName, testCase)
- for _, platform := range core.SupportedPlatforms() {
- sessionFile := fmt.Sprintf("../../test_data/driver/network/sendconfigs/%s", platform)
+ t.Run(testName, f)
+ }
+}
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
+type sendConfigsFunctionalTestcase struct {
+ description string
+ stripPrompt bool
+ eager bool
+}
+
+func getTestSendConfigsFunctionalConfig(t *testing.T, testName, platformName string) []string {
+ commands := map[string][]string{
+ platform.CiscoIosxe: {"interface loopback0", "no interface loopback0"},
+ platform.CiscoIosxr: {"interface loopback0", "no interface loopback0", "commit"},
+ platform.CiscoNxos: {"interface loopback0", "no interface loopback0"},
+ platform.AristaEos: {"interface loopback0", "no interface loopback0"},
+ platform.JuniperJunos: {
+ "set interfaces fxp0 unit 0 description \"scrapli was here\"",
+ "delete interfaces fxp0 unit 0 description",
+ "commit",
+ },
+ platform.NokiaSrl: {
+ "interface ethernet-1/50",
+ "subinterface 0",
+ "ipv4 address 1.1.1.1/30",
+ "discard now",
+ },
+ }
- f := testSendConfigs(d, configsMap[platform])
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
+ c, ok := commands[platformName]
+ if !ok {
+ t.Skipf("%s: skipping platform '%s', no command in commands map", testName, platformName)
}
+
+ return c
}
-func TestSendConfigsFromFile(t *testing.T) {
- for _, platform := range core.SupportedPlatforms() {
- sessionFile := fmt.Sprintf("../../test_data/driver/network/sendconfigs/%s", platform)
- configs := fmt.Sprintf(
- "../../test_data/driver/network/sendconfigsfromfile/%s_configs",
- platform,
+func testSendConfigsFunctional(
+ testName, platformName, transportName string,
+ testCase *sendConfigsFunctionalTestcase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d := prepareFunctionalDriver(t, testName, platformName, transportName)
+
+ r, err := d.SendConfigs(
+ getTestSendConfigsFunctionalConfig(t, testName, platformName),
+ )
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error running network Driver SendCommand, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if r.Failed != nil {
+ t.Fatalf("%s: response object indicates failure",
+ testName)
+ }
+
+ err = d.Close()
+ if err != nil {
+ t.Fatalf("%s: failed closing connection",
+ testName)
+ }
+
+ actualOut := r.JoinedResult()
+
+ if *update {
+ writeGoldenFunctional(
+ t,
+ fmt.Sprintf("%s-%s-%s", testName, platformName, transportName),
+ actualOut,
+ )
+ }
+
+ cleanF := util.GetCleanFunc(platformName)
+
+ expectedOut := readFile(
+ t,
+ fmt.Sprintf("golden/%s-%s-%s-out.txt", testName, platformName, transportName),
)
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
+ if !cmp.Equal(
+ util.GetCleanFunc(platformName)(actualOut),
+ cleanF(string(expectedOut)),
+ ) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestSendConfigsFunctional(t *testing.T) {
+ cases := map[string]*sendConfigsFunctionalTestcase{
+ "functional-send-configs-simple": {
+ description: "simple send command test",
+ stripPrompt: false,
+ eager: false,
+ },
+ }
+
+ if !*functional {
+ t.Skip("skip: functional tests skipped without the '-functional' flag being passed")
+ }
+
+ for testName, testCase := range cases {
+ for _, platformName := range platform.GetPlatformNames() {
+ if !util.PlatformOK(platforms, platformName) {
+ t.Logf("%s: skipping platform '%s'", testName, platformName)
+
+ continue
+ }
+
+ for _, transportName := range transport.GetTransportNames() {
+ if !util.TransportOK(transports, transportName) {
+ t.Logf("%s: skipping transport '%s'", testName, transportName)
- f := testSendConfigsFromFile(d, configs)
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
+ continue
+ }
+
+ f := testSendConfigsFunctional(testName, platformName, transportName, testCase)
+
+ t.Run(
+ fmt.Sprintf(
+ "%s;platform=%s;transport=%s",
+ testName,
+ platformName,
+ transportName,
+ ),
+ f,
+ )
+
+ interTestSleep()
+ }
+ }
}
}
diff --git a/driver/network/sendinteractive.go b/driver/network/sendinteractive.go
index d893461..72dfe02 100644
--- a/driver/network/sendinteractive.go
+++ b/driver/network/sendinteractive.go
@@ -2,34 +2,35 @@ package network
import (
"github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/driver/base"
+ "github.com/scrapli/scrapligo/response"
+ "github.com/scrapli/scrapligo/util"
)
-// SendInteractive send interactive commands to a device, accepts a slice of `SendInteractiveEvent`
-// and variadic of `SendOption`s.
+// SendInteractive sends a slice of channel.SendInteractiveEvent to the device. This method wraps
+// the generic driver level SendInteractive but handles acquiring the desired privilege level prior
+// to returning the generic driver method.
func (d *Driver) SendInteractive(
events []*channel.SendInteractiveEvent,
- o ...base.SendOption,
-) (*base.Response, error) {
- finalOpts := d.ParseSendOptions(o)
- joinedEventInputs := base.JoinEventInputs(events)
+ opts ...util.Option,
+) (*response.Response, error) {
+ op, err := NewOperation(opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ targetPriv := op.PrivilegeLevel
- if finalOpts.DesiredPrivilegeLevel == "" {
- finalOpts.DesiredPrivilegeLevel = d.DefaultDesiredPriv
+ if targetPriv == "" {
+ targetPriv = d.DefaultDesiredPriv
}
- if d.CurrentPriv != finalOpts.DesiredPrivilegeLevel {
- err := d.AcquirePriv(finalOpts.DesiredPrivilegeLevel)
- if err != nil {
- return nil, err
- }
+ err = d.AcquirePriv(targetPriv)
+ if err != nil {
+ return nil, err
}
- return d.Driver.FullSendInteractive(
+ return d.Driver.SendInteractive(
events,
- finalOpts.InteractionCompletePatterns,
- finalOpts.FailedWhenContains,
- finalOpts.TimeoutOps,
- joinedEventInputs,
+ opts...,
)
}
diff --git a/driver/network/sendinteractive_test.go b/driver/network/sendinteractive_test.go
deleted file mode 100644
index 1f86980..0000000
--- a/driver/network/sendinteractive_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package network_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/scrapli/scrapligo/util/testhelper"
-
- "github.com/scrapli/scrapligo/driver/network"
-
- "github.com/scrapli/scrapligo/channel"
-
- "github.com/scrapli/scrapligo/driver/core"
-)
-
-func platformInteractiveMap() map[string][]*channel.SendInteractiveEvent {
- return map[string][]*channel.SendInteractiveEvent{
- "cisco_iosxe": {
- &channel.SendInteractiveEvent{
- ChannelInput: "clear logging",
- ChannelResponse: "[confirm]",
- HideInput: false,
- },
- &channel.SendInteractiveEvent{
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- },
- "arista_eos": {
- &channel.SendInteractiveEvent{
- ChannelInput: "clear logging",
- ChannelResponse: "[confirm]",
- HideInput: false,
- },
- &channel.SendInteractiveEvent{
- ChannelInput: "",
- ChannelResponse: "",
- HideInput: false,
- },
- },
- "cisco_iosxr": {
- {
- ChannelInput: "clear logging",
- ChannelResponse: "Clear logging buffer",
- HideInput: false,
- },
- {
- ChannelInput: "y",
- ChannelResponse: "",
- HideInput: false,
- },
- },
- }
-}
-
-func testSendInteractive(
- d *network.Driver,
- events []*channel.SendInteractiveEvent,
-) func(t *testing.T) {
- return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening patched driver: %v", openErr)
- }
-
- r, interactErr := d.SendInteractive(events)
- if interactErr != nil {
- t.Fatalf("failed sending interactive: %v", interactErr)
- }
-
- if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
- }
- }
-}
-
-func TestSendInteractive(t *testing.T) {
- interactiveMap := platformInteractiveMap()
-
- for _, platform := range core.SupportedPlatforms() {
- if platform == "cisco_nxos" {
- // gotta figure out an interactive command on nxos
- continue
- }
-
- if platform == "juniper_junos" {
- // gotta figure out an interactive command on junos
- continue
- }
-
- if platform == "nokia_sros" {
- // gotta figure out an interactive command on sros
- continue
- }
-
- if platform == "nokia_sros_classic" {
- // gotta figure out an interactive command on sros
- continue
- }
-
- if platform == "paloalto_panos" {
- // gotta figure out an interactive command on sros
- continue
- }
-
- sessionFile := fmt.Sprintf("../../test_data/driver/network/sendinteractive/%s", platform)
-
- d := testhelper.CreatePatchedDriver(t, sessionFile, platform)
-
- f := testSendInteractive(d, interactiveMap[platform])
- t.Run(fmt.Sprintf("Platform=%s", platform), f)
- }
-}
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-standard-out.txt
new file mode 100644
index 0000000..55f0ae1
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-standard-out.txt
@@ -0,0 +1,38 @@
+! Command: show running-config
+! device: ceos (cEOSLab, EOS-4.28.0F-26924507.4280F (engineering build))
+!
+no aaa root
+!
+username admin privilege 15 role network-admin secret sha512 $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname ceos
+!
+spanning-tree mode mstp
+!
+management api http-commands
+ no shutdown
+!
+management api gnmi
+ transport grpc default
+!
+management api netconf
+ transport ssh default
+!
+interface Ethernet1
+ description tacocat
+!
+interface Ethernet2
+!
+interface Management0
+ ip address 172.20.20.14/24
+ ipv6 address 2001:172:20:20::14/64
+!
+ip routing
+!
+ip route 0.0.0.0/0 172.20.20.1
+!
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-system-out.txt
new file mode 100644
index 0000000..55f0ae1
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-arista_eos-system-out.txt
@@ -0,0 +1,38 @@
+! Command: show running-config
+! device: ceos (cEOSLab, EOS-4.28.0F-26924507.4280F (engineering build))
+!
+no aaa root
+!
+username admin privilege 15 role network-admin secret sha512 $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname ceos
+!
+spanning-tree mode mstp
+!
+management api http-commands
+ no shutdown
+!
+management api gnmi
+ transport grpc default
+!
+management api netconf
+ transport ssh default
+!
+interface Ethernet1
+ description tacocat
+!
+interface Ethernet2
+!
+interface Management0
+ ip address 172.20.20.14/24
+ ipv6 address 2001:172:20:20::14/64
+!
+ip routing
+!
+ip route 0.0.0.0/0 172.20.20.1
+!
+end
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_iosxe_expected b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-standard-out.txt
similarity index 66%
rename from test_data/cfg/getconfig/cisco_iosxe_expected
rename to driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-standard-out.txt
index 4225877..9ccf8be 100644
--- a/test_data/cfg/getconfig/cisco_iosxe_expected
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-standard-out.txt
@@ -1,24 +1,25 @@
Building configuration...
-Current configuration : 7021 bytes
+Current configuration : 6955 bytes
!
-! Last configuration change at 21:01:33 UTC Fri Aug 13 2021 by boxen
+! Last configuration change at 18:33:59 UTC Sat Jul 2 2022 by admin
!
version 16.12
service timestamps debug datetime msec
service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
service call-home
platform qfp utilization monitor load 80
platform punt-keepalive disable-kernel-core
platform console serial
!
-hostname csr1000v
+hostname vr-csr
!
boot-start-marker
boot-end-marker
!
!
-enable secret 9 $9$xvWnx8Fe35f8xE$E9ijp7GM/V48P5y1Uz3IEPtotXgwkJKYJmN0q3q2E92
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
!
no aaa new-model
call-home
@@ -36,7 +37,7 @@ call-home
!
!
!
-ip domain name example.com
+ip domain name boxen.box
!
!
!
@@ -71,17 +72,46 @@ multilink bundle-name authenticated
!
!
!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
crypto pki trustpoint SLA-TrustPoint
enrollment pkcs12
revocation-check crl
!
-crypto pki trustpoint TP-self-signed-2167864204
- enrollment selfsigned
- subject-name cn=IOS-Self-Signed-Certificate-2167864204
- revocation-check none
- rsakeypair TP-self-signed-2167864204
-!
!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
crypto pki certificate chain SLA-TrustPoint
certificate ca 01
30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
@@ -111,48 +141,16 @@ crypto pki certificate chain SLA-TrustPoint
418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
D697DF7F 28
quit
-crypto pki certificate chain TP-self-signed-2167864204
- certificate self-signed 01
- 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
- 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
- 69666963 6174652D 32313637 38363432 3034301E 170D3231 30373235 31353439
- 33385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
- 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 31363738
- 36343230 34308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
- 0A028201 010090B0 F05FB8AB AD49C746 E0E13514 4B38CD91 4ADF3EF2 B3CF2BC4
- C49462F1 617D87E6 258C5B30 BAFDF20D 017F956F 858B4F81 C5D8FC8C 96DA18A8
- C94A2B6E 9ACF758B C3286D35 DE74A105 7637FB0C F2E61342 6A1DB252 E76086A8
- B05C3BE3 D95861A5 556B0D9F 15BEAA75 775DF3A3 9D80D9FA 7B4C504F 5290479B
- C3236B08 78868EF4 A3E18D3C 044B4B06 CD84F6C4 CEF866C6 39945043 B72030C7
- 5B5F09D8 52455372 BC9D201A EF7C1FC2 E36723CF EF63263E 6D3373F5 E0D748D9
- 00F56A23 43FD322D 47DD5317 92554645 82C595B3 F1EA7B99 8380658B 630427F1
- 082591D3 A4B99509 6E02DA7B 2B1313CE C4182A7B C6CCB791 26FB250A D7C2EB6A
- 172FA5EB 31810203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
- 301F0603 551D2304 18301680 14E175F8 9F74A118 E8A9D3D9 FBB1A965 BCE79C72
- E2301D06 03551D0E 04160414 E175F89F 74A118E8 A9D3D9FB B1A965BC E79C72E2
- 300D0609 2A864886 F70D0101 05050003 82010100 4BF490EF A8AB5DEB B289BA2E
- 384C6C49 972BB2C8 EBA52D0A B6C6F3C6 B358460A F6F7683E BB2F485B A2F91729
- 09E6BD3E AEE4C42D A301F0C6 18B6D44C 63CD73A4 162BE8B4 EABBAAAC 30FE6B61
- 2DC406C4 4C02C209 969D3CBB 8A96C50A 6689E027 3C9CD450 DBFF6B66 C5EF62CC
- 1E6C06CB 21BD59CE 788FDB10 FDDE1E9C 743627B4 B58A4E22 F831217C 73B23453
- 64C32F68 7998963B C4C788F7 F605D534 BD2FFD2D D1F77DAF B1892B16 CD95FD7E
- 93486965 B3C8FD0E A15E11AB B52223B6 25CD010A 0BBFF878 37C30ACE F746FFF7
- 16CBDE21 17F0F923 B6AA34F5 5428ED59 D5BED985 A6FF9ED5 7E4F09E7 32B4B8B7
- F22ED054 79059E02 2A972370 68C3400F BCA7C853
- quit
!
-license udi pid CSR1000V sn 9FK0UZW73QE
+license udi pid CSR1000V sn 9MM1GU3SD2V
diagnostic bootup level minimal
-archive
- log config
- logging enable
- path bootflash:
memory free low-watermark processor 72329
!
!
spanning-tree extend system-id
!
username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
!
redundancy
!
@@ -247,17 +245,10 @@ interface GigabitEthernet10
no mop enabled
no mop sysid
!
-!
-virtual-service csr_mgmt
-!
ip forward-protocol nd
no ip http server
-no ip http secure-server
+ip http secure-server
!
-ip ssh pubkey-chain
- username boxen
- key-hash ssh-rsa 5CC74A68B18B026A1709FB09D1F44E2F
-ip scp server enable
!
!
!
@@ -274,18 +265,22 @@ control-plane
!
line con 0
stopbits 1
-line vty 0 4
+line vty 0
+ login local
+ transport input all
+line vty 1
login local
+ length 0
transport input all
-line vty 5 15
+line vty 2 4
login local
transport input all
!
-netconf ssh
!
!
!
!
!
netconf-yang
+restconf
end
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_iosxe b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-system-out.txt
similarity index 60%
rename from test_data/cfg/getconfig/cisco_iosxe
rename to driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-system-out.txt
index 9183e65..9ccf8be 100644
--- a/test_data/cfg/getconfig/cisco_iosxe
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-system-out.txt
@@ -1,39 +1,25 @@
-Warning: Permanently added '[localhost]:21022' (RSA) to the list of known hosts.
-Password:
-
-
-
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#show version | i Version
-Cisco IOS XE Software, Version 16.12.03
-Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.12.3, RELEASE SOFTWARE (fc5)
-licensed under the GNU General Public License ("GPL") Version 2.0. The
-software code licensed under GPL Version 2.0 is free software that comes
-GPL code under the terms of GPL Version 2.0. For more details, see the
-csr1000v#show running-config
Building configuration...
-Current configuration : 7021 bytes
+Current configuration : 6955 bytes
!
-! Last configuration change at 21:01:33 UTC Fri Aug 13 2021 by boxen
+! Last configuration change at 18:33:59 UTC Sat Jul 2 2022 by admin
!
version 16.12
service timestamps debug datetime msec
service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
service call-home
platform qfp utilization monitor load 80
platform punt-keepalive disable-kernel-core
platform console serial
!
-hostname csr1000v
+hostname vr-csr
!
boot-start-marker
boot-end-marker
!
!
-enable secret 9 $9$xvWnx8Fe35f8xE$E9ijp7GM/V48P5y1Uz3IEPtotXgwkJKYJmN0q3q2E92
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
!
no aaa new-model
call-home
@@ -51,7 +37,7 @@ call-home
!
!
!
-ip domain name example.com
+ip domain name boxen.box
!
!
!
@@ -86,17 +72,46 @@ multilink bundle-name authenticated
!
!
!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
crypto pki trustpoint SLA-TrustPoint
enrollment pkcs12
revocation-check crl
!
-crypto pki trustpoint TP-self-signed-2167864204
- enrollment selfsigned
- subject-name cn=IOS-Self-Signed-Certificate-2167864204
- revocation-check none
- rsakeypair TP-self-signed-2167864204
-!
!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
crypto pki certificate chain SLA-TrustPoint
certificate ca 01
30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
@@ -126,48 +141,16 @@ crypto pki certificate chain SLA-TrustPoint
418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
D697DF7F 28
quit
-crypto pki certificate chain TP-self-signed-2167864204
- certificate self-signed 01
- 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
- 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
- 69666963 6174652D 32313637 38363432 3034301E 170D3231 30373235 31353439
- 33385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
- 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 31363738
- 36343230 34308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
- 0A028201 010090B0 F05FB8AB AD49C746 E0E13514 4B38CD91 4ADF3EF2 B3CF2BC4
- C49462F1 617D87E6 258C5B30 BAFDF20D 017F956F 858B4F81 C5D8FC8C 96DA18A8
- C94A2B6E 9ACF758B C3286D35 DE74A105 7637FB0C F2E61342 6A1DB252 E76086A8
- B05C3BE3 D95861A5 556B0D9F 15BEAA75 775DF3A3 9D80D9FA 7B4C504F 5290479B
- C3236B08 78868EF4 A3E18D3C 044B4B06 CD84F6C4 CEF866C6 39945043 B72030C7
- 5B5F09D8 52455372 BC9D201A EF7C1FC2 E36723CF EF63263E 6D3373F5 E0D748D9
- 00F56A23 43FD322D 47DD5317 92554645 82C595B3 F1EA7B99 8380658B 630427F1
- 082591D3 A4B99509 6E02DA7B 2B1313CE C4182A7B C6CCB791 26FB250A D7C2EB6A
- 172FA5EB 31810203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
- 301F0603 551D2304 18301680 14E175F8 9F74A118 E8A9D3D9 FBB1A965 BCE79C72
- E2301D06 03551D0E 04160414 E175F89F 74A118E8 A9D3D9FB B1A965BC E79C72E2
- 300D0609 2A864886 F70D0101 05050003 82010100 4BF490EF A8AB5DEB B289BA2E
- 384C6C49 972BB2C8 EBA52D0A B6C6F3C6 B358460A F6F7683E BB2F485B A2F91729
- 09E6BD3E AEE4C42D A301F0C6 18B6D44C 63CD73A4 162BE8B4 EABBAAAC 30FE6B61
- 2DC406C4 4C02C209 969D3CBB 8A96C50A 6689E027 3C9CD450 DBFF6B66 C5EF62CC
- 1E6C06CB 21BD59CE 788FDB10 FDDE1E9C 743627B4 B58A4E22 F831217C 73B23453
- 64C32F68 7998963B C4C788F7 F605D534 BD2FFD2D D1F77DAF B1892B16 CD95FD7E
- 93486965 B3C8FD0E A15E11AB B52223B6 25CD010A 0BBFF878 37C30ACE F746FFF7
- 16CBDE21 17F0F923 B6AA34F5 5428ED59 D5BED985 A6FF9ED5 7E4F09E7 32B4B8B7
- F22ED054 79059E02 2A972370 68C3400F BCA7C853
- quit
!
-license udi pid CSR1000V sn 9FK0UZW73QE
+license udi pid CSR1000V sn 9MM1GU3SD2V
diagnostic bootup level minimal
-archive
- log config
- logging enable
- path bootflash:
memory free low-watermark processor 72329
!
!
spanning-tree extend system-id
!
username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
!
redundancy
!
@@ -262,17 +245,10 @@ interface GigabitEthernet10
no mop enabled
no mop sysid
!
-!
-virtual-service csr_mgmt
-!
ip forward-protocol nd
no ip http server
-no ip http secure-server
+ip http secure-server
!
-ip ssh pubkey-chain
- username boxen
- key-hash ssh-rsa 5CC74A68B18B026A1709FB09D1F44E2F
-ip scp server enable
!
!
!
@@ -289,20 +265,22 @@ control-plane
!
line con 0
stopbits 1
-line vty 0 4
+line vty 0
login local
transport input all
-line vty 5 15
+line vty 1
+ login local
+ length 0
+ transport input all
+line vty 2 4
login local
transport input all
!
-netconf ssh
!
!
!
!
!
netconf-yang
-end
-
-csr1000v#
\ No newline at end of file
+restconf
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-telnet-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-telnet-out.txt
new file mode 100644
index 0000000..9ccf8be
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxe-telnet-out.txt
@@ -0,0 +1,286 @@
+Building configuration...
+
+Current configuration : 6955 bytes
+!
+! Last configuration change at 18:33:59 UTC Sat Jul 2 2022 by admin
+!
+version 16.12
+service timestamps debug datetime msec
+service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
+service call-home
+platform qfp utilization monitor load 80
+platform punt-keepalive disable-kernel-core
+platform console serial
+!
+hostname vr-csr
+!
+boot-start-marker
+boot-end-marker
+!
+!
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
+!
+no aaa new-model
+call-home
+ ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
+ ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
+ contact-email-addr sch-smart-licensing@cisco.com
+ profile "CiscoTAC-1"
+ active
+ destination transport-method http
+ no destination transport-method email
+!
+!
+!
+!
+!
+!
+!
+ip domain name boxen.box
+!
+!
+!
+login on-success log
+!
+!
+!
+!
+!
+!
+!
+subscriber templating
+!
+!
+!
+!
+!
+!
+multilink bundle-name authenticated
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
+crypto pki trustpoint SLA-TrustPoint
+ enrollment pkcs12
+ revocation-check crl
+!
+!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
+crypto pki certificate chain SLA-TrustPoint
+ certificate ca 01
+ 30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
+ 32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
+ 6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
+ 3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
+ 43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
+ 526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
+ 82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
+ CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
+ 1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
+ 4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
+ 7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
+ 68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
+ C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
+ C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
+ DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
+ 06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
+ 4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
+ 03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
+ 604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
+ D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
+ 467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
+ 7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
+ 5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
+ 80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
+ 418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
+ D697DF7F 28
+ quit
+!
+license udi pid CSR1000V sn 9MM1GU3SD2V
+diagnostic bootup level minimal
+memory free low-watermark processor 72329
+!
+!
+spanning-tree extend system-id
+!
+username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
+!
+redundancy
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+interface GigabitEthernet1
+ ip address 10.0.0.15 255.255.255.0
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet2
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet3
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet4
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet5
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet6
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet7
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet8
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet9
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet10
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+ip forward-protocol nd
+no ip http server
+ip http secure-server
+!
+!
+!
+!
+!
+!
+!
+!
+control-plane
+!
+!
+!
+!
+!
+!
+line con 0
+ stopbits 1
+line vty 0
+ login local
+ transport input all
+line vty 1
+ login local
+ length 0
+ transport input all
+line vty 2 4
+ login local
+ transport input all
+!
+!
+!
+!
+!
+!
+netconf-yang
+restconf
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..050fa89
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-standard-out.txt
@@ -0,0 +1,86 @@
+Sat Jul 2 18:36:52.882 UTC
+Building configuration...
+!! IOS XR Configuration version = 6.5.3
+!! Last configuration change at Sat Jul 2 18:34:56 2022 by boxen
+!
+hostname vr-xrv9k
+username boxen
+ group root-lr
+ group cisco-support
+ secret 5 $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+!
+username clab
+ group root-lr
+ group cisco-support
+ password 7 1511070D060A7A767B
+!
+call-home
+ service active
+ contact smart-licensing
+ profile CiscoTAC-1
+ active
+ destination transport-method http
+ !
+!
+interface MgmtEth0/RP0/CPU0/0
+ ipv4 address 10.0.0.15 255.255.255.0
+!
+interface GigabitEthernet0/0/0/0
+ shutdown
+!
+interface GigabitEthernet0/0/0/1
+ shutdown
+!
+interface GigabitEthernet0/0/0/2
+ shutdown
+!
+interface GigabitEthernet0/0/0/3
+ shutdown
+!
+interface GigabitEthernet0/0/0/4
+ shutdown
+!
+interface GigabitEthernet0/0/0/5
+ shutdown
+!
+interface GigabitEthernet0/0/0/6
+ shutdown
+!
+interface GigabitEthernet0/0/0/7
+ shutdown
+!
+interface GigabitEthernet0/0/0/8
+ shutdown
+!
+interface GigabitEthernet0/0/0/9
+ shutdown
+!
+interface GigabitEthernet0/0/0/10
+ shutdown
+!
+interface GigabitEthernet0/0/0/11
+ shutdown
+!
+interface GigabitEthernet0/0/0/12
+ shutdown
+!
+interface GigabitEthernet0/0/0/13
+ shutdown
+!
+interface GigabitEthernet0/0/0/14
+ shutdown
+!
+interface GigabitEthernet0/0/0/15
+ shutdown
+!
+grpc
+ port 57400
+ no-tls
+!
+netconf-yang agent
+ ssh
+!
+ssh server rate-limit 600
+ssh server v2
+ssh server netconf vrf default
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-system-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..22240f6
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_iosxr-system-out.txt
@@ -0,0 +1,86 @@
+Sat Jul 2 18:36:48.868 UTC
+Building configuration...
+!! IOS XR Configuration version = 6.5.3
+!! Last configuration change at Sat Jul 2 18:34:56 2022 by boxen
+!
+hostname vr-xrv9k
+username boxen
+ group root-lr
+ group cisco-support
+ secret 5 $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+!
+username clab
+ group root-lr
+ group cisco-support
+ password 7 1511070D060A7A767B
+!
+call-home
+ service active
+ contact smart-licensing
+ profile CiscoTAC-1
+ active
+ destination transport-method http
+ !
+!
+interface MgmtEth0/RP0/CPU0/0
+ ipv4 address 10.0.0.15 255.255.255.0
+!
+interface GigabitEthernet0/0/0/0
+ shutdown
+!
+interface GigabitEthernet0/0/0/1
+ shutdown
+!
+interface GigabitEthernet0/0/0/2
+ shutdown
+!
+interface GigabitEthernet0/0/0/3
+ shutdown
+!
+interface GigabitEthernet0/0/0/4
+ shutdown
+!
+interface GigabitEthernet0/0/0/5
+ shutdown
+!
+interface GigabitEthernet0/0/0/6
+ shutdown
+!
+interface GigabitEthernet0/0/0/7
+ shutdown
+!
+interface GigabitEthernet0/0/0/8
+ shutdown
+!
+interface GigabitEthernet0/0/0/9
+ shutdown
+!
+interface GigabitEthernet0/0/0/10
+ shutdown
+!
+interface GigabitEthernet0/0/0/11
+ shutdown
+!
+interface GigabitEthernet0/0/0/12
+ shutdown
+!
+interface GigabitEthernet0/0/0/13
+ shutdown
+!
+interface GigabitEthernet0/0/0/14
+ shutdown
+!
+interface GigabitEthernet0/0/0/15
+ shutdown
+!
+grpc
+ port 57400
+ no-tls
+!
+netconf-yang agent
+ ssh
+!
+ssh server rate-limit 600
+ssh server v2
+ssh server netconf vrf default
+end
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_nxos_expected b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-standard-out.txt
similarity index 85%
rename from test_data/cfg/getconfig/cisco_nxos_expected
rename to driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-standard-out.txt
index 1e29d79..b593bc9 100644
--- a/test_data/cfg/getconfig/cisco_nxos_expected
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-standard-out.txt
@@ -1,28 +1,31 @@
!Command: show running-config
-!Running configuration last done at: Fri Aug 13 20:36:00 2021
-!Time: Fri Aug 13 22:31:58 2021
+!Running configuration last done at: Sat Jul 2 18:32:27 2022
+!Time: Sat Jul 2 18:37:36 2022
version 9.2(4) Bios:version
-vdc switch id 1
+hostname vr-n9kv
+vdc vr-n9kv id 1
limit-resource vlan minimum 16 maximum 4094
limit-resource vrf minimum 2 maximum 4096
limit-resource port-channel minimum 0 maximum 511
- limit-resource u4route-mem minimum 128 maximum 128
+ limit-resource u4route-mem minimum 248 maximum 248
limit-resource u6route-mem minimum 96 maximum 96
limit-resource m4route-mem minimum 58 maximum 58
limit-resource m6route-mem minimum 8 maximum 8
-feature telnet
+
feature nxapi
feature scp-server
+feature netconf
+feature grpc
no password strength-check
-username admin password 5 $5$LOIMHI$hIaO64VM40/x.MTQoeWg8/IAn2iBY5jv4WZyzQbb5q9 role network-admin
-username boxen password 5 $5$rFrywOjz$buvWY6uEPf79GVyfGNO6SGOi5gbxV2VAcsbBtyXDZyB role network-admin
+username admin password 5 $5$OCIHOM$KYnQ6u7CdgTtDJ1OVIMRFfG4B0LiZP4TCu6NCr8SMU2 role network-admin
+username boxen password 5 $5$ipZx5veq$a7WoQ5.Cnki64vgXPPjcUvQwgKsur3OKpOtGGILPlF/ role network-admin
username boxen passphrase lifetime 99999 warntime 14 gracetime 3
ip domain-lookup
copp profile strict
-snmp-server user admin network-admin auth md5 0xd42fc9f6e153a348e1ab40f0f5b84589 priv 0xd42fc9f6e153a348e1ab40f0f5b84589 localizedkey
-snmp-server user boxen network-admin auth md5 0xc168bfc2b500129bd35ee550b6d5d93d priv 0xc168bfc2b500129bd35ee550b6d5d93d localizedkey
+snmp-server user admin network-admin auth md5 0xc2ddb65d774688cbe39ccc99407c3504 priv 0xc2ddb65d774688cbe39ccc99407c3504 localizedkey
+snmp-server user boxen network-admin auth md5 0xbbde1f880043a9581715afb5bd97c938 priv 0xbbde1f880043a9581715afb5bd97c938 localizedkey
rmon event 1 description FATAL(1) owner PMON@FATAL
rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
rmon event 3 description ERROR(3) owner PMON@ERROR
diff --git a/test_data/driver/network/expected/cisco_nxos_long_expected b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-system-out.txt
similarity index 78%
rename from test_data/driver/network/expected/cisco_nxos_long_expected
rename to driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-system-out.txt
index 051b3bc..d3e7a35 100644
--- a/test_data/driver/network/expected/cisco_nxos_long_expected
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-cisco_nxos-system-out.txt
@@ -1,33 +1,36 @@
!Command: show running-config
-!Running configuration last done at: TIME_STAMP_REPLACED
-!Time: TIME_STAMP_REPLACED
+!Running configuration last done at: Sat Jul 2 18:32:27 2022
+!Time: Sat Jul 2 18:37:35 2022
version 9.2(4) Bios:version
-vdc switch id 1
- limit-resource vlan minimum RESOURCES_REPLACED
- limit-resource vrf minimum RESOURCES_REPLACED
- limit-resource port-channel minimum RESOURCES_REPLACED
- limit-resource u4route-mem minimum RESOURCES_REPLACED
- limit-resource u6route-mem minimum RESOURCES_REPLACED
- limit-resource m4route-mem minimum RESOURCES_REPLACED
- limit-resource m6route-mem minimum RESOURCES_REPLACED
-feature telnet
+hostname vr-n9kv
+vdc vr-n9kv id 1
+ limit-resource vlan minimum 16 maximum 4094
+ limit-resource vrf minimum 2 maximum 4096
+ limit-resource port-channel minimum 0 maximum 511
+ limit-resource u4route-mem minimum 248 maximum 248
+ limit-resource u6route-mem minimum 96 maximum 96
+ limit-resource m4route-mem minimum 58 maximum 58
+ limit-resource m6route-mem minimum 8 maximum 8
+
feature nxapi
feature scp-server
+feature netconf
+feature grpc
no password strength-check
-CRYPTO_REPLACED
-CRYPTO_REPLACED
+username admin password 5 $5$OCIHOM$KYnQ6u7CdgTtDJ1OVIMRFfG4B0LiZP4TCu6NCr8SMU2 role network-admin
+username boxen password 5 $5$ipZx5veq$a7WoQ5.Cnki64vgXPPjcUvQwgKsur3OKpOtGGILPlF/ role network-admin
username boxen passphrase lifetime 99999 warntime 14 gracetime 3
ip domain-lookup
copp profile strict
-CRYPTO_REPLACED
-CRYPTO_REPLACED
+snmp-server user admin network-admin auth md5 0xc2ddb65d774688cbe39ccc99407c3504 priv 0xc2ddb65d774688cbe39ccc99407c3504 localizedkey
+snmp-server user boxen network-admin auth md5 0xbbde1f880043a9581715afb5bd97c938 priv 0xbbde1f880043a9581715afb5bd97c938 localizedkey
rmon event 1 description FATAL(1) owner PMON@FATAL
rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
rmon event 3 description ERROR(3) owner PMON@ERROR
rmon event 4 description WARNING(4) owner PMON@WARNING
-CRYPTO_REPLACED
+rmon event 5 description INFORMATION(5) owner PMON@INFO
vlan 1
diff --git a/test_data/driver/network/expected/juniper_junos_long_expected b/driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-standard-out.txt
similarity index 81%
rename from test_data/driver/network/expected/juniper_junos_long_expected
rename to driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-standard-out.txt
index 7173dde..992ec4a 100644
--- a/test_data/driver/network/expected/juniper_junos_long_expected
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-standard-out.txt
@@ -1,25 +1,31 @@
-TIME_STAMP_REPLACED
+## Last commit: 2022-07-02 18:36:25 UTC by boxen
version 17.3R2.10;
system {
+ host-name vr-vqfx;
root-authentication {
-CRYPTO_REPLACED
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
login {
+ user admin {
+ uid 2001;
+ class super-user;
+ authentication {
+ encrypted-password "$6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1."; ## SECRET-DATA
+ }
+ }
user boxen {
uid 2000;
class super-user;
authentication {
-CRYPTO_REPLACED
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
}
}
services {
- ssh {
- protocol-version v2;
- }
- telnet;
+ ssh;
netconf {
ssh;
+ rfc-compliant;
}
web-management {
http {
diff --git a/test_data/cfg/getconfig/juniper_junos_expected b/driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-system-out.txt
similarity index 81%
rename from test_data/cfg/getconfig/juniper_junos_expected
rename to driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-system-out.txt
index eaf1bcc..992ec4a 100644
--- a/test_data/cfg/getconfig/juniper_junos_expected
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-juniper_junos-system-out.txt
@@ -1,25 +1,31 @@
-## Last commit: 2021-08-13 21:03:52 UTC by boxen
+## Last commit: 2022-07-02 18:36:25 UTC by boxen
version 17.3R2.10;
system {
+ host-name vr-vqfx;
root-authentication {
- encrypted-password "6RhR81Jm4DEXKIbZNGjv.agJvM.FlIZWtFqX/966PZk0r4/Ps3LlS.OQZn9fHoVGuYJ7Q.hj2OQLyPJO6Mq7aQ3xLQiNrx/"; ## SECRET-DATA
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
login {
+ user admin {
+ uid 2001;
+ class super-user;
+ authentication {
+ encrypted-password "$6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1."; ## SECRET-DATA
+ }
+ }
user boxen {
uid 2000;
class super-user;
authentication {
- encrypted-password "6iYt26fU9gkt6bgxPs.VqHgCoLuSD6Kxv1JUHJLQzXJgzAEUIxobvxWwRErtpaOFvBOjIHr3KMI7sEo.V/7xLXzr0Ok20h0"; ## SECRET-DATA
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
}
}
services {
- ssh {
- protocol-version v2;
- }
- telnet;
+ ssh;
netconf {
ssh;
+ rfc-compliant;
}
web-management {
http {
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-standard-out.txt
new file mode 100644
index 0000000..afcc728
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-standard-out.txt
@@ -0,0 +1,129 @@
+================================================================================
+ethernet-1/1 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/2 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/3 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/4 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/5 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/6 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/7 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/8 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/9 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/10 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/11 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/12 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/13 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/14 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/15 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/16 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/17 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/18 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/19 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/20 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/21 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/22 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/23 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/24 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/25 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/26 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/27 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/28 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/29 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/30 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/31 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/32 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/33 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/34 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/35 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/36 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/37 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/38 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/39 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/40 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/41 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/42 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/43 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/44 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/45 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/46 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/47 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/48 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/49 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/50 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/51 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/52 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/53 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/54 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/55 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/56 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+mgmt0 is up, speed 1G, type None
+ mgmt0.0 is up
+ Network-instance: mgmt
+ Encapsulation : null
+ Type : None
+ IPv4 addr : 172.20.20.16/24 (dhcp, preferred)
+ IPv6 addr : 2001:172:20:20::16/64 (dhcp, preferred)
+ IPv6 addr : fe80::42:acff:fe14:1410/64 (link-layer, preferred)
+--------------------------------------------------------------------------------
+================================================================================
+Summary
+ 0 loopback interfaces configured
+ 0 ethernet interfaces are up, 56 are down
+ 1 management interfaces are up, 0 are down
+ 1 subinterfaces are up, 0 are down
+================================================================================
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-system-out.txt b/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-system-out.txt
new file mode 100644
index 0000000..afcc728
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-command-simple-nokia_srl-system-out.txt
@@ -0,0 +1,129 @@
+================================================================================
+ethernet-1/1 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/2 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/3 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/4 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/5 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/6 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/7 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/8 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/9 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/10 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/11 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/12 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/13 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/14 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/15 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/16 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/17 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/18 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/19 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/20 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/21 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/22 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/23 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/24 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/25 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/26 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/27 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/28 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/29 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/30 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/31 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/32 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/33 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/34 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/35 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/36 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/37 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/38 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/39 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/40 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/41 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/42 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/43 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/44 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/45 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/46 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/47 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/48 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/49 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/50 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/51 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/52 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/53 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/54 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/55 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/56 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+mgmt0 is up, speed 1G, type None
+ mgmt0.0 is up
+ Network-instance: mgmt
+ Encapsulation : null
+ Type : None
+ IPv4 addr : 172.20.20.16/24 (dhcp, preferred)
+ IPv6 addr : 2001:172:20:20::16/64 (dhcp, preferred)
+ IPv6 addr : fe80::42:acff:fe14:1410/64 (link-layer, preferred)
+--------------------------------------------------------------------------------
+================================================================================
+Summary
+ 0 loopback interfaces configured
+ 0 ethernet interfaces are up, 56 are down
+ 1 management interfaces are up, 0 are down
+ 1 subinterfaces are up, 0 are down
+================================================================================
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-standard-out.txt
new file mode 100644
index 0000000..8c8c955
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-standard-out.txt
@@ -0,0 +1,44 @@
+
+
+! Command: show running-config
+! device: ceos (cEOSLab, EOS-4.28.0F-26924507.4280F (engineering build))
+!
+no aaa root
+!
+username admin privilege 15 role network-admin secret sha512 $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname ceos
+!
+spanning-tree mode mstp
+!
+management api http-commands
+ no shutdown
+!
+management api gnmi
+ transport grpc default
+!
+management api netconf
+ transport ssh default
+!
+interface Ethernet1
+ description tacocat
+!
+interface Ethernet2
+!
+interface Management0
+ ip address 172.20.20.14/24
+ ipv6 address 2001:172:20:20::14/64
+!
+ip routing
+!
+ip route 0.0.0.0/0 172.20.20.1
+!
+end
+ Address
+Interface IP Address Status Protocol MTU Owner
+----------------- --------------------- ------------ -------------- ---------- -------
+Management0 172.20.20.14/24 up up 1500
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-system-out.txt
new file mode 100644
index 0000000..8c8c955
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-arista_eos-system-out.txt
@@ -0,0 +1,44 @@
+
+
+! Command: show running-config
+! device: ceos (cEOSLab, EOS-4.28.0F-26924507.4280F (engineering build))
+!
+no aaa root
+!
+username admin privilege 15 role network-admin secret sha512 $6$hB2JUt/ViRqix1FE$LeMDLUUvYUB9RcfqIWNYTZcvQX8lBHHeC5FjEkk/Uj3HJKw4fOTXMHNBU6/x3yS2hUrrM7m/xVTYzrQV5YLkD/
+!
+transceiver qsfp default-mode 4x10G
+!
+service routing protocols model multi-agent
+!
+hostname ceos
+!
+spanning-tree mode mstp
+!
+management api http-commands
+ no shutdown
+!
+management api gnmi
+ transport grpc default
+!
+management api netconf
+ transport ssh default
+!
+interface Ethernet1
+ description tacocat
+!
+interface Ethernet2
+!
+interface Management0
+ ip address 172.20.20.14/24
+ ipv6 address 2001:172:20:20::14/64
+!
+ip routing
+!
+ip route 0.0.0.0/0 172.20.20.1
+!
+end
+ Address
+Interface IP Address Status Protocol MTU Owner
+----------------- --------------------- ------------ -------------- ---------- -------
+Management0 172.20.20.14/24 up up 1500
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..e515f04
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-standard-out.txt
@@ -0,0 +1,299 @@
+
+
+Building configuration...
+
+Current configuration : 6957 bytes
+!
+! Last configuration change at 18:35:11 UTC Sat Jul 2 2022 by NETCONF
+!
+version 16.12
+service timestamps debug datetime msec
+service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
+service call-home
+platform qfp utilization monitor load 80
+platform punt-keepalive disable-kernel-core
+platform console serial
+!
+hostname vr-csr
+!
+boot-start-marker
+boot-end-marker
+!
+!
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
+!
+no aaa new-model
+call-home
+ ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
+ ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
+ contact-email-addr sch-smart-licensing@cisco.com
+ profile "CiscoTAC-1"
+ active
+ destination transport-method http
+ no destination transport-method email
+!
+!
+!
+!
+!
+!
+!
+ip domain name boxen.box
+!
+!
+!
+login on-success log
+!
+!
+!
+!
+!
+!
+!
+subscriber templating
+!
+!
+!
+!
+!
+!
+multilink bundle-name authenticated
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
+crypto pki trustpoint SLA-TrustPoint
+ enrollment pkcs12
+ revocation-check crl
+!
+!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
+crypto pki certificate chain SLA-TrustPoint
+ certificate ca 01
+ 30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
+ 32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
+ 6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
+ 3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
+ 43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
+ 526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
+ 82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
+ CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
+ 1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
+ 4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
+ 7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
+ 68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
+ C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
+ C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
+ DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
+ 06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
+ 4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
+ 03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
+ 604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
+ D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
+ 467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
+ 7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
+ 5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
+ 80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
+ 418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
+ D697DF7F 28
+ quit
+!
+license udi pid CSR1000V sn 9MM1GU3SD2V
+diagnostic bootup level minimal
+memory free low-watermark processor 72329
+!
+!
+spanning-tree extend system-id
+!
+username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
+!
+redundancy
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+interface GigabitEthernet1
+ ip address 10.0.0.15 255.255.255.0
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet2
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet3
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet4
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet5
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet6
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet7
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet8
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet9
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet10
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+ip forward-protocol nd
+no ip http server
+ip http secure-server
+!
+!
+!
+!
+!
+!
+!
+!
+control-plane
+!
+!
+!
+!
+!
+!
+line con 0
+ stopbits 1
+line vty 0
+ login local
+ transport input all
+line vty 1
+ login local
+ length 0
+ transport input all
+line vty 2 4
+ login local
+ transport input all
+!
+!
+!
+!
+!
+!
+netconf-yang
+restconf
+end
+Interface IP-Address OK? Method Status Protocol
+GigabitEthernet1 10.0.0.15 YES NVRAM up up
+GigabitEthernet2 unassigned YES NVRAM administratively down down
+GigabitEthernet3 unassigned YES NVRAM administratively down down
+GigabitEthernet4 unassigned YES NVRAM administratively down down
+GigabitEthernet5 unassigned YES NVRAM administratively down down
+GigabitEthernet6 unassigned YES NVRAM administratively down down
+GigabitEthernet7 unassigned YES NVRAM administratively down down
+GigabitEthernet8 unassigned YES NVRAM administratively down down
+GigabitEthernet9 unassigned YES NVRAM administratively down down
+GigabitEthernet10 unassigned YES NVRAM administratively down down
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-system-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..adad2af
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-system-out.txt
@@ -0,0 +1,299 @@
+
+
+Building configuration...
+
+Current configuration : 6957 bytes
+!
+! Last configuration change at 18:35:09 UTC Sat Jul 2 2022 by NETCONF
+!
+version 16.12
+service timestamps debug datetime msec
+service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
+service call-home
+platform qfp utilization monitor load 80
+platform punt-keepalive disable-kernel-core
+platform console serial
+!
+hostname vr-csr
+!
+boot-start-marker
+boot-end-marker
+!
+!
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
+!
+no aaa new-model
+call-home
+ ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
+ ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
+ contact-email-addr sch-smart-licensing@cisco.com
+ profile "CiscoTAC-1"
+ active
+ destination transport-method http
+ no destination transport-method email
+!
+!
+!
+!
+!
+!
+!
+ip domain name boxen.box
+!
+!
+!
+login on-success log
+!
+!
+!
+!
+!
+!
+!
+subscriber templating
+!
+!
+!
+!
+!
+!
+multilink bundle-name authenticated
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
+crypto pki trustpoint SLA-TrustPoint
+ enrollment pkcs12
+ revocation-check crl
+!
+!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
+crypto pki certificate chain SLA-TrustPoint
+ certificate ca 01
+ 30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
+ 32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
+ 6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
+ 3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
+ 43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
+ 526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
+ 82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
+ CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
+ 1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
+ 4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
+ 7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
+ 68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
+ C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
+ C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
+ DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
+ 06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
+ 4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
+ 03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
+ 604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
+ D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
+ 467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
+ 7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
+ 5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
+ 80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
+ 418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
+ D697DF7F 28
+ quit
+!
+license udi pid CSR1000V sn 9MM1GU3SD2V
+diagnostic bootup level minimal
+memory free low-watermark processor 72329
+!
+!
+spanning-tree extend system-id
+!
+username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
+!
+redundancy
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+interface GigabitEthernet1
+ ip address 10.0.0.15 255.255.255.0
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet2
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet3
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet4
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet5
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet6
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet7
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet8
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet9
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet10
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+ip forward-protocol nd
+no ip http server
+ip http secure-server
+!
+!
+!
+!
+!
+!
+!
+!
+control-plane
+!
+!
+!
+!
+!
+!
+line con 0
+ stopbits 1
+line vty 0
+ login local
+ transport input all
+line vty 1
+ login local
+ length 0
+ transport input all
+line vty 2 4
+ login local
+ transport input all
+!
+!
+!
+!
+!
+!
+netconf-yang
+restconf
+end
+Interface IP-Address OK? Method Status Protocol
+GigabitEthernet1 10.0.0.15 YES NVRAM up up
+GigabitEthernet2 unassigned YES NVRAM administratively down down
+GigabitEthernet3 unassigned YES NVRAM administratively down down
+GigabitEthernet4 unassigned YES NVRAM administratively down down
+GigabitEthernet5 unassigned YES NVRAM administratively down down
+GigabitEthernet6 unassigned YES NVRAM administratively down down
+GigabitEthernet7 unassigned YES NVRAM administratively down down
+GigabitEthernet8 unassigned YES NVRAM administratively down down
+GigabitEthernet9 unassigned YES NVRAM administratively down down
+GigabitEthernet10 unassigned YES NVRAM administratively down down
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-telnet-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-telnet-out.txt
new file mode 100644
index 0000000..e515f04
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxe-telnet-out.txt
@@ -0,0 +1,299 @@
+
+
+Building configuration...
+
+Current configuration : 6957 bytes
+!
+! Last configuration change at 18:35:11 UTC Sat Jul 2 2022 by NETCONF
+!
+version 16.12
+service timestamps debug datetime msec
+service timestamps log datetime msec
+! Call-home is enabled by Smart-Licensing.
+service call-home
+platform qfp utilization monitor load 80
+platform punt-keepalive disable-kernel-core
+platform console serial
+!
+hostname vr-csr
+!
+boot-start-marker
+boot-end-marker
+!
+!
+enable secret 9 $9$iYHNnk9hB4ElFk$3JjDfWOwfPRJK5SJKUo8YGs7whG5fra4jK9i1UWeVPc
+!
+no aaa new-model
+call-home
+ ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
+ ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
+ contact-email-addr sch-smart-licensing@cisco.com
+ profile "CiscoTAC-1"
+ active
+ destination transport-method http
+ no destination transport-method email
+!
+!
+!
+!
+!
+!
+!
+ip domain name boxen.box
+!
+!
+!
+login on-success log
+!
+!
+!
+!
+!
+!
+!
+subscriber templating
+!
+!
+!
+!
+!
+!
+multilink bundle-name authenticated
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+crypto pki trustpoint TP-self-signed-2238708418
+ enrollment selfsigned
+ subject-name cn=IOS-Self-Signed-Certificate-2238708418
+ revocation-check none
+ rsakeypair TP-self-signed-2238708418
+!
+crypto pki trustpoint SLA-TrustPoint
+ enrollment pkcs12
+ revocation-check crl
+!
+!
+crypto pki certificate chain TP-self-signed-2238708418
+ certificate self-signed 01
+ 30820330 30820218 A0030201 02020101 300D0609 2A864886 F70D0101 05050030
+ 31312F30 2D060355 04031326 494F532D 53656C66 2D536967 6E65642D 43657274
+ 69666963 6174652D 32323338 37303834 3138301E 170D3232 30363330 32323232
+ 30385A17 0D333030 31303130 30303030 305A3031 312F302D 06035504 03132649
+ 4F532D53 656C662D 5369676E 65642D43 65727469 66696361 74652D32 32333837
+ 30383431 38308201 22300D06 092A8648 86F70D01 01010500 0382010F 00308201
+ 0A028201 0100BCA5 C2E6AC5C 85914549 108DF5FE F2114718 F34C742C 6E9CF63C
+ 023DDA72 4C91709B 33EAE0D3 2EE64AA6 9129684A 2FF0CE22 D5C3FB59 7BEAB45F
+ FCE5F2B7 1A217F1C 896A283C 02CCE4E1 87384E1C 2E16E3F2 82649F6C 72B54B23
+ B644DC20 711C97A3 4063D958 EDC75BED 22EA75F9 0BB9AC47 022E9B30 B330821F
+ 23A1DF85 C8F6106F 9F2DD4DB E79F10CC 5450D55A 605E5569 F71E5CB3 9CDE7D24
+ F9CD60EF 580BF9E1 988100A6 CC9F1348 F4A87513 7B9AF3DC 6A3944BE 09ECBDC5
+ 6A7ADA11 6FF8E363 055DC30F 2D3F2AAE E4C58318 8C71B5A0 F485ABFE DEF900FF
+ 8542C79C E532603C E4CBCB57 916AE711 2C5358A6 614C5A8A 18D0C44C 09AEE19B
+ E406D18F 108F0203 010001A3 53305130 0F060355 1D130101 FF040530 030101FF
+ 301F0603 551D2304 18301680 143DC814 27F6C4DE E5EB66B6 80AE8774 0CD5DD7D
+ 40301D06 03551D0E 04160414 3DC81427 F6C4DEE5 EB66B680 AE87740C D5DD7D40
+ 300D0609 2A864886 F70D0101 05050003 82010100 909937D8 9436805B EB1FCC7F
+ 2D5719A6 64CB48EE BDC8080F 2E7D1F37 C361C17A F5DAE1F4 83CE7095 B9DB2A80
+ E9F641AB 4BE747D7 C87DDCA4 EB2A067C 8B72A1FF 11E68F5F 41B4A501 A9C9D408
+ 0F1F433C 9D39326E 6B76255A AE423053 3A1B1DEE 9BF92BDC BD4D5445 8985599F
+ 7BC437E2 C2D0A2E1 763DFCEF EBD9C9BC 50586772 B1E82B79 17B56943 B5ED11EF
+ 5F6010A9 52D98E13 F9F41BE5 50F0A06C 6C9B5D6E AF622C75 95E9094E 571068D9
+ 4C5BF832 88494BC8 0715D2CA 95013F5C 11CA1019 EC1E366F 63C33284 FE009EAE
+ C01B47FF 34C9C68C 599E3F09 ECDAD741 D45E35AF 6DE0F5E6 006BFCA0 01F0E56F
+ C8FAACF0 C09F1C49 4ACF0F38 8D4FC8F2 86FA27F9
+ quit
+crypto pki certificate chain SLA-TrustPoint
+ certificate ca 01
+ 30820321 30820209 A0030201 02020101 300D0609 2A864886 F70D0101 0B050030
+ 32310E30 0C060355 040A1305 43697363 6F312030 1E060355 04031317 43697363
+ 6F204C69 63656E73 696E6720 526F6F74 20434130 1E170D31 33303533 30313934
+ 3834375A 170D3338 30353330 31393438 34375A30 32310E30 0C060355 040A1305
+ 43697363 6F312030 1E060355 04031317 43697363 6F204C69 63656E73 696E6720
+ 526F6F74 20434130 82012230 0D06092A 864886F7 0D010101 05000382 010F0030
+ 82010A02 82010100 A6BCBD96 131E05F7 145EA72C 2CD686E6 17222EA1 F1EFF64D
+ CBB4C798 212AA147 C655D8D7 9471380D 8711441E 1AAF071A 9CAE6388 8A38E520
+ 1C394D78 462EF239 C659F715 B98C0A59 5BBB5CBD 0CFEBEA3 700A8BF7 D8F256EE
+ 4AA4E80D DB6FD1C9 60B1FD18 FFC69C96 6FA68957 A2617DE7 104FDC5F EA2956AC
+ 7390A3EB 2B5436AD C847A2C5 DAB553EB 69A9A535 58E9F3E3 C0BD23CF 58BD7188
+ 68E69491 20F320E7 948E71D7 AE3BCC84 F10684C7 4BC8E00F 539BA42B 42C68BB7
+ C7479096 B4CB2D62 EA2F505D C7B062A4 6811D95B E8250FC4 5D5D5FB8 8F27D191
+ C55F0D76 61F9A4CD 3D992327 A8BB03BD 4E6D7069 7CBADF8B DF5F4368 95135E44
+ DFC7C6CF 04DD7FD1 02030100 01A34230 40300E06 03551D0F 0101FF04 04030201
+ 06300F06 03551D13 0101FF04 05300301 01FF301D 0603551D 0E041604 1449DC85
+ 4B3D31E5 1B3E6A17 606AF333 3D3B4C73 E8300D06 092A8648 86F70D01 010B0500
+ 03820101 00507F24 D3932A66 86025D9F E838AE5C 6D4DF6B0 49631C78 240DA905
+ 604EDCDE FF4FED2B 77FC460E CD636FDB DD44681E 3A5673AB 9093D3B1 6C9E3D8B
+ D98987BF E40CBD9E 1AECA0C2 2189BB5C 8FA85686 CD98B646 5575B146 8DFC66A8
+ 467A3DF4 4D565700 6ADF0F0D CF835015 3C04FF7C 21E878AC 11BA9CD2 55A9232C
+ 7CA7B7E6 C1AF74F6 152E99B7 B1FCF9BB E973DE7F 5BDDEB86 C71E3B49 1765308B
+ 5FB0DA06 B92AFE7F 494E8A9E 07B85737 F3A58BE1 1A48A229 C37C1E69 39F08678
+ 80DDCD16 D6BACECA EEBC7CF9 8428787B 35202CDC 60E4616A B623CDBD 230E3AFB
+ 418616A9 4093E049 4D10AB75 27E86F73 932E35B5 8862FDAE 0275156F 719BB2F0
+ D697DF7F 28
+ quit
+!
+license udi pid CSR1000V sn 9MM1GU3SD2V
+diagnostic bootup level minimal
+memory free low-watermark processor 72329
+!
+!
+spanning-tree extend system-id
+!
+username boxen privilege 15 password 0 b0x3N-b0x3N
+username admin privilege 15 password 0 admin
+!
+redundancy
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+interface GigabitEthernet1
+ ip address 10.0.0.15 255.255.255.0
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet2
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet3
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet4
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet5
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet6
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet7
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet8
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet9
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+interface GigabitEthernet10
+ no ip address
+ shutdown
+ negotiation auto
+ no mop enabled
+ no mop sysid
+!
+ip forward-protocol nd
+no ip http server
+ip http secure-server
+!
+!
+!
+!
+!
+!
+!
+!
+control-plane
+!
+!
+!
+!
+!
+!
+line con 0
+ stopbits 1
+line vty 0
+ login local
+ transport input all
+line vty 1
+ login local
+ length 0
+ transport input all
+line vty 2 4
+ login local
+ transport input all
+!
+!
+!
+!
+!
+!
+netconf-yang
+restconf
+end
+Interface IP-Address OK? Method Status Protocol
+GigabitEthernet1 10.0.0.15 YES NVRAM up up
+GigabitEthernet2 unassigned YES NVRAM administratively down down
+GigabitEthernet3 unassigned YES NVRAM administratively down down
+GigabitEthernet4 unassigned YES NVRAM administratively down down
+GigabitEthernet5 unassigned YES NVRAM administratively down down
+GigabitEthernet6 unassigned YES NVRAM administratively down down
+GigabitEthernet7 unassigned YES NVRAM administratively down down
+GigabitEthernet8 unassigned YES NVRAM administratively down down
+GigabitEthernet9 unassigned YES NVRAM administratively down down
+GigabitEthernet10 unassigned YES NVRAM administratively down down
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..98c083a
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-standard-out.txt
@@ -0,0 +1,111 @@
+
+
+Sat Jul 2 18:36:59.348 UTC
+Building configuration...
+!! IOS XR Configuration version = 6.5.3
+!! Last configuration change at Sat Jul 2 18:34:56 2022 by boxen
+!
+hostname vr-xrv9k
+username boxen
+ group root-lr
+ group cisco-support
+ secret 5 $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+!
+username clab
+ group root-lr
+ group cisco-support
+ password 7 1511070D060A7A767B
+!
+call-home
+ service active
+ contact smart-licensing
+ profile CiscoTAC-1
+ active
+ destination transport-method http
+ !
+!
+interface MgmtEth0/RP0/CPU0/0
+ ipv4 address 10.0.0.15 255.255.255.0
+!
+interface GigabitEthernet0/0/0/0
+ shutdown
+!
+interface GigabitEthernet0/0/0/1
+ shutdown
+!
+interface GigabitEthernet0/0/0/2
+ shutdown
+!
+interface GigabitEthernet0/0/0/3
+ shutdown
+!
+interface GigabitEthernet0/0/0/4
+ shutdown
+!
+interface GigabitEthernet0/0/0/5
+ shutdown
+!
+interface GigabitEthernet0/0/0/6
+ shutdown
+!
+interface GigabitEthernet0/0/0/7
+ shutdown
+!
+interface GigabitEthernet0/0/0/8
+ shutdown
+!
+interface GigabitEthernet0/0/0/9
+ shutdown
+!
+interface GigabitEthernet0/0/0/10
+ shutdown
+!
+interface GigabitEthernet0/0/0/11
+ shutdown
+!
+interface GigabitEthernet0/0/0/12
+ shutdown
+!
+interface GigabitEthernet0/0/0/13
+ shutdown
+!
+interface GigabitEthernet0/0/0/14
+ shutdown
+!
+interface GigabitEthernet0/0/0/15
+ shutdown
+!
+grpc
+ port 57400
+ no-tls
+!
+netconf-yang agent
+ ssh
+!
+ssh server rate-limit 600
+ssh server v2
+ssh server netconf vrf default
+end
+Sat Jul 2 18:36:59.892 UTC
+
+ Intf Intf LineP Encap MTU BW
+ Name State State Type (byte) (Kbps)
+--------------------------------------------------------------------------------
+ Nu0 up up Null 1500 0
+ Mg0/RP0/CPU0/0 up up ARPA 1514 1000000
+ Gi0/0/0/0 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/1 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/2 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/3 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/4 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/5 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/6 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/7 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/8 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/9 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/10 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/11 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/12 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/13 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/14 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/15 admin-down admin-down ARPA 1514 1000000
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-system-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..9afec4b
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_iosxr-system-out.txt
@@ -0,0 +1,111 @@
+
+
+Sat Jul 2 18:36:56.653 UTC
+Building configuration...
+!! IOS XR Configuration version = 6.5.3
+!! Last configuration change at Sat Jul 2 18:34:56 2022 by boxen
+!
+hostname vr-xrv9k
+username boxen
+ group root-lr
+ group cisco-support
+ secret 5 $1$Eqd9$r77Xc5HHjEiT.ushQEHMu1
+!
+username clab
+ group root-lr
+ group cisco-support
+ password 7 1511070D060A7A767B
+!
+call-home
+ service active
+ contact smart-licensing
+ profile CiscoTAC-1
+ active
+ destination transport-method http
+ !
+!
+interface MgmtEth0/RP0/CPU0/0
+ ipv4 address 10.0.0.15 255.255.255.0
+!
+interface GigabitEthernet0/0/0/0
+ shutdown
+!
+interface GigabitEthernet0/0/0/1
+ shutdown
+!
+interface GigabitEthernet0/0/0/2
+ shutdown
+!
+interface GigabitEthernet0/0/0/3
+ shutdown
+!
+interface GigabitEthernet0/0/0/4
+ shutdown
+!
+interface GigabitEthernet0/0/0/5
+ shutdown
+!
+interface GigabitEthernet0/0/0/6
+ shutdown
+!
+interface GigabitEthernet0/0/0/7
+ shutdown
+!
+interface GigabitEthernet0/0/0/8
+ shutdown
+!
+interface GigabitEthernet0/0/0/9
+ shutdown
+!
+interface GigabitEthernet0/0/0/10
+ shutdown
+!
+interface GigabitEthernet0/0/0/11
+ shutdown
+!
+interface GigabitEthernet0/0/0/12
+ shutdown
+!
+interface GigabitEthernet0/0/0/13
+ shutdown
+!
+interface GigabitEthernet0/0/0/14
+ shutdown
+!
+interface GigabitEthernet0/0/0/15
+ shutdown
+!
+grpc
+ port 57400
+ no-tls
+!
+netconf-yang agent
+ ssh
+!
+ssh server rate-limit 600
+ssh server v2
+ssh server netconf vrf default
+end
+Sat Jul 2 18:36:57.175 UTC
+
+ Intf Intf LineP Encap MTU BW
+ Name State State Type (byte) (Kbps)
+--------------------------------------------------------------------------------
+ Nu0 up up Null 1500 0
+ Mg0/RP0/CPU0/0 up up ARPA 1514 1000000
+ Gi0/0/0/0 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/1 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/2 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/3 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/4 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/5 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/6 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/7 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/8 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/9 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/10 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/11 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/12 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/13 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/14 admin-down admin-down ARPA 1514 1000000
+ Gi0/0/0/15 admin-down admin-down ARPA 1514 1000000
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..d59a35c
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-standard-out.txt
@@ -0,0 +1,438 @@
+
+
+!Command: show running-config
+!Running configuration last done at: Sat Jul 2 18:32:27 2022
+!Time: Sat Jul 2 18:37:41 2022
+
+version 9.2(4) Bios:version
+hostname vr-n9kv
+vdc vr-n9kv id 1
+ limit-resource vlan minimum 16 maximum 4094
+ limit-resource vrf minimum 2 maximum 4096
+ limit-resource port-channel minimum 0 maximum 511
+ limit-resource u4route-mem minimum 248 maximum 248
+ limit-resource u6route-mem minimum 96 maximum 96
+ limit-resource m4route-mem minimum 58 maximum 58
+ limit-resource m6route-mem minimum 8 maximum 8
+
+feature nxapi
+feature scp-server
+feature netconf
+feature grpc
+
+no password strength-check
+username admin password 5 $5$OCIHOM$KYnQ6u7CdgTtDJ1OVIMRFfG4B0LiZP4TCu6NCr8SMU2 role network-admin
+username boxen password 5 $5$ipZx5veq$a7WoQ5.Cnki64vgXPPjcUvQwgKsur3OKpOtGGILPlF/ role network-admin
+username boxen passphrase lifetime 99999 warntime 14 gracetime 3
+ip domain-lookup
+copp profile strict
+snmp-server user admin network-admin auth md5 0xc2ddb65d774688cbe39ccc99407c3504 priv 0xc2ddb65d774688cbe39ccc99407c3504 localizedkey
+snmp-server user boxen network-admin auth md5 0xbbde1f880043a9581715afb5bd97c938 priv 0xbbde1f880043a9581715afb5bd97c938 localizedkey
+rmon event 1 description FATAL(1) owner PMON@FATAL
+rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
+rmon event 3 description ERROR(3) owner PMON@ERROR
+rmon event 4 description WARNING(4) owner PMON@WARNING
+rmon event 5 description INFORMATION(5) owner PMON@INFO
+
+vlan 1
+
+vrf context management
+
+interface Ethernet1/1
+
+interface Ethernet1/2
+
+interface Ethernet1/3
+
+interface Ethernet1/4
+
+interface Ethernet1/5
+
+interface Ethernet1/6
+
+interface Ethernet1/7
+
+interface Ethernet1/8
+
+interface Ethernet1/9
+
+interface Ethernet1/10
+
+interface Ethernet1/11
+
+interface Ethernet1/12
+
+interface Ethernet1/13
+
+interface Ethernet1/14
+
+interface Ethernet1/15
+
+interface Ethernet1/16
+
+interface Ethernet1/17
+
+interface Ethernet1/18
+
+interface Ethernet1/19
+
+interface Ethernet1/20
+
+interface Ethernet1/21
+
+interface Ethernet1/22
+
+interface Ethernet1/23
+
+interface Ethernet1/24
+
+interface Ethernet1/25
+
+interface Ethernet1/26
+
+interface Ethernet1/27
+
+interface Ethernet1/28
+
+interface Ethernet1/29
+
+interface Ethernet1/30
+
+interface Ethernet1/31
+
+interface Ethernet1/32
+
+interface Ethernet1/33
+
+interface Ethernet1/34
+
+interface Ethernet1/35
+
+interface Ethernet1/36
+
+interface Ethernet1/37
+
+interface Ethernet1/38
+
+interface Ethernet1/39
+
+interface Ethernet1/40
+
+interface Ethernet1/41
+
+interface Ethernet1/42
+
+interface Ethernet1/43
+
+interface Ethernet1/44
+
+interface Ethernet1/45
+
+interface Ethernet1/46
+
+interface Ethernet1/47
+
+interface Ethernet1/48
+
+interface Ethernet1/49
+
+interface Ethernet1/50
+
+interface Ethernet1/51
+
+interface Ethernet1/52
+
+interface Ethernet1/53
+
+interface Ethernet1/54
+
+interface Ethernet1/55
+
+interface Ethernet1/56
+
+interface Ethernet1/57
+
+interface Ethernet1/58
+
+interface Ethernet1/59
+
+interface Ethernet1/60
+
+interface Ethernet1/61
+
+interface Ethernet1/62
+
+interface Ethernet1/63
+
+interface Ethernet1/64
+
+interface Ethernet1/65
+
+interface Ethernet1/66
+
+interface Ethernet1/67
+
+interface Ethernet1/68
+
+interface Ethernet1/69
+
+interface Ethernet1/70
+
+interface Ethernet1/71
+
+interface Ethernet1/72
+
+interface Ethernet1/73
+
+interface Ethernet1/74
+
+interface Ethernet1/75
+
+interface Ethernet1/76
+
+interface Ethernet1/77
+
+interface Ethernet1/78
+
+interface Ethernet1/79
+
+interface Ethernet1/80
+
+interface Ethernet1/81
+
+interface Ethernet1/82
+
+interface Ethernet1/83
+
+interface Ethernet1/84
+
+interface Ethernet1/85
+
+interface Ethernet1/86
+
+interface Ethernet1/87
+
+interface Ethernet1/88
+
+interface Ethernet1/89
+
+interface Ethernet1/90
+
+interface Ethernet1/91
+
+interface Ethernet1/92
+
+interface Ethernet1/93
+
+interface Ethernet1/94
+
+interface Ethernet1/95
+
+interface Ethernet1/96
+
+interface Ethernet1/97
+
+interface Ethernet1/98
+
+interface Ethernet1/99
+
+interface Ethernet1/100
+
+interface Ethernet1/101
+
+interface Ethernet1/102
+
+interface Ethernet1/103
+
+interface Ethernet1/104
+
+interface Ethernet1/105
+
+interface Ethernet1/106
+
+interface Ethernet1/107
+
+interface Ethernet1/108
+
+interface Ethernet1/109
+
+interface Ethernet1/110
+
+interface Ethernet1/111
+
+interface Ethernet1/112
+
+interface Ethernet1/113
+
+interface Ethernet1/114
+
+interface Ethernet1/115
+
+interface Ethernet1/116
+
+interface Ethernet1/117
+
+interface Ethernet1/118
+
+interface Ethernet1/119
+
+interface Ethernet1/120
+
+interface Ethernet1/121
+
+interface Ethernet1/122
+
+interface Ethernet1/123
+
+interface Ethernet1/124
+
+interface Ethernet1/125
+
+interface Ethernet1/126
+
+interface Ethernet1/127
+
+interface Ethernet1/128
+
+interface mgmt0
+ vrf member management
+ ip address 10.0.0.15/24
+line console
+line vty
+boot nxos bootflash:/nxos.9.2.4.bin
+--------------------------------------------------------------------------------
+Port Name Status Vlan Duplex Speed Type
+--------------------------------------------------------------------------------
+mgmt0 -- connected routed full 1000 --
+
+--------------------------------------------------------------------------------
+Port Name Status Vlan Duplex Speed Type
+--------------------------------------------------------------------------------
+Eth1/1 -- connected 1 full auto 10g
+Eth1/2 -- connected 1 full auto 10g
+Eth1/3 -- connected 1 full auto 10g
+Eth1/4 -- connected 1 full auto 10g
+Eth1/5 -- connected 1 full auto 10g
+Eth1/6 -- connected 1 full auto 10g
+Eth1/7 -- connected 1 full auto 10g
+Eth1/8 -- connected 1 full auto 10g
+Eth1/9 -- notconnec 1 auto auto 10g
+Eth1/10 -- notconnec 1 auto auto 10g
+Eth1/11 -- notconnec 1 auto auto 10g
+Eth1/12 -- notconnec 1 auto auto 10g
+Eth1/13 -- notconnec 1 auto auto 10g
+Eth1/14 -- notconnec 1 auto auto 10g
+Eth1/15 -- notconnec 1 auto auto 10g
+Eth1/16 -- notconnec 1 auto auto 10g
+Eth1/17 -- notconnec 1 auto auto 10g
+Eth1/18 -- notconnec 1 auto auto 10g
+Eth1/19 -- notconnec 1 auto auto 10g
+Eth1/20 -- notconnec 1 auto auto 10g
+Eth1/21 -- notconnec 1 auto auto 10g
+Eth1/22 -- notconnec 1 auto auto 10g
+Eth1/23 -- notconnec 1 auto auto 10g
+Eth1/24 -- notconnec 1 auto auto 10g
+Eth1/25 -- notconnec 1 auto auto 10g
+Eth1/26 -- notconnec 1 auto auto 10g
+Eth1/27 -- notconnec 1 auto auto 10g
+Eth1/28 -- notconnec 1 auto auto 10g
+Eth1/29 -- notconnec 1 auto auto 10g
+Eth1/30 -- notconnec 1 auto auto 10g
+Eth1/31 -- notconnec 1 auto auto 10g
+Eth1/32 -- notconnec 1 auto auto 10g
+Eth1/33 -- notconnec 1 auto auto 10g
+Eth1/34 -- notconnec 1 auto auto 10g
+Eth1/35 -- notconnec 1 auto auto 10g
+Eth1/36 -- notconnec 1 auto auto 10g
+Eth1/37 -- notconnec 1 auto auto 10g
+Eth1/38 -- notconnec 1 auto auto 10g
+Eth1/39 -- notconnec 1 auto auto 10g
+Eth1/40 -- notconnec 1 auto auto 10g
+Eth1/41 -- notconnec 1 auto auto 10g
+Eth1/42 -- notconnec 1 auto auto 10g
+Eth1/43 -- notconnec 1 auto auto 10g
+Eth1/44 -- notconnec 1 auto auto 10g
+Eth1/45 -- notconnec 1 auto auto 10g
+Eth1/46 -- notconnec 1 auto auto 10g
+Eth1/47 -- notconnec 1 auto auto 10g
+Eth1/48 -- notconnec 1 auto auto 10g
+Eth1/49 -- notconnec 1 auto auto 10g
+Eth1/50 -- notconnec 1 auto auto 10g
+Eth1/51 -- notconnec 1 auto auto 10g
+Eth1/52 -- notconnec 1 auto auto 10g
+Eth1/53 -- notconnec 1 auto auto 10g
+Eth1/54 -- notconnec 1 auto auto 10g
+Eth1/55 -- notconnec 1 auto auto 10g
+Eth1/56 -- notconnec 1 auto auto 10g
+Eth1/57 -- notconnec 1 auto auto 10g
+Eth1/58 -- notconnec 1 auto auto 10g
+Eth1/59 -- notconnec 1 auto auto 10g
+Eth1/60 -- notconnec 1 auto auto 10g
+Eth1/61 -- notconnec 1 auto auto 10g
+Eth1/62 -- notconnec 1 auto auto 10g
+Eth1/63 -- notconnec 1 auto auto 10g
+Eth1/64 -- notconnec 1 auto auto 10g
+Eth1/65 -- notconnec 1 auto auto 10g
+Eth1/66 -- notconnec 1 auto auto 10g
+Eth1/67 -- notconnec 1 auto auto 10g
+Eth1/68 -- notconnec 1 auto auto 10g
+Eth1/69 -- notconnec 1 auto auto 10g
+Eth1/70 -- notconnec 1 auto auto 10g
+Eth1/71 -- notconnec 1 auto auto 10g
+Eth1/72 -- notconnec 1 auto auto 10g
+Eth1/73 -- notconnec 1 auto auto 10g
+Eth1/74 -- notconnec 1 auto auto 10g
+Eth1/75 -- notconnec 1 auto auto 10g
+Eth1/76 -- notconnec 1 auto auto 10g
+Eth1/77 -- notconnec 1 auto auto 10g
+Eth1/78 -- notconnec 1 auto auto 10g
+Eth1/79 -- notconnec 1 auto auto 10g
+Eth1/80 -- notconnec 1 auto auto 10g
+Eth1/81 -- notconnec 1 auto auto 10g
+Eth1/82 -- notconnec 1 auto auto 10g
+Eth1/83 -- notconnec 1 auto auto 10g
+Eth1/84 -- notconnec 1 auto auto 10g
+Eth1/85 -- notconnec 1 auto auto 10g
+Eth1/86 -- notconnec 1 auto auto 10g
+Eth1/87 -- notconnec 1 auto auto 10g
+Eth1/88 -- notconnec 1 auto auto 10g
+Eth1/89 -- notconnec 1 auto auto 10g
+Eth1/90 -- notconnec 1 auto auto 10g
+Eth1/91 -- notconnec 1 auto auto 10g
+Eth1/92 -- notconnec 1 auto auto 10g
+Eth1/93 -- notconnec 1 auto auto 10g
+Eth1/94 -- notconnec 1 auto auto 10g
+Eth1/95 -- notconnec 1 auto auto 10g
+Eth1/96 -- notconnec 1 auto auto 10g
+Eth1/97 -- notconnec 1 auto auto 10g
+Eth1/98 -- notconnec 1 auto auto 10g
+Eth1/99 -- notconnec 1 auto auto 10g
+Eth1/100 -- notconnec 1 auto auto 10g
+Eth1/101 -- notconnec 1 auto auto 10g
+Eth1/102 -- notconnec 1 auto auto 10g
+Eth1/103 -- notconnec 1 auto auto 10g
+Eth1/104 -- notconnec 1 auto auto 10g
+Eth1/105 -- notconnec 1 auto auto 10g
+Eth1/106 -- notconnec 1 auto auto 10g
+Eth1/107 -- notconnec 1 auto auto 10g
+Eth1/108 -- notconnec 1 auto auto 10g
+Eth1/109 -- notconnec 1 auto auto 10g
+Eth1/110 -- notconnec 1 auto auto 10g
+Eth1/111 -- notconnec 1 auto auto 10g
+Eth1/112 -- notconnec 1 auto auto 10g
+Eth1/113 -- notconnec 1 auto auto 10g
+Eth1/114 -- notconnec 1 auto auto 10g
+Eth1/115 -- notconnec 1 auto auto 10g
+Eth1/116 -- notconnec 1 auto auto 10g
+Eth1/117 -- notconnec 1 auto auto 10g
+Eth1/118 -- notconnec 1 auto auto 10g
+Eth1/119 -- notconnec 1 auto auto 10g
+Eth1/120 -- notconnec 1 auto auto 10g
+Eth1/121 -- notconnec 1 auto auto 10g
+Eth1/122 -- notconnec 1 auto auto 10g
+Eth1/123 -- notconnec 1 auto auto 10g
+Eth1/124 -- notconnec 1 auto auto 10g
+Eth1/125 -- notconnec 1 auto auto 10g
+Eth1/126 -- notconnec 1 auto auto 10g
+Eth1/127 -- notconnec 1 auto auto 10g
+Eth1/128 -- notconnec 1 auto auto 10g
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..d95af74
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-cisco_nxos-system-out.txt
@@ -0,0 +1,438 @@
+
+
+!Command: show running-config
+!Running configuration last done at: Sat Jul 2 18:32:27 2022
+!Time: Sat Jul 2 18:37:39 2022
+
+version 9.2(4) Bios:version
+hostname vr-n9kv
+vdc vr-n9kv id 1
+ limit-resource vlan minimum 16 maximum 4094
+ limit-resource vrf minimum 2 maximum 4096
+ limit-resource port-channel minimum 0 maximum 511
+ limit-resource u4route-mem minimum 248 maximum 248
+ limit-resource u6route-mem minimum 96 maximum 96
+ limit-resource m4route-mem minimum 58 maximum 58
+ limit-resource m6route-mem minimum 8 maximum 8
+
+feature nxapi
+feature scp-server
+feature netconf
+feature grpc
+
+no password strength-check
+username admin password 5 $5$OCIHOM$KYnQ6u7CdgTtDJ1OVIMRFfG4B0LiZP4TCu6NCr8SMU2 role network-admin
+username boxen password 5 $5$ipZx5veq$a7WoQ5.Cnki64vgXPPjcUvQwgKsur3OKpOtGGILPlF/ role network-admin
+username boxen passphrase lifetime 99999 warntime 14 gracetime 3
+ip domain-lookup
+copp profile strict
+snmp-server user admin network-admin auth md5 0xc2ddb65d774688cbe39ccc99407c3504 priv 0xc2ddb65d774688cbe39ccc99407c3504 localizedkey
+snmp-server user boxen network-admin auth md5 0xbbde1f880043a9581715afb5bd97c938 priv 0xbbde1f880043a9581715afb5bd97c938 localizedkey
+rmon event 1 description FATAL(1) owner PMON@FATAL
+rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
+rmon event 3 description ERROR(3) owner PMON@ERROR
+rmon event 4 description WARNING(4) owner PMON@WARNING
+rmon event 5 description INFORMATION(5) owner PMON@INFO
+
+vlan 1
+
+vrf context management
+
+interface Ethernet1/1
+
+interface Ethernet1/2
+
+interface Ethernet1/3
+
+interface Ethernet1/4
+
+interface Ethernet1/5
+
+interface Ethernet1/6
+
+interface Ethernet1/7
+
+interface Ethernet1/8
+
+interface Ethernet1/9
+
+interface Ethernet1/10
+
+interface Ethernet1/11
+
+interface Ethernet1/12
+
+interface Ethernet1/13
+
+interface Ethernet1/14
+
+interface Ethernet1/15
+
+interface Ethernet1/16
+
+interface Ethernet1/17
+
+interface Ethernet1/18
+
+interface Ethernet1/19
+
+interface Ethernet1/20
+
+interface Ethernet1/21
+
+interface Ethernet1/22
+
+interface Ethernet1/23
+
+interface Ethernet1/24
+
+interface Ethernet1/25
+
+interface Ethernet1/26
+
+interface Ethernet1/27
+
+interface Ethernet1/28
+
+interface Ethernet1/29
+
+interface Ethernet1/30
+
+interface Ethernet1/31
+
+interface Ethernet1/32
+
+interface Ethernet1/33
+
+interface Ethernet1/34
+
+interface Ethernet1/35
+
+interface Ethernet1/36
+
+interface Ethernet1/37
+
+interface Ethernet1/38
+
+interface Ethernet1/39
+
+interface Ethernet1/40
+
+interface Ethernet1/41
+
+interface Ethernet1/42
+
+interface Ethernet1/43
+
+interface Ethernet1/44
+
+interface Ethernet1/45
+
+interface Ethernet1/46
+
+interface Ethernet1/47
+
+interface Ethernet1/48
+
+interface Ethernet1/49
+
+interface Ethernet1/50
+
+interface Ethernet1/51
+
+interface Ethernet1/52
+
+interface Ethernet1/53
+
+interface Ethernet1/54
+
+interface Ethernet1/55
+
+interface Ethernet1/56
+
+interface Ethernet1/57
+
+interface Ethernet1/58
+
+interface Ethernet1/59
+
+interface Ethernet1/60
+
+interface Ethernet1/61
+
+interface Ethernet1/62
+
+interface Ethernet1/63
+
+interface Ethernet1/64
+
+interface Ethernet1/65
+
+interface Ethernet1/66
+
+interface Ethernet1/67
+
+interface Ethernet1/68
+
+interface Ethernet1/69
+
+interface Ethernet1/70
+
+interface Ethernet1/71
+
+interface Ethernet1/72
+
+interface Ethernet1/73
+
+interface Ethernet1/74
+
+interface Ethernet1/75
+
+interface Ethernet1/76
+
+interface Ethernet1/77
+
+interface Ethernet1/78
+
+interface Ethernet1/79
+
+interface Ethernet1/80
+
+interface Ethernet1/81
+
+interface Ethernet1/82
+
+interface Ethernet1/83
+
+interface Ethernet1/84
+
+interface Ethernet1/85
+
+interface Ethernet1/86
+
+interface Ethernet1/87
+
+interface Ethernet1/88
+
+interface Ethernet1/89
+
+interface Ethernet1/90
+
+interface Ethernet1/91
+
+interface Ethernet1/92
+
+interface Ethernet1/93
+
+interface Ethernet1/94
+
+interface Ethernet1/95
+
+interface Ethernet1/96
+
+interface Ethernet1/97
+
+interface Ethernet1/98
+
+interface Ethernet1/99
+
+interface Ethernet1/100
+
+interface Ethernet1/101
+
+interface Ethernet1/102
+
+interface Ethernet1/103
+
+interface Ethernet1/104
+
+interface Ethernet1/105
+
+interface Ethernet1/106
+
+interface Ethernet1/107
+
+interface Ethernet1/108
+
+interface Ethernet1/109
+
+interface Ethernet1/110
+
+interface Ethernet1/111
+
+interface Ethernet1/112
+
+interface Ethernet1/113
+
+interface Ethernet1/114
+
+interface Ethernet1/115
+
+interface Ethernet1/116
+
+interface Ethernet1/117
+
+interface Ethernet1/118
+
+interface Ethernet1/119
+
+interface Ethernet1/120
+
+interface Ethernet1/121
+
+interface Ethernet1/122
+
+interface Ethernet1/123
+
+interface Ethernet1/124
+
+interface Ethernet1/125
+
+interface Ethernet1/126
+
+interface Ethernet1/127
+
+interface Ethernet1/128
+
+interface mgmt0
+ vrf member management
+ ip address 10.0.0.15/24
+line console
+line vty
+boot nxos bootflash:/nxos.9.2.4.bin
+--------------------------------------------------------------------------------
+Port Name Status Vlan Duplex Speed Type
+--------------------------------------------------------------------------------
+mgmt0 -- connected routed full 1000 --
+
+--------------------------------------------------------------------------------
+Port Name Status Vlan Duplex Speed Type
+--------------------------------------------------------------------------------
+Eth1/1 -- connected 1 full auto 10g
+Eth1/2 -- connected 1 full auto 10g
+Eth1/3 -- connected 1 full auto 10g
+Eth1/4 -- connected 1 full auto 10g
+Eth1/5 -- connected 1 full auto 10g
+Eth1/6 -- connected 1 full auto 10g
+Eth1/7 -- connected 1 full auto 10g
+Eth1/8 -- connected 1 full auto 10g
+Eth1/9 -- notconnec 1 auto auto 10g
+Eth1/10 -- notconnec 1 auto auto 10g
+Eth1/11 -- notconnec 1 auto auto 10g
+Eth1/12 -- notconnec 1 auto auto 10g
+Eth1/13 -- notconnec 1 auto auto 10g
+Eth1/14 -- notconnec 1 auto auto 10g
+Eth1/15 -- notconnec 1 auto auto 10g
+Eth1/16 -- notconnec 1 auto auto 10g
+Eth1/17 -- notconnec 1 auto auto 10g
+Eth1/18 -- notconnec 1 auto auto 10g
+Eth1/19 -- notconnec 1 auto auto 10g
+Eth1/20 -- notconnec 1 auto auto 10g
+Eth1/21 -- notconnec 1 auto auto 10g
+Eth1/22 -- notconnec 1 auto auto 10g
+Eth1/23 -- notconnec 1 auto auto 10g
+Eth1/24 -- notconnec 1 auto auto 10g
+Eth1/25 -- notconnec 1 auto auto 10g
+Eth1/26 -- notconnec 1 auto auto 10g
+Eth1/27 -- notconnec 1 auto auto 10g
+Eth1/28 -- notconnec 1 auto auto 10g
+Eth1/29 -- notconnec 1 auto auto 10g
+Eth1/30 -- notconnec 1 auto auto 10g
+Eth1/31 -- notconnec 1 auto auto 10g
+Eth1/32 -- notconnec 1 auto auto 10g
+Eth1/33 -- notconnec 1 auto auto 10g
+Eth1/34 -- notconnec 1 auto auto 10g
+Eth1/35 -- notconnec 1 auto auto 10g
+Eth1/36 -- notconnec 1 auto auto 10g
+Eth1/37 -- notconnec 1 auto auto 10g
+Eth1/38 -- notconnec 1 auto auto 10g
+Eth1/39 -- notconnec 1 auto auto 10g
+Eth1/40 -- notconnec 1 auto auto 10g
+Eth1/41 -- notconnec 1 auto auto 10g
+Eth1/42 -- notconnec 1 auto auto 10g
+Eth1/43 -- notconnec 1 auto auto 10g
+Eth1/44 -- notconnec 1 auto auto 10g
+Eth1/45 -- notconnec 1 auto auto 10g
+Eth1/46 -- notconnec 1 auto auto 10g
+Eth1/47 -- notconnec 1 auto auto 10g
+Eth1/48 -- notconnec 1 auto auto 10g
+Eth1/49 -- notconnec 1 auto auto 10g
+Eth1/50 -- notconnec 1 auto auto 10g
+Eth1/51 -- notconnec 1 auto auto 10g
+Eth1/52 -- notconnec 1 auto auto 10g
+Eth1/53 -- notconnec 1 auto auto 10g
+Eth1/54 -- notconnec 1 auto auto 10g
+Eth1/55 -- notconnec 1 auto auto 10g
+Eth1/56 -- notconnec 1 auto auto 10g
+Eth1/57 -- notconnec 1 auto auto 10g
+Eth1/58 -- notconnec 1 auto auto 10g
+Eth1/59 -- notconnec 1 auto auto 10g
+Eth1/60 -- notconnec 1 auto auto 10g
+Eth1/61 -- notconnec 1 auto auto 10g
+Eth1/62 -- notconnec 1 auto auto 10g
+Eth1/63 -- notconnec 1 auto auto 10g
+Eth1/64 -- notconnec 1 auto auto 10g
+Eth1/65 -- notconnec 1 auto auto 10g
+Eth1/66 -- notconnec 1 auto auto 10g
+Eth1/67 -- notconnec 1 auto auto 10g
+Eth1/68 -- notconnec 1 auto auto 10g
+Eth1/69 -- notconnec 1 auto auto 10g
+Eth1/70 -- notconnec 1 auto auto 10g
+Eth1/71 -- notconnec 1 auto auto 10g
+Eth1/72 -- notconnec 1 auto auto 10g
+Eth1/73 -- notconnec 1 auto auto 10g
+Eth1/74 -- notconnec 1 auto auto 10g
+Eth1/75 -- notconnec 1 auto auto 10g
+Eth1/76 -- notconnec 1 auto auto 10g
+Eth1/77 -- notconnec 1 auto auto 10g
+Eth1/78 -- notconnec 1 auto auto 10g
+Eth1/79 -- notconnec 1 auto auto 10g
+Eth1/80 -- notconnec 1 auto auto 10g
+Eth1/81 -- notconnec 1 auto auto 10g
+Eth1/82 -- notconnec 1 auto auto 10g
+Eth1/83 -- notconnec 1 auto auto 10g
+Eth1/84 -- notconnec 1 auto auto 10g
+Eth1/85 -- notconnec 1 auto auto 10g
+Eth1/86 -- notconnec 1 auto auto 10g
+Eth1/87 -- notconnec 1 auto auto 10g
+Eth1/88 -- notconnec 1 auto auto 10g
+Eth1/89 -- notconnec 1 auto auto 10g
+Eth1/90 -- notconnec 1 auto auto 10g
+Eth1/91 -- notconnec 1 auto auto 10g
+Eth1/92 -- notconnec 1 auto auto 10g
+Eth1/93 -- notconnec 1 auto auto 10g
+Eth1/94 -- notconnec 1 auto auto 10g
+Eth1/95 -- notconnec 1 auto auto 10g
+Eth1/96 -- notconnec 1 auto auto 10g
+Eth1/97 -- notconnec 1 auto auto 10g
+Eth1/98 -- notconnec 1 auto auto 10g
+Eth1/99 -- notconnec 1 auto auto 10g
+Eth1/100 -- notconnec 1 auto auto 10g
+Eth1/101 -- notconnec 1 auto auto 10g
+Eth1/102 -- notconnec 1 auto auto 10g
+Eth1/103 -- notconnec 1 auto auto 10g
+Eth1/104 -- notconnec 1 auto auto 10g
+Eth1/105 -- notconnec 1 auto auto 10g
+Eth1/106 -- notconnec 1 auto auto 10g
+Eth1/107 -- notconnec 1 auto auto 10g
+Eth1/108 -- notconnec 1 auto auto 10g
+Eth1/109 -- notconnec 1 auto auto 10g
+Eth1/110 -- notconnec 1 auto auto 10g
+Eth1/111 -- notconnec 1 auto auto 10g
+Eth1/112 -- notconnec 1 auto auto 10g
+Eth1/113 -- notconnec 1 auto auto 10g
+Eth1/114 -- notconnec 1 auto auto 10g
+Eth1/115 -- notconnec 1 auto auto 10g
+Eth1/116 -- notconnec 1 auto auto 10g
+Eth1/117 -- notconnec 1 auto auto 10g
+Eth1/118 -- notconnec 1 auto auto 10g
+Eth1/119 -- notconnec 1 auto auto 10g
+Eth1/120 -- notconnec 1 auto auto 10g
+Eth1/121 -- notconnec 1 auto auto 10g
+Eth1/122 -- notconnec 1 auto auto 10g
+Eth1/123 -- notconnec 1 auto auto 10g
+Eth1/124 -- notconnec 1 auto auto 10g
+Eth1/125 -- notconnec 1 auto auto 10g
+Eth1/126 -- notconnec 1 auto auto 10g
+Eth1/127 -- notconnec 1 auto auto 10g
+Eth1/128 -- notconnec 1 auto auto 10g
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/juniper_junos_session b/driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-standard-out.txt
similarity index 79%
rename from test_data/driver/network/sendcommands/juniper_junos_session
rename to driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-standard-out.txt
index 832965c..95c5a14 100644
--- a/test_data/driver/network/sendcommands/juniper_junos_session
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-standard-out.txt
@@ -1,43 +1,33 @@
-READ: Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-boxen>
-boxen> set cli screen-length 0
-Screen length set to 0
-boxen> set cli screen-width 511
-Screen width set to 511
-
-boxen> set cli complete-on-space off
-Disabling complete-on-space
-
-boxen> show configuration | match 10.0.0.15
- address 10.0.0.15/24;
-
-boxen> show configuration
-TIME_STAMP_REPLACED
+## Last commit: 2022-07-02 18:36:25 UTC by boxen
version 17.3R2.10;
system {
+ host-name vr-vqfx;
root-authentication {
-CRYPTO_REPLACED
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
login {
+ user admin {
+ uid 2001;
+ class super-user;
+ authentication {
+ encrypted-password "$6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1."; ## SECRET-DATA
+ }
+ }
user boxen {
uid 2000;
class super-user;
authentication {
-CRYPTO_REPLACED
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
}
}
services {
- ssh {
- protocol-version v2;
- }
- telnet;
+ ssh;
netconf {
ssh;
+ rfc-compliant;
}
web-management {
http {
@@ -130,5 +120,7 @@ interfaces {
}
}
}
-
-boxen>
\ No newline at end of file
+Hostname: vr-vqfx
+Model: vsrx
+Junos: 17.3R2.10
+JUNOS Software Release [17.3R2.10]
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/juniper_junos b/driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-system-out.txt
similarity index 73%
rename from test_data/cfg/getconfig/juniper_junos
rename to driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-system-out.txt
index 785b520..95c5a14 100644
--- a/test_data/cfg/getconfig/juniper_junos
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-juniper_junos-system-out.txt
@@ -1,45 +1,33 @@
-Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-boxen>
-boxen> set cli screen-length 0
-Screen length set to 0
-boxen> set cli screen-width 511
-Screen width set to 511
-
-boxen> set cli complete-on-space off
-Disabling complete-on-space
-
-boxen> show version
-Model: srx4100
-Junos: 17.3R2.10
-JUNOS Software Release [17.3R2.10]
-
-boxen> show configuration
-## Last commit: 2021-08-13 21:03:52 UTC by boxen
+## Last commit: 2022-07-02 18:36:25 UTC by boxen
version 17.3R2.10;
system {
+ host-name vr-vqfx;
root-authentication {
- encrypted-password "6RhR81Jm4DEXKIbZNGjv.agJvM.FlIZWtFqX/966PZk0r4/Ps3LlS.OQZn9fHoVGuYJ7Q.hj2OQLyPJO6Mq7aQ3xLQiNrx/"; ## SECRET-DATA
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
login {
+ user admin {
+ uid 2001;
+ class super-user;
+ authentication {
+ encrypted-password "$6$TregNTlP$1KCLfHR7suzNfRhwa/8yrcRpVbBPuu9dtMseLfz/Ju5Q.e/MsA4RdQtUhnQxWS1u6SuUwV/Ss.4Lu89ddCND1."; ## SECRET-DATA
+ }
+ }
user boxen {
uid 2000;
class super-user;
authentication {
- encrypted-password "6iYt26fU9gkt6bgxPs.VqHgCoLuSD6Kxv1JUHJLQzXJgzAEUIxobvxWwRErtpaOFvBOjIHr3KMI7sEo.V/7xLXzr0Ok20h0"; ## SECRET-DATA
+ encrypted-password "$1$a$/nr2uXmtaTqW504hxM3Yw0"; ## SECRET-DATA
}
}
}
services {
- ssh {
- protocol-version v2;
- }
- telnet;
+ ssh;
netconf {
ssh;
+ rfc-compliant;
}
web-management {
http {
@@ -132,5 +120,7 @@ interfaces {
}
}
}
-
-boxen>
\ No newline at end of file
+Hostname: vr-vqfx
+Model: vsrx
+Junos: 17.3R2.10
+JUNOS Software Release [17.3R2.10]
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-standard-out.txt
new file mode 100644
index 0000000..1ff5873
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-standard-out.txt
@@ -0,0 +1,146 @@
+
+
+================================================================================
+ethernet-1/1 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/2 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/3 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/4 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/5 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/6 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/7 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/8 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/9 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/10 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/11 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/12 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/13 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/14 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/15 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/16 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/17 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/18 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/19 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/20 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/21 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/22 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/23 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/24 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/25 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/26 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/27 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/28 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/29 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/30 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/31 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/32 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/33 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/34 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/35 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/36 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/37 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/38 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/39 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/40 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/41 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/42 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/43 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/44 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/45 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/46 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/47 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/48 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/49 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/50 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/51 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/52 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/53 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/54 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/55 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/56 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+mgmt0 is up, speed 1G, type None
+ mgmt0.0 is up
+ Network-instance: mgmt
+ Encapsulation : null
+ Type : None
+ IPv4 addr : 172.20.20.16/24 (dhcp, preferred)
+ IPv6 addr : 2001:172:20:20::16/64 (dhcp, preferred)
+ IPv6 addr : fe80::42:acff:fe14:1410/64 (link-layer, preferred)
+--------------------------------------------------------------------------------
+================================================================================
+Summary
+ 0 loopback interfaces configured
+ 0 ethernet interfaces are up, 56 are down
+ 1 management interfaces are up, 0 are down
+ 1 subinterfaces are up, 0 are down
+================================================================================
+ +--------------+----+-------------+--------------+-------------+-------------+
+ | Module Type | ID | Admin State | Operational | Model | Temperature |
+ | | | | State | | |
+ +==============+====+=============+==============+=============+=============+
+ | control | A | N/A | up | imm48-25g-s | 50 |
+ | | | | | fp28+8-100g | |
+ | | | | | -qsfp28 | |
+ | linecard | 1 | N/A | empty | | 0 |
+ | fan-tray | 1 | N/A | empty | | 0 |
+ | fan-tray | 2 | N/A | empty | | 0 |
+ | fan-tray | 3 | N/A | empty | | 0 |
+ | fan-tray | 4 | N/A | empty | | 0 |
+ | power-supply | 1 | N/A | empty | | |
+ | power-supply | 2 | N/A | empty | | |
+ +--------------+----+-------------+--------------+-------------+-------------+
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-system-out.txt b/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-system-out.txt
new file mode 100644
index 0000000..1ff5873
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-commands-simple-nokia_srl-system-out.txt
@@ -0,0 +1,146 @@
+
+
+================================================================================
+ethernet-1/1 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/2 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/3 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/4 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/5 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/6 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/7 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/8 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/9 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/10 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/11 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/12 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/13 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/14 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/15 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/16 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/17 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/18 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/19 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/20 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/21 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/22 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/23 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/24 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/25 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/26 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/27 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/28 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/29 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/30 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/31 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/32 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/33 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/34 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/35 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/36 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/37 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/38 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/39 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/40 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/41 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/42 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/43 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/44 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/45 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/46 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/47 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/48 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/49 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/50 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/51 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/52 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/53 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/54 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/55 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+ethernet-1/56 is down, reason port-admin-disabled
+--------------------------------------------------------------------------------
+mgmt0 is up, speed 1G, type None
+ mgmt0.0 is up
+ Network-instance: mgmt
+ Encapsulation : null
+ Type : None
+ IPv4 addr : 172.20.20.16/24 (dhcp, preferred)
+ IPv6 addr : 2001:172:20:20::16/64 (dhcp, preferred)
+ IPv6 addr : fe80::42:acff:fe14:1410/64 (link-layer, preferred)
+--------------------------------------------------------------------------------
+================================================================================
+Summary
+ 0 loopback interfaces configured
+ 0 ethernet interfaces are up, 56 are down
+ 1 management interfaces are up, 0 are down
+ 1 subinterfaces are up, 0 are down
+================================================================================
+ +--------------+----+-------------+--------------+-------------+-------------+
+ | Module Type | ID | Admin State | Operational | Model | Temperature |
+ | | | | State | | |
+ +==============+====+=============+==============+=============+=============+
+ | control | A | N/A | up | imm48-25g-s | 50 |
+ | | | | | fp28+8-100g | |
+ | | | | | -qsfp28 | |
+ | linecard | 1 | N/A | empty | | 0 |
+ | fan-tray | 1 | N/A | empty | | 0 |
+ | fan-tray | 2 | N/A | empty | | 0 |
+ | fan-tray | 3 | N/A | empty | | 0 |
+ | fan-tray | 4 | N/A | empty | | 0 |
+ | power-supply | 1 | N/A | empty | | |
+ | power-supply | 2 | N/A | empty | | |
+ +--------------+----+-------------+--------------+-------------+-------------+
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-standard-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-standard-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-system-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-arista_eos-system-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-standard-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-system-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-telnet-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-telnet-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxe-telnet-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..a3731cf
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+Sat Jul 2 18:37:07.181 UTC
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..717c207
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_iosxr-system-out.txt
@@ -0,0 +1,3 @@
+
+
+Sat Jul 2 18:37:04.120 UTC
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-standard-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-cisco_nxos-system-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-standard-out.txt
new file mode 100644
index 0000000..f81d117
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-standard-out.txt
@@ -0,0 +1,5 @@
+[edit]
+[edit]
+commit complete
+
+[edit]
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-system-out.txt
new file mode 100644
index 0000000..f81d117
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-juniper_junos-system-out.txt
@@ -0,0 +1,5 @@
+[edit]
+[edit]
+commit complete
+
+[edit]
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-standard-out.txt
new file mode 100644
index 0000000..5e3002b
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-standard-out.txt
@@ -0,0 +1,4 @@
+
+
+
+All changes have been discarded. Leaving candidate mode.
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-system-out.txt b/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-system-out.txt
new file mode 100644
index 0000000..5e3002b
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-config-simple-nokia_srl-system-out.txt
@@ -0,0 +1,4 @@
+
+
+
+All changes have been discarded. Leaving candidate mode.
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-standard-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-system-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-arista_eos-system-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-standard-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-system-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-system-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-telnet-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-telnet-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxe-telnet-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-standard-out.txt
new file mode 100644
index 0000000..b29c086
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-standard-out.txt
@@ -0,0 +1,6 @@
+
+
+
+
+
+Sat Jul 2 18:37:14.266 UTC
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-system-out.txt
new file mode 100644
index 0000000..b9198a9
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_iosxr-system-out.txt
@@ -0,0 +1,6 @@
+
+
+
+
+
+Sat Jul 2 18:37:11.125 UTC
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-standard-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-standard-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-system-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-cisco_nxos-system-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-standard-out.txt
new file mode 100644
index 0000000..79fa62d
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-standard-out.txt
@@ -0,0 +1,8 @@
+
+
+
+[edit]
+[edit]
+commit complete
+
+[edit]
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-system-out.txt
new file mode 100644
index 0000000..79fa62d
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-juniper_junos-system-out.txt
@@ -0,0 +1,8 @@
+
+
+
+[edit]
+[edit]
+commit complete
+
+[edit]
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-standard-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-standard-out.txt
new file mode 100644
index 0000000..12be592
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-standard-out.txt
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+All changes have been discarded. Leaving candidate mode.
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-system-out.txt b/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-system-out.txt
new file mode 100644
index 0000000..12be592
--- /dev/null
+++ b/driver/network/test-fixtures/golden/functional-send-configs-simple-nokia_srl-system-out.txt
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+All changes have been discarded. Leaving candidate mode.
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/send-command-acquire-priv-in.txt b/driver/network/test-fixtures/golden/send-command-acquire-priv-in.txt
new file mode 100644
index 0000000..bbac166
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-command-acquire-priv-in.txt
@@ -0,0 +1,9 @@
+
+
+enable
+
+
+
+
+show run int vlan1
+
diff --git a/driver/network/test-fixtures/golden/send-command-acquire-priv-out.txt b/driver/network/test-fixtures/golden/send-command-acquire-priv-out.txt
new file mode 100644
index 0000000..ad32e91
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-command-acquire-priv-out.txt
@@ -0,0 +1,7 @@
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/send-command-simple-in.txt b/driver/network/test-fixtures/golden/send-command-simple-in.txt
new file mode 100644
index 0000000..e835dfa
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-command-simple-in.txt
@@ -0,0 +1,4 @@
+
+
+show run int vlan1
+
diff --git a/driver/network/test-fixtures/golden/send-command-simple-out.txt b/driver/network/test-fixtures/golden/send-command-simple-out.txt
new file mode 100644
index 0000000..ad32e91
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-command-simple-out.txt
@@ -0,0 +1,7 @@
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/send-commands-from-file-simple-in.txt b/driver/network/test-fixtures/golden/send-commands-from-file-simple-in.txt
new file mode 100644
index 0000000..396ca1f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-commands-from-file-simple-in.txt
@@ -0,0 +1,7 @@
+
+
+show run int vlan1
+
+
+show run int vlan1
+
diff --git a/driver/network/test-fixtures/golden/send-commands-from-file-simple-out.txt b/driver/network/test-fixtures/golden/send-commands-from-file-simple-out.txt
new file mode 100644
index 0000000..11d1a23
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-commands-from-file-simple-out.txt
@@ -0,0 +1,16 @@
+
+
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/send-commands-simple-in.txt b/driver/network/test-fixtures/golden/send-commands-simple-in.txt
new file mode 100644
index 0000000..396ca1f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-commands-simple-in.txt
@@ -0,0 +1,7 @@
+
+
+show run int vlan1
+
+
+show run int vlan1
+
diff --git a/driver/network/test-fixtures/golden/send-commands-simple-out.txt b/driver/network/test-fixtures/golden/send-commands-simple-out.txt
new file mode 100644
index 0000000..11d1a23
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-commands-simple-out.txt
@@ -0,0 +1,16 @@
+
+
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
\ No newline at end of file
diff --git a/driver/network/test-fixtures/golden/send-config-simple-in.txt b/driver/network/test-fixtures/golden/send-config-simple-in.txt
new file mode 100644
index 0000000..7a83ffd
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-config-simple-in.txt
@@ -0,0 +1,12 @@
+
+
+configure terminal
+
+
+
+
+interface loopback1
+
+
+no interface loopback1
+
diff --git a/driver/network/test-fixtures/golden/send-config-simple-out.txt b/driver/network/test-fixtures/golden/send-config-simple-out.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-config-simple-out.txt
@@ -0,0 +1 @@
+
diff --git a/driver/network/test-fixtures/golden/send-configs-from-file-simple-in.txt b/driver/network/test-fixtures/golden/send-configs-from-file-simple-in.txt
new file mode 100644
index 0000000..7a83ffd
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-configs-from-file-simple-in.txt
@@ -0,0 +1,12 @@
+
+
+configure terminal
+
+
+
+
+interface loopback1
+
+
+no interface loopback1
+
diff --git a/driver/network/test-fixtures/golden/send-configs-from-file-simple-out.txt b/driver/network/test-fixtures/golden/send-configs-from-file-simple-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-configs-from-file-simple-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/golden/send-configs-simple-in.txt b/driver/network/test-fixtures/golden/send-configs-simple-in.txt
new file mode 100644
index 0000000..7a83ffd
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-configs-simple-in.txt
@@ -0,0 +1,12 @@
+
+
+configure terminal
+
+
+
+
+interface loopback1
+
+
+no interface loopback1
+
diff --git a/driver/network/test-fixtures/golden/send-configs-simple-out.txt b/driver/network/test-fixtures/golden/send-configs-simple-out.txt
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/driver/network/test-fixtures/golden/send-configs-simple-out.txt
@@ -0,0 +1,3 @@
+
+
+
diff --git a/driver/network/test-fixtures/send-command-acquire-priv.txt b/driver/network/test-fixtures/send-command-acquire-priv.txt
new file mode 100644
index 0000000..5fd8900
--- /dev/null
+++ b/driver/network/test-fixtures/send-command-acquire-priv.txt
@@ -0,0 +1,12 @@
+C3560CX>enable
+C3560CX#
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-command-simple.txt b/driver/network/test-fixtures/send-command-simple.txt
new file mode 100644
index 0000000..68f2f76
--- /dev/null
+++ b/driver/network/test-fixtures/send-command-simple.txt
@@ -0,0 +1,10 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-commands-from-file-simple-inputs.txt b/driver/network/test-fixtures/send-commands-from-file-simple-inputs.txt
new file mode 100644
index 0000000..9ea914c
--- /dev/null
+++ b/driver/network/test-fixtures/send-commands-from-file-simple-inputs.txt
@@ -0,0 +1,2 @@
+show run int vlan1
+show run int vlan1
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-commands-from-file-simple.txt b/driver/network/test-fixtures/send-commands-from-file-simple.txt
new file mode 100644
index 0000000..badaaad
--- /dev/null
+++ b/driver/network/test-fixtures/send-commands-from-file-simple.txt
@@ -0,0 +1,19 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-commands-simple.txt b/driver/network/test-fixtures/send-commands-simple.txt
new file mode 100644
index 0000000..badaaad
--- /dev/null
+++ b/driver/network/test-fixtures/send-commands-simple.txt
@@ -0,0 +1,19 @@
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#show run int vlan1
+Building configuration...
+
+Current configuration : 38 bytes
+!
+interface Vlan1
+ no ip address
+end
+
+C3560CX#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-config-simple.txt b/driver/network/test-fixtures/send-config-simple.txt
new file mode 100644
index 0000000..7746a3c
--- /dev/null
+++ b/driver/network/test-fixtures/send-config-simple.txt
@@ -0,0 +1,6 @@
+C3560CX#
+C3560CX#configure terminal
+C3560CX(config)#
+C3560CX(config)#interface loopback1
+C3560CX(config-if)#no interface loopback1
+C3560CX(config)#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-configs-from-file-simple-inputs.txt b/driver/network/test-fixtures/send-configs-from-file-simple-inputs.txt
new file mode 100644
index 0000000..94ad921
--- /dev/null
+++ b/driver/network/test-fixtures/send-configs-from-file-simple-inputs.txt
@@ -0,0 +1,2 @@
+interface loopback1
+no interface loopback1
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-configs-from-file-simple.txt b/driver/network/test-fixtures/send-configs-from-file-simple.txt
new file mode 100644
index 0000000..7746a3c
--- /dev/null
+++ b/driver/network/test-fixtures/send-configs-from-file-simple.txt
@@ -0,0 +1,6 @@
+C3560CX#
+C3560CX#configure terminal
+C3560CX(config)#
+C3560CX(config)#interface loopback1
+C3560CX(config-if)#no interface loopback1
+C3560CX(config)#
\ No newline at end of file
diff --git a/driver/network/test-fixtures/send-configs-simple.txt b/driver/network/test-fixtures/send-configs-simple.txt
new file mode 100644
index 0000000..7746a3c
--- /dev/null
+++ b/driver/network/test-fixtures/send-configs-simple.txt
@@ -0,0 +1,6 @@
+C3560CX#
+C3560CX#configure terminal
+C3560CX(config)#
+C3560CX(config)#interface loopback1
+C3560CX(config-if)#no interface loopback1
+C3560CX(config)#
\ No newline at end of file
diff --git a/driver/opoptions/callback.go b/driver/opoptions/callback.go
new file mode 100644
index 0000000..6843498
--- /dev/null
+++ b/driver/opoptions/callback.go
@@ -0,0 +1,149 @@
+package opoptions
+
+import (
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithCallbackContains sets the "contains" value of a generic.Callback.
+func WithCallbackContains(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.Contains = s
+
+ return nil
+ }
+}
+
+// WithCallbackNotContains sets the "not contains" value of a generic.Callback.
+func WithCallbackNotContains(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.NotContains = s
+
+ return nil
+ }
+}
+
+// WithCallbackContainsRe sets the "contains" regex value of a generic.Callback.
+func WithCallbackContainsRe(p *regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.ContainsRe = p
+
+ return nil
+ }
+}
+
+// WithCallbackInsensitive sets the "Insensitive" value of a generic.Callback -- this will force
+// read bytes to lower case for comparison with the contains value.
+func WithCallbackInsensitive(b bool) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.Insensitive = b
+
+ return nil
+ }
+}
+
+// WithCallbackResetOutput will cause the generic.Callback to "reset" or zero the read bytes
+// after execution and before returning ot the next callback loop.
+func WithCallbackResetOutput() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.ResetOutput = true
+
+ return nil
+ }
+}
+
+// WithCallbackOnce indicates that this callback should only ever execute once.
+func WithCallbackOnce() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.Once = true
+
+ return nil
+ }
+}
+
+// WithCallbackComplete indicates that if encountered, this callback means that the
+// SendWithCallbacks operation is complete.
+func WithCallbackComplete() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.Complete = true
+
+ return nil
+ }
+}
+
+// WithCallbackName sets a Name value on the callback, this is purely for human readability.
+func WithCallbackName(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.Name = s
+
+ return nil
+ }
+}
+
+// WithCallbackNextTimeout sets the timeout value to use for the *next* read duration after this
+// callback is executed.
+func WithCallbackNextTimeout(d time.Duration) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*generic.Callback)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.NextTimeout = d
+
+ return nil
+ }
+}
diff --git a/driver/opoptions/callback_test.go b/driver/opoptions/callback_test.go
new file mode 100644
index 0000000..f51baf1
--- /dev/null
+++ b/driver/opoptions/callback_test.go
@@ -0,0 +1,504 @@
+package opoptions_test
+
+import (
+ "errors"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithCallbackContains(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackContains(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.Contains, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected contains do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Contains,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithCallbackContains(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-callback-contains": {
+ description: "simple set option test",
+ s: "potato",
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "potato",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackContains(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackNotContains(
+ testName string,
+ testCase *optionsStringTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackNotContains(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.NotContains, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected not contains do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.NotContains,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithCallbackNotContains(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-callback-not-contains": {
+ description: "simple set option test",
+ s: "potato",
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "potato",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackNotContains(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackContainsRe(
+ testName string,
+ testCase *optionsRegexpTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackContainsRe(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.ContainsRe.String(), testCase.p.String()) {
+ t.Fatalf(
+ "%s: actual and expected contains regex do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Contains,
+ testCase.p,
+ )
+ }
+ }
+}
+
+func TestWithCallbackContainsRe(t *testing.T) {
+ cases := map[string]*optionsRegexpTestCase{
+ "set-callback-contains-re": {
+ description: "simple set option test",
+ p: regexp.MustCompile("potato"),
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: regexp.MustCompile("potato"),
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackContainsRe(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackInsensitive(
+ testName string,
+ testCase *optionsBoolTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackInsensitive(testCase.b)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.Insensitive, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected insensitive do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Insensitive,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithCallbackInsensitive(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-callback-insensitive": {
+ description: "simple set option test",
+ b: true,
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackInsensitive(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackResetOutput(
+ testName string,
+ testCase *optionsBoolTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackResetOutput()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.ResetOutput, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected reset output do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ResetOutput,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithCallbackResetOutput(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-callback-reset-output": {
+ description: "simple set option test",
+ b: true,
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackResetOutput(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackOnce(
+ testName string,
+ testCase *optionsBoolTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackOnce()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.Once, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected once do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Once,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithCallbackOnce(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-callback-once": {
+ description: "simple set option test",
+ b: true,
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackOnce(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackComplete(
+ testName string,
+ testCase *optionsBoolTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackComplete()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.Complete, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected complete do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Complete,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithCallbackComplete(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-callback-complete": {
+ description: "simple set option test",
+ b: true,
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackComplete(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackName(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackName(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.Name, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected name do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Name,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithCallbackName(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-callback-name": {
+ description: "simple set option test",
+ s: "potato",
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "potato",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackName(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCallbackNextTimeout(
+ testName string,
+ testCase *optionsDurationTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCallbackNextTimeout(testCase.d)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Callback)
+
+ if !cmp.Equal(oo.NextTimeout, testCase.d) {
+ t.Fatalf(
+ "%s: actual and expected next timeout do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.NextTimeout,
+ testCase.d,
+ )
+ }
+ }
+}
+
+func TestWithCallbackNextTimeout(t *testing.T) {
+ cases := map[string]*optionsDurationTestCase{
+ "set-callback-duration": {
+ description: "simple set option test",
+ d: 99 * time.Second,
+ o: &generic.Callback{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ d: 99 * time.Second,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCallbackNextTimeout(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/opoptions/channel.go b/driver/opoptions/channel.go
new file mode 100644
index 0000000..8309ba4
--- /dev/null
+++ b/driver/opoptions/channel.go
@@ -0,0 +1,76 @@
+package opoptions
+
+import (
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithNoStripPrompt disables stripping the prompt out from the read bytes.
+func WithNoStripPrompt() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.OperationOptions)
+
+ if ok {
+ c.StripPrompt = false
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
+
+// WithEager forces the channel read operation into "eager" mode -- that is, it will no longer read
+// inputs off of the channel prior to sending a return, hence "eager".
+func WithEager() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.OperationOptions)
+
+ if ok {
+ c.Eager = true
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
+
+// WithTimeoutOps modifies the timeout "ops" value, or the timeout for a given operation. This only
+// modifies the timeout for the current operation and does not update the actual Channel TimeoutOps
+// value permanently.
+func WithTimeoutOps(t time.Duration) util.Option {
+ return func(o interface{}) error {
+ switch oo := o.(type) {
+ case *channel.OperationOptions:
+ oo.Timeout = t
+ case *netconf.OperationOptions:
+ oo.Timeout = t
+ default:
+ return util.ErrIgnoredOption
+ }
+
+ return nil
+ }
+}
+
+// WithCompletePatterns is a slice of regex patterns that, if seen, indicate that the operation is
+// complete -- this is used with SendInteractive.
+func WithCompletePatterns(p []*regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.OperationOptions)
+
+ if ok {
+ c.CompletePatterns = p
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
diff --git a/driver/opoptions/channel_test.go b/driver/opoptions/channel_test.go
new file mode 100644
index 0000000..8c37901
--- /dev/null
+++ b/driver/opoptions/channel_test.go
@@ -0,0 +1,248 @@
+package opoptions_test
+
+import (
+ "errors"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithStripPrompt(testName string, testCase *optionsBoolTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithNoStripPrompt()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.OperationOptions)
+
+ if !cmp.Equal(oo.StripPrompt, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected strip prompts do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.StripPrompt,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithStripPrompt(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-strip-prompt": {
+ description: "simple set option test",
+ b: false,
+ o: &channel.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: false,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithStripPrompt(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithEager(testName string, testCase *optionsBoolTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithEager()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.OperationOptions)
+
+ if !cmp.Equal(oo.Eager, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected eagers do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Eager,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithEager(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-eager": {
+ description: "simple set option test",
+ b: true,
+ o: &channel.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithEager(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithTimeout(testName string, testCase *optionsDurationTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithTimeoutOps(testCase.d)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ var timeout time.Duration
+
+ switch oo := testCase.o.(type) {
+ case *channel.OperationOptions:
+ timeout = oo.Timeout
+ case *netconf.OperationOptions:
+ timeout = oo.Timeout
+ }
+
+ if !cmp.Equal(timeout, testCase.d) {
+ t.Fatalf(
+ "%s: actual and expected timeouts do not match\nactual: %v\nexpected:%v",
+ testName,
+ timeout,
+ testCase.d,
+ )
+ }
+ }
+}
+
+func TestWithTimeout(t *testing.T) {
+ cases := map[string]*optionsDurationTestCase{
+ "set-timeout": {
+ description: "simple set option test",
+ d: 99 * time.Second,
+ o: &channel.OperationOptions{},
+ isignored: false,
+ },
+ "set-timeout-netconf": {
+ description: "simple set option test",
+ d: 99 * time.Second,
+ o: &netconf.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ d: 99 * time.Second,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTimeout(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithCompletePatterns(testName string, testCase *struct {
+ description string
+ p []*regexp.Regexp
+ o interface{}
+ isignored bool
+},
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithCompletePatterns(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.OperationOptions)
+
+ if !cmp.Equal(oo.CompletePatterns[0].String(), testCase.p[0].String()) {
+ t.Fatalf(
+ "%s: actual and expected complete patterns do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.CompletePatterns[0].String(),
+ testCase.p[0].String(),
+ )
+ }
+ }
+}
+
+func TestWithCompletePatterns(t *testing.T) {
+ cases := map[string]*struct {
+ description string
+ p []*regexp.Regexp
+ o interface{}
+ isignored bool
+ }{
+ "set-complete-patterns": {
+ description: "simple set option test",
+ p: []*regexp.Regexp{regexp.MustCompile(`pattern!`)},
+ o: &channel.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: nil,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithCompletePatterns(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/opoptions/generic.go b/driver/opoptions/generic.go
new file mode 100644
index 0000000..1382d71
--- /dev/null
+++ b/driver/opoptions/generic.go
@@ -0,0 +1,38 @@
+package opoptions
+
+import (
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithStopOnFailed will force any multi input operation to stop at the first sign of "failed"
+// output, meaning the first time there is output from the FailedWhenContains slice in the channel
+// output.
+func WithStopOnFailed() util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.OperationOptions)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.StopOnFailed = true
+
+ return nil
+ }
+}
+
+// WithFailedWhenContains sets the slice of strings indicating failure for a given operation.
+func WithFailedWhenContains(fw []string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.OperationOptions)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.FailedWhenContains = fw
+
+ return nil
+ }
+}
diff --git a/driver/opoptions/generic_test.go b/driver/opoptions/generic_test.go
new file mode 100644
index 0000000..d58a596
--- /dev/null
+++ b/driver/opoptions/generic_test.go
@@ -0,0 +1,121 @@
+package opoptions_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithStopOnFailed(testName string, testCase *optionsBoolTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithStopOnFailed()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error %s",
+ testName, err,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.OperationOptions)
+
+ if !cmp.Equal(oo.StopOnFailed, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected stop on faileds do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.StopOnFailed,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithStopOnFailed(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-stop-on-failed": {
+ description: "simple set option test",
+ b: true,
+ o: &generic.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &channel.OperationOptions{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithStopOnFailed(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithFailedWhenContains(
+ testName string,
+ testCase *optionsStringSliceTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithFailedWhenContains(testCase.ss)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.OperationOptions)
+
+ if !cmp.Equal(oo.FailedWhenContains, testCase.ss) {
+ t.Fatalf(
+ "%s: actual and expected failed when contains values do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.FailedWhenContains,
+ testCase.ss,
+ )
+ }
+ }
+}
+
+func TestWithFailedWhenContains(t *testing.T) {
+ cases := map[string]*optionsStringSliceTestCase{
+ "set-failed-when-contains": {
+ description: "simple set option test with an invalid transport type",
+ ss: []string{"failed", "when", "contains"},
+ o: &generic.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ss: []string{"failed", "when", "contains"},
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithFailedWhenContains(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/opoptions/netconf.go b/driver/opoptions/netconf.go
new file mode 100644
index 0000000..ca3ba64
--- /dev/null
+++ b/driver/opoptions/netconf.go
@@ -0,0 +1,36 @@
+package opoptions
+
+import (
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithFilterType allows for changing of the default filter type (subtree) for NETCONF operations.
+func WithFilterType(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*netconf.OperationOptions)
+
+ if ok {
+ c.FilterType = s
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
+
+// WithDefaultType allows for changing of the default "default type" type for NETCONF operations.
+func WithDefaultType(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*netconf.OperationOptions)
+
+ if ok {
+ c.DefaultType = s
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
diff --git a/driver/opoptions/netconf_test.go b/driver/opoptions/netconf_test.go
new file mode 100644
index 0000000..f1ed51b
--- /dev/null
+++ b/driver/opoptions/netconf_test.go
@@ -0,0 +1,139 @@
+package opoptions_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithFilterType(testName string, testCase *struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+},
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithFilterType(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*netconf.OperationOptions)
+
+ if !cmp.Equal(oo.FilterType, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected filter types do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.FilterType,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithFilterType(t *testing.T) {
+ cases := map[string]*struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+ }{
+ "set-filter-type": {
+ description: "simple set option test",
+ s: "xpath",
+ o: &netconf.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "xpath",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithFilterType(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithDefaultType(testName string, testCase *struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+},
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithDefaultType(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*netconf.OperationOptions)
+
+ if !cmp.Equal(oo.DefaultType, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected filter types do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.DefaultType,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithDefaultType(t *testing.T) {
+ cases := map[string]*struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+ }{
+ "set-default-type": {
+ description: "simple set option test",
+ s: "potato",
+ o: &netconf.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "xpath",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithDefaultType(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/opoptions/network.go b/driver/opoptions/network.go
new file mode 100644
index 0000000..2b171f0
--- /dev/null
+++ b/driver/opoptions/network.go
@@ -0,0 +1,23 @@
+package opoptions
+
+import (
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithPrivilegeLevel sets the desired privilege level for the given operation -- typically this
+// is used when wanting to send configs at a "non-standard" configuration privilege level, such as
+// "configuration-private" or "configuration-exclusive".
+func WithPrivilegeLevel(s string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.OperationOptions)
+
+ if ok {
+ d.PrivilegeLevel = s
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
diff --git a/driver/opoptions/network_test.go b/driver/opoptions/network_test.go
new file mode 100644
index 0000000..edd613d
--- /dev/null
+++ b/driver/opoptions/network_test.go
@@ -0,0 +1,64 @@
+package opoptions_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/opoptions"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithPrivilegeLevel(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := opoptions.WithPrivilegeLevel(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.OperationOptions)
+
+ if !cmp.Equal(oo.PrivilegeLevel, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected privilege levels do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PrivilegeLevel,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithPrivilegeLevel(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-privilege-level": {
+ description: "simple set option test",
+ s: "notconfig",
+ o: &network.OperationOptions{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "notconfig",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPrivilegeLevel(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/opoptions/optionsop_test.go b/driver/opoptions/optionsop_test.go
new file mode 100644
index 0000000..b8885b8
--- /dev/null
+++ b/driver/opoptions/optionsop_test.go
@@ -0,0 +1,68 @@
+package opoptions_test
+
+import (
+ "flag"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+type optionsBoolTestCase struct {
+ description string
+ b bool
+ o interface{}
+ isignored bool
+}
+
+type optionsStringTestCase struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+ iserr bool
+}
+
+type optionsStringSliceTestCase struct {
+ description string
+ ss []string
+ o interface{}
+ isignored bool
+}
+
+type optionsDurationTestCase struct {
+ description string
+ d time.Duration
+ o interface{}
+ isignored bool
+}
+
+type optionsRegexpTestCase struct {
+ description string
+ p *regexp.Regexp
+ o interface{}
+ isignored bool
+}
diff --git a/driver/options/auth.go b/driver/options/auth.go
new file mode 100644
index 0000000..5f143f1
--- /dev/null
+++ b/driver/options/auth.go
@@ -0,0 +1,88 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithAuthUsername provides the username to use for authentication to a device.
+func WithAuthUsername(s string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.User = s
+
+ return nil
+ }
+}
+
+// WithAuthPassword provides the password to use for authentication to a device.
+func WithAuthPassword(s string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.Password = s
+
+ return nil
+ }
+}
+
+// WithAuthSecondary provides the "secondary" password to use for authentication to a device -- this
+// is usually the "enable" password.
+func WithAuthSecondary(s string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.AuthSecondary = s
+
+ return nil
+ }
+}
+
+// WithAuthPassphrase provides the ssh key passphrase to use during authentication to a device.
+func WithAuthPassphrase(s string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.PrivateKeyPassPhrase = s
+
+ return nil
+ }
+}
+
+// WithAuthBypass allows for skipping of "in channel" authentication. This "in channel"
+// authentication occurs when connecting with the System or Telnet transports and happens right
+// after the initial transport connection is opened up. This option allows for skipping this, you
+// may want to do this if connecting to a terminal server or using the System transport with
+// a patched open binary (ex: docker/kubectl exec).
+func WithAuthBypass() util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.AuthBypass = true
+
+ return nil
+ }
+}
diff --git a/driver/options/auth_test.go b/driver/options/auth_test.go
new file mode 100644
index 0000000..31ec11d
--- /dev/null
+++ b/driver/options/auth_test.go
@@ -0,0 +1,275 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testWithAuthUsername(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthUsername(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.User, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected usernames do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.User,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithAuthUsername(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-auth-username": {
+ description: "simple set option test",
+ s: "userperson",
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "userperson",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthUsername(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithAuthPassword(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthPassword(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.Password, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected passwords do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.Password,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithAuthPassword(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-auth-password": {
+ description: "simple set option test",
+ s: "supergoodpassword",
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "userperson",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthPassword(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithAuthSecondary(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthSecondary(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.Driver)
+
+ if !cmp.Equal(oo.AuthSecondary, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected secondary passwords do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.AuthSecondary,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithAuthSecondary(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-auth-secondary": {
+ description: "simple set option test",
+ s: "supergoodsecondarypassword",
+ o: &network.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "userperson",
+ o: &transport.Args{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthSecondary(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithAuthPassphrase(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthPassphrase(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.SSHArgs)
+
+ if !cmp.Equal(oo.PrivateKeyPassPhrase, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected passphrases do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PrivateKeyPassPhrase,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithAuthPassphrase(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-auth-passphrase": {
+ description: "simple set option test",
+ s: "verysecurepassphrase",
+ o: &transport.SSHArgs{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "userperson",
+ o: &transport.Args{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthPassphrase(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithAuthBypass(testName string, testCase *optionsBoolTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthBypass()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.AuthBypass, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected auth bypass do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.AuthBypass,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithAuthBypass(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-auth-bypass": {
+ description: "simple set option test",
+ b: true,
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &transport.Args{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthBypass(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/channel.go b/driver/options/channel.go
new file mode 100644
index 0000000..5a6ae8c
--- /dev/null
+++ b/driver/options/channel.go
@@ -0,0 +1,138 @@
+package options
+
+import (
+ "io"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithPromptPattern allows for providing a custom regex pattern to use for the channel
+// PromptPattern.
+func WithPromptPattern(p *regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.PromptPattern = p
+
+ return nil
+ }
+}
+
+// WithUsernamePattern allows for patching the "in channel" authentication username pattern -- this
+// is only relevant when using the Telnet transport.
+func WithUsernamePattern(p *regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.UsernamePattern = p
+
+ return nil
+ }
+}
+
+// WithPasswordPattern allows for patching the "in channel" authentication password prompt pattern,
+// this is only relevant for Telnet and System transports.
+func WithPasswordPattern(p *regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.PasswordPattern = p
+
+ return nil
+ }
+}
+
+// WithPassphrasePattern allows for patching the "in channel" authentication SSH key passphrase
+// pattern.
+func WithPassphrasePattern(p *regexp.Regexp) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.PassphrasePattern = p
+
+ return nil
+ }
+}
+
+// WithReturnChar allows for patching the channel ReturnChar value -- *typically* you should not
+// need to use this option.
+func WithReturnChar(s string) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.ReturnChar = []byte(s)
+
+ return nil
+ }
+}
+
+// WithTimeoutOps allows for modifying the channel TimeoutOps value -- this is the value that gets
+// set as the TimeoutOps for the Channel at driver creation. The TimeoutOps value is what governs
+// the "operation" timeouts for Channel read operations.
+func WithTimeoutOps(t time.Duration) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.TimeoutOps = t
+
+ return nil
+ }
+}
+
+// WithReadDelay sets the ReadDelay for the channel read loop. This value is the sleep time between
+// dequeue'ing data from the read queue that the transport fills. This value defaults to 5ms.
+func WithReadDelay(t time.Duration) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.ReadDelay = t
+
+ return nil
+ }
+}
+
+// WithChannelLog accepts an io.Writer that can be used to write all Channel read data out to.
+func WithChannelLog(w io.Writer) util.Option {
+ return func(o interface{}) error {
+ c, ok := o.(*channel.Channel)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ c.ChannelLog = w
+
+ return nil
+ }
+}
diff --git a/driver/options/channel_test.go b/driver/options/channel_test.go
new file mode 100644
index 0000000..e9328b5
--- /dev/null
+++ b/driver/options/channel_test.go
@@ -0,0 +1,447 @@
+package options_test
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testWithPromptPattern(testName string, testCase *optionsRegexpTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithPromptPattern(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.PromptPattern.String(), testCase.p.String()) {
+ t.Fatalf(
+ "%s: actual and expected prompt patterns do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PromptPattern,
+ testCase.p,
+ )
+ }
+ }
+}
+
+func TestWithPromptPattern(t *testing.T) {
+ cases := map[string]*optionsRegexpTestCase{
+ "set-prompt-pattern": {
+ description: "simple set option test",
+ p: regexp.MustCompile("neatpattern"),
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: regexp.MustCompile("neatpattern"),
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPromptPattern(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithUsernamePattern(testName string, testCase *optionsRegexpTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithUsernamePattern(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.UsernamePattern.String(), testCase.p.String()) {
+ t.Fatalf(
+ "%s: actual and expected username patterns do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.UsernamePattern,
+ testCase.p,
+ )
+ }
+ }
+}
+
+func TestWithUsernamePattern(t *testing.T) {
+ cases := map[string]*optionsRegexpTestCase{
+ "set-username-pattern": {
+ description: "simple set option test",
+ p: regexp.MustCompile("findthisusername!"),
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: regexp.MustCompile("findthisusername!"),
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithUsernamePattern(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithPasswordPattern(testName string, testCase *optionsRegexpTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithPasswordPattern(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.PasswordPattern.String(), testCase.p.String()) {
+ t.Fatalf(
+ "%s: actual and expected password patterns do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PasswordPattern,
+ testCase.p,
+ )
+ }
+ }
+}
+
+func TestWithPasswordPattern(t *testing.T) {
+ cases := map[string]*optionsRegexpTestCase{
+ "set-password-pattern": {
+ description: "simple set option test",
+ p: regexp.MustCompile("helloisthatyoupasswordprompt!"),
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: regexp.MustCompile("helloisthatyoupasswordprompt!"),
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPasswordPattern(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithPassphrasePattern(
+ testName string,
+ testCase *optionsRegexpTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithPassphrasePattern(testCase.p)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.PassphrasePattern.String(), testCase.p.String()) {
+ t.Fatalf(
+ "%s: actual and expected passphrase patterns do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PassphrasePattern,
+ testCase.p,
+ )
+ }
+ }
+}
+
+func TestWithPassphrasePattern(t *testing.T) {
+ cases := map[string]*optionsRegexpTestCase{
+ "set-pasphrase-pattern": {
+ description: "simple set option test",
+ p: regexp.MustCompile("sneakypassphrase"),
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ p: regexp.MustCompile("sneakypassphrase"),
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPassphrasePattern(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithReturnChar(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithReturnChar(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.ReturnChar, []byte(testCase.s)) {
+ t.Fatalf(
+ "%s: actual and expected return char do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.ReturnChar,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithReturnChar(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-return-char": {
+ description: "simple set option test",
+ s: `\r\n`,
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: `\r\n`,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithReturnChar(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithTimeoutOps(testName string, testCase *optionsDurationTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithTimeoutOps(testCase.d)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.TimeoutOps, testCase.d) {
+ t.Fatalf(
+ "%s: actual and expected timeout ops do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.TimeoutOps,
+ testCase.d,
+ )
+ }
+ }
+}
+
+func TestWithTimeoutOps(t *testing.T) {
+ cases := map[string]*optionsDurationTestCase{
+ "set-timeout-ops": {
+ description: "simple set option test",
+ d: 123 * time.Second,
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ d: 123 * time.Second,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTimeoutOps(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithReadDelay(testName string, testCase *optionsDurationTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithReadDelay(testCase.d)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(oo.ReadDelay, testCase.d) {
+ t.Fatalf(
+ "%s: actual and expected read delay times do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.ReadDelay,
+ testCase.d,
+ )
+ }
+ }
+}
+
+func TestWithReadDelay(t *testing.T) {
+ cases := map[string]*optionsDurationTestCase{
+ "set-read-delay": {
+ description: "simple set option test",
+ d: 123 * time.Second,
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ d: 123 * time.Second,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithReadDelay(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+type optionsWithChannelLog struct {
+ description string
+ log io.Writer
+ o interface{}
+ isignored bool
+}
+
+func testWithChannelLog(testName string, testCase *optionsWithChannelLog) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithChannelLog(testCase.log)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*channel.Channel)
+
+ if !cmp.Equal(fmt.Sprintf("%v", oo.ChannelLog), fmt.Sprintf("%v", testCase.log)) {
+ t.Fatalf(
+ "%s: actual and expected channel log objects do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.ChannelLog,
+ testCase.log,
+ )
+ }
+ }
+}
+
+func TestWithChannelLog(t *testing.T) {
+ var logB bytes.Buffer
+
+ cases := map[string]*optionsWithChannelLog{
+ "set-channel-log": {
+ description: "simple set option test",
+ log: &logB,
+ o: &channel.Channel{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ log: &logB,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithChannelLog(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/generic.go b/driver/options/generic.go
new file mode 100644
index 0000000..298206a
--- /dev/null
+++ b/driver/options/generic.go
@@ -0,0 +1,88 @@
+package options
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithTransportType allows for changing the underlying transport type, valid options are "system",
+// (default, /bin/ssh wrapper), "standard" (crypto/ssh), and "telnet".
+func WithTransportType(transportType string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ switch transportType {
+ case transport.SystemTransport,
+ transport.StandardTransport,
+ transport.TelnetTransport,
+ transport.FileTransport:
+ d.TransportType = transportType
+ default:
+ return fmt.Errorf(
+ "%w: unknown transport type '%s' provided",
+ util.ErrBadOption,
+ transportType,
+ )
+ }
+
+ return nil
+ }
+}
+
+// WithFailedWhenContains allows for setting a slice of strings that if seen in output indicate the
+// input has failed.
+func WithFailedWhenContains(fw []string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.FailedWhenContains = fw
+
+ return nil
+ }
+}
+
+// WithOnOpen allows for setting a function that is called after successfully opening a connection,
+// but before returning the connection to the user. Users may use this function to "prepare" a
+// device by doing things like disabling paging, or setting some terminal values.
+func WithOnOpen(f func(d *generic.Driver) error) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.OnOpen = f
+
+ return nil
+ }
+}
+
+// WithOnClose allows for setting a function that is called prior to closing the transport. Users
+// may wish to execute some command(s) that help gracefully close their connection prior to
+// terminating the transport.
+func WithOnClose(f func(d *generic.Driver) error) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.OnClose = f
+
+ return nil
+ }
+}
diff --git a/driver/options/generic_test.go b/driver/options/generic_test.go
new file mode 100644
index 0000000..a63c98f
--- /dev/null
+++ b/driver/options/generic_test.go
@@ -0,0 +1,240 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testWithTransportType(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithTransportType(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ if errors.Is(err, util.ErrIgnoredOption) && testCase.isignored {
+ return
+ }
+
+ if !errors.Is(err, util.ErrIgnoredOption) && testCase.iserr {
+ return
+ }
+
+ t.Fatalf("%s: encountered an error but we should not", testName)
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if !cmp.Equal(oo.TransportType, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected transport types do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.TransportType,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithTransportType(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-transport-type": {
+ description: "simple set option test",
+ s: transport.StandardTransport,
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "set-transport-type-invalid": {
+ description: "simple set option test with an invalid transport type",
+ s: "potato",
+ o: &generic.Driver{},
+ isignored: false,
+ iserr: true,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: transport.StandardTransport,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTransportType(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithFailedWhenContains(
+ testName string,
+ testCase *optionsStringSliceTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithFailedWhenContains(testCase.ss)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if !cmp.Equal(oo.FailedWhenContains, testCase.ss) {
+ t.Fatalf(
+ "%s: actual and expected failed when contains values do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.FailedWhenContains,
+ testCase.ss,
+ )
+ }
+ }
+}
+
+func TestWithFailedWhenContains(t *testing.T) {
+ cases := map[string]*optionsStringSliceTestCase{
+ "set-failed-when-contains": {
+ description: "simple set option test",
+ ss: []string{"failed", "when", "contains"},
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ss: []string{"failed", "when", "contains"},
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithFailedWhenContains(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithOnOpen(testName string, testCase *optionsGenericDriverOnXTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithOnOpen(testCase.f)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if oo.OnOpen == nil {
+ t.Fatalf(
+ "%s: on open function is *not* set",
+ testName,
+ )
+ }
+ }
+}
+
+func TestWithOnOpen(t *testing.T) {
+ cases := map[string]*optionsGenericDriverOnXTestCase{
+ "set-on-open": {
+ description: "simple set option test",
+ f: func(d *generic.Driver) error { return nil },
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ f: func(d *generic.Driver) error { return nil },
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithOnOpen(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithOnClose(
+ testName string,
+ testCase *optionsGenericDriverOnXTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithOnClose(testCase.f)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if oo.OnClose == nil {
+ t.Fatalf(
+ "%s: on close function is *not* set",
+ testName,
+ )
+ }
+ }
+}
+
+func TestWithOnClose(t *testing.T) {
+ cases := map[string]*optionsGenericDriverOnXTestCase{
+ "set-on-close": {
+ description: "simple set option test",
+ f: func(d *generic.Driver) error { return nil },
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ f: func(d *generic.Driver) error { return nil },
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithOnClose(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/logging.go b/driver/options/logging.go
new file mode 100644
index 0000000..ad79965
--- /dev/null
+++ b/driver/options/logging.go
@@ -0,0 +1,49 @@
+package options
+
+import (
+ "log"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithLogger accepts a logging.Instance and applies it to the driver object.
+func WithLogger(l *logging.Instance) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.Logger = l
+
+ return nil
+ }
+}
+
+// WithDefaultLogger applies the default logging setup to a driver object. This means log.Print
+// for the logger function, and "info" for the log level.
+func WithDefaultLogger() util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*generic.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ l, err := logging.NewInstance(
+ logging.WithLevel("info"),
+ logging.WithLogger(log.Print),
+ )
+ if err != nil {
+ return err
+ }
+
+ d.Logger = l
+
+ return nil
+ }
+}
diff --git a/driver/options/logging_test.go b/driver/options/logging_test.go
new file mode 100644
index 0000000..89ec1ed
--- /dev/null
+++ b/driver/options/logging_test.go
@@ -0,0 +1,122 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type withLoggerTestCase struct {
+ description string
+ l *logging.Instance
+ o interface{}
+ isignored bool
+}
+
+func testWithLogger(testName string, testCase *withLoggerTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithLogger(testCase.l)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if !cmp.Equal(oo.Logger, testCase.l) {
+ t.Fatalf(
+ "%s: actual and expected loggers do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.Logger,
+ testCase.l,
+ )
+ }
+ }
+}
+
+func TestWithLogger(t *testing.T) {
+ cases := map[string]*withLoggerTestCase{
+ "set-logger": {
+ description: "simple set option test",
+ l: &logging.Instance{},
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ l: &logging.Instance{},
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithLogger(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithDefaultLogger(testName string, testCase *optionsNoneTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithDefaultLogger()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*generic.Driver)
+
+ if oo.Logger == nil {
+ t.Fatalf(
+ "%s: default logger not set",
+ testName,
+ )
+ }
+ }
+}
+
+func TestWithDefaultLogger(t *testing.T) {
+ cases := map[string]*optionsNoneTestCase{
+ "set-default-logger": {
+ description: "simple set option test",
+ o: &generic.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithDefaultLogger(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/netconf.go b/driver/options/netconf.go
new file mode 100644
index 0000000..b41a99f
--- /dev/null
+++ b/driver/options/netconf.go
@@ -0,0 +1,47 @@
+package options
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithNetconfPreferredVersion allows for providing a preferred NETCONF version to use with the
+// server. The server must advertise the preferred capability or the connection will fail.
+func WithNetconfPreferredVersion(s string) util.Option {
+ return func(o interface{}) error {
+ switch s {
+ case netconf.V1Dot0, netconf.V1Dot1:
+ default:
+ return fmt.Errorf("%w: invalid netconf version '%s' provided", util.ErrBadOption, s)
+ }
+
+ d, ok := o.(*netconf.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.PreferredVersion = s
+
+ return nil
+ }
+}
+
+// WithNetconfForceSelfClosingTags forces the use of "self-closing HTML tags" -- this should only be
+// relevant for Juniper devices.
+func WithNetconfForceSelfClosingTags() util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*netconf.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.ForceSelfClosingTags = true
+
+ return nil
+ }
+}
diff --git a/driver/options/netconf_test.go b/driver/options/netconf_test.go
new file mode 100644
index 0000000..110ce2c
--- /dev/null
+++ b/driver/options/netconf_test.go
@@ -0,0 +1,130 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testWithNetconfPreferredVersion(
+ testName string,
+ testCase *optionsStringTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithNetconfPreferredVersion(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*netconf.Driver)
+
+ if !cmp.Equal(oo.PreferredVersion, testCase.s) {
+ t.Fatalf(
+ "%s: actual and preferred versions do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.PreferredVersion,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithNetconfPreferredVersion(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-netconf-preferred-version-1.0": {
+ description: "simple set option test",
+ s: "1.0",
+ o: &netconf.Driver{},
+ isignored: false,
+ },
+ "set-netconf-preferred-version-1.1": {
+ description: "simple set option test",
+ s: "1.1",
+ o: &netconf.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "1.0",
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithNetconfPreferredVersion(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithNetconfForceSelfClosingTags(
+ testName string,
+ testCase *optionsBoolTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithNetconfForceSelfClosingTags()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*netconf.Driver)
+
+ if !cmp.Equal(oo.ForceSelfClosingTags, testCase.b) {
+ t.Fatalf(
+ "%s: actual and preferred versions do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ForceSelfClosingTags,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithNetconfForceSelfClosingTags(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-netconf-force-self-closing-tags": {
+ description: "simple set option test",
+ b: true,
+ o: &netconf.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &network.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithNetconfForceSelfClosingTags(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/network.go b/driver/options/network.go
new file mode 100644
index 0000000..e03db02
--- /dev/null
+++ b/driver/options/network.go
@@ -0,0 +1,43 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithNetworkOnOpen allows for setting a function that is called after successfully opening a
+// connection, but before returning the connection to the user. Users may use this function to
+// "prepare" a device by doing things like disabling paging, or setting some terminal values. Note
+// that this is different from WithOnOpen which operates at the generic.Driver level rather than the
+// network.Driver level!
+func WithNetworkOnOpen(f func(d *network.Driver) error) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.OnOpen = f
+
+ return nil
+ }
+}
+
+// WithNetworkOnClose allows for setting a function that is called prior to closing the transport.
+// Users may wish to execute some command(s) that help gracefully close their connection prior to
+// terminating the transport. Note that this is different from WithOnClose which operates at the
+// generic.Driver level rather than the network.Driver level!
+func WithNetworkOnClose(f func(d *network.Driver) error) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.OnClose = f
+
+ return nil
+ }
+}
diff --git a/driver/options/network_test.go b/driver/options/network_test.go
new file mode 100644
index 0000000..fe2fae4
--- /dev/null
+++ b/driver/options/network_test.go
@@ -0,0 +1,118 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testWithNetworkOnOpen(
+ testName string,
+ testCase *optionsNetworkDriverOnXTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithNetworkOnOpen(testCase.f)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.Driver)
+
+ if oo.OnOpen == nil {
+ t.Fatalf(
+ "%s: on open function is *not* set",
+ testName,
+ )
+ }
+ }
+}
+
+func TestWithNetworkOnOpen(t *testing.T) {
+ cases := map[string]*optionsNetworkDriverOnXTestCase{
+ "set-on-open": {
+ description: "simple set option test",
+ f: func(d *network.Driver) error { return nil },
+ o: &network.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ f: func(d *network.Driver) error { return nil },
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithNetworkOnOpen(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithNetworkOnClose(
+ testName string,
+ testCase *optionsNetworkDriverOnXTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithNetworkOnClose(testCase.f)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.Driver)
+
+ if oo.OnClose == nil {
+ t.Fatalf(
+ "%s: on close function is *not* set",
+ testName,
+ )
+ }
+ }
+}
+
+func TestWithNetworkOnClose(t *testing.T) {
+ cases := map[string]*optionsNetworkDriverOnXTestCase{
+ "set-on-close": {
+ description: "simple set option test",
+ f: func(d *network.Driver) error { return nil },
+ o: &network.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ f: func(d *network.Driver) error { return nil },
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithNetworkOnClose(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/options_test.go b/driver/options/options_test.go
new file mode 100644
index 0000000..0ed375b
--- /dev/null
+++ b/driver/options/options_test.go
@@ -0,0 +1,98 @@
+package options_test
+
+import (
+ "flag"
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+type optionsBoolTestCase struct {
+ description string
+ b bool
+ o interface{}
+ isignored bool
+}
+
+type optionsIntTestCase struct {
+ description string
+ i int
+ o interface{}
+ isignored bool
+}
+
+type optionsStringTestCase struct {
+ description string
+ s string
+ o interface{}
+ isignored bool
+ iserr bool
+}
+
+type optionsStringSliceTestCase struct {
+ description string
+ ss []string
+ o interface{}
+ isignored bool
+}
+
+type optionsRegexpTestCase struct {
+ description string
+ p *regexp.Regexp
+ o interface{}
+ isignored bool
+}
+
+type optionsDurationTestCase struct {
+ description string
+ d time.Duration
+ o interface{}
+ isignored bool
+}
+
+type optionsGenericDriverOnXTestCase struct {
+ description string
+ f func(d *generic.Driver) error
+ o interface{}
+ isignored bool
+}
+
+type optionsNetworkDriverOnXTestCase struct {
+ description string
+ f func(d *network.Driver) error
+ o interface{}
+ isignored bool
+}
+
+type optionsNoneTestCase struct {
+ description string
+ o interface{}
+ isignored bool
+}
diff --git a/driver/options/privilege.go b/driver/options/privilege.go
new file mode 100644
index 0000000..912699b
--- /dev/null
+++ b/driver/options/privilege.go
@@ -0,0 +1,37 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithPrivilegeLevels allows for setting a custom map of privilege levels for a network driver.
+func WithPrivilegeLevels(privilegeLevels map[string]*network.PrivilegeLevel) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.PrivilegeLevels = privilegeLevels
+
+ return nil
+ }
+}
+
+// WithDefaultDesiredPriv modifies a network.Driver's default desired privilege level -- that is
+// the privilege level at which "show" commands are sent.
+func WithDefaultDesiredPriv(s string) util.Option {
+ return func(o interface{}) error {
+ d, ok := o.(*network.Driver)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ d.DefaultDesiredPriv = s
+
+ return nil
+ }
+}
diff --git a/driver/options/privilege_test.go b/driver/options/privilege_test.go
new file mode 100644
index 0000000..9848733
--- /dev/null
+++ b/driver/options/privilege_test.go
@@ -0,0 +1,152 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type optionsPrivilegeLevelsTestCase struct {
+ description string
+ privs network.PrivilegeLevels
+ o interface{}
+ isignored bool
+}
+
+func testWithPrivilegeLevels(
+ testName string,
+ testCase *optionsPrivilegeLevelsTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithPrivilegeLevels(testCase.privs)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.Driver)
+
+ if !cmp.Equal(oo.PrivilegeLevels["exec"].Pattern, testCase.privs["exec"].Pattern) {
+ t.Fatalf(
+ "%s: actual and expected privilege levels do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.PrivilegeLevels,
+ testCase.privs,
+ )
+ }
+ }
+}
+
+func TestWithPrivilegeLevels(t *testing.T) {
+ cases := map[string]*optionsPrivilegeLevelsTestCase{
+ "set-privilege-levels": {
+ description: "simple set option test",
+ privs: map[string]*network.PrivilegeLevel{
+ "exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}>$`,
+ Name: "exec",
+ PreviousPriv: "",
+ Deescalate: "",
+ Escalate: "",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ },
+ o: &network.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ privs: map[string]*network.PrivilegeLevel{
+ "exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}>$`,
+ Name: "exec",
+ PreviousPriv: "",
+ Deescalate: "",
+ Escalate: "",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ },
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPrivilegeLevels(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithDefaultDesiredPriv(
+ testName string,
+ testCase *optionsStringTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithDefaultDesiredPriv(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*network.Driver)
+
+ if !cmp.Equal(oo.DefaultDesiredPriv, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected default desired privilege levels do not "+
+ "match\nactual: %s\nexpected:%s",
+ testName,
+ oo.DefaultDesiredPriv,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithDefaultDesiredPriv(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-default-desired-priv": {
+ description: "simple set option test",
+ s: "exec",
+ o: &network.Driver{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "exec",
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithDefaultDesiredPriv(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/transport.go b/driver/options/transport.go
new file mode 100644
index 0000000..b7b1b5e
--- /dev/null
+++ b/driver/options/transport.go
@@ -0,0 +1,68 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithTransportReadSize sets the number of bytes each transport read operation should try to read.
+// The default value is 65535.
+func WithTransportReadSize(i int) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.ReadSize = i
+
+ return nil
+ }
+}
+
+// WithPort sets the TCP port for the connection, this defaults to 22 in all cases, so if using
+// Telnet make sure you update the port!
+func WithPort(i int) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.Port = i
+
+ return nil
+ }
+}
+
+// WithTermHeight sets the size to request for terminal (pty) height.
+func WithTermHeight(i int) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.TermHeight = i
+
+ return nil
+ }
+}
+
+// WithTermWidth sets the size to request for terminal (pty) width.
+func WithTermWidth(i int) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.Args)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.TermWidth = i
+
+ return nil
+ }
+}
diff --git a/driver/options/transport_test.go b/driver/options/transport_test.go
new file mode 100644
index 0000000..e5436f6
--- /dev/null
+++ b/driver/options/transport_test.go
@@ -0,0 +1,228 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testWithTransportReadSize(testName string, testCase *optionsIntTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithTransportReadSize(testCase.i)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.ReadSize, testCase.i) {
+ t.Fatalf(
+ "%s: actual and expected transport read sizes do not match\nactual: %d\nexpected:%d",
+ testName,
+ oo.ReadSize,
+ testCase.i,
+ )
+ }
+ }
+}
+
+func TestWithTransportReadSize(t *testing.T) {
+ cases := map[string]*optionsIntTestCase{
+ "set-transport-read-size": {
+ description: "simple set option test",
+ i: 99,
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ i: 99,
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTransportReadSize(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithPort(testName string, testCase *optionsIntTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithPort(testCase.i)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.Port, testCase.i) {
+ t.Fatalf(
+ "%s: actual and expected ports do not match\nactual: %d\nexpected:%d",
+ testName,
+ oo.Port,
+ testCase.i,
+ )
+ }
+ }
+}
+
+func TestWithPort(t *testing.T) {
+ cases := map[string]*optionsIntTestCase{
+ "set-port": {
+ description: "simple set option test",
+ i: 99,
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ i: 99,
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithPort(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithTermHeight(
+ testName string,
+ testCase *optionsIntTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithTermHeight(testCase.i)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.TermHeight, testCase.i) {
+ t.Fatalf(
+ "%s: actual and expected term heights do not match\nactual: %d\nexpected:%d",
+ testName,
+ oo.TermHeight,
+ testCase.i,
+ )
+ }
+ }
+}
+
+func TestWithTermHeight(t *testing.T) {
+ cases := map[string]*optionsIntTestCase{
+ "set-port": {
+ description: "simple set option test",
+ i: 99,
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ i: 99,
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTermHeight(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithTermWidth(
+ testName string,
+ testCase *optionsIntTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithTermWidth(testCase.i)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Args)
+
+ if !cmp.Equal(oo.TermWidth, testCase.i) {
+ t.Fatalf(
+ "%s: actual and expected term widths do not match\nactual: %d\nexpected:%d",
+ testName,
+ oo.TermWidth,
+ testCase.i,
+ )
+ }
+ }
+}
+
+func TestWithTermWidth(t *testing.T) {
+ cases := map[string]*optionsIntTestCase{
+ "set-term-width": {
+ description: "simple set option test",
+ i: 99,
+ o: &transport.Args{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ i: 99,
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithTermWidth(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/transportfile.go b/driver/options/transportfile.go
new file mode 100644
index 0000000..94cf568
--- /dev/null
+++ b/driver/options/transportfile.go
@@ -0,0 +1,22 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithFileTransportFile sets the file to open for File Transport object, this should only be used
+// for testing.
+func WithFileTransportFile(s string) util.Option {
+ return func(o interface{}) error {
+ t, ok := o.(*transport.File)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ t.F = s
+
+ return nil
+ }
+}
diff --git a/driver/options/transportfile_test.go b/driver/options/transportfile_test.go
new file mode 100644
index 0000000..6c8724f
--- /dev/null
+++ b/driver/options/transportfile_test.go
@@ -0,0 +1,66 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testFileTransportFile(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithFileTransportFile(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.File)
+
+ if !cmp.Equal(oo.F, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected transport files do not match\nactual: %s\nexpected:%s",
+ testName,
+ oo.F,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestFileTransportFile(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-file-transport-file": {
+ description: "simple set option test",
+ s: "some dumb file",
+ o: &transport.File{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "some dumb file",
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testFileTransportFile(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/transportssh.go b/driver/options/transportssh.go
new file mode 100644
index 0000000..59e64c1
--- /dev/null
+++ b/driver/options/transportssh.go
@@ -0,0 +1,151 @@
+package options
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithAuthPrivateKey sets the SSH key path and passphrase to use for SSH key based auth.
+func WithAuthPrivateKey(ks, ps string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.PrivateKeyPath = ks
+ a.PrivateKeyPassPhrase = ps
+
+ return nil
+ }
+}
+
+// WithAuthNoStrictKey disables strict SSH key checking.
+func WithAuthNoStrictKey() util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ a.StrictKey = false
+
+ return nil
+ }
+}
+
+// WithSSHConfigFile sets the ssh configuration file to use with SSH connections.
+func WithSSHConfigFile(s string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ sshF, err := util.ResolveFilePath(s)
+ if err != nil {
+ return util.ErrFileNotFoundError
+ }
+
+ a.ConfigFile = sshF
+
+ return nil
+ }
+}
+
+// WithSSHConfigFileSystem attempts to set the ssh configuration file to system default paths --
+// this will check ~/.ssh/config first, and if it does not find a config file there, will check
+// /etc/ssh/ssh_config. If neither path are resolvable an error is returned.
+func WithSSHConfigFileSystem() util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ var sshF string
+
+ var err error
+
+ sshF, err = util.ResolveFilePath("~/.ssh/config")
+ if err == nil {
+ a.ConfigFile = sshF
+
+ return nil
+ }
+
+ sshF, err = util.ResolveFilePath("/etc/ssh/ssh_config")
+ if err == nil {
+ a.ConfigFile = sshF
+
+ return nil
+ }
+
+ return fmt.Errorf(
+ "%w: failed resolving ssh config file",
+ util.ErrBadOption,
+ )
+ }
+}
+
+// WithSSHKnownHostsFile sets the ssh known hosts file to use with SSH connections.
+func WithSSHKnownHostsFile(s string) util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ sshF, err := util.ResolveFilePath(s)
+ if err != nil {
+ return util.ErrFileNotFoundError
+ }
+
+ a.KnownHostsFile = sshF
+
+ return nil
+ }
+}
+
+// WithSSHKnownHostsFileSystem attempts to set the ssh known hosts file to system default paths --
+// this will check ~/.ssh/known_hosts first, and if it does not find a config file there, will check
+// /etc/ssh/ssh_known_hosts. If neither path are resolvable an error is returned.
+func WithSSHKnownHostsFileSystem() util.Option {
+ return func(o interface{}) error {
+ a, ok := o.(*transport.SSHArgs)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ var sshF string
+
+ var err error
+
+ sshF, err = util.ResolveFilePath("~/.ssh/known_hosts")
+ if err == nil {
+ a.KnownHostsFile = sshF
+
+ return nil
+ }
+
+ sshF, err = util.ResolveFilePath("/etc/ssh/ssh_known_hosts")
+ if err == nil {
+ a.KnownHostsFile = sshF
+
+ return nil
+ }
+
+ return fmt.Errorf(
+ "%w: failed resolving ssh known hosts file",
+ util.ErrBadOption,
+ )
+ }
+}
diff --git a/driver/options/transportssh_test.go b/driver/options/transportssh_test.go
new file mode 100644
index 0000000..8baf0bc
--- /dev/null
+++ b/driver/options/transportssh_test.go
@@ -0,0 +1,280 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type optionsWithTwoStringTestCase struct {
+ description string
+ ks string
+ ps string
+ o interface{}
+ isignored bool
+}
+
+func testWithAuthPrivateKey(
+ testName string,
+ testCase *optionsWithTwoStringTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthPrivateKey(testCase.ks, testCase.ps)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.SSHArgs)
+
+ if !cmp.Equal(oo.PrivateKeyPath, testCase.ks) {
+ t.Fatalf(
+ "%s: actual and expected private key paths do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.PrivateKeyPath,
+ testCase.ks,
+ )
+ }
+
+ if !cmp.Equal(oo.PrivateKeyPassPhrase, testCase.ps) {
+ t.Fatalf(
+ "%s: actual and expected private key paths do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.PrivateKeyPath,
+ testCase.ks,
+ )
+ }
+ }
+}
+
+func TestWithAuthPrivateKey(t *testing.T) {
+ cases := map[string]*optionsWithTwoStringTestCase{
+ "set-transport-auth-private-key": {
+ description: "simple set option test",
+ ks: "/key/path/thingy",
+ ps: "keypassphrase",
+ o: &transport.SSHArgs{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ks: "/key/path/thingy",
+ ps: "keypassphrase",
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthPrivateKey(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithAuthNoStrictKey(testName string, testCase *optionsBoolTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithAuthNoStrictKey()(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.SSHArgs)
+
+ if !cmp.Equal(oo.StrictKey, testCase.b) {
+ t.Fatalf(
+ "%s: actual and expected strict keys do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.StrictKey,
+ testCase.b,
+ )
+ }
+ }
+}
+
+func TestWithAuthNoStrictKey(t *testing.T) {
+ cases := map[string]*optionsBoolTestCase{
+ "set-transport-auth-strict-key": {
+ description: "simple set option test",
+ b: false,
+ o: &transport.SSHArgs{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ b: true,
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithAuthNoStrictKey(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithSSHConfigFile(testName string, testCase *optionsStringTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithSSHConfigFile(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ if errors.Is(err, util.ErrIgnoredOption) && testCase.isignored {
+ return
+ }
+
+ if !errors.Is(err, util.ErrIgnoredOption) && testCase.iserr {
+ return
+ }
+
+ t.Fatalf("%s: encountered an error but we should not", testName)
+ }
+
+ oo, _ := testCase.o.(*transport.SSHArgs)
+
+ if !cmp.Equal(oo.ConfigFile, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected resolved ssh config files do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ConfigFile,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithSSHConfigFile(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-ssh-config-file": {
+ description: "simple set option test",
+ s: "transportssh_test.go", // option resolves the file, so make it a real one
+ o: &transport.SSHArgs{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "transportssh_test.go", // option resolves the file, so make it a real one
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ "set-ssh-config-file-cant-resolve": {
+ description: "option fails to resolves provided file path",
+ s: "not/a/real/file",
+ o: &transport.SSHArgs{},
+ isignored: false,
+ iserr: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithSSHConfigFile(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testWithSSHKnownHostsFile(
+ testName string,
+ testCase *optionsStringTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithSSHKnownHostsFile(testCase.s)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ if errors.Is(err, util.ErrIgnoredOption) && testCase.isignored {
+ return
+ }
+
+ if !errors.Is(err, util.ErrIgnoredOption) && testCase.iserr {
+ return
+ }
+
+ t.Fatalf("%s: encountered an error but we should not", testName)
+ }
+
+ oo, _ := testCase.o.(*transport.SSHArgs)
+
+ if !cmp.Equal(oo.KnownHostsFile, testCase.s) {
+ t.Fatalf(
+ "%s: actual and expected resolved known hosts files do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.KnownHostsFile,
+ testCase.s,
+ )
+ }
+ }
+}
+
+func TestWithSSHKnownHostsFile(t *testing.T) {
+ cases := map[string]*optionsStringTestCase{
+ "set-ssh-known-hosts-file": {
+ description: "simple set option test",
+ s: "transportssh_test.go", // option resolves the file, so make it a real one
+ o: &transport.SSHArgs{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ s: "transportssh_test.go", // option resolves the file, so make it a real one
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ "set-ssh-known-hosts-file-cant-resolve": {
+ description: "option fails to resolves provided file path",
+ s: "not/a/real/file",
+ o: &transport.SSHArgs{},
+ isignored: false,
+ iserr: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testWithSSHKnownHostsFile(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+// not testing system ssh config file option yet due to not wanting to mess with patching
+// file paths somehow (does something like pyfakefs exist in go?)
diff --git a/driver/options/transportstandard.go b/driver/options/transportstandard.go
new file mode 100644
index 0000000..1dbae09
--- /dev/null
+++ b/driver/options/transportstandard.go
@@ -0,0 +1,38 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithStandardTransportExtraCiphers extends the list of ciphers supported by the standard
+// transport.
+func WithStandardTransportExtraCiphers(l []string) util.Option {
+ return func(o interface{}) error {
+ t, ok := o.(*transport.Standard)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ t.ExtraCiphers = l
+
+ return nil
+ }
+}
+
+// WithStandardTransportExtraKexs extends the list of kext (key exchange algorithms) supported by
+// the standard transport.
+func WithStandardTransportExtraKexs(l []string) util.Option {
+ return func(o interface{}) error {
+ t, ok := o.(*transport.Standard)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ t.ExtraKexs = l
+
+ return nil
+ }
+}
diff --git a/driver/options/transportstandard_test.go b/driver/options/transportstandard_test.go
new file mode 100644
index 0000000..80816cf
--- /dev/null
+++ b/driver/options/transportstandard_test.go
@@ -0,0 +1,123 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testStandardTransportExtraCiphers(
+ testName string,
+ testCase *optionsStringSliceTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithStandardTransportExtraCiphers(testCase.ss)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Standard)
+
+ if !cmp.Equal(oo.ExtraCiphers, testCase.ss) {
+ t.Fatalf(
+ "%s: actual and expected transport extra ciphers do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ExtraCiphers,
+ testCase.ss,
+ )
+ }
+ }
+}
+
+func TestStandardTransportExtraCiphers(t *testing.T) {
+ cases := map[string]*optionsStringSliceTestCase{
+ "set-system-transport-open-args": {
+ description: "simple set option test",
+ ss: []string{"some", "extra", "ciphers"},
+ o: &transport.Standard{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ss: []string{"some", "extra", "ciphers"},
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStandardTransportExtraCiphers(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testStandardTransportExtraKexs(
+ testName string,
+ testCase *optionsStringSliceTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithStandardTransportExtraKexs(testCase.ss)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.Standard)
+
+ if !cmp.Equal(oo.ExtraKexs, testCase.ss) {
+ t.Fatalf(
+ "%s: actual and expected transport extra kexs do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ExtraKexs,
+ testCase.ss,
+ )
+ }
+ }
+}
+
+func TestStandardTransportExtraKexs(t *testing.T) {
+ cases := map[string]*optionsStringSliceTestCase{
+ "set-system-transport-open-args": {
+ description: "simple set option test",
+ ss: []string{"some", "extra", "kexs"},
+ o: &transport.Standard{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ss: []string{"some", "extra", "kexs"},
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStandardTransportExtraKexs(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/driver/options/transportsystem.go b/driver/options/transportsystem.go
new file mode 100644
index 0000000..534a006
--- /dev/null
+++ b/driver/options/transportsystem.go
@@ -0,0 +1,22 @@
+package options
+
+import (
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithSystemTransportOpenArgs sets the ExtraArgs value of the System Transport, these arguments are
+// appended to the System transport open command (the command that spawns the connection).
+func WithSystemTransportOpenArgs(l []string) util.Option {
+ return func(o interface{}) error {
+ t, ok := o.(*transport.System)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ t.ExtraArgs = append(t.ExtraArgs, l...)
+
+ return nil
+ }
+}
diff --git a/driver/options/transportsystem_test.go b/driver/options/transportsystem_test.go
new file mode 100644
index 0000000..5e63ab6
--- /dev/null
+++ b/driver/options/transportsystem_test.go
@@ -0,0 +1,69 @@
+package options_test
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func testSystemTransportOpenArgs(
+ testName string,
+ testCase *optionsStringSliceTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ err := options.WithSystemTransportOpenArgs(testCase.ss)(testCase.o)
+ if err != nil {
+ if errors.Is(err, util.ErrIgnoredOption) && !testCase.isignored {
+ t.Fatalf(
+ "%s: option should be ignored, but returned different error",
+ testName,
+ )
+ }
+
+ return
+ }
+
+ oo, _ := testCase.o.(*transport.System)
+
+ if !cmp.Equal(oo.ExtraArgs, testCase.ss) {
+ t.Fatalf(
+ "%s: actual and expected transport extra args do not match\nactual: %v\nexpected:%v",
+ testName,
+ oo.ExtraArgs,
+ testCase.ss,
+ )
+ }
+ }
+}
+
+func TestSystemTransportOpenArgs(t *testing.T) {
+ cases := map[string]*optionsStringSliceTestCase{
+ "set-system-transport-open-args": {
+ description: "simple set option test",
+ ss: []string{"some", "neat", "args"},
+ o: &transport.System{},
+ isignored: false,
+ },
+ "ignored": {
+ description: "skipped due to ignored type",
+ ss: []string{"some", "neat", "args"},
+ o: &generic.Driver{},
+ isignored: true,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testSystemTransportOpenArgs(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..6f85502
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,9 @@
+Examples
+========
+
+This directory contains a handful of examples of basic usage of scrapligo. Note that scrapligo
+follows a fairly similar style/approach that it's Python predecessor laid out -- there are
+"generic" drivers, "network" drivers, and "netconf" drivers. You should take a look at the
+"generic" drivers section first, even if you know you only want to do NETCONF operations -- the
+reason for this is the network and netconf drivers build on/expand on the generic driver so many
+of the options or settings you may care about are showcased in the generic driver examples.
\ No newline at end of file
diff --git a/examples/generic_driver/basics/README.md b/examples/generic_driver/basics/README.md
new file mode 100644
index 0000000..a29a4f4
--- /dev/null
+++ b/examples/generic_driver/basics/README.md
@@ -0,0 +1,9 @@
+Basics
+======
+
+This example shows the basics of creating a `generic.Driver` object and sending some inputs and
+commands.
+
+Note that by default scrapligo does strict SSH key checking, in most lab scenarios you probably
+want to disable this by passing the `options.WithAuthNoStrictKey()` option to the `New` driver
+function.
diff --git a/examples/base_driver/main.go b/examples/generic_driver/basics/main.go
similarity index 56%
rename from examples/base_driver/main.go
rename to examples/generic_driver/basics/main.go
index 0f3aa88..b860951 100644
--- a/examples/base_driver/main.go
+++ b/examples/generic_driver/basics/main.go
@@ -3,46 +3,53 @@ package main
import (
"fmt"
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
"github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/driver/base"
)
func main() {
- d, err := base.NewDriver(
+ d, err := generic.NewDriver(
"sandbox-iosxe-latest-1.cisco.com",
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
)
-
if err != nil {
fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
}
err = d.Open()
if err != nil {
fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
}
defer d.Close()
// fetch the prompt
- prompt, err := d.Channel.GetPrompt()
+ prompt, err := d.GetPrompt()
if err != nil {
fmt.Printf("failed to get prompt; error: %+v\n", err)
- } else {
- fmt.Printf("found prompt: %s\n\n\n", prompt)
+
+ return
}
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+
// send some input
- // at the "base" level there are no convenience wrappers around the channel supporting options,
- // so you have to specify all the parameters when using the channel directly
- output, err := d.Channel.SendInput("show version | i IOS", true, false, -1)
+ output, err := d.Channel.SendInput("show version | i IOS")
if err != nil {
fmt.Printf("failed to send input to device; error: %+v\n", err)
- } else {
- fmt.Printf("output received (SendInput):\n %s\n\n\n", output)
+
+ return
}
+ fmt.Printf("output received (SendInput):\n %s\n\n\n", output)
+
// send an interactive input
// SendInteractive expects a slice of `SendInteractiveEvent` objects
events := make([]*channel.SendInteractiveEvent, 2)
@@ -57,16 +64,19 @@ func main() {
HideInput: false,
}
- // you can access the channel directly, however there are no convenience wrappers around the
- // channel supporting options, so you have to specify all the parameters when using it directly
- interactiveOutput, err := d.Channel.SendInteractive(events, nil, -1)
+ // note that there is a convenience wrapper around send interactive in the generic driver as
+ // well, so you could simply do `d.SendInteractive` here rather than poking the channel directly
+ interactiveOutput, err := d.Channel.SendInteractive(events)
if err != nil {
fmt.Printf("failed to send interactive input to device; error: %+v\n", err)
- } else {
- fmt.Printf("output received (SendInteractive):\n %s\n\n\n", interactiveOutput)
+
+ return
}
- // send a command -- as this is "base" there will have been no paging disabling, so have to
+ fmt.Printf("output received (SendInteractive):\n %s\n\n\n", interactiveOutput)
+
+ // send a command -- as this is a "base" driver (meaning there is no context of the type of
+ // device we are connecting to) there will have been no paging disabling, so have to
// either disable paging yourself or send a command that will not make the device page the
// output!
r, err := d.SendCommand("show version | i uptime")
@@ -74,9 +84,10 @@ func main() {
fmt.Printf("failed to send command; error: %+v\n", err)
return
}
+
fmt.Printf(
"sent command '%s', output received (SendCommand):\n %s\n\n\n",
- r.ChannelInput,
+ r.Input,
r.Result,
)
}
diff --git a/examples/generic_driver/channel_logging/README.md b/examples/generic_driver/channel_logging/README.md
new file mode 100644
index 0000000..360fd97
--- /dev/null
+++ b/examples/generic_driver/channel_logging/README.md
@@ -0,0 +1,6 @@
+Channel Logging
+===============
+
+It is possible to log all reads/writes to the underlying transport via the ChannelLog parameter.
+Pass an `io.Writer` object to your driver creation, and you can read the bytes back out of that
+object when you are done with the connection.
\ No newline at end of file
diff --git a/examples/network_driver/channellog/main.go b/examples/generic_driver/channel_logging/main.go
similarity index 62%
rename from examples/network_driver/channellog/main.go
rename to examples/generic_driver/channel_logging/main.go
index 052daf9..f3b382d 100644
--- a/examples/network_driver/channellog/main.go
+++ b/examples/generic_driver/channel_logging/main.go
@@ -4,46 +4,47 @@ import (
"bytes"
"fmt"
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
)
func main() {
// WithChannelLog accepts an io.Writer type object, create and pass it to the driver creation
var channelLog bytes.Buffer
- // use the NewCoreDriver factory and pass in a platform argument
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
- base.WithChannelLog(&channelLog),
+ d, err := generic.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ options.WithChannelLog(&channelLog),
)
-
if err != nil {
fmt.Printf("failed to create driver; error: %+v\n", err)
+
return
}
err = d.Open()
if err != nil {
fmt.Printf("failed to open driver; error: %+v\n", err)
+
return
}
+
defer d.Close()
prompt, err := d.GetPrompt()
if err != nil {
fmt.Printf("failed to get prompt; error: %+v\n", err)
+
return
}
+
fmt.Printf("found prompt: %s\n\n\n", prompt)
// We can then read and print out the channel log data like normal
- b := make([]byte, 65535)
+ b := make([]byte, channelLog.Len())
_, _ = channelLog.Read(b)
fmt.Printf("Channel log output:\n%s", b)
}
diff --git a/examples/generic_driver/default_logging/README.md b/examples/generic_driver/default_logging/README.md
new file mode 100644
index 0000000..5fda901
--- /dev/null
+++ b/examples/generic_driver/default_logging/README.md
@@ -0,0 +1,7 @@
+Default Logging
+===============
+
+The `options.WithDefaultLogger` function applies a simple logging instance with a single log
+emitter of `log.Print` at the "info" level. If you wanted to do fancier things with logging you
+can create your own `logging.Instance` at whatever log level and with whatever log emitter
+functions you want rather than using this default logger.
\ No newline at end of file
diff --git a/examples/generic_driver/default_logging/main.go b/examples/generic_driver/default_logging/main.go
new file mode 100644
index 0000000..3884b32
--- /dev/null
+++ b/examples/generic_driver/default_logging/main.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+)
+
+func main() {
+ d, err := generic.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ // the WithDefaultLogger option applies a simple log.Print logger at level info
+ options.WithDefaultLogger(),
+ )
+ if err != nil {
+ fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ r, err := d.SendCommand("show version | i Version")
+ if err != nil {
+ fmt.Printf("failed to run command; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("got some output: %s\n\n\n", r.Result)
+}
diff --git a/examples/generic_driver/generic_on_open_and_close/README.md b/examples/generic_driver/generic_on_open_and_close/README.md
new file mode 100644
index 0000000..db86ac1
--- /dev/null
+++ b/examples/generic_driver/generic_on_open_and_close/README.md
@@ -0,0 +1,16 @@
+On Open/Close
+=============
+
+Sometimes you may have some tasks that should be done upon opening a connection or prior to
+closing a connection. The obvious example of this is disabling paging (ex: "term len 0"), but
+there could be other tasks such as responding to some EULA/prompt, entering a special config
+mode, disabling console logging, or gracefully tearing down a user session (this last one being
+an "on close" type of thing of course). The `OnOpen` and `OnClose` options give you the ability
+to pass a function that accepts a single argument of the driver you are creating, thereby
+giving you to access the "send" methods allowing you to send "term len 0" or whatever other
+inputs you need.
+
+You could of course always accomplish these types of "on open" and "on close" activities
+"normally" by simply sending commands/inputs after driver creation, but the goal of these
+arguments are to give a place to handle "boilerplate" type tasks without having to muddle up
+your programs with these boring details.
\ No newline at end of file
diff --git a/examples/generic_driver/generic_on_open_and_close/main.go b/examples/generic_driver/generic_on_open_and_close/main.go
new file mode 100644
index 0000000..1679cef
--- /dev/null
+++ b/examples/generic_driver/generic_on_open_and_close/main.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+)
+
+// custom "on X" functions (on open/close) accept a single argument of a pointer to a generic.Driver
+// you can then access the driver to run any kind of setup/tear-down commands that you may need to
+// prepare your device.
+func customOnOpen(d *generic.Driver) error {
+ _, err := d.SendCommand("terminal length 0")
+ if err != nil {
+ return err
+ }
+
+ _, err = d.SendCommand("terminal width 512")
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func main() {
+ d, err := generic.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ // apply your custom OnOpen function with the `WithOnOpen` option
+ options.WithOnOpen(customOnOpen),
+ )
+ if err != nil {
+ fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ prompt, err := d.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+}
diff --git a/examples/generic_driver/interactive_prompts/README.md b/examples/generic_driver/interactive_prompts/README.md
new file mode 100644
index 0000000..bc4665f
--- /dev/null
+++ b/examples/generic_driver/interactive_prompts/README.md
@@ -0,0 +1,10 @@
+Interactive Prompts
+===================
+
+Sometimes you may need to "interact" with devices -- or put another way, you may need to respond
+to a prompt from the device. The `SendInteractive` method is used to handle these things. This
+method accepts a slice of `channel.SendInteractiveEvent` which define the input, the expected
+output, and whether the device will "hide" the input (as is the case with password prompts).
+This is a fairly simple/dumb method, but works well enough. If you need a more
+elaborate/advanced way to handle a multitude of prompts/outputs you may want to check out the
+`SendWithCallbacks` method instead.
\ No newline at end of file
diff --git a/examples/network_driver/interactive/main.go b/examples/generic_driver/interactive_prompts/main.go
similarity index 66%
rename from examples/network_driver/interactive/main.go
rename to examples/generic_driver/interactive_prompts/main.go
index b5637f8..82ec9b9 100644
--- a/examples/network_driver/interactive/main.go
+++ b/examples/generic_driver/interactive_prompts/main.go
@@ -3,32 +3,32 @@ package main
import (
"fmt"
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
+
"github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
)
func main() {
- // use the NewCoreDriver factory and pass in a platform argument
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
+ d, err := generic.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
)
-
if err != nil {
fmt.Printf("failed to create driver; error: %+v\n", err)
+
return
}
err = d.Open()
if err != nil {
fmt.Printf("failed to open driver; error: %+v\n", err)
+
return
}
+
defer d.Close()
events := []*channel.SendInteractiveEvent{
@@ -47,7 +47,9 @@ func main() {
r, err := d.SendInteractive(events)
if err != nil {
fmt.Printf("failed to get prompt; error: %+v\n", err)
+
return
}
+
fmt.Printf("interact response:\n%s\n", r.Result)
}
diff --git a/examples/generic_driver/textfsm_integration/README.md b/examples/generic_driver/textfsm_integration/README.md
new file mode 100644
index 0000000..b935023
--- /dev/null
+++ b/examples/generic_driver/textfsm_integration/README.md
@@ -0,0 +1,6 @@
+(Go)TextFSM Integration
+=======================
+
+scrapligo `Response` objects contain a method `TextFsmParse`. This method accepts a file or URL
+path to a template to use to parse the captured response output. This can be used to turn
+"unstructured" data into "structured" data.
\ No newline at end of file
diff --git a/examples/network_driver/textfsm/cisco_ios_show_version.textfsm b/examples/generic_driver/textfsm_integration/cisco_ios_show_version.textfsm
similarity index 100%
rename from examples/network_driver/textfsm/cisco_ios_show_version.textfsm
rename to examples/generic_driver/textfsm_integration/cisco_ios_show_version.textfsm
diff --git a/examples/network_driver/textfsm/main.go b/examples/generic_driver/textfsm_integration/main.go
similarity index 73%
rename from examples/network_driver/textfsm/main.go
rename to examples/generic_driver/textfsm_integration/main.go
index db96dc7..2661ee8 100644
--- a/examples/network_driver/textfsm/main.go
+++ b/examples/generic_driver/textfsm_integration/main.go
@@ -4,48 +4,51 @@ import (
"flag"
"fmt"
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/options"
)
func main() {
// File from https://github.com/networktocode/ntc-templates/blob/master/ntc_templates/templates/cisco_ios_show_version.textfsm
arg := flag.String(
"file",
- "examples/network_driver/textfsm/cisco_ios_show_version.textfsm",
+ "examples/generic_driver/textfsm_integration/cisco_ios_show_version.textfsm",
"argument from user",
)
flag.Parse()
- d, err := core.NewCoreDriver(
+ d, err := generic.NewDriver(
"sandbox-iosxe-latest-1.cisco.com",
- "cisco_iosxe",
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
)
-
if err != nil {
fmt.Printf("failed to create driver; error: %+v\n", err)
+
return
}
err = d.Open()
if err != nil {
fmt.Printf("failed to open driver; error: %+v\n", err)
+
return
}
+
defer d.Close()
r, err := d.SendCommand("show version")
if err != nil {
fmt.Printf("failed to send command; error: %+v\n", err)
+
return
}
parsedOut, err := r.TextFsmParse(*arg)
if err != nil {
fmt.Printf("failed to parse command; error: %+v\n", err)
+
return
}
diff --git a/examples/netconf/main.go b/examples/netconf/main.go
deleted file mode 100644
index 54c76e6..0000000
--- a/examples/netconf/main.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/netconf"
-)
-
-func main() {
-
- d, _ := netconf.NewNetconfDriver(
- "localhost",
- base.WithPort(21830),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("vrnetlab"),
- base.WithAuthPassword("VR-netlab9"),
- )
-
- err := d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- r, err := d.GetConfig("running")
- if err != nil {
- fmt.Printf("failed to get config; error: %+v\n", err)
- return
- }
-
- fmt.Printf("Get Config Response:\n%s\n", r.Result)
-
- filter := "" +
- "\n" +
- " \n" +
- " \n" +
- " GigabitEthernet1\n" +
- " \n" +
- " \n" +
- " "
- r, err = d.Get(netconf.WithNetconfFilter(filter))
- if err != nil {
- fmt.Printf("failed to get with filter; error: %+v\n", err)
- return
- }
-
- fmt.Printf("Get Response: %s\n", r.Result)
-
- edit := "" +
- "\n" +
- " \n" +
- " \n" +
- " GigabitEthernet1 \n" +
- " scrapliGO was here! \n" +
- " \n" +
- " \n" +
- " "
- r, err = d.EditConfig("running", edit)
- if err != nil {
- fmt.Printf("failed to edit config; error: %+v\n", err)
- return
- }
-
- fmt.Printf("Edit Config Response: %s\n", r.Result)
-
- _, _ = d.Commit()
-}
diff --git a/examples/netconf_driver/basics/README.md b/examples/netconf_driver/basics/README.md
new file mode 100644
index 0000000..73bce7a
--- /dev/null
+++ b/examples/netconf_driver/basics/README.md
@@ -0,0 +1,8 @@
+Basics
+======
+
+The `netconf.Driver` is, as you may expect, a driver for doing NETCONF things. This driver
+implements many of the standard NETCONF RPCs, including GetConfig, Get, Commit, Discard, etc..
+
+Note that the default port is always 22 -- so if your NETCONF server is listening on 830 (or
+anything else for that matter), make sure you pass the `WithPort` option during driver creation.
\ No newline at end of file
diff --git a/examples/netconf_driver/basics/main.go b/examples/netconf_driver/basics/main.go
new file mode 100644
index 0000000..d258422
--- /dev/null
+++ b/examples/netconf_driver/basics/main.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/netconf"
+ "github.com/scrapli/scrapligo/driver/options"
+)
+
+func main() {
+ d, err := netconf.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ options.WithPort(830),
+ )
+ if err != nil {
+ fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ r, err := d.GetConfig("running")
+ if err != nil {
+ fmt.Printf("failed executing GetConfig; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("Config result: %s", r.Result)
+}
diff --git a/examples/network_driver/basics/README.md b/examples/network_driver/basics/README.md
new file mode 100644
index 0000000..d2710bc
--- /dev/null
+++ b/examples/network_driver/basics/README.md
@@ -0,0 +1,14 @@
+Basics
+======
+
+This example shows the basics of creating a `network.Driver` object and sending some inputs and
+commands.
+
+Note that by default scrapligo does strict SSH key checking, in most lab scenarios you probably
+want to disable this by passing the `options.WithAuthNoStrictKey()` option to the `New` driver
+function.
+
+Unlike the "generic" driver, the "network" driver has an additional requirement that you *must*
+pass a default desired privilege level as well as a map of privilege levels. In *most* cases you
+will not be instantiating a network driver directly, but instead doing so via the platforms
+factory (see the platforms example directory).
diff --git a/examples/network_driver/basics/main.go b/examples/network_driver/basics/main.go
new file mode 100644
index 0000000..950385d
--- /dev/null
+++ b/examples/network_driver/basics/main.go
@@ -0,0 +1,125 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/channel"
+)
+
+func main() {
+ d, err := network.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ // network drivers *must* have a default desired privilege level and privileges set!
+ options.WithDefaultDesiredPriv("privilege-exec"),
+ options.WithPrivilegeLevels(map[string]*network.PrivilegeLevel{
+ "exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}>$`,
+ Name: "exec",
+ PreviousPriv: "",
+ Deescalate: "",
+ Escalate: "",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ "privilege-exec": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}#$`,
+ Name: "privilege-exec",
+ PreviousPriv: "exec",
+ Deescalate: "disable",
+ Escalate: "enable",
+ EscalateAuth: true,
+ EscalatePrompt: `(?im)^(?:enable\s){0,1}password:\s?$`,
+ },
+ "configuration": {
+ Pattern: `(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$`,
+ NotContains: []string{"tcl)"},
+ Name: "configuration",
+ PreviousPriv: "privilege-exec",
+ Deescalate: "end",
+ Escalate: "configure terminal",
+ EscalateAuth: false,
+ EscalatePrompt: "",
+ },
+ }),
+ )
+ if err != nil {
+ fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ // fetch the prompt
+ prompt, err := d.Channel.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+
+ // send some input
+ output, err := d.Channel.SendInput("show version | i IOS")
+ if err != nil {
+ fmt.Printf("failed to send input to device; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("output received (SendInput):\n %s\n\n\n", output)
+
+ // send an interactive input
+ // SendInteractive expects a slice of `SendInteractiveEvent` objects
+ events := make([]*channel.SendInteractiveEvent, 2)
+ events[0] = &channel.SendInteractiveEvent{
+ ChannelInput: "clear logging",
+ ChannelResponse: "[confirm]",
+ HideInput: false,
+ }
+ events[1] = &channel.SendInteractiveEvent{
+ ChannelInput: "",
+ ChannelResponse: "#",
+ HideInput: false,
+ }
+
+ interactiveOutput, err := d.SendInteractive(events)
+ if err != nil {
+ fmt.Printf("failed to send interactive input to device; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("output received (SendInteractive):\n %s\n\n\n", interactiveOutput.Result)
+
+ // send a command -- as this is a "base" driver (meaning there is no context of the type of
+ // device we are connecting to) there will have been no paging disabling, so have to
+ // either disable paging yourself or send a command that will not make the device page the
+ // output!
+ r, err := d.SendCommand("show version | i uptime")
+ if err != nil {
+ fmt.Printf("failed to send command; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf(
+ "sent command '%s', output received (SendCommand):\n %s\n\n\n",
+ r.Input,
+ r.Result,
+ )
+}
diff --git a/examples/network_driver/custom_onopen/main.go b/examples/network_driver/custom_onopen/main.go
deleted file mode 100644
index 97072aa..0000000
--- a/examples/network_driver/custom_onopen/main.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-func customOnOpen(d *network.Driver) error {
- err := d.AcquirePriv(d.DefaultDesiredPriv)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal length 0", nil)
- if err != nil {
- return err
- }
-
- _, err = d.SendCommand("terminal width 512", nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func main() {
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
-
- // because of mostly copying python into go and being much less flexible w/ some magic typing
- // there is currently no way to pass an on open with the normal variadic args (because those
- // are *driver* options, not *network driver* options) -- probably this can be relaxed with
- // generics soon? in any case, you can still update/assign a custom on open function like so:
- d.OnOpen = customOnOpen
-
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- prompt, err := d.GetPrompt()
- if err != nil {
- fmt.Printf("failed to get prompt; error: %+v\n", err)
- return
- }
- fmt.Printf("found prompt: %s\n\n\n", prompt)
-}
diff --git a/examples/network_driver/factory/main.go b/examples/network_driver/factory/main.go
deleted file mode 100644
index 7dc438f..0000000
--- a/examples/network_driver/factory/main.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
-)
-
-func main() {
- // use the NewCoreDriver factory and pass in a platform argument
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
-
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- prompt, err := d.GetPrompt()
- if err != nil {
- fmt.Printf("failed to get prompt; error: %+v\n", err)
- return
- }
- fmt.Printf("found prompt: %s\n\n\n", prompt)
-}
diff --git a/examples/network_driver/load_config/main.go b/examples/network_driver/load_config/main.go
deleted file mode 100644
index 730ae26..0000000
--- a/examples/network_driver/load_config/main.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package main
-
-import (
- "fmt"
- "io/ioutil"
-
- "github.com/scrapli/scrapligo/cfg"
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
-)
-
-//Pushes config from a file to a file on device and
-//loads it to the device. Cleans up afterwords.
-func main() {
- d, err := core.NewCoreDriver(
- "hostname",
- "juniper_junos",
- base.WithPort(22),
- base.WithAuthUsername("root"),
- base.WithAuthPassword("password"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
-
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- c, err := cfg.NewCfgDriver(
- d,
- "juniper_junos",
- )
- if err != nil {
- fmt.Printf("failed to create cfg driver; error: %+v\n", err)
- return
- }
-
- b, err := ioutil.ReadFile("sample.conf")
- if err != nil {
- fmt.Print(err)
- }
- fmt.Print(string(b))
-
- prepareErr := c.Prepare()
- if prepareErr != nil {
- fmt.Printf("failed running prepare method: %v", prepareErr)
- }
-
- _, err = c.LoadConfig(
- string(b),
- false, //don't load replace. Load merge/set instead
- )
- if err != nil {
- fmt.Printf("failed to load config; error: %+v\n", err)
- return
- }
-
- _, err = c.CommitConfig()
- if err != nil {
- fmt.Printf("failed to commit config; error: %+v\n", err)
- return
- }
- fmt.Printf("Done loading config\n")
-}
diff --git a/examples/network_driver/load_config/sample.conf b/examples/network_driver/load_config/sample.conf
deleted file mode 100644
index 70ae28f..0000000
--- a/examples/network_driver/load_config/sample.conf
+++ /dev/null
@@ -1,10 +0,0 @@
-interfaces {
- ge-0/0/1 {
- unit 0 {
- family inet {
- address 192.168.100.1/24;
- }
- }
- }
-}
-
diff --git a/examples/network_driver/logging/main.go b/examples/network_driver/logging/main.go
deleted file mode 100644
index 04f2821..0000000
--- a/examples/network_driver/logging/main.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
- "github.com/scrapli/scrapligo/logging"
-)
-
-func main() {
- // logging can be enabled by passing a function that accepts a variadic of interface, so
- // basically you can pass things like `log.Print` or `logrus.Error` etc.. This applies to both
- // error and debug logging.
- logging.SetDebugLogger(log.Print)
-
- // use the NewCoreDriver factory and pass in a platform argument
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
-
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- prompt, err := d.GetPrompt()
- if err != nil {
- fmt.Printf("failed to get prompt; error: %+v\n", err)
- return
- }
- fmt.Printf("found prompt: %s\n\n\n", prompt)
-}
diff --git a/examples/network_driver/network_on_open_and_close/README.md b/examples/network_driver/network_on_open_and_close/README.md
new file mode 100644
index 0000000..a4cafe1
--- /dev/null
+++ b/examples/network_driver/network_on_open_and_close/README.md
@@ -0,0 +1,7 @@
+On Open/Close
+=============
+
+The "network" layer `OnOpen` and `OnClose` attributes function exactly the same as the "generic"
+layer functions, with the exception that the driver value passed into the user provided
+functions is of type `network.Driver` rather than `generic.Driver`. Typically, users will only
+use one of the two "flavors" of OnX functions, but if you wanted to you could of course use both!
\ No newline at end of file
diff --git a/examples/network_driver/network_on_open_and_close/main.go b/examples/network_driver/network_on_open_and_close/main.go
new file mode 100644
index 0000000..011d458
--- /dev/null
+++ b/examples/network_driver/network_on_open_and_close/main.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+)
+
+// custom "on X" functions (on open/close) accept a single argument of a pointer to a ntework.Driver
+// you can then access the driver to run any kind of setup/tear-down commands that you may need to
+// prepare your device.
+func customOnOpen(d *network.Driver) error {
+ _, err := d.SendCommand("terminal length 0")
+ if err != nil {
+ return err
+ }
+
+ _, err = d.SendCommand("terminal width 512")
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func main() {
+ d, err := network.NewDriver(
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ // apply your custom OnOpen function with the `WithNetworkOnOpen` option
+ options.WithNetworkOnOpen(customOnOpen),
+ )
+ if err != nil {
+ fmt.Printf("failed to create driver; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ prompt, err := d.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+}
diff --git a/examples/network_driver/platforms/README.md b/examples/network_driver/platforms/README.md
new file mode 100644
index 0000000..e554308
--- /dev/null
+++ b/examples/network_driver/platforms/README.md
@@ -0,0 +1,40 @@
+Platforms
+=========
+
+scrapli and scrapligo have always had support for a few popular network operating systems.
+"Support" in this context means that there have been some sane default settings relevant to
+these platforms -- things like sane privilege levels, proper disabling of paging, and a default
+understanding of the "normal" privilege level for sending "show" commands. These platforms are:
+
+- Cisco IOSXE
+- Cisco IOSXR
+- Cisco NXOS
+- Arista EOS
+- Juniper JunOS
+
+In earlier versions of scrapligo there was a driver layer called "core". At this layer there
+were structs representing each of these network operating systems. This is/was perfectly
+workable, however it was not very flexible. Scrapligo now supports platform definition via YAML
+or JSON, and embeds YAML platform definitions for each of the above platforms plus Nokia SRLinux
+by default. These platform definitions live in scrapligo/assets.
+
+To instantiate a "platform" you can use the `platform.NewPlatform` function. You must pass a
+platform name or file in addition to your "normal" driver creation options. The value returned
+from this NewPlatform function is a *Platform instance. Depending on the type of platform you
+are instantiating you then need to call the `GetNetworkDriver` or `GetGenericDriver` method of
+the Platform, this will return, as you may expect, the corresponding driver type. After this,
+all operations proceed as "normal".
+
+As mentioned, first argument to the `platform.NewPlatform` function is either a platform name or
+a file path. The valid options for platform name are:
+
+- cisco_iosxe
+- cisco_iosxr
+- cisco_nxos
+- arista_eos
+- juniper_junos
+- nokia_srl
+
+When passing any of the above platform names, scrapligo will refer to the platform definitions
+included in the binary as assets. If you preferred to provide your own platform definition,
+you can do so by simply passing a file path.
\ No newline at end of file
diff --git a/examples/network_driver/platforms/main.go b/examples/network_driver/platforms/main.go
new file mode 100644
index 0000000..55638c5
--- /dev/null
+++ b/examples/network_driver/platforms/main.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/platform"
+
+ "github.com/scrapli/scrapligo/channel"
+)
+
+func main() {
+ p, err := platform.NewPlatform(
+ // cisco_iosxe refers to the included cisco iosxe platform definition
+ "cisco_iosxe",
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ )
+ if err != nil {
+ fmt.Printf("failed to create platform; error: %+v\n", err)
+
+ return
+ }
+
+ // fetch the network driver instance from the platform. you need to call this method explicitly
+ // because the platform may be generic or network -- by having the explicit method to fetch the
+ // driver you can avoid having to type cast things yourself. if you had a generic driver based
+ // platform you could call `GetGenericDriver` instead.
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ fmt.Printf("failed to fetch network driver from the platform; error: %+v\n", err)
+
+ return
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ // fetch the prompt
+ prompt, err := d.Channel.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+
+ // send some input
+ output, err := d.Channel.SendInput("show version | i IOS")
+ if err != nil {
+ fmt.Printf("failed to send input to device; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("output received (SendInput):\n %s\n\n\n", output)
+
+ // send an interactive input
+ // SendInteractive expects a slice of `SendInteractiveEvent` objects
+ events := make([]*channel.SendInteractiveEvent, 2)
+ events[0] = &channel.SendInteractiveEvent{
+ ChannelInput: "clear logging",
+ ChannelResponse: "[confirm]",
+ HideInput: false,
+ }
+ events[1] = &channel.SendInteractiveEvent{
+ ChannelInput: "",
+ ChannelResponse: "#",
+ HideInput: false,
+ }
+
+ interactiveOutput, err := d.SendInteractive(events)
+ if err != nil {
+ fmt.Printf("failed to send interactive input to device; error: %+v\n", err)
+ }
+
+ fmt.Printf("output received (SendInteractive):\n %s\n\n\n", interactiveOutput.Result)
+
+ // send a command -- as this is a driver created from a *platform* it will have some things
+ // already done for us -- including disabling paging, so this command that would produce more
+ // output than the default terminal lines will not cause any issues.
+ r, err := d.SendCommand("show version")
+ if err != nil {
+ fmt.Printf("failed to send command; error: %+v\n", err)
+ return
+ }
+
+ fmt.Printf(
+ "sent command '%s', output received (SendCommand):\n %s\n\n\n",
+ r.Input,
+ r.Result,
+ )
+}
diff --git a/examples/network_driver/privilege_levels/README.md b/examples/network_driver/privilege_levels/README.md
new file mode 100644
index 0000000..de747c5
--- /dev/null
+++ b/examples/network_driver/privilege_levels/README.md
@@ -0,0 +1,17 @@
+Privilege Levels
+================
+
+When using a "network" driver, scrapligo has the concept of privilege levels. Each privilege
+level corresponds to a "mode" or a prompt flavor on a device. Using Cisco IOSXE as an example,
+there are at least three privilege levels -- "exec" (> prompt), "privilege-exec" (# prompt), and
+"configuration" (config# prompt). scrapligo keeps track of the current privilege level, and it
+understands how to traverse the privilege levels. When you send a "command" scrapligo will
+auto-acquire the "default desired privilege level". This default privilege leve is more or less
+the "normal" privilege level you would operate in when connecting to a device. When sending a
+"config" scrapligo will auto acquire the "configuration" privilege level.
+
+While you probably won't need to do so often, you can explicitly acquire a privilege level with
+the "AcquirePriv" method. If you need to send configs in a non-standard configuration level,
+such as "configuration-exclusive" you can pass the `WithPrivilegeLevel` option to any config
+methods. Note that the privilege levels must be defined/exist in the platform definition you are
+building your connection from!
\ No newline at end of file
diff --git a/examples/network_driver/privilege_levels/main.go b/examples/network_driver/privilege_levels/main.go
new file mode 100644
index 0000000..686f759
--- /dev/null
+++ b/examples/network_driver/privilege_levels/main.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/platform"
+)
+
+func main() {
+ p, err := platform.NewPlatform(
+ // cisco_iosxe refers to the included cisco iosxe platform definition
+ "cisco_iosxe",
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ )
+ if err != nil {
+ fmt.Printf("failed to create platform; error: %+v\n", err)
+
+ return
+ }
+
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ fmt.Printf("failed to fetch network driver from the platform; error: %+v\n", err)
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+ defer d.Close()
+
+ prompt, err := d.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+
+ // acquire configuration privilege level
+ err = d.AcquirePriv("configuration")
+ if err != nil {
+ fmt.Printf("failed to acquire configuration privilege level; error: %+v\n", err)
+
+ return
+ }
+
+ // fetch the prompt again to make sure we are in config mode
+ prompt, err = d.GetPrompt()
+ if err != nil {
+ fmt.Printf("failed to get prompt; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("found prompt: %s\n\n\n", prompt)
+
+ // now run a command that "should" be ran from privilege-exec mode -- you'll see that scrapligo
+ // will automagically acquire privilege-exec -- this is the "default desired privilege level"
+ // and this default priv level is *always* acquired before running "send commandX" methods.
+ r, err := d.SendCommand("show run")
+ if err != nil {
+ fmt.Printf("failed to execute show command; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("got running config: %s\n\n\n", r.Result)
+}
diff --git a/examples/network_driver/sending_configs/README.md b/examples/network_driver/sending_configs/README.md
new file mode 100644
index 0000000..cb12093
--- /dev/null
+++ b/examples/network_driver/sending_configs/README.md
@@ -0,0 +1,19 @@
+Sending Configs
+===============
+
+Sending configs is easy! Just use the `SendConfig`, `SendConfigs`, or `SendConfigsFromFile`
+methods. The first accepts a single string of configs and will split them on new lines, sending
+each line individually. The `SendConfigs` method accepts a slice of configs, and the
+`SendConfigsFromFile` of course accepts a file path which it loads and sends line by line to the
+device.
+
+Note that there are some "types" of configurations that will cause some issues for
+scrapli/scrapligo -- these are "vi-like" configurations. This is most commonly seen when
+configuring banners (like motd banner). These types of config modes "break" scrapligo because
+there is no prompt painted after each line is entered, and scrapligo never sends subsequent
+lines of config until the prompt is "re-found". In order to not break scrapligo in these types
+of config sections you can send them with the `WithEager` option. This "eager" mode causes
+scrapligo to no longer wait until it sees the prompt pattern after sending each config line.
+Generally you probably don't want to use eager unless you need to as it can cause scrapligo to
+fire the configs to the device much too quickly which sometimes causes devices to... lose their
+mind for lack of a better explanation!
\ No newline at end of file
diff --git a/examples/network_driver/sending_configs/main.go b/examples/network_driver/sending_configs/main.go
new file mode 100644
index 0000000..2d36ad9
--- /dev/null
+++ b/examples/network_driver/sending_configs/main.go
@@ -0,0 +1,53 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/platform"
+)
+
+func main() {
+ p, err := platform.NewPlatform(
+ // cisco_iosxe refers to the included cisco iosxe platform definition
+ "cisco_iosxe",
+ "sandbox-iosxe-latest-1.cisco.com",
+ options.WithAuthNoStrictKey(),
+ options.WithAuthUsername("developer"),
+ options.WithAuthPassword("C1sco12345"),
+ )
+ if err != nil {
+ fmt.Printf("failed to create platform; error: %+v\n", err)
+
+ return
+ }
+
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ fmt.Printf("failed to fetch network driver from the platform; error: %+v\n", err)
+ }
+
+ err = d.Open()
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ defer d.Close()
+
+ // sending configs is just as easy as sending commands, scrapligo will auto acquire the
+ // "configuration" privilege level, if your platform does *not* have a "configuration" priv
+ // level things will fail! if there is an alternative to configuration (such as "exclusive"),
+ // you can explicitly execute configs in that privilege level with the
+ // `options.WithPrivilegeLevel` option.
+ r, err := d.SendConfigs([]string{"interface loopback999", "description tacocat"})
+ if err != nil {
+ fmt.Printf("failed to open driver; error: %+v\n", err)
+
+ return
+ }
+
+ fmt.Printf("sending configs took %f seconds", r.ElapsedTime)
+}
diff --git a/examples/network_driver/simple/commandsfile b/examples/network_driver/simple/commandsfile
deleted file mode 100644
index c673d34..0000000
--- a/examples/network_driver/simple/commandsfile
+++ /dev/null
@@ -1,2 +0,0 @@
-show version
-show ip int brief
\ No newline at end of file
diff --git a/examples/network_driver/simple/main.go b/examples/network_driver/simple/main.go
deleted file mode 100644
index bf9a24f..0000000
--- a/examples/network_driver/simple/main.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/driver/core"
-)
-
-// const commandsFile = "commandsfile"
-
-func main() {
- arg := flag.String("file", "examples/network_driver/simple/commandsfile", "argument from user")
- flag.Parse()
-
- d, err := core.NewCoreDriver(
- "ios-xe-mgmt.cisco.com",
- "cisco_iosxe",
- base.WithPort(8181),
- base.WithAuthStrictKey(false),
- base.WithAuthUsername("developer"),
- base.WithAuthPassword("C1sco12345"),
- )
-
- if err != nil {
- fmt.Printf("failed to create driver; error: %+v\n", err)
- return
- }
-
- err = d.Open()
- if err != nil {
- fmt.Printf("failed to open driver; error: %+v\n", err)
- return
- }
- defer d.Close()
-
- // fetch the prompt
- prompt, err := d.GetPrompt()
- if err != nil {
- fmt.Printf("failed to get prompt; error: %+v\n", err)
- } else {
- fmt.Printf("found prompt: %s\n\n\n", prompt)
- }
-
- // send some commands from a file
- mr, err := d.SendCommandsFromFile(*arg)
- if err != nil {
- fmt.Printf("failed to send commands from file; error: %+v\n", err)
- return
- }
- for _, r := range mr.Responses {
- fmt.Printf("sent command '%s', output received:\n %s\n\n\n", r.ChannelInput, r.Result)
- }
-
- // send some configs
- configs := []string{
- "interface loopback0",
- "interface loopback0 description tacocat",
- "no interface loopback0",
- }
-
- _, err = d.SendConfigs(configs)
- if err != nil {
- fmt.Printf("failed to send configs; error: %+v\n", err)
- return
- }
-}
diff --git a/go.mod b/go.mod
index b2ba243..cc22f88 100644
--- a/go.mod
+++ b/go.mod
@@ -3,11 +3,9 @@ module github.com/scrapli/scrapligo
go 1.16
require (
- github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94
- github.com/creack/pty v1.1.11
- github.com/google/go-cmp v0.5.6
+ github.com/creack/pty v1.1.18
+ github.com/google/go-cmp v0.5.8
github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4
- golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
- golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
- golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
+ golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
+ gopkg.in/yaml.v3 v3.0.0
)
diff --git a/go.sum b/go.sum
index 61b1644..82f0610 100644
--- a/go.sum
+++ b/go.sum
@@ -1,20 +1,21 @@
-github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94 h1:NGwk1xS2VoCNj8Y7RSVM/oEI6p7yUihghKjNbY5QRDs=
-github.com/carlmontanari/difflibgo v0.0.0-20210718170140-424f52054f94/go.mod h1:+3MuSIeC3qmdSesR12cTLeb47R/Vvo+bHdB6hC5HShk=
-github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
+github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4 h1:FHUL2HofYJuslFOQdy/JjjP36zxqIpd/dcoiwLMIs7k=
github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4/go.mod h1:CJYqpTg9u5VPCoD0VEl9E68prCIiWQD8m457k098DdQ=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0=
+golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/logging/formatter.go b/logging/formatter.go
new file mode 100644
index 0000000..ceac555
--- /dev/null
+++ b/logging/formatter.go
@@ -0,0 +1,32 @@
+package logging
+
+import (
+ "fmt"
+ "strings"
+)
+
+const (
+ colorStop = "\033[0m"
+)
+
+var colorMap = map[string]string{ //nolint:gochecknoglobals
+ Debug: "\033[34m",
+ Info: "\033[32m",
+ Critical: "\033[31m",
+}
+
+// DefaultFormatter is the default logging instance formatter -- this formatter simply adds colors
+// to the log message based on log level.
+func DefaultFormatter(l, m string) string {
+ switch l {
+ case Debug:
+ l = colorMap[Debug] + strings.ToUpper(l) + colorStop
+ case Info:
+ l = colorMap[Info] + strings.ToUpper(l) + colorStop
+ case Critical:
+ l = colorMap[Critical] + strings.ToUpper(l) + colorStop
+ }
+
+ // pad the level extra for the ansi code magic
+ return fmt.Sprintf("%17s %s", l, m)
+}
diff --git a/logging/logging.go b/logging/logging.go
index 6533e00..cbece45 100644
--- a/logging/logging.go
+++ b/logging/logging.go
@@ -1,41 +1,126 @@
package logging
-import "fmt"
+import (
+ "fmt"
-// Logger accepts logging interface to set as library logger(s).
-type Logger func(...interface{})
+ "github.com/scrapli/scrapligo/util"
+)
-// debugLog default DebugLog -- defaults to nil.
-var debugLog Logger //nolint:gochecknoglobals
+const (
+ // Debug is the debug log level.
+ Debug = "debug"
+ // Info is the info(rmational) log level.
+ Info = "info"
+ // Critical is the critical log level.
+ Critical = "critical"
+)
-// errorLog default ErrorLog -- defaults to nil.
-var errorLog Logger
+// NewInstance returns a new logging Instance.
+func NewInstance(opts ...util.Option) (*Instance, error) {
+ i := &Instance{
+ Level: Info,
+ Formatter: DefaultFormatter,
+ Loggers: nil,
+ }
+
+ for _, o := range opts {
+ err := o(i)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return i, nil
+}
+
+// Instance is a simple logging object.
+type Instance struct {
+ Level string
+ Formatter func(string, string) string
+ Loggers []func(...interface{})
+}
+
+// Emit "emits" a logging message m to all the loggers in the Instance.
+func (i *Instance) Emit(m interface{}) {
+ for _, f := range i.Loggers {
+ f(m)
+ }
+}
+
+func (i *Instance) shouldLog(l string) bool {
+ if len(i.Loggers) == 0 {
+ return false
+ }
+
+ switch i.Level {
+ case Debug:
+ return true
+ case Info:
+ switch l {
+ case Info, Critical:
+ return true
+ default:
+ return false
+ }
+ case Critical:
+ if l == Critical {
+ return true
+ }
+ }
-// SetDebugLogger function to set debug logger to something that implements `Logger`.
-func SetDebugLogger(logger Logger) {
- debugLog = logger
+ return false
}
-// SetErrorLogger function to set error logger to something that implements `Logger`.
-func SetErrorLogger(logger Logger) {
- errorLog = logger
+// Debug accepts a Debug level log message with no formatting.
+func (i *Instance) Debug(f string) {
+ if !i.shouldLog(Debug) {
+ return
+ }
+
+ i.Emit(i.Formatter(Debug, f))
+}
+
+// Debugf accepts a Debug level log message normal fmt.Sprintf type formatting.
+func (i *Instance) Debugf(f string, a ...interface{}) {
+ if !i.shouldLog(Debug) {
+ return
+ }
+
+ i.Emit(i.Formatter(Debug, fmt.Sprintf(f, a...)))
+}
+
+// Info accepts an Info level log message with no formatting.
+func (i *Instance) Info(f string) {
+ if !i.shouldLog(Info) {
+ return
+ }
+
+ i.Emit(i.Formatter(Info, f))
}
-// LogDebug writes debug message to the debug log.
-func LogDebug(msg string) {
- if debugLog != nil {
- debugLog(msg)
+// Infof accepts an Info level log message normal fmt.Sprintf type formatting.
+func (i *Instance) Infof(f string, a ...interface{}) {
+ if !i.shouldLog(Info) {
+ return
}
+
+ i.Emit(i.Formatter(Info, fmt.Sprintf(f, a...)))
}
-// LogError writes error message to the error log.
-func LogError(msg string) {
- if errorLog != nil {
- errorLog(msg)
+// Critical accepts a Critical level log message with no formatting.
+func (i *Instance) Critical(f string) {
+ if !i.shouldLog(Critical) {
+ return
}
+
+ i.Emit(i.Formatter(Critical, f))
}
-// FormatLogMessage formats log message payload, adding contextual info about the host.
-func FormatLogMessage(level, host string, port int, msg string) string {
- return fmt.Sprintf("%s::%s::%d::%s", level, host, port, msg)
+// Criticalf accepts a Critical level log message normal fmt.Sprintf type formatting.
+func (i *Instance) Criticalf(f string, a ...interface{}) {
+ if !i.shouldLog(Critical) {
+ return
+ }
+
+ i.Emit(i.Formatter(Critical, fmt.Sprintf(f, a...)))
}
diff --git a/logging/logging_test.go b/logging/logging_test.go
new file mode 100644
index 0000000..462ca8c
--- /dev/null
+++ b/logging/logging_test.go
@@ -0,0 +1,30 @@
+package logging_test
+
+import (
+ "flag"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
diff --git a/logging/options.go b/logging/options.go
new file mode 100644
index 0000000..8d3bb4e
--- /dev/null
+++ b/logging/options.go
@@ -0,0 +1,61 @@
+package logging
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// WithLevel sets the logging level for a logging instance.
+func WithLevel(s string) util.Option {
+ return func(o interface{}) error {
+ i, ok := o.(*Instance)
+
+ if !ok {
+ return util.ErrIgnoredOption
+ }
+
+ s = strings.ToLower(s)
+
+ switch s {
+ case Info, Debug, Critical:
+ i.Level = s
+
+ return nil
+ }
+
+ return fmt.Errorf("%w: log level '%s' is not a valid level", util.ErrBadOption, s)
+ }
+}
+
+// WithLogger appends a logger (a function that accepts an interface) to the Loggers slice of a
+// logging instance.
+func WithLogger(f func(...interface{})) util.Option {
+ return func(o interface{}) error {
+ i, ok := o.(*Instance)
+
+ if ok {
+ i.Loggers = append(i.Loggers, f)
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
+
+// WithFormatter sets the Formatter function of the logging instance.
+func WithFormatter(f func(string, string) string) util.Option {
+ return func(o interface{}) error {
+ i, ok := o.(*Instance)
+
+ if ok {
+ i.Formatter = f
+
+ return nil
+ }
+
+ return util.ErrIgnoredOption
+ }
+}
diff --git a/netconf/capabilities.go b/netconf/capabilities.go
deleted file mode 100644
index fd63183..0000000
--- a/netconf/capabilities.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package netconf
-
-import (
- "bytes"
- "fmt"
- "regexp"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-func (c *Channel) getServerCapabilities(authenticationBuf []byte) *channelResult {
- for {
- b, err := c.BaseChannel.Read()
- if err != nil {
- return &channelResult{
- result: authenticationBuf,
- error: err,
- }
- }
-
- authenticationBuf = append(authenticationBuf, b...)
-
- if bytes.Contains(authenticationBuf, []byte("]]>]]>")) {
- return &channelResult{
- result: authenticationBuf,
- error: nil,
- }
- }
- }
-}
-
-func (c *Channel) parseServerCapabilities(authenticationBuf []byte) error {
- // match w/ or w/out hello namespace
- serverHelloPattern := regexp.MustCompile(`(?is)(<(\w+:)?hello.*(\w+:)?hello>)`)
- serverHelloMatch := serverHelloPattern.Match(authenticationBuf)
-
- if !serverHelloMatch {
- logging.LogError(
- c.BaseChannel.FormatLogMessage("error", "could not parse server capabilities"),
- )
-
- return ErrCapabilitiesExchangeFailed
- }
-
- // rather than deal w/ xml like scrapli python does, just regex the caps out
- serverCapabilitiesPattern := regexp.MustCompile(
- `(?i)(?:<(?:\w+:)?capability>)(.*?)(?:(?:\w+:)?capability>)`,
- )
- serverCapabilitiesMatches := serverCapabilitiesPattern.FindAllSubmatch(authenticationBuf, -1)
-
- serverCapabilities := make([]string, 1)
- for _, match := range serverCapabilitiesMatches {
- serverCapabilities = append(serverCapabilities, string(match[1]))
- }
-
- c.serverCapabilities = serverCapabilities
-
- return nil
-}
-
-func (c *Channel) serverCapabilitiesContains(requestedCapability string) bool {
- for _, serverCapability := range c.serverCapabilities {
- if serverCapability == requestedCapability {
- return true
- }
- }
-
- return false
-}
-
-func (c *Channel) processCapabilitiesExchange() error {
- if c.serverCapabilitiesContains(Version11Capability) {
- c.SelectedNetconfVersion = Version11
- } else if c.serverCapabilitiesContains(Version10Capability) {
- c.SelectedNetconfVersion = Version10
- } else {
- logging.LogError(c.BaseChannel.FormatLogMessage(
- "error", "did not receive netconf capabilities for version 1.0 or 1.1"),
- )
- return ErrCapabilitiesExchangeFailed
- }
-
- if c.PreferredNetconfVersion != "" {
- if c.PreferredNetconfVersion == Version10 &&
- c.serverCapabilitiesContains(Version10Capability) {
- c.SelectedNetconfVersion = Version10
- } else if c.PreferredNetconfVersion == Version11 &&
- c.serverCapabilitiesContains(Version11Capability) {
- c.SelectedNetconfVersion = Version11
- } else {
- logging.LogDebug(c.BaseChannel.FormatLogMessage(
- "info", "user provided preferred netconf version not available"),
- )
- }
- }
-
- if c.SelectedNetconfVersion == Version10 {
- c.BaseChannel.CommsPromptPattern = regexp.MustCompile(Version10DelimiterPattern)
- } else {
- c.BaseChannel.CommsPromptPattern = regexp.MustCompile(Version11DelimiterPattern)
- }
-
- return nil
-}
-
-func (c *Channel) sendClientCapabilities() error {
- clientCapabilities := Version11Capabilities
- if c.SelectedNetconfVersion == Version10 {
- clientCapabilities = Version10Capabilities
- }
-
- logging.LogDebug(c.BaseChannel.FormatLogMessage(
- "info", fmt.Sprintf("sending client capabilities: %s\n", clientCapabilities)),
- )
-
- err := c.BaseChannel.Write([]byte(clientCapabilities), false)
- if err != nil {
- return err
- }
-
- err = c.readUntilInput([]byte(clientCapabilities[1:]))
- if err != nil {
- return err
- }
-
- err = c.BaseChannel.SendReturn()
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/netconf/channel.go b/netconf/channel.go
deleted file mode 100644
index c0aa0ba..0000000
--- a/netconf/channel.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package netconf
-
-import (
- "bytes"
- "regexp"
- "time"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/channel"
-)
-
-// Channel the Netconf Channel that extends the base SSH channel.
-type Channel struct {
- BaseChannel *channel.Channel
- PreferredNetconfVersion string
- SelectedNetconfVersion string
- ForceSelfClosingTag *bool
- serverCapabilities []string
- serverEcho *bool
-}
-
-type channelResult struct {
- result []byte
- error error
-}
-
-// OpenNetconf open a netconf channel to the device, handles capabilities exchange.
-func (c *Channel) OpenNetconf(authenticationBuf []byte) error {
- if !bytes.Contains(authenticationBuf, []byte("]]>]]>")) {
- var _c = make(chan *channelResult)
-
- go func() {
- r := c.getServerCapabilities(authenticationBuf)
- _c <- r
- close(_c)
- }()
-
- timer := time.NewTimer(c.BaseChannel.TimeoutOps)
-
- select {
- case r := <-_c:
- authenticationBuf = r.result
- case <-timer.C:
- logging.LogError(
- c.BaseChannel.FormatLogMessage(
- "error",
- "timed out attempting to read server capabilities",
- ),
- )
-
- return channel.ErrAuthTimeout
- }
- }
-
- err := c.parseServerCapabilities(authenticationBuf)
- if err != nil {
- return err
- }
-
- err = c.processCapabilitiesExchange()
- if err != nil {
- return err
- }
-
- err = c.sendClientCapabilities()
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *Channel) readUntilInput(channelInput []byte) error {
- var b []byte
-
- if c.serverEcho == nil {
- // echo check hasnt been figured out yet
- return nil
- }
-
- if !*c.serverEcho || len(channelInput) == 0 {
- return nil
- }
-
- for {
- chunk, err := c.BaseChannel.Read()
-
- b = append(b, chunk...)
-
- if err != nil {
- return err
- }
-
- if bytes.Contains(b, channelInput) || bytes.Contains(b, []byte("rpc>")) {
- return err
- }
- }
-}
-
-func (c *Channel) readUntilPrompt(b []byte, prompt *string) ([]byte, error) {
- matchPattern := c.BaseChannel.CommsPromptPattern
- if prompt != nil {
- matchPattern = regexp.MustCompile(*prompt)
- }
-
- for {
- chunk, err := c.BaseChannel.Read()
- b = append(b, chunk...)
-
- if err != nil {
- return b, err
- }
-
- channelMatch := matchPattern.Match(b)
- if channelMatch {
- logging.LogDebug(c.BaseChannel.FormatLogMessage("debug", "found prompt match"))
-
- return b, err
- }
- }
-}
diff --git a/netconf/commit.go b/netconf/commit.go
deleted file mode 100644
index 0745a0a..0000000
--- a/netconf/commit.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// Commit issue commit rpc to device.
-func (d *Driver) Commit() (*Response, error) {
- netconfMessage := d.BuildCommitElem()
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/commont_test.go b/netconf/commont_test.go
deleted file mode 100644
index c9c5771..0000000
--- a/netconf/commont_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package netconf_test
-
-import (
- "testing"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/netconf"
-)
-
-type functionalTestHostConnData struct {
- Host string
- Port int
-}
-
-func functionalTestHosts() map[string]*functionalTestHostConnData {
- return map[string]*functionalTestHostConnData{
- "cisco_iosxe_1_0": {
- Host: "localhost",
- Port: 21022,
- },
- "cisco_iosxe_1_1": {
- Host: "localhost",
- Port: 21830,
- },
- "cisco_iosxr_1_1": {
- Host: "localhost",
- Port: 23830,
- },
- "juniper_junos_1_0": {
- Host: "localhost",
- Port: 25022,
- },
- }
-}
-
-func newFunctionalTestDriver(
- t *testing.T,
- host, transportName string,
- port int,
-) *netconf.Driver {
- d, driverErr := netconf.NewNetconfDriver(
- host,
- base.WithAuthUsername("boxen"),
- base.WithAuthPassword("b0x3N-b0x3N"),
- base.WithPort(port),
- base.WithTransportType(transportName),
- base.WithAuthStrictKey(false),
- )
-
- if driverErr != nil {
- t.Fatalf("failed creating test device: %v", driverErr)
- }
-
- return d
-}
diff --git a/netconf/constants.go b/netconf/constants.go
deleted file mode 100644
index c1c57f2..0000000
--- a/netconf/constants.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package netconf
-
-// constants to represent netconf capabilities and patterns.
-const (
- Version10 = "1.0"
- Version10Capability = "urn:ietf:params:netconf:base:1.0"
- Version10Capabilities = "" +
- "\n" +
- "\n" +
- " \n" +
- " urn:ietf:params:netconf:base:1.0 \n" +
- " \n" +
- " ]]>]]>"
- Version10DelimiterPattern = "]]>]]>"
- Version11 = "1.1"
- Version11Capability = "urn:ietf:params:netconf:base:1.1"
- Version11Capabilities = "" +
- "\n" +
- "\n" +
- " \n" +
- " urn:ietf:params:netconf:base:1.1 \n" +
- " \n" +
- " ]]>]]>"
- Version11DelimiterPattern = `(?m)^##$`
- Version11ChunkPattern = `(?ms)(\d+)\n(.*?)#`
- XMLHeader = ""
- DefaultPort = 830
-)
diff --git a/netconf/copyconfig.go b/netconf/copyconfig.go
deleted file mode 100644
index d235f55..0000000
--- a/netconf/copyconfig.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// CopyConfig issues copy-config rpc to device.
-func (d *Driver) CopyConfig(source, target string) (*Response, error) {
- netconfMessage := d.BuildCopyConfigElem(source, target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/deleteconfig.go b/netconf/deleteconfig.go
deleted file mode 100644
index 482788d..0000000
--- a/netconf/deleteconfig.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// DeleteConfig issue delete rpc to device.
-func (d *Driver) DeleteConfig(target string) (*Response, error) {
- netconfMessage := d.BuildDeleteConfigElem(target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/discard.go b/netconf/discard.go
deleted file mode 100644
index 8d65753..0000000
--- a/netconf/discard.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// Discard issue discard rpc to device.
-func (d *Driver) Discard() (*Response, error) {
- netconfMessage := d.BuildDiscardElem()
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/driver.go b/netconf/driver.go
deleted file mode 100644
index 4aaefb1..0000000
--- a/netconf/driver.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package netconf
-
-import (
- "errors"
- "regexp"
-
- "github.com/scrapli/scrapligo/logging"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/transport"
-)
-
-// ErrCapabilitiesExchangeFailed error for failure of capabilities exchange.
-var ErrCapabilitiesExchangeFailed = errors.New("failure during capabilities exchange")
-
-// Driver the Netconf Driver struct, extends the base driver, and embeds netconf channel.
-type Driver struct {
- base.Driver
- NetconfChannel *Channel
- readableDatastores [][]byte
- writeableDatastores [][]byte
- messageID int
- // these are not settable via NewNetconfDriver, just set them manually before open if you care
- StripNamespaces bool
- StrictDatastores bool
-}
-
-// NewNetconfDriver return an instance of netconf `Driver`.
-func NewNetconfDriver(
- host string,
- options ...base.Option,
-) (*Driver, error) {
- options = append([]base.Option{base.WithPort(DefaultPort)}, options...)
-
- newDriver, err := base.NewDriver(host, options...)
-
- if err != nil {
- return nil, err
- }
-
- d := &Driver{
- Driver: *newDriver,
- StripNamespaces: false,
- StrictDatastores: true,
- messageID: 101,
- }
-
- nc := &Channel{
- BaseChannel: d.Channel,
- serverEcho: d.NetconfEcho,
- ForceSelfClosingTag: d.NetconfForceSelfClosingTags,
- }
-
- d.NetconfChannel = nc
-
- // ignoring user input on comms prompt pattern too as we know what we need to look for
- d.Channel.CommsPromptPattern = regexp.MustCompile(Version10DelimiterPattern)
-
- // temp for appeasing linting until they are used
- _ = d.readableDatastores
- _ = d.writeableDatastores
-
- return d, nil
-}
-
-// Open netconf open method, opens transport in netconf "mode".
-func (d *Driver) Open() error {
- err := d.Transport.OpenNetconf()
- if err != nil {
- return err
- }
-
- var authenticationBuf []byte
-
- if d.TransportType == transport.SystemTransportName {
- r, authErr := d.Channel.AuthenticateSSH(d.AuthPassword, d.AuthPrivateKeyPassphrase)
- if authErr != nil {
- logging.LogError(
- d.FormatLogMessage("error", "authentication failed, connection not opened"),
- )
-
- return authErr
- }
-
- authenticationBuf = r
- }
-
- err = d.NetconfChannel.OpenNetconf(authenticationBuf)
-
- return err
-}
-
-// Close closes the connection.
-func (d *Driver) Close() error {
- err := d.Transport.Close()
- return err
-}
-
-// ParseNetconfOptions parse provided netconf options.
-func (d *Driver) ParseNetconfOptions(
- o []Option,
-) *Options {
- finalOpts := &Options{
- Filter: DefaultNetconfOptionsFilter,
- FilterType: DefaultNetconfOptionsFilterType,
- DefaultType: DefaultNetconfOptionsDefaultType,
- }
-
- if len(o) > 0 && o[0] != nil {
- for _, option := range o {
- option(finalOpts)
- }
- }
-
- return finalOpts
-}
-
-func (d *Driver) finalizeAndSendMessage(
- netconfMessage *Message,
-) (*Response, error) {
- bytesNetconfMessage, err := d.NetconfChannel.BuildFinalMessage(netconfMessage)
- if err != nil {
- return NewNetconfResponse(
- d.Host,
- d.NetconfChannel.SelectedNetconfVersion,
- d.Transport.BaseTransportArgs.Port,
- []byte(""),
- netconfMessage,
- d.StripNamespaces,
- ), err
- }
-
- r := NewNetconfResponse(
- d.Host,
- d.NetconfChannel.SelectedNetconfVersion,
- d.Transport.BaseTransportArgs.Port,
- bytesNetconfMessage,
- netconfMessage,
- d.StripNamespaces,
- )
-
- channelResponse, err := d.NetconfChannel.SendInputNetconf(bytesNetconfMessage)
-
- r.Record(channelResponse)
-
- if err != nil {
- return r, err
- }
-
- return r, nil
-}
diff --git a/netconf/editconfig.go b/netconf/editconfig.go
deleted file mode 100644
index ec2d034..0000000
--- a/netconf/editconfig.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// EditConfig edit device configuration.
-func (d *Driver) EditConfig(target, config string) (*Response, error) {
- netconfMessage := d.BuildEditConfigElem(config, target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/get.go b/netconf/get.go
deleted file mode 100644
index b83c670..0000000
--- a/netconf/get.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package netconf
-
-// Get execute get rpc with optional filters.
-func (d *Driver) Get(o ...Option) (*Response, error) {
- finalOpts := d.ParseNetconfOptions(o)
- netconfMessage, err := d.BuildGetElem(finalOpts.Filter, finalOpts.FilterType)
-
- if err != nil {
- return nil, err
- }
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/getconfig.go b/netconf/getconfig.go
deleted file mode 100644
index 64573f8..0000000
--- a/netconf/getconfig.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package netconf
-
-// GetConfig execute get-config rpc with optional filters.
-func (d *Driver) GetConfig(source string, o ...Option) (*Response, error) {
- finalOpts := d.ParseNetconfOptions(o)
-
- netconfMessage, err := d.BuildGetConfigElem(
- source,
- finalOpts.Filter,
- finalOpts.FilterType,
- finalOpts.DefaultType,
- )
-
- if err != nil {
- return nil, err
- }
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/getconfig_test.go b/netconf/getconfig_test.go
deleted file mode 100644
index 40f9889..0000000
--- a/netconf/getconfig_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package netconf_test
-
-import (
- "fmt"
- "strings"
- "testing"
-
- "github.com/scrapli/scrapligo/netconf"
-
- "github.com/scrapli/scrapligo/transport"
- "github.com/scrapli/scrapligo/util/testhelper"
-)
-
-func testFunctionalGetConfig(d *netconf.Driver) func(t *testing.T) {
- return func(t *testing.T) {
- openErr := d.Open()
- if openErr != nil {
- t.Fatalf("failed opening driver: %v", openErr)
- }
-
- r, cmdErr := d.GetConfig("running")
- if cmdErr != nil {
- t.Fatalf("failed sending config: %v", cmdErr)
- }
-
- if r.Failed != nil {
- t.Fatalf("response object indicates failure; error: %+v\n", r.Failed)
- }
- }
-}
-
-func TestFunctionalGetConfig(t *testing.T) {
- if !*testhelper.Functional {
- t.Skip("skip: functional tests skipped unless the '-functional' flag is passed")
- }
-
- testHosts := functionalTestHosts()
-
- for _, transportName := range transport.SupportedNetconfTransports() {
- if !testhelper.RunTransport(transportName) {
- t.Logf("skip; transport %s deselected for testing\n", transportName)
- continue
- }
-
- // for now just making sure the damn thing runs eventually load up expected output and
- // compare actual<>expected like the other tests.
- for platform, connectionData := range testHosts {
- d := newFunctionalTestDriver(t, connectionData.Host, transportName, connectionData.Port)
-
- if strings.Contains(platform, "junos") {
- t := true
- d.NetconfChannel.ForceSelfClosingTag = &t
- }
-
- f := testFunctionalGetConfig(d)
-
- t.Run(fmt.Sprintf("Platform=%s;Transport=%s", platform, transportName), f)
- }
- }
-}
diff --git a/netconf/lock.go b/netconf/lock.go
deleted file mode 100644
index 79f0a33..0000000
--- a/netconf/lock.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// Lock issue lock rpc to device.
-func (d *Driver) Lock(target string) (*Response, error) {
- netconfMessage := d.BuildLockElem(target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/message.go b/netconf/message.go
deleted file mode 100644
index 39019a8..0000000
--- a/netconf/message.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package netconf
-
-import (
- "bytes"
- "encoding/xml"
- "fmt"
- "regexp"
-)
-
-// BuildFinalMessage creates the final message to send to the device.
-func (c *Channel) BuildFinalMessage(xmlMessage interface{}) ([]byte, error) {
- message, err := xml.Marshal(xmlMessage)
- if err != nil {
- return []byte{}, err
- }
-
- message = append([]byte(XMLHeader), message...)
-
- if *c.ForceSelfClosingTag {
- // this grossness is 100% only for junos who seem to have lost their ever loving mind...
- // ` ` will cause junos (at least 17.x) to return an
- // error whilst ` ` will not. functionally 100% identical but
- // sure juniper do you or whatever...
- p := regexp.MustCompile(`(?:<(\w+)>.* )`)
- o := p.FindAllSubmatch(message, -1)
-
- if len(o) > 0 {
- newSource := []byte(fmt.Sprintf("<%s/> ", o[0][1]))
- message = bytes.Replace(message, o[0][0], newSource, 1)
- }
- }
-
- if c.SelectedNetconfVersion == Version11 {
- message = append([]byte(fmt.Sprintf("#%d\n", len(message))), message...)
- message = append(message, []byte("\n##")...)
- } else {
- message = append(message, []byte("]]>]]>")...)
- }
-
- return message, nil
-}
diff --git a/netconf/options.go b/netconf/options.go
deleted file mode 100644
index da8727c..0000000
--- a/netconf/options.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package netconf
-
-// Default values for netconf filter/default options.
-const (
- DefaultNetconfOptionsFilter = ""
- DefaultNetconfOptionsFilterType = "subtree"
- DefaultNetconfOptionsDefaultType = ""
-)
-
-// Options struct representing options for netconf operations.
-type Options struct {
- Filter string
- FilterType string
- DefaultType string
-}
-
-// Option netconf operation options.
-type Option func(*Options)
-
-// WithNetconfFilter add filter to a netconf operation.
-func WithNetconfFilter(filter string) Option {
- return func(o *Options) {
- o.Filter = filter
- }
-}
-
-// WithNetconfFilterType add filter-type to a netconf operation.
-func WithNetconfFilterType(filterType string) Option {
- return func(o *Options) {
- o.FilterType = filterType
- }
-}
-
-// WithNetconfDefaultType add default type to a netconf operation.
-func WithNetconfDefaultType(defaultType string) Option {
- return func(o *Options) {
- o.DefaultType = defaultType
- }
-}
diff --git a/netconf/payload.go b/netconf/payload.go
deleted file mode 100644
index 3a09db6..0000000
--- a/netconf/payload.go
+++ /dev/null
@@ -1,400 +0,0 @@
-package netconf
-
-import (
- "encoding/xml"
- "errors"
-)
-
-// ErrUnknownFilterType error for when user provides an unknown filter type.
-var ErrUnknownFilterType = errors.New("unknown filter type")
-
-// ErrDefaultsType error for when user provides an unknown default type.
-var ErrDefaultsType = errors.New("unknown defaults type")
-
-const (
- // FilterSubtreeType constant for filter type of subtree.
- FilterSubtreeType = "subtree"
- // FilterXpathType constant for filter type of xpath.
- FilterXpathType = "xpath"
- // DefaultsReportAllType constant for default type of "report-all".
- DefaultsReportAllType = "report-all"
- // DefaultsTrimType constant for default type of "trim".
- DefaultsTrimType = "trim"
- // DefaultsExplicitType constant for default type of "explicit".
- DefaultsExplicitType = "explicit"
- // DefaultsReportAllTaggedType constant for default type of "report-all-tagged".
- DefaultsReportAllTaggedType = "report-all-tagged"
- // DefaultsNamespace constant for defaults namespace.
- DefaultsNamespace = "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"
-)
-
-// Message struct representing the base rpc message payload.
-type Message struct {
- XMLName xml.Name `xml:"rpc"`
- Namespace string `xml:"xmlns,attr"`
- MessageID int `xml:"message-id,attr"`
- Payload interface{} `xml:",innerxml"`
-}
-
-// BuildPayload build an XML payload to send to netconf device.
-func (d *Driver) BuildPayload(payload interface{}) *Message {
- baseElem := &Message{
- XMLName: xml.Name{},
- Namespace: "urn:ietf:params:xml:ns:netconf:base:1.0",
- MessageID: d.messageID,
- Payload: payload,
- }
-
- d.messageID++
-
- return baseElem
-}
-
-// BuildRPCElem creates an element for a rpc operation.
-func (d *Driver) BuildRPCElem(
- filter string,
-) (*Message, error) {
- netconfInput := d.BuildPayload(filter)
-
- return netconfInput, nil
-}
-
-// datastore source
-
-// SourceElement struct representing the individual source message element.
-type SourceElement struct {
- XMLName xml.Name
-}
-
-// Source struct representing the parent source message element.
-type Source struct {
- XMLName xml.Name `xml:"source"`
- Source *SourceElement `xml:""`
-}
-
-// BuildSourceElem creates the "source" (for get-config for example) element of a netconf payload.
-func (d *Driver) BuildSourceElem(source string) *Source {
- sourceElem := &Source{
- XMLName: xml.Name{},
- Source: &SourceElement{XMLName: xml.Name{Local: source}},
- }
-
- return sourceElem
-}
-
-// datastore target
-
-// TargetElement struct representing the individual target message element.
-type TargetElement struct {
- XMLName xml.Name
-}
-
-// Target struct representing the parent target message element.
-type Target struct {
- XMLName xml.Name `xml:"target"`
- Source *TargetElement `xml:""`
-}
-
-// BuildTargetElem creates the "target" (for edit-config for example) element of a netconf payload.
-func (d *Driver) BuildTargetElem(target string) *Target {
- targetElem := &Target{
- XMLName: xml.Name{},
- Source: &TargetElement{XMLName: xml.Name{Local: target}},
- }
-
- return targetElem
-}
-
-// filter
-
-// Filter struct representing the parent filter message element.
-type Filter struct {
- XMLName xml.Name `xml:"filter"`
- Type string `xml:"type,attr"`
- Select string `xml:"select,attr,omitempty"`
- Payload string `xml:",innerxml"`
-}
-
-// BuildFilterElem creates the "filter" element of a netconf payload.
-func (d *Driver) BuildFilterElem(filter, filterType string) (*Filter, error) {
- if filter == "" || filterType == "" {
- return nil, nil
- }
-
- if filterType == FilterSubtreeType {
- return &Filter{
- XMLName: xml.Name{},
- Type: filterType,
- Select: "",
- Payload: filter,
- }, nil
- } else if filterType == FilterXpathType {
- return &Filter{
- XMLName: xml.Name{},
- Type: filterType,
- Select: filter,
- }, nil
- }
-
- return nil, ErrUnknownFilterType
-}
-
-// with-defaults
-
-// DefaultType struct representing the parent default/with-defaults message element.
-type DefaultType struct {
- XMLName xml.Name `xml:"with-defaults"`
- Namespace string `xml:"xmlns,attr"`
- Type string `xml:",innerxml"`
-}
-
-// BuildDefaultsElem creates the "default" element of a netconf payload.
-func (d *Driver) BuildDefaultsElem(defaultsType string) (*DefaultType, error) {
- if defaultsType == "" {
- return nil, nil
- }
-
- if defaultsType != DefaultsReportAllType &&
- defaultsType != DefaultsTrimType &&
- defaultsType != DefaultsExplicitType &&
- defaultsType != DefaultsReportAllTaggedType {
- return nil, ErrDefaultsType
- }
-
- return &DefaultType{
- XMLName: xml.Name{},
- Namespace: DefaultsNamespace,
- Type: defaultsType,
- }, nil
-}
-
-// get
-
-// Get struct representing the get message element.
-type Get struct {
- XMLName xml.Name `xml:"get"`
- Source *Source `xml:""`
- Filter *Filter `xml:""`
-}
-
-// BuildGetElem creates a get element for a get operation.
-func (d *Driver) BuildGetElem(
- filter, filterType string,
-) (*Message, error) {
- filterElem, err := d.BuildFilterElem(filter, filterType)
- if err != nil {
- return nil, err
- }
-
- getElem := &Get{
- XMLName: xml.Name{},
- Filter: filterElem,
- }
-
- netconfInput := d.BuildPayload(getElem)
-
- return netconfInput, nil
-}
-
-// get-config
-
-// GetConfig struct representing the get-config message element.
-type GetConfig struct {
- XMLName xml.Name `xml:"get-config"`
- Source *Source `xml:""`
- Filter *Filter `xml:""`
- Defaults *DefaultType `xml:""`
-}
-
-// BuildGetConfigElem creates a get-config element for a get operation.
-func (d *Driver) BuildGetConfigElem(
- source, filter, filterType, defaultType string,
-) (*Message, error) {
- filterElem, err := d.BuildFilterElem(filter, filterType)
- if err != nil {
- return nil, err
- }
-
- defaultsElem, err := d.BuildDefaultsElem(defaultType)
- if err != nil {
- return nil, err
- }
-
- getConfigElem := &GetConfig{
- XMLName: xml.Name{},
- Source: d.BuildSourceElem(source),
- Filter: filterElem,
- Defaults: defaultsElem,
- }
-
- netconfInput := d.BuildPayload(getConfigElem)
-
- return netconfInput, nil
-}
-
-// edit-config
-
-// EditConfig struct representing the edit-config message element.
-type EditConfig struct {
- XMLName xml.Name `xml:"edit-config"`
- Target *Target `xml:""`
- Payload string `xml:",innerxml"`
-}
-
-// BuildEditConfigElem creates a edit-config element for a get operation.
-func (d *Driver) BuildEditConfigElem(
- config, target string,
-) *Message {
- editConfigElem := &EditConfig{
- XMLName: xml.Name{},
- Target: d.BuildTargetElem(target),
- Payload: config,
- }
-
- netconfInput := d.BuildPayload(editConfigElem)
-
- return netconfInput
-}
-
-// delete-config
-
-// DeleteConfig struct representing the delete-config message element.
-type DeleteConfig struct {
- XMLName xml.Name `xml:"delete-config"`
- Target *Target `xml:""`
-}
-
-// BuildDeleteConfigElem creates a delete-config element for a get operation.
-func (d *Driver) BuildDeleteConfigElem(
- target string,
-) *Message {
- deleteConfigElem := &DeleteConfig{
- XMLName: xml.Name{},
- Target: d.BuildTargetElem(target),
- }
-
- netconfInput := d.BuildPayload(deleteConfigElem)
-
- return netconfInput
-}
-
-// copy-config
-
-// CopyConfig struct representing the copy-config message element.
-type CopyConfig struct {
- XMLName xml.Name `xml:"copy-config"`
- Source *Source `xml:""`
- Target *Target `xml:""`
-}
-
-// BuildCopyConfigElem creates a copy-config element for a copy-config operation.
-func (d *Driver) BuildCopyConfigElem(
- source,
- target string,
-) *Message {
- copyConfigElem := &CopyConfig{
- XMLName: xml.Name{},
- Source: d.BuildSourceElem(source),
- Target: d.BuildTargetElem(target),
- }
-
- netconfInput := d.BuildPayload(copyConfigElem)
-
- return netconfInput
-}
-
-// commit
-
-// Commit struct representing the commit message element.
-type Commit struct {
- XMLName xml.Name `xml:"commit"`
-}
-
-// BuildCommitElem creates a commit element for a get operation.
-func (d *Driver) BuildCommitElem() *Message {
- commitElem := &Commit{
- XMLName: xml.Name{},
- }
-
- netconfInput := d.BuildPayload(commitElem)
-
- return netconfInput
-}
-
-// discard
-
-// Discard struct representing the discard message element.
-type Discard struct {
- XMLName xml.Name `xml:"discard-changes"`
-}
-
-// BuildDiscardElem creates a discard element for a get operation.
-func (d *Driver) BuildDiscardElem() *Message {
- discardElem := &Discard{
- XMLName: xml.Name{},
- }
-
- netconfInput := d.BuildPayload(discardElem)
-
- return netconfInput
-}
-
-// lock
-
-// Lock struct representing the lock message element.
-type Lock struct {
- XMLName xml.Name `xml:"lock"`
- Target *Target `xml:""`
-}
-
-// BuildLockElem creates a lock element for a get operation.
-func (d *Driver) BuildLockElem(target string) *Message {
- lockElem := &Lock{
- XMLName: xml.Name{},
- Target: d.BuildTargetElem(target),
- }
-
- netconfInput := d.BuildPayload(lockElem)
-
- return netconfInput
-}
-
-// unlock
-
-// Unlock struct representing the unlock message element.
-type Unlock struct {
- XMLName xml.Name `xml:"unlock"`
- Target *Target `xml:""`
-}
-
-// BuildUnlockElem creates a unlock element for a get operation.
-func (d *Driver) BuildUnlockElem(target string) *Message {
- unlockElem := &Unlock{
- XMLName: xml.Name{},
- Target: d.BuildTargetElem(target),
- }
-
- netconfInput := d.BuildPayload(unlockElem)
-
- return netconfInput
-}
-
-// validate
-
-// Validate struct representing the validate message element.
-type Validate struct {
- XMLName xml.Name `xml:"validate"`
- Source *Source `xml:""`
-}
-
-// BuildValidateElem creates a validate element for a get operation.
-func (d *Driver) BuildValidateElem(source string) *Message {
- validateElem := &Validate{
- XMLName: xml.Name{},
- Source: d.BuildSourceElem(source),
- }
-
- netconfInput := d.BuildPayload(validateElem)
-
- return netconfInput
-}
diff --git a/netconf/response.go b/netconf/response.go
deleted file mode 100644
index 2d5a269..0000000
--- a/netconf/response.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package netconf
-
-import (
- "bytes"
- "fmt"
- "regexp"
- "strconv"
- "time"
-
- "github.com/scrapli/scrapligo/driver/base"
-
- "github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-// Netconf11ChunkPatternCompiled pre compiled pattern to match netconf 1.1 chunk pattern.
-var Netconf11ChunkPatternCompiled = regexp.MustCompile(Version11ChunkPattern)
-
-// Response the netconf response object.
-type Response struct {
- Host string
- Port int
- ChannelInput []byte
- XMLInput interface{}
- RawResult []byte
- Result string
- StartTime time.Time
- EndTime time.Time
- ElapsedTime float64
- FailedWhenContains [][]byte
- Failed error
- StripNamespaces bool
- NetconfVersion string
- ErrorMessages [][]string
-}
-
-// NewNetconfResponse return a new netconf response object.
-func NewNetconfResponse(
- host, netconfVersion string,
- port int,
- channelInput []byte,
- xmlInput interface{},
- stripNamespaces bool,
-) *Response {
- var failedWhenContains [][]byte
- failedWhenContains = append(
- failedWhenContains,
- []byte(""),
- []byte(""),
- []byte(" "),
- []byte(""),
- )
-
- r := &Response{
- Host: host,
- Port: port,
- ChannelInput: channelInput,
- XMLInput: xmlInput,
- RawResult: nil,
- Result: "",
- StartTime: time.Now(),
- EndTime: time.Time{},
- ElapsedTime: 0,
- NetconfVersion: netconfVersion,
- StripNamespaces: stripNamespaces,
- FailedWhenContains: failedWhenContains,
- }
-
- return r
-}
-
-// Record records a netconf response.
-func (r *Response) Record(rawResult []byte) {
- r.EndTime = time.Now()
- r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
-
- r.RawResult = rawResult
-
- b := util.BytesContainsAnySubBytes(r.RawResult, r.FailedWhenContains)
- if len(b) > 0 {
- r.Failed = &base.OperationError{
- Input: string(r.ChannelInput),
- Output: r.Result,
- ErrorString: string(b),
- }
- }
-
- if r.NetconfVersion == Version10 {
- r.recordResponse10()
- } else if r.NetconfVersion == Version11 {
- r.recordResponse11()
- }
-}
-
-func (r *Response) recordResponse10() {
- tmpResult := r.RawResult
- tmpResult = bytes.TrimPrefix(tmpResult, []byte(XMLHeader))
- tmpResult = bytes.TrimSuffix(tmpResult, []byte(Version10DelimiterPattern))
- r.Result = string(tmpResult)
-}
-
-func (r *Response) validateChunkSize(chunkSize int, chunk []byte) {
- // does this need more ... "massaging" like scrapli?
- // chunk regex matches the newline before the chunk size or end of message delimiter, so we
- // subtract one for that newline char
- if len(chunk)-1 != chunkSize {
- errMsg := fmt.Sprintf("return element lengh invalid, expted: %d, got %d for element: %s\n",
- chunkSize,
- len(chunk)-1,
- chunk)
-
- r.Failed = &base.OperationError{
- Input: string(r.ChannelInput),
- Output: r.Result,
- ErrorString: errMsg,
- }
-
- logging.LogError(
- logging.FormatLogMessage(
- "info",
- r.Host,
- r.Port,
- errMsg,
- ),
- )
- }
-}
-
-func (r *Response) recordResponse11() {
- resultSectionLens := Netconf11ChunkPatternCompiled.FindAllSubmatch(r.RawResult, -1)
-
- var joinedResult []byte
-
- for _, resultSectionMatch := range resultSectionLens {
- chunkSize, _ := strconv.Atoi(string(resultSectionMatch[1]))
- chunk := resultSectionMatch[2]
-
- r.validateChunkSize(chunkSize, chunk)
-
- joinedResult = append(joinedResult, chunk[:len(chunk)-1]...)
- }
-
- joinedResult = bytes.TrimPrefix(joinedResult, []byte(XMLHeader))
- r.Result = string(joinedResult)
-}
diff --git a/netconf/rpc.go b/netconf/rpc.go
deleted file mode 100644
index f454ad3..0000000
--- a/netconf/rpc.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package netconf
-
-// RPC sends a "bare" rpc to the device.
-func (d *Driver) RPC(o ...Option) (*Response, error) {
- finalOpts := d.ParseNetconfOptions(o)
- netconfMessage, err := d.BuildRPCElem(finalOpts.Filter)
-
- if err != nil {
- return nil, err
- }
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/sendinput.go b/netconf/sendinput.go
deleted file mode 100644
index 24aeaa2..0000000
--- a/netconf/sendinput.go
+++ /dev/null
@@ -1,151 +0,0 @@
-package netconf
-
-import (
- "bytes"
- "fmt"
- "time"
-
- "github.com/scrapli/scrapligo/channel"
- "github.com/scrapli/scrapligo/logging"
-)
-
-func (c *Channel) sendInput(channelInput []byte, stripPrompt, eager bool) *channelResult {
- logging.LogDebug(
- c.BaseChannel.FormatLogMessage(
- "info",
- fmt.Sprintf(
- "\"sending channelInput: %s; stripPrompt: %t; eager: %v",
- channelInput,
- stripPrompt,
- eager,
- ),
- ),
- )
-
- var b []byte
-
- err := c.BaseChannel.Write(channelInput, false)
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: nil,
- }
- }
-
- err = c.readUntilInput(channelInput)
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: err,
- }
- }
-
- err = c.BaseChannel.SendReturn()
- if err != nil {
- return &channelResult{
- result: []byte(""),
- error: err,
- }
- }
-
- if !eager {
- postInputBuf, readErr := c.readUntilPrompt(b, nil)
-
- if readErr != nil {
- return &channelResult{
- result: []byte(""),
- error: readErr,
- }
- }
-
- b = append(b, postInputBuf...)
- }
-
- return &channelResult{
- result: c.BaseChannel.RestructureOutput(b, stripPrompt),
- error: nil,
- }
-}
-
-// SendInputBytes same as SendInput but accepting bytes straight away (this is mostly used for
-// netconf).
-func (c *Channel) SendInputBytes(
- channelInput []byte,
- stripPrompt, eager bool,
- timeoutOps time.Duration,
-) ([]byte, error) {
- _c := make(chan *channelResult)
-
- go func() {
- r := c.sendInput(channelInput, stripPrompt, eager)
- _c <- r
- close(_c)
- }()
-
- timer := time.NewTimer(c.BaseChannel.DetermineOperationTimeout(timeoutOps))
-
- select {
- case r := <-_c:
- return r.result, r.error
- case <-timer.C:
- logging.LogError(
- c.BaseChannel.FormatLogMessage("error", "timed out sending input to device"),
- )
-
- return []byte{}, channel.ErrChannelTimeout
- }
-}
-
-// SendInputNetconf like channel's `SendInput` but specifically for netconf; operates in eager mode
-// unlike "normal" send input operations.
-func (c *Channel) SendInputNetconf(channelInput []byte) ([]byte, error) {
- b, err := c.SendInputBytes(channelInput, false, true, -1)
- if err != nil {
- return b, err
- }
-
- if bytes.Contains(b, channelInput) {
- b = bytes.Split(b, channelInput)[1]
- }
-
- b, err = c.readUntilPrompt(b, nil)
- if err != nil {
- return b, err
- }
-
- if c.serverEcho == nil {
- logging.LogDebug(c.BaseChannel.FormatLogMessage(
- "debug", "server echo is unset, determining if server echoes inputs now"),
- )
-
- if bytes.Contains(b, channelInput) {
- logging.LogDebug(c.BaseChannel.FormatLogMessage(
- "info", "server echoes inputs, setting serverEcho to 'true'"),
- )
-
- echo := true
- c.serverEcho = &echo
-
- b, err = c.readUntilPrompt([]byte{}, nil)
- if err != nil {
- return b, err
- }
- } else {
- logging.LogDebug(c.BaseChannel.FormatLogMessage(
- "info", "server does *not* echo inputs, setting serverEcho to 'false'"),
- )
-
- echo := false
- c.serverEcho = &echo
- }
- }
-
- if c.SelectedNetconfVersion == Version11 {
- returnErr := c.BaseChannel.SendReturn()
- if returnErr != nil {
- return b, returnErr
- }
- }
-
- return b, nil
-}
diff --git a/netconf/unlock.go b/netconf/unlock.go
deleted file mode 100644
index 5822ddc..0000000
--- a/netconf/unlock.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// Unlock issue unlock rpc to device.
-func (d *Driver) Unlock(target string) (*Response, error) {
- netconfMessage := d.BuildUnlockElem(target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/netconf/validate.go b/netconf/validate.go
deleted file mode 100644
index 83ab79c..0000000
--- a/netconf/validate.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package netconf
-
-// Validate issue validate rpc to device.
-func (d *Driver) Validate(target string) (*Response, error) {
- netconfMessage := d.BuildValidateElem(target)
-
- return d.finalizeAndSendMessage(netconfMessage)
-}
diff --git a/platform/definition.go b/platform/definition.go
new file mode 100644
index 0000000..195b62a
--- /dev/null
+++ b/platform/definition.go
@@ -0,0 +1,270 @@
+package platform
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/scrapli/scrapligo/assets"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+
+ "gopkg.in/yaml.v3"
+)
+
+const (
+ // AristaEos is a constant representing the platform string/name for Arista EOS devices.
+ AristaEos = "arista_eos"
+ // CiscoIosxe is a constant representing the platform string/name for Cisco IOSXE devices.
+ CiscoIosxe = "cisco_iosxe"
+ // CiscoIosxr is a constant representing the platform string/name for Cisco IOSXR devices.
+ CiscoIosxr = "cisco_iosxr"
+ // CiscoNxos is a constant representing the platform string/name for Cisco NXOS devices.
+ CiscoNxos = "cisco_nxos"
+ // JuniperJunos is a constant representing the platform string/name for Juniper JunOS devices.
+ JuniperJunos = "juniper_junos"
+ // NokiaSrl is a constant representing the platform string/name for Nokia SRL/SRLinux devices.
+ NokiaSrl = "nokia_srl"
+)
+
+// GetPlatformNames is used to get the "core" (as in embedded in assets and used in testing)
+// platform names.
+func GetPlatformNames() []string {
+ return []string{
+ AristaEos, CiscoIosxe, CiscoIosxr, CiscoNxos, JuniperJunos, NokiaSrl,
+ }
+}
+
+func loadPlatformDefinitionFromAssets(f string) ([]byte, error) {
+ if !strings.HasSuffix(f, ".yaml") {
+ f += ".yaml"
+ }
+
+ return assets.Assets.ReadFile(fmt.Sprintf("platforms/%s", f))
+}
+
+func loadPlatformDefinition(f string) (*Definition, error) {
+ b, err := loadPlatformDefinitionFromAssets(f)
+ if err != nil {
+ b, err = util.ResolveAtFileOrURL(f)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ pd := &Definition{}
+
+ err = yaml.Unmarshal(b, pd)
+ if err != nil {
+ return nil, err
+ }
+
+ return pd, nil
+}
+
+func setDriver(host string, p *Platform, opts ...util.Option) error {
+ finalOpts := p.AsOptions()
+ finalOpts = append(finalOpts, opts...)
+
+ var err error
+
+ switch p.DriverType {
+ case "generic":
+ var d *generic.Driver
+
+ d, err = generic.NewDriver(host, finalOpts...)
+ if err != nil {
+ return err
+ }
+
+ p.genericDriver = d
+ case "network":
+ var d *network.Driver
+
+ d, err = network.NewDriver(host, finalOpts...)
+ if err != nil {
+ return err
+ }
+
+ p.networkDriver = d
+ }
+
+ return err
+}
+
+// NewPlatformVariant returns an instance of Platform from the platform definition file f. The
+// provided variant data is merged back into the "base" platform definition. The host and
+// any provided options are stored and will be applied when fetching the generic or network driver
+// via the GetGenericDriver or GetNetworkDriver methods.
+func NewPlatformVariant(f, variant, host string, opts ...util.Option) (*Platform, error) {
+ pd, err := loadPlatformDefinition(f)
+ if err != nil {
+ return nil, err
+ }
+
+ p := pd.Default
+
+ vp, ok := pd.Variants[variant]
+ if !ok {
+ return nil, fmt.Errorf("%w: no variant '%s' in platform", util.ErrPlatformError, variant)
+ }
+
+ p.mergeVariant(vp)
+
+ err = setDriver(host, p, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return p, nil
+}
+
+// NewPlatform returns an instance of Platform from the platform definition file f. The host and
+// any provided options are stored and will be applied when fetching the generic or network driver
+// via the GetGenericDriver or GetNetworkDriver methods.
+func NewPlatform(f, host string, opts ...util.Option) (*Platform, error) {
+ pd, err := loadPlatformDefinition(f)
+ if err != nil {
+ return nil, err
+ }
+
+ p := pd.Default
+
+ err = setDriver(host, p, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return p, nil
+}
+
+// Definition is a struct representing a JSON or YAML platform definition file.
+type Definition struct {
+ Default *Platform `json:"default" yaml:"default"`
+ Variants map[string]*Platform `json:"variants" yaml:"variants"`
+}
+
+// Platform is a struct that contains JSON or YAML data that represent the attributes required to
+// create a generic or network driver to connect to a given device type.
+type Platform struct {
+ // DriverType generic||network
+ DriverType string `yaml:"driver-type"`
+
+ FailedWhenContains []string `json:"failed-when-contains" yaml:"failed-when-contains"`
+ OnOpen onXDefinitions `json:"on-open" yaml:"on-open"`
+ OnClose onXDefinitions `json:"on-close" yaml:"on-close"`
+
+ PrivilegeLevels network.PrivilegeLevels `json:"privilege-levels" yaml:"privilege-levels"`
+ DefaultDesiredPrivilegeLevel string `json:"default-desired-privilege-level" yaml:"default-desired-privilege-level"`
+ NetworkOnOpen onXDefinitions `json:"network-on-open" yaml:"network-on-open"`
+ NetworkOnClose onXDefinitions `json:"network-on-close" yaml:"network-on-close"`
+
+ Options optionDefinitions `json:"options" yaml:"options"`
+
+ genericDriver *generic.Driver
+ networkDriver *network.Driver
+}
+
+func (p *Platform) mergeVariant(v *Platform) {
+ if v.DriverType != "" {
+ p.DriverType = v.DriverType
+ }
+
+ if len(v.FailedWhenContains) > 0 {
+ p.FailedWhenContains = v.FailedWhenContains
+ }
+
+ if v.OnOpen != nil {
+ p.OnOpen = v.OnOpen
+ }
+
+ if v.OnClose != nil {
+ p.OnClose = v.OnClose
+ }
+
+ if len(v.PrivilegeLevels) > 0 {
+ p.PrivilegeLevels = v.PrivilegeLevels
+ }
+
+ if v.DefaultDesiredPrivilegeLevel != "" {
+ p.DefaultDesiredPrivilegeLevel = v.DefaultDesiredPrivilegeLevel
+ }
+
+ if v.NetworkOnOpen != nil {
+ p.NetworkOnOpen = v.NetworkOnOpen
+ }
+
+ if v.NetworkOnClose != nil {
+ p.NetworkOnClose = v.NetworkOnClose
+ }
+}
+
+// GetGenericDriver returns an instance of generic.Driver built from the Platform data. If the
+// platform data (JSON/YAML) specifies a network driver type this will return an error.
+func (p *Platform) GetGenericDriver() (*generic.Driver, error) {
+ if p.genericDriver == nil {
+ return nil, fmt.Errorf(
+ "%w: requested generic driver, but generic driver is nil",
+ util.ErrPlatformError,
+ )
+ }
+
+ return p.genericDriver, nil
+}
+
+// GetNetworkDriver returns an instance of network.Driver built from the Platform data. If the
+// platform data (JSON/YAML) specifies a generic driver type this will return an error.
+func (p *Platform) GetNetworkDriver() (*network.Driver, error) {
+ if p.networkDriver == nil {
+ return nil, fmt.Errorf(
+ "%w: requested network driver, but network driver is nil",
+ util.ErrPlatformError,
+ )
+ }
+
+ return p.networkDriver, nil
+}
+
+func (p *Platform) genericOptions() []util.Option {
+ opts := make([]util.Option, 0)
+
+ if len(p.FailedWhenContains) > 0 {
+ opts = append(opts, options.WithFailedWhenContains(p.FailedWhenContains))
+ }
+
+ if len(p.OnOpen) > 0 {
+ opts = append(opts, options.WithOnOpen(p.OnOpen.asGenericOnX()))
+ }
+
+ if len(p.OnClose) > 0 {
+ opts = append(opts, options.WithOnClose(p.OnClose.asGenericOnX()))
+ }
+
+ return opts
+}
+
+// AsOptions returns a slice of options that the platform represents.
+func (p *Platform) AsOptions() []util.Option {
+ opts := p.genericOptions()
+
+ opts = append(
+ opts,
+ options.WithPrivilegeLevels(p.PrivilegeLevels),
+ options.WithDefaultDesiredPriv(p.DefaultDesiredPrivilegeLevel),
+ )
+
+ if len(p.NetworkOnOpen) > 0 {
+ opts = append(opts, options.WithNetworkOnOpen(p.NetworkOnOpen.asNetworkOnX()))
+ }
+
+ if len(p.NetworkOnClose) > 0 {
+ opts = append(opts, options.WithNetworkOnClose(p.NetworkOnClose.asNetworkOnX()))
+ }
+
+ opts = append(opts, p.Options.asOptions()...)
+
+ return opts
+}
diff --git a/platform/definition_test.go b/platform/definition_test.go
new file mode 100644
index 0000000..cdc76c6
--- /dev/null
+++ b/platform/definition_test.go
@@ -0,0 +1,121 @@
+package platform_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/platform"
+)
+
+type testPlatformTestCase struct {
+ description string
+ f string
+}
+
+func testNewPlatform(testName string, testCase *testPlatformTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ p, err := platform.NewPlatform(testCase.f, "localhost")
+ if err != nil {
+ t.Fatalf("failed creating platform instance, error: %s", err)
+ }
+
+ actual, err := json.Marshal(p)
+ if err != nil {
+ t.Fatalf("failed marshaling platform, error: %s", err)
+ }
+
+ if *update {
+ writeGolden(t, testName, actual)
+ }
+
+ expected := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actual, expected) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actual,
+ expected,
+ )
+ }
+ }
+}
+
+func TestNewPlatform(t *testing.T) {
+ cases := map[string]*testPlatformTestCase{
+ "new-platform-from-assets": {
+ description: "simple test to generate platform from embedded assets",
+ f: "cisco_iosxe",
+ },
+ "new-platform-from-file": {
+ description: "simple test to generate platform from user provided file",
+ f: "./test-fixtures/explicit_cisco_iosxe.yaml",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testNewPlatform(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+type testPlatformVariantTestCase struct {
+ description string
+ f string
+ variant string
+}
+
+func testNewPlatformVariant(
+ testName string,
+ testCase *testPlatformVariantTestCase,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ p, err := platform.NewPlatformVariant(testCase.f, testCase.variant, "localhost")
+ if err != nil {
+ t.Fatalf("failed creating platform instance, error: %s", err)
+ }
+
+ actual, err := json.Marshal(p)
+ if err != nil {
+ t.Fatalf("failed marshaling platform, error: %s", err)
+ }
+
+ if *update {
+ writeGolden(t, testName, actual)
+ }
+
+ expected := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actual, expected) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actual,
+ expected,
+ )
+ }
+ }
+}
+
+func TestNewPlatformVariant(t *testing.T) {
+ cases := map[string]*testPlatformVariantTestCase{
+ "new-platform-variant-from-file": {
+ description: "simple test to generate platform variant",
+ f: "./test-fixtures/explicit_cisco_iosxe.yaml",
+ variant: "testing1",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testNewPlatformVariant(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/platform/onx.go b/platform/onx.go
new file mode 100644
index 0000000..610ca70
--- /dev/null
+++ b/platform/onx.go
@@ -0,0 +1,100 @@
+package platform
+
+import (
+ "fmt"
+
+ "github.com/scrapli/scrapligo/driver/generic"
+ "github.com/scrapli/scrapligo/driver/network"
+
+ "github.com/scrapli/scrapligo/channel"
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ // OpChannelWrite is a constant that represents the channel write operation string in a
+ // platform definition file.
+ OpChannelWrite = "channel.write"
+ // OpChannelReturn is a constant that represents the channel return operation string in a
+ // platform definition file.
+ OpChannelReturn = "channel.return"
+ // OpAcquirePriv is a constant that represents the network driver acquire priv operation string
+ // in a platform definition file.
+ OpAcquirePriv = "acquire-priv"
+ // OpDriverSendCommand is a constant that represents the generic or network driver send command
+ // operation in a platform definition file.
+ OpDriverSendCommand = "driver.send-command"
+)
+
+type onXDefinitions []map[string]interface{}
+
+func channelWrite(op map[string]interface{}, c *channel.Channel) error {
+ i, ok := op["input"].(string)
+ if !ok {
+ return fmt.Errorf("%w: bad value", util.ErrBadOption)
+ }
+
+ r, ok := op["redacted"].(bool)
+ if !ok {
+ r = false
+ }
+
+ return c.Write([]byte(i), r)
+}
+
+func (o *onXDefinitions) asGenericOnX() func(d *generic.Driver) error {
+ return func(d *generic.Driver) error {
+ for _, op := range *o {
+ var err error
+
+ switch op["operation"].(string) {
+ case OpChannelWrite:
+ err = channelWrite(op, d.Channel)
+ case OpChannelReturn:
+ err = d.Channel.WriteReturn()
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+}
+
+func (o *onXDefinitions) asNetworkOnX() func(d *network.Driver) error {
+ return func(d *network.Driver) error {
+ for _, op := range *o {
+ var err error
+
+ switch op["operation"].(string) {
+ case OpChannelWrite:
+ err = channelWrite(op, d.Channel)
+ case OpChannelReturn:
+ err = d.Channel.WriteReturn()
+ case OpAcquirePriv:
+ target := d.DefaultDesiredPriv
+
+ t, ok := op["target"].(string)
+ if ok {
+ target = t
+ }
+
+ err = d.AcquirePriv(target)
+ case OpDriverSendCommand:
+ c, ok := op["command"].(string)
+ if !ok {
+ return fmt.Errorf("%w: bad value", util.ErrBadOption)
+ }
+
+ _, err = d.SendCommand(c)
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+}
diff --git a/platform/onx_test.go b/platform/onx_test.go
new file mode 100644
index 0000000..e679b07
--- /dev/null
+++ b/platform/onx_test.go
@@ -0,0 +1,117 @@
+package platform_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+type testOnXTestCase struct {
+ description string
+ payloadFile string
+ platformFile string
+}
+
+func testOnXGeneric(testName string, testCase *testOnXTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(
+ t,
+ testName,
+ testCase.platformFile,
+ testCase.payloadFile,
+ )
+
+ err := d.Driver.OnOpen(d.Driver)
+ if err != nil {
+ t.Fatalf("%s: response object indicates failure", testName)
+ }
+
+ actualOut := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualOut)
+ }
+
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualOut, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestOnXGeneric(t *testing.T) {
+ cases := map[string]*testOnXTestCase{
+ "generic-on-x-simple": {
+ description: "simple generic on-x operation test",
+ platformFile: "test-platform.yaml",
+ payloadFile: "generic-on-x-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testOnXGeneric(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
+
+func testOnXNetwork(testName string, testCase *testOnXTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ d, fileTransportObj := prepareDriver(
+ t,
+ testName,
+ testCase.platformFile,
+ testCase.payloadFile,
+ )
+
+ err := d.OnOpen(d)
+ if err != nil {
+ t.Fatalf("%s: response object indicates failure", testName)
+ }
+
+ actualOut := bytes.Join(fileTransportObj.Writes, []byte("\n"))
+
+ if *update {
+ writeGolden(t, testName, actualOut)
+ }
+
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualOut, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestOnXNetwork(t *testing.T) {
+ cases := map[string]*testOnXTestCase{
+ "network-on-x-simple": {
+ description: "simple network on-x operation test",
+ platformFile: "test-platform.yaml",
+ payloadFile: "network-on-x-simple.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testOnXNetwork(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/platform/options.go b/platform/options.go
new file mode 100644
index 0000000..deaa960
--- /dev/null
+++ b/platform/options.go
@@ -0,0 +1,90 @@
+package platform
+
+import (
+ "regexp"
+ "time"
+
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ port = "port"
+
+ authBypass = "auth-bypass"
+ authStrictKey = "auth-strict-key"
+
+ promptPattern = "prompt-pattern"
+ usernamePattern = "username-pattern"
+ passwordPattern = "password-pattern"
+ passphrasePattern = "passphrase-pattern"
+
+ returnChar = "return-char"
+
+ // read delay in seconds for channel read loop.
+ readDelay = "read-delay"
+
+ // timeouts in seconds.
+ timeoutOps = "timeout-ops"
+
+ transportType = "transport-type"
+ // read size for transport read chunk.
+ transportReadSize = "read-size"
+ transportPtyHeight = "transport-pty-height"
+ transportPtyWidth = "transport-pty-width"
+
+ transportSystemOpenArgs = "transport-system-open-args"
+)
+
+type optionDefinition struct {
+ Option string `json:"option" yaml:"option"`
+ Value interface{} `json:"value" yaml:"value"`
+}
+
+type optionDefinitions []*optionDefinition
+
+func (o *optionDefinitions) asOptions() []util.Option { //nolint: gocyclo
+ opts := make([]util.Option, len(*o))
+
+ for i, opt := range *o {
+ switch opt.Option {
+ case port:
+ opts[i] = options.WithPort(opt.Value.(int))
+ case authBypass:
+ opts[i] = options.WithAuthBypass()
+ case authStrictKey:
+ opts[i] = options.WithAuthNoStrictKey()
+ case promptPattern:
+ opts[i] = options.WithPromptPattern(regexp.MustCompile(opt.Value.(string)))
+ case usernamePattern:
+ opts[i] = options.WithUsernamePattern(regexp.MustCompile(opt.Value.(string)))
+ case passwordPattern:
+ opts[i] = options.WithPasswordPattern(regexp.MustCompile(opt.Value.(string)))
+ case passphrasePattern:
+ opts[i] = options.WithPassphrasePattern(regexp.MustCompile(opt.Value.(string)))
+ case returnChar:
+ opts[i] = options.WithReturnChar(opt.Value.(string))
+ case readDelay:
+ opts[i] = options.WithReadDelay(
+ time.Duration(opt.Value.(float64) * float64(time.Second)),
+ )
+ case timeoutOps:
+ opts[i] = options.WithTimeoutOps(
+ time.Duration(opt.Value.(float64) * float64(time.Second)),
+ )
+ case transportType:
+ opts[i] = options.WithTransportType(opt.Value.(string))
+ case transportReadSize:
+ opts[i] = options.WithTransportReadSize(opt.Value.(int))
+ case transportPtyHeight:
+ opts[i] = options.WithTermHeight(opt.Value.(int))
+ case transportPtyWidth:
+ opts[i] = options.WithTermWidth(opt.Value.(int))
+ case transportSystemOpenArgs:
+ opts[i] = options.WithSystemTransportOpenArgs(opt.Value.([]string))
+ }
+ }
+
+ return opts
+}
diff --git a/platform/platform_test.go b/platform/platform_test.go
new file mode 100644
index 0000000..2688b52
--- /dev/null
+++ b/platform/platform_test.go
@@ -0,0 +1,106 @@
+package platform_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/scrapli/scrapligo/driver/network"
+ "github.com/scrapli/scrapligo/driver/options"
+
+ "github.com/scrapli/scrapligo/platform"
+
+ "github.com/scrapli/scrapligo/transport"
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func resolveFile(t *testing.T, f string) string {
+ f, err := filepath.Abs(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return f
+}
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualOut []byte) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+
+ err := os.WriteFile(goldenOut, actualOut, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func prepareDriver(
+ t *testing.T,
+ testName,
+ platformFile,
+ payloadFile string,
+) (*network.Driver, *transport.File) {
+ p, err := platform.NewPlatform(
+ resolveFile(t, platformFile),
+ "dummy",
+ options.WithTransportType(transport.FileTransport),
+ options.WithFileTransportFile(resolveFile(t, payloadFile)),
+ options.WithTransportReadSize(1),
+ )
+ if err != nil {
+ t.Errorf("%s: encountered error creating Platform, error: %s", testName, err)
+ }
+
+ d, err := p.GetNetworkDriver()
+ if err != nil {
+ t.Errorf(
+ "%s: encountered error fetching network driver from platform, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ err = d.Channel.Open()
+ if err != nil {
+ t.Errorf("%s: encountered error opening Channel, error: %s", testName, err)
+ }
+
+ fileTransportObj, ok := d.Transport.Impl.(*transport.File)
+ if !ok {
+ t.Fatal("transport implementation is not Transport File")
+ }
+
+ return d, fileTransportObj
+}
diff --git a/platform/test-fixtures/explicit_cisco_iosxe.yaml b/platform/test-fixtures/explicit_cisco_iosxe.yaml
new file mode 100644
index 0000000..5052c4b
--- /dev/null
+++ b/platform/test-fixtures/explicit_cisco_iosxe.yaml
@@ -0,0 +1,67 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}>$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-@/:]{1,63}#$'
+ previous-priv: 'exec'
+ deescalate: 'disable'
+ escalate: 'enable'
+ escalate-auth: true
+ escalate-prompt: '^(?:enable\s){0,1}password:\s?$'
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@/:]{1,63}\([\w.\-@/:+]{0,32}\)#$'
+ not-contains:
+ - 'tcl)'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ tclsh:
+ name: 'tclsh'
+ pattern: '(?im)^([\w.\-@/+>:]+\(tcl\)[>#]|\+>)$'
+ previous-priv: 'privilege-exec'
+ deescalate: 'tclquit'
+ escalate: 'tclsh'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'privilege-exec'
+ failed-when-contains:
+ - '% Ambiguous command'
+ - '% Incomplete command'
+ - '% Invalid input detected'
+ - '% Unknown command'
+ textfsm-platform: 'cisco_iosxe' # ignored in go because no ntc-templates
+ network-on-open:
+ - operation: 'acquire-priv' # targets default desired priv by default
+ - operation: 'driver.send-command'
+ command: 'terminal width 512'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ network-on-close:
+ - operation: 'acquire-priv'
+ - operation: 'channel.write'
+ input: 'exit'
+ - operation: 'channel.return'
+variants:
+ testing1:
+ privilege-levels:
+ overwriteprivs:
+ name: 'overwriteprivs'
+ pattern: '(?im)^[\w.\-@/:]{1,63}>$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
\ No newline at end of file
diff --git a/platform/test-fixtures/generic-on-x-simple.txt b/platform/test-fixtures/generic-on-x-simple.txt
new file mode 100644
index 0000000..adef55f
--- /dev/null
+++ b/platform/test-fixtures/generic-on-x-simple.txt
@@ -0,0 +1,2 @@
+C3560CX#something
+C3560CX#
\ No newline at end of file
diff --git a/platform/test-fixtures/golden/generic-on-x-simple-out.txt b/platform/test-fixtures/golden/generic-on-x-simple-out.txt
new file mode 100644
index 0000000..6be79b2
--- /dev/null
+++ b/platform/test-fixtures/golden/generic-on-x-simple-out.txt
@@ -0,0 +1,2 @@
+something
+
diff --git a/platform/test-fixtures/golden/network-on-x-simple-out.txt b/platform/test-fixtures/golden/network-on-x-simple-out.txt
new file mode 100644
index 0000000..7ea147b
--- /dev/null
+++ b/platform/test-fixtures/golden/network-on-x-simple-out.txt
@@ -0,0 +1,15 @@
+
+
+enable
+
+
+
+
+terminal width 32767
+
+
+terminal length 0
+
+
+something
+
diff --git a/platform/test-fixtures/golden/new-platform-from-assets-out.txt b/platform/test-fixtures/golden/new-platform-from-assets-out.txt
new file mode 100644
index 0000000..1f9f3a8
--- /dev/null
+++ b/platform/test-fixtures/golden/new-platform-from-assets-out.txt
@@ -0,0 +1 @@
+{"DriverType":"network","failed-when-contains":["% Ambiguous command","% Incomplete command","% Invalid input detected","% Unknown command"],"on-open":null,"on-close":null,"privilege-levels":{"configuration":{"Name":"configuration","Pattern":"(?im)^[\\w.\\-@/:]{1,63}\\([\\w.\\-@/:+]{0,32}\\)#$","NotContains":["tcl)"],"PreviousPriv":"privilege-exec","Deescalate":"end","Escalate":"configure terminal","EscalateAuth":false,"EscalatePrompt":""},"exec":{"Name":"exec","Pattern":"(?im)^[\\w.\\-@/:]{1,63}\u003e$","NotContains":null,"PreviousPriv":"","Deescalate":"","Escalate":"","EscalateAuth":false,"EscalatePrompt":""},"privilege-exec":{"Name":"privilege-exec","Pattern":"(?im)^[\\w.\\-@/:]{1,63}#$","NotContains":null,"PreviousPriv":"exec","Deescalate":"disable","Escalate":"enable","EscalateAuth":true,"EscalatePrompt":"(?im)^(?:enable\\s){0,1}password:\\s?$"},"tclsh":{"Name":"tclsh","Pattern":"(?im)^([\\w.\\-@/+\u003e:]+\\(tcl\\)[\u003e#]|\\+\u003e)$","NotContains":null,"PreviousPriv":"privilege-exec","Deescalate":"tclquit","Escalate":"tclsh","EscalateAuth":false,"EscalatePrompt":""}},"default-desired-privilege-level":"privilege-exec","network-on-open":[{"operation":"acquire-priv"},{"command":"terminal width 512","operation":"driver.send-command"},{"command":"terminal length 0","operation":"driver.send-command"}],"network-on-close":[{"operation":"acquire-priv"},{"input":"exit","operation":"channel.write"},{"operation":"channel.return"}],"options":null}
\ No newline at end of file
diff --git a/platform/test-fixtures/golden/new-platform-from-file-out.txt b/platform/test-fixtures/golden/new-platform-from-file-out.txt
new file mode 100644
index 0000000..e23b0bb
--- /dev/null
+++ b/platform/test-fixtures/golden/new-platform-from-file-out.txt
@@ -0,0 +1 @@
+{"DriverType":"network","failed-when-contains":["% Ambiguous command","% Incomplete command","% Invalid input detected","% Unknown command"],"on-open":null,"on-close":null,"privilege-levels":{"configuration":{"Name":"configuration","Pattern":"(?im)^[\\w.\\-@/:]{1,63}\\([\\w.\\-@/:+]{0,32}\\)#$","NotContains":["tcl)"],"PreviousPriv":"privilege-exec","Deescalate":"end","Escalate":"configure terminal","EscalateAuth":false,"EscalatePrompt":""},"exec":{"Name":"exec","Pattern":"(?im)^[\\w.\\-@/:]{1,63}\u003e$","NotContains":null,"PreviousPriv":"","Deescalate":"","Escalate":"","EscalateAuth":false,"EscalatePrompt":""},"privilege-exec":{"Name":"privilege-exec","Pattern":"(?im)^[\\w.\\-@/:]{1,63}#$","NotContains":null,"PreviousPriv":"exec","Deescalate":"disable","Escalate":"enable","EscalateAuth":true,"EscalatePrompt":"^(?:enable\\s){0,1}password:\\s?$"},"tclsh":{"Name":"tclsh","Pattern":"(?im)^([\\w.\\-@/+\u003e:]+\\(tcl\\)[\u003e#]|\\+\u003e)$","NotContains":null,"PreviousPriv":"privilege-exec","Deescalate":"tclquit","Escalate":"tclsh","EscalateAuth":false,"EscalatePrompt":""}},"default-desired-privilege-level":"privilege-exec","network-on-open":[{"operation":"acquire-priv"},{"command":"terminal width 512","operation":"driver.send-command"},{"command":"terminal length 0","operation":"driver.send-command"}],"network-on-close":[{"operation":"acquire-priv"},{"input":"exit","operation":"channel.write"},{"operation":"channel.return"}],"options":null}
\ No newline at end of file
diff --git a/platform/test-fixtures/golden/new-platform-variant-from-file-out.txt b/platform/test-fixtures/golden/new-platform-variant-from-file-out.txt
new file mode 100644
index 0000000..4a087bb
--- /dev/null
+++ b/platform/test-fixtures/golden/new-platform-variant-from-file-out.txt
@@ -0,0 +1 @@
+{"DriverType":"network","failed-when-contains":["% Ambiguous command","% Incomplete command","% Invalid input detected","% Unknown command"],"on-open":null,"on-close":null,"privilege-levels":{"overwriteprivs":{"Name":"overwriteprivs","Pattern":"(?im)^[\\w.\\-@/:]{1,63}\u003e$","NotContains":null,"PreviousPriv":"","Deescalate":"","Escalate":"","EscalateAuth":false,"EscalatePrompt":""}},"default-desired-privilege-level":"privilege-exec","network-on-open":[{"operation":"acquire-priv"},{"command":"terminal width 512","operation":"driver.send-command"},{"command":"terminal length 0","operation":"driver.send-command"}],"network-on-close":[{"operation":"acquire-priv"},{"input":"exit","operation":"channel.write"},{"operation":"channel.return"}],"options":null}
\ No newline at end of file
diff --git a/platform/test-fixtures/network-on-x-simple.txt b/platform/test-fixtures/network-on-x-simple.txt
new file mode 100644
index 0000000..6cfd0ae
--- /dev/null
+++ b/platform/test-fixtures/network-on-x-simple.txt
@@ -0,0 +1,6 @@
+C3560CX>enable
+C3560CX#
+C3560CX#terminal width 32767
+C3560CX#terminal length 0
+C3560CX#something
+C3560CX#
diff --git a/platform/test-fixtures/test-platform.yaml b/platform/test-fixtures/test-platform.yaml
new file mode 100644
index 0000000..b368c3f
--- /dev/null
+++ b/platform/test-fixtures/test-platform.yaml
@@ -0,0 +1,42 @@
+---
+default:
+ driver-type: 'network'
+ privilege-levels:
+ exec:
+ name: 'exec'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}>\s?$'
+ previous-priv:
+ deescalate:
+ escalate:
+ escalate-auth: false
+ escalate-prompt:
+ privilege-exec:
+ name: 'privilege-exec'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}#\s?$'
+ previous-priv: 'exec'
+ deescalate: 'disable'
+ escalate: 'enable'
+ escalate-auth: true
+ escalate-prompt: '(?im)^[pP]assword:\s?$'
+ configuration:
+ name: 'configuration'
+ pattern: '(?im)^[\w.\-@()/: ]{1,63}\(config[\w.\-@/:]{0,32}\)#\s?$'
+ previous-priv: 'privilege-exec'
+ deescalate: 'end'
+ escalate: 'configure terminal'
+ escalate-auth: false
+ escalate-prompt:
+ default-desired-privilege-level: 'privilege-exec'
+ on-open:
+ - operation: 'channel.write'
+ input: 'something'
+ - operation: 'channel.return'
+ network-on-open:
+ - operation: 'acquire-priv'
+ - operation: 'driver.send-command'
+ command: 'terminal width 32767'
+ - operation: 'driver.send-command'
+ command: 'terminal length 0'
+ - operation: 'channel.write'
+ input: 'something'
+ - operation: 'channel.return'
\ No newline at end of file
diff --git a/response/errors.go b/response/errors.go
new file mode 100644
index 0000000..486949c
--- /dev/null
+++ b/response/errors.go
@@ -0,0 +1,43 @@
+package response
+
+import "fmt"
+
+// OperationError is an error object returned when a scrapli operation completes "successfully" --
+// as in does not have an EOF/timeout or otherwise unrecoverable error -- but contains output in the
+// device's response indicating that an input was bad/invalid or device failed to process it at
+// that time.
+type OperationError struct {
+ Input string
+ Output string
+ ErrorString string
+}
+
+// Error returns an error string for the OperationError object.
+func (e *OperationError) Error() string {
+ return fmt.Sprintf(
+ "operation error from input '%s'. indicated error '%s'",
+ e.Input,
+ e.ErrorString,
+ )
+}
+
+// MultiOperationError is an error object for scrapli *multi* operations.
+type MultiOperationError struct {
+ Operations []*OperationError
+}
+
+// Error returns an error string for the MultiOperationError object.
+func (e *MultiOperationError) Error() string {
+ if len(e.Operations) == 1 {
+ return fmt.Sprintf(
+ "operation error from input '%s'. indicated error '%s'",
+ e.Operations[0].Input,
+ e.Operations[0].ErrorString,
+ )
+ }
+
+ return fmt.Sprintf(
+ "operation error from multiple inputs. %d indicated errors",
+ len(e.Operations),
+ )
+}
diff --git a/driver/base/multiresponse.go b/response/multi.go
similarity index 77%
rename from driver/base/multiresponse.go
rename to response/multi.go
index 1de6c78..3b6d9cb 100644
--- a/driver/base/multiresponse.go
+++ b/response/multi.go
@@ -1,28 +1,20 @@
-package base
+package response
import (
- "fmt"
"strings"
"time"
)
-type MultiOperationError struct {
- Operations []*OperationError
-}
-
-func (e *MultiOperationError) Error() string {
- if len(e.Operations) == 1 {
- return fmt.Sprintf(
- "operation error from input '%s'. indicated error '%s'",
- e.Operations[0].Input,
- e.Operations[0].ErrorString,
- )
+// NewMultiResponse creates a new MultiResponse object.
+func NewMultiResponse(host string) *MultiResponse {
+ r := &MultiResponse{
+ Host: host,
+ StartTime: time.Now(),
+ EndTime: time.Time{},
+ ElapsedTime: 0,
}
- return fmt.Sprintf(
- "operation error from multiple inputs. %d indicated errors",
- len(e.Operations),
- )
+ return r
}
// MultiResponse defines a response object for plural operations -- contains a slice of `Responses`
@@ -36,20 +28,11 @@ type MultiResponse struct {
Failed error
}
-// NewMultiResponse creates a new MultiResponse object.
-func NewMultiResponse(host string) *MultiResponse {
- r := &MultiResponse{
- Host: host,
- StartTime: time.Now(),
- EndTime: time.Time{},
- ElapsedTime: 0,
- }
-
- return r
-}
-
// AppendResponse appends a response to the `Responses` attribute of the MultiResponse object.
func (mr *MultiResponse) AppendResponse(r *Response) {
+ mr.EndTime = time.Now()
+ mr.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
+
re, _ := r.Failed.(*OperationError)
if re != nil {
diff --git a/response/netconf.go b/response/netconf.go
new file mode 100644
index 0000000..9307176
--- /dev/null
+++ b/response/netconf.go
@@ -0,0 +1,157 @@
+package response
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ v1Dot0 = "1.0"
+ v1Dot1 = "1.1"
+ v1Dot0Delim = "]]>]]>"
+ xmlHeader = ""
+)
+
+type netconfPatterns struct {
+ v1dot1Chunk *regexp.Regexp
+ rpcErrors *regexp.Regexp
+}
+
+var (
+ netconfPatternsInstance *netconfPatterns //nolint:gochecknoglobals
+ netconfPatternsInstanceOnce sync.Once //nolint:gochecknoglobals
+)
+
+func getNetconfPatterns() *netconfPatterns {
+ netconfPatternsInstanceOnce.Do(func() {
+ netconfPatternsInstance = &netconfPatterns{
+ v1dot1Chunk: regexp.MustCompile(`(?ms)(\d+)\n(.*?)#`),
+ rpcErrors: regexp.MustCompile(`(?s)(.*) `),
+ }
+ })
+
+ return netconfPatternsInstance
+}
+
+// NewNetconfResponse prepares a new NetconfResponse object.
+func NewNetconfResponse(
+ input []byte,
+ host string,
+ port int,
+ version string,
+) *NetconfResponse {
+ return &NetconfResponse{
+ Host: host,
+ Port: port,
+ Input: input,
+ Result: "",
+ StartTime: time.Now(),
+ EndTime: time.Time{},
+ ElapsedTime: 0,
+ FailedWhenContains: [][]byte{
+ []byte(""),
+ []byte(""),
+ []byte(" "),
+ []byte(""),
+ },
+ NetconfVersion: version,
+ }
+}
+
+// NetconfResponse is a struct returned from all netconf driver operations.
+type NetconfResponse struct {
+ Host string
+ Port int
+ Input []byte
+ RawResult []byte
+ Result string
+ StartTime time.Time
+ EndTime time.Time
+ ElapsedTime float64
+ FailedWhenContains [][]byte
+ Failed error
+ StripNamespaces bool
+ NetconfVersion string
+ ErrorMessages [][]string
+ SubscriptionID int
+}
+
+// Record records the output of a NETCONF operation.
+func (r *NetconfResponse) Record(b []byte) {
+ r.EndTime = time.Now()
+ r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
+
+ r.RawResult = b
+
+ if util.ByteContainsAny(r.RawResult, r.FailedWhenContains) {
+ patterns := getNetconfPatterns()
+
+ r.Failed = &OperationError{
+ Input: string(r.Input),
+ Output: r.Result,
+ ErrorString: string(patterns.rpcErrors.Find(r.RawResult)),
+ }
+ }
+
+ switch r.NetconfVersion {
+ case v1Dot0:
+ r.record1dot0()
+ case v1Dot1:
+ r.record1dot1()
+ }
+}
+
+func (r *NetconfResponse) record1dot0() {
+ b := r.RawResult
+
+ b = bytes.TrimPrefix(b, []byte(xmlHeader))
+ b = bytes.TrimSuffix(b, []byte(v1Dot0Delim))
+
+ r.Result = string(bytes.TrimSpace(b))
+}
+
+func (r *NetconfResponse) validateChunk(i int, b []byte) {
+ // does this need more ... "massaging" like scrapli?
+ // chunk regex matches the newline before the chunk size or end of message delimiter, so we
+ // subtract one for that newline char
+ if len(b)-1 != i {
+ errMsg := fmt.Sprintf("return element lengh invalid, expted: %d, got %d for element: %s\n",
+ i,
+ len(b)-1,
+ b)
+
+ r.Failed = &OperationError{
+ Input: string(r.Input),
+ Output: r.Result,
+ ErrorString: errMsg,
+ }
+ }
+}
+
+func (r *NetconfResponse) record1dot1() {
+ patterns := getNetconfPatterns()
+
+ chunkSections := patterns.v1dot1Chunk.FindAllSubmatch(r.RawResult, -1)
+
+ var joined []byte
+
+ for _, chunkSection := range chunkSections {
+ chunk := chunkSection[2]
+
+ size, _ := strconv.Atoi(string(chunkSection[1]))
+
+ r.validateChunk(size, chunk)
+
+ joined = append(joined, chunk[:len(chunk)-1]...)
+ }
+
+ joined = bytes.TrimPrefix(joined, []byte(xmlHeader))
+
+ r.Result = string(bytes.TrimSpace(joined))
+}
diff --git a/response/netconf_test.go b/response/netconf_test.go
new file mode 100644
index 0000000..0325f26
--- /dev/null
+++ b/response/netconf_test.go
@@ -0,0 +1,73 @@
+package response_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/response"
+)
+
+type testNetconfRecordTestCase struct {
+ description string
+ version string
+ payloadFile string
+}
+
+func testNetconfRecord(testName string, testCase *testNetconfRecordTestCase) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ r := response.NewNetconfResponse(nil, "localhost", 830, testCase.version)
+
+ r.Record(readFile(t, testCase.payloadFile))
+
+ // set the timestamp bits to nil so we dont compare those
+ r.StartTime = time.Time{}
+ r.EndTime = time.Time{}
+ r.ElapsedTime = 0
+
+ actual, err := json.Marshal(r)
+ if err != nil {
+ t.Fatalf("failed marshaling platform, error: %s", err)
+ }
+
+ if *update {
+ writeGolden(t, testName, actual)
+ }
+
+ expected := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actual, expected) {
+ t.Fatalf(
+ "%s: actual and expected inputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actual,
+ expected,
+ )
+ }
+ }
+}
+
+func TestNetconfRecord(t *testing.T) {
+ cases := map[string]*testNetconfRecordTestCase{
+ "record-response-10": {
+ description: "simple test to test recording netconf 1.0 response",
+ version: "1.0",
+ payloadFile: "netconf-output-10.txt",
+ },
+ "record-response-11": {
+ description: "simple test to test recording netconf 1.1 response",
+ version: "1.1",
+ payloadFile: "netconf-output-11.txt",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testNetconfRecord(testName, testCase)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/response/response.go b/response/response.go
new file mode 100644
index 0000000..b33a23f
--- /dev/null
+++ b/response/response.go
@@ -0,0 +1,70 @@
+package response
+
+import (
+ "time"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+// NewResponse prepares a new Response object.
+func NewResponse(
+ input,
+ host string,
+ port int,
+ failedWhenContains []string,
+) *Response {
+ return &Response{
+ Host: host,
+ Port: port,
+ Input: input,
+ Result: "",
+ StartTime: time.Now(),
+ EndTime: time.Time{},
+ ElapsedTime: 0,
+ FailedWhenContains: failedWhenContains,
+ }
+}
+
+// Response is a struct returned from most (all?) generic and network driver "single" operations.
+type Response struct {
+ Host string
+ Port int
+ Input string
+ RawResult []byte
+ Result string
+ StartTime time.Time
+ EndTime time.Time
+ ElapsedTime float64
+ FailedWhenContains []string
+ // Failed returns an error if any of the `FailedWhenContains` substrings are seen in the output
+ // returned from the device. This error indicates that the operation has completed successfully,
+ // but that an input was bad/invalid or device failed to process it at that time
+ Failed error
+}
+
+// Record records the output of an operation.
+func (r *Response) Record(b []byte) {
+ r.EndTime = time.Now()
+ r.ElapsedTime = r.EndTime.Sub(r.StartTime).Seconds()
+
+ r.RawResult = b
+ r.Result = string(b)
+
+ s := util.StringContainsAnySubStrs(r.Result, r.FailedWhenContains)
+
+ if len(s) > 0 {
+ r.Failed = &OperationError{
+ Input: r.Input,
+ Output: r.Result,
+ ErrorString: s,
+ }
+ }
+}
+
+// TextFsmParse parses recorded output w/ a provided textfsm template.
+// the argument is interpreted as URL or filesystem path, for example:
+// response.TextFsmParse("http://example.com/textfsm.template") or
+// response.TextFsmParse("./local/textfsm.template").
+func (r *Response) TextFsmParse(path string) ([]map[string]interface{}, error) {
+ return util.TextFsmParse(r.Result, path)
+}
diff --git a/response/response_test.go b/response/response_test.go
new file mode 100644
index 0000000..4864983
--- /dev/null
+++ b/response/response_test.go
@@ -0,0 +1,52 @@
+package response_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualOut []byte) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+
+ err := os.WriteFile(goldenOut, actualOut, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/response/test-fixtures/golden/record-response-10-out.txt b/response/test-fixtures/golden/record-response-10-out.txt
new file mode 100644
index 0000000..ba2c332
--- /dev/null
+++ b/response/test-fixtures/golden/record-response-10-out.txt
@@ -0,0 +1 @@
+{"Host":"localhost","Port":830,"Input":null,"RawResult":"PHJwYy1yZXBseSB4bWxucz0idXJuOmlldGY6cGFyYW1zOnhtbDpuczpuZXRjb25mOmJhc2U6MS4wIiBtZXNzYWdlLWlkPSIxMDEiPjxkYXRhPjxjbGktY29uZmlnLWRhdGEtYmxvY2s+IQpUSU1FU1RBTVAKIQp2ZXJzaW9uIDE2LjEyCnNlcnZpY2UgdGltZXN0YW1wcyBkZWJ1ZyBkYXRldGltZSBtc2VjCnNlcnZpY2UgdGltZXN0YW1wcyBsb2cgZGF0ZXRpbWUgbXNlYwpzZXJ2aWNlIGNhbGwtaG9tZQpwbGF0Zm9ybSBxZnAgdXRpbGl6YXRpb24gbW9uaXRvciBsb2FkIDgwCnBsYXRmb3JtIHB1bnQta2VlcGFsaXZlIGRpc2FibGUta2VybmVsLWNvcmUKcGxhdGZvcm0gY29uc29sZSBzZXJpYWwKIQpob3N0bmFtZSBjc3IxMDAwdgohCmJvb3Qtc3RhcnQtbWFya2VyCmJvb3QtZW5kLW1hcmtlcgohCiEKZW5hYmxlIHNlY3JldCA5ICQ5JHh2V254OEZlMzVmOHhFJEU5aWpwN0dNL1Y0OFA1eTFVejNJRVB0b3RYZ3drSktZSm1OMHEzcTJFOTIKIQpubyBhYWEgbmV3LW1vZGVsCmNhbGwtaG9tZQogISBJZiBjb250YWN0IGVtYWlsIGFkZHJlc3MgaW4gY2FsbC1ob21lIGlzIGNvbmZpZ3VyZWQgYXMgc2NoLXNtYXJ0LWxpY2Vuc2luZ0BjaXNjby5jb20KICEgdGhlIGVtYWlsIGFkZHJlc3MgY29uZmlndXJlZCBpbiBDaXNjbyBTbWFydCBMaWNlbnNlIFBvcnRhbCB3aWxsIGJlIHVzZWQgYXMgY29udGFjdCBlbWFpbCBhZGRyZXNzIHRvIHNlbmQgU0NIIG5vdGlmaWNhdGlvbnMuCiBjb250YWN0LWVtYWlsLWFkZHIgc2NoLXNtYXJ0LWxpY2Vuc2luZ0BjaXNjby5jb20KIHByb2ZpbGUgIkNpc2NvVEFDLTEiCiAgYWN0aXZlCiAgZGVzdGluYXRpb24gdHJhbnNwb3J0LW1ldGhvZCBodHRwCiAgbm8gZGVzdGluYXRpb24gdHJhbnNwb3J0LW1ldGhvZCBlbWFpbAohCiEKIQohCiEKIQohCmlwIGRvbWFpbiBuYW1lIGV4YW1wbGUuY29tCiEKIQohCmxvZ2luIG9uLXN1Y2Nlc3MgbG9nCiEKIQohCiEKIQohCiEKc3Vic2NyaWJlciB0ZW1wbGF0aW5nCiEKIQohCiEKIQohCm11bHRpbGluayBidW5kbGUtbmFtZSBhdXRoZW50aWNhdGVkCiEKIQohCiEKIQohCiEKIQohCiEKIQohCiEKIQohCkNFUlRJRklDQVRFUyBBTkQgTElDRU5TRQpkaWFnbm9zdGljIGJvb3R1cCBsZXZlbCBtaW5pbWFsCmFyY2hpdmUKIGxvZyBjb25maWcKICBsb2dnaW5nIGVuYWJsZQogcGF0aCBib290Zmxhc2g6Cm1lbW9yeSBmcmVlIGxvdy13YXRlcm1hcmsgcHJvY2Vzc29yIDcyMzI5CiEKIQpzcGFubmluZy10cmVlIGV4dGVuZCBzeXN0ZW0taWQKIQp1c2VybmFtZSBib3hlbiBwcml2aWxlZ2UgMTUgcGFzc3dvcmQgMCBiMHgzTi1iMHgzTgohCnJlZHVuZGFuY3kKIQohCiEKIQohCiEKIQohCiEKIQohCiEKIQohCiEKIQohCiEKIQohCiEKIQohCmludGVyZmFjZSBHaWdhYml0RXRoZXJuZXQxCiBpcCBhZGRyZXNzIDEwLjAuMC4xNSAyNTUuMjU1LjI1NS4wCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDIKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDMKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDQKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDUKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDYKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDcKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDgKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDkKIG5vIGlwIGFkZHJlc3MKIHNodXRkb3duCiBuZWdvdGlhdGlvbiBhdXRvCiBubyBtb3AgZW5hYmxlZAogbm8gbW9wIHN5c2lkCiEKaW50ZXJmYWNlIEdpZ2FiaXRFdGhlcm5ldDEwCiBubyBpcCBhZGRyZXNzCiBzaHV0ZG93bgogbmVnb3RpYXRpb24gYXV0bwogbm8gbW9wIGVuYWJsZWQKIG5vIG1vcCBzeXNpZAohCiEKdmlydHVhbC1zZXJ2aWNlIGNzcl9tZ210CiEKaXAgZm9yd2FyZC1wcm90b2NvbCBuZApubyBpcCBodHRwIHNlcnZlcgpubyBpcCBodHRwIHNlY3VyZS1zZXJ2ZXIKIQppcCBzc2ggcHVia2V5LWNoYWluCiAgdXNlcm5hbWUgYm94ZW4KICAga2V5LWhhc2ggc3NoLXJzYSA1Q0M3NEE2OEIxOEIwMjZBMTcwOUZCMDlEMUY0NEUyRgppcCBzY3Agc2VydmVyIGVuYWJsZQohCiEKIQohCiEKIQohCmNvbnRyb2wtcGxhbmUKIQohCiEKIQohCiEKbGluZSBjb24gMAogc3RvcGJpdHMgMQpsaW5lIHZ0eSAwIDQKIGxvZ2luIGxvY2FsCiB0cmFuc3BvcnQgaW5wdXQgYWxsCmxpbmUgdnR5IDUgMTUKIGxvZ2luIGxvY2FsCiB0cmFuc3BvcnQgaW5wdXQgYWxsCiEKbmV0Y29uZiBzc2gKIQohCiEKIQohCm5ldGNvbmYteWFuZwplbmQ8L2NsaS1jb25maWctZGF0YS1ibG9jaz48L2RhdGE+PC9ycGMtcmVwbHk+","Result":"\u003crpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"101\"\u003e\u003cdata\u003e\u003ccli-config-data-block\u003e!\nTIMESTAMP\n!\nversion 16.12\nservice timestamps debug datetime msec\nservice timestamps log datetime msec\nservice call-home\nplatform qfp utilization monitor load 80\nplatform punt-keepalive disable-kernel-core\nplatform console serial\n!\nhostname csr1000v\n!\nboot-start-marker\nboot-end-marker\n!\n!\nenable secret 9 $9$xvWnx8Fe35f8xE$E9ijp7GM/V48P5y1Uz3IEPtotXgwkJKYJmN0q3q2E92\n!\nno aaa new-model\ncall-home\n ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com\n ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.\n contact-email-addr sch-smart-licensing@cisco.com\n profile \"CiscoTAC-1\"\n active\n destination transport-method http\n no destination transport-method email\n!\n!\n!\n!\n!\n!\n!\nip domain name example.com\n!\n!\n!\nlogin on-success log\n!\n!\n!\n!\n!\n!\n!\nsubscriber templating\n!\n!\n!\n!\n!\n!\nmultilink bundle-name authenticated\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\nCERTIFICATES AND LICENSE\ndiagnostic bootup level minimal\narchive\n log config\n logging enable\n path bootflash:\nmemory free low-watermark processor 72329\n!\n!\nspanning-tree extend system-id\n!\nusername boxen privilege 15 password 0 b0x3N-b0x3N\n!\nredundancy\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\n!\ninterface GigabitEthernet1\n ip address 10.0.0.15 255.255.255.0\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet2\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet3\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet4\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet5\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet6\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet7\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet8\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet9\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\ninterface GigabitEthernet10\n no ip address\n shutdown\n negotiation auto\n no mop enabled\n no mop sysid\n!\n!\nvirtual-service csr_mgmt\n!\nip forward-protocol nd\nno ip http server\nno ip http secure-server\n!\nip ssh pubkey-chain\n username boxen\n key-hash ssh-rsa 5CC74A68B18B026A1709FB09D1F44E2F\nip scp server enable\n!\n!\n!\n!\n!\n!\n!\ncontrol-plane\n!\n!\n!\n!\n!\n!\nline con 0\n stopbits 1\nline vty 0 4\n login local\n transport input all\nline vty 5 15\n login local\n transport input all\n!\nnetconf ssh\n!\n!\n!\n!\n!\nnetconf-yang\nend\u003c/cli-config-data-block\u003e\u003c/data\u003e\u003c/rpc-reply\u003e","StartTime":"0001-01-01T00:00:00Z","EndTime":"0001-01-01T00:00:00Z","ElapsedTime":0,"FailedWhenContains":["PHJwYy1lcnJvcj4=","PHJwYy1lcnJvcnM+","PC9ycGMtZXJyb3I+","PC9ycGMtZXJyb3JzPg=="],"Failed":null,"StripNamespaces":false,"NetconfVersion":"1.0","ErrorMessages":null,"SubscriptionID":0}
\ No newline at end of file
diff --git a/response/test-fixtures/golden/record-response-11-out.txt b/response/test-fixtures/golden/record-response-11-out.txt
new file mode 100644
index 0000000..1cd833b
--- /dev/null
+++ b/response/test-fixtures/golden/record-response-11-out.txt
@@ -0,0 +1 @@
+{"Host":"localhost","Port":830,"Input":null,"RawResult":"IzQxMTcKPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJwYy1yZXBseSB4bWxucz0idXJuOmlldGY6cGFyYW1zOnhtbDpuczpuZXRjb25mOmJhc2U6MS4wIiBtZXNzYWdlLWlkPSIxMDEiPjxkYXRhPjxuYXRpdmUgeG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtbmF0aXZlIj48dmVyc2lvbj4xNi4xMjwvdmVyc2lvbj48Ym9vdC1zdGFydC1tYXJrZXIvPjxib290LWVuZC1tYXJrZXIvPjxtZW1vcnk+PGZyZWU+PGxvdy13YXRlcm1hcms+PHByb2Nlc3Nvcj43MjMyOTwvcHJvY2Vzc29yPjwvbG93LXdhdGVybWFyaz48L2ZyZWU+PC9tZW1vcnk+PGNhbGwtaG9tZT48Y29udGFjdC1lbWFpbC1hZGRyIHhtbG5zPSJodHRwOi8vY2lzY28uY29tL25zL3lhbmcvQ2lzY28tSU9TLVhFLWNhbGwtaG9tZSI+c2NoLXNtYXJ0LWxpY2Vuc2luZ0BjaXNjby5jb208L2NvbnRhY3QtZW1haWwtYWRkcj48cHJvZmlsZSB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1jYWxsLWhvbWUiPjxwcm9maWxlLW5hbWU+Q2lzY29UQUMtMTwvcHJvZmlsZS1uYW1lPjxhY3RpdmU+dHJ1ZTwvYWN0aXZlPjwvcHJvZmlsZT48L2NhbGwtaG9tZT48c2VydmljZT48dGltZXN0YW1wcz48ZGVidWc+PGRhdGV0aW1lPjxtc2VjPjwvbXNlYz48L2RhdGV0aW1lPjwvZGVidWc+PGxvZz48ZGF0ZXRpbWU+PG1zZWMvPjwvZGF0ZXRpbWU+PC9sb2c+PC90aW1lc3RhbXBzPjxjYWxsLWhvbWUvPjwvc2VydmljZT48cGxhdGZvcm0+PGNvbnNvbGUgeG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtcGxhdGZvcm0iPjxvdXRwdXQ+c2VyaWFsPC9vdXRwdXQ+PC9jb25zb2xlPjxwdW50LWtlZXBhbGl2ZSB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1wbGF0Zm9ybSI+PGRpc2FibGUta2VybmVsLWNvcmU+dHJ1ZTwvZGlzYWJsZS1rZXJuZWwtY29yZT48L3B1bnQta2VlcGFsaXZlPjwvcGxhdGZvcm0+PGhvc3RuYW1lPnZyLWNzcjwvaG9zdG5hbWU+PGVuYWJsZT48c2VjcmV0Pjx0eXBlPjk8L3R5cGU+PHNlY3JldD4kOSRETVJQdy50NVJKdk5EVSRhLlQwUElFOHJlNXZOZnZEaWw4M2N4dTI0VXVmLnd5cmJFcy5SQ3NkSllvPC9zZWNyZXQ+PC9zZWNyZXQ+PC9lbmFibGU+PHVzZXJuYW1lPjxuYW1lPmFkbWluPC9uYW1lPjxwcml2aWxlZ2U+MTU8L3ByaXZpbGVnZT48cGFzc3dvcmQ+PHBhc3N3b3JkPmFkbWluPC9wYXNzd29yZD48L3Bhc3N3b3JkPjwvdXNlcm5hbWU+PHVzZXJuYW1lPjxuYW1lPmJveGVuPC9uYW1lPjxwcml2aWxlZ2U+MTU8L3ByaXZpbGVnZT48cGFzc3dvcmQ+PGVuY3J5cHRpb24+MDwvZW5jcnlwdGlvbj48cGFzc3dvcmQ+YjB4M04tYjB4M048L3Bhc3N3b3JkPjwvcGFzc3dvcmQ+PC91c2VybmFtZT48aXA+PGRvbWFpbj48bmFtZT5ib3hlbi5ib3g8L25hbWU+PC9kb21haW4+PGZvcndhcmQtcHJvdG9jb2w+PHByb3RvY29sPm5kPC9wcm90b2NvbD48L2ZvcndhcmQtcHJvdG9jb2w+PG11bHRpY2FzdD48cm91dGUtbGltaXQgeG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtbXVsdGljYXN0Ij4yMTQ3NDgzNjQ3PC9yb3V0ZS1saW1pdD48L211bHRpY2FzdD48cGltPjxhdXRvcnAtY29udGFpbmVyIHhtbG5zPSJodHRwOi8vY2lzY28uY29tL25zL3lhbmcvQ2lzY28tSU9TLVhFLW11bHRpY2FzdCI+PGF1dG9ycD5mYWxzZTwvYXV0b3JwPjwvYXV0b3JwLWNvbnRhaW5lcj48L3BpbT48YWNjZXNzLWxpc3Q+PGV4dGVuZGVkIHhtbG5zPSJodHRwOi8vY2lzY28uY29tL25zL3lhbmcvQ2lzY28tSU9TLVhFLWFjbCI+PG5hbWU+bWVyYWtpLWZxZG4tZG5zPC9uYW1lPjwvZXh0ZW5kZWQ+PC9hY2Nlc3MtbGlzdD48aHR0cCB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1odHRwIj48c2VydmVyPmZhbHNlPC9zZXJ2ZXI+PHNlY3VyZS1zZXJ2ZXI+dHJ1ZTwvc2VjdXJlLXNlcnZlcj48L2h0dHA+PC9pcD48aW50ZXJmYWNlPjxHaWdhYml0RXRoZXJuZXQ+PG5hbWU+MTwvbmFtZT48aXA+PGFkZHJlc3M+PHByaW1hcnk+PGFkZHJlc3M+MTAuMC4wLjE1PC9hZGRyZXNzPjxtYXNrPjI1NS4yNTUuMjU1LjA8L21hc2s+PC9wcmltYXJ5PjwvYWRkcmVzcz48L2lwPjxtb3A+PGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+PHN5c2lkPmZhbHNlPC9zeXNpZD48L21vcD48bmVnb3RpYXRpb24geG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtZXRoZXJuZXQiPjxhdXRvPnRydWU8L2F1dG8+PC9uZWdvdGlhdGlvbj48L0dpZ2FiaXRFdGhlcm5ldD48R2lnYWJpdEV0aGVybmV0PjxuYW1lPjEwPC9uYW1lPjxzaHV0ZG93bi8+PG1vcD48ZW5hYmxlZD5mYWxzZTwvZW5hYmxlZD48c3lzaWQ+ZmFsc2U8L3N5c2lkPjwvbW9wPjxuZWdvdGlhdGlvbiB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1ldGhlcm5ldCI+PGF1dG8+dHJ1ZTwvYXV0bz48L25lZ290aWF0aW9uPjwvR2lnYWJpdEV0aGVybmV0PjxHaWdhYml0RXRoZXJuZXQ+PG5hbWU+MjwvbmFtZT48c2h1dGRvd24vPjxtb3A+PGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+PHN5c2lkPmZhbHNlPC9zeXNpZD48L21vcD48bmVnb3RpYXRpb24geG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtZXRoZXJuZXQiPjxhdXRvPnRydWU8L2F1dG8+PC9uZWdvdGlhdGlvbj48L0dpZ2FiaXRFdGhlcm5ldD48R2lnYWJpdEV0aGVybmV0PjxuYW1lPjM8L25hbWU+PHNodXRkb3duLz48bW9wPjxlbmFibGVkPmZhbHNlPC9lbmFibGVkPjxzeXNpZD5mYWxzZTwvc3lzaWQ+PC9tb3A+PG5lZ290aWF0aW9uIHhtbG5zPSJodHRwOi8vY2lzY28uY29tL25zL3lhbmcvQ2lzY28tSU9TLVhFLWV0aGVybmV0Ij48YXV0bz50cnVlPC9hdXRvPjwvbmVnb3RpYXRpb24+PC9HaWdhYml0RXRoZXJuZXQ+PEdpZ2FiaXRFdGhlcm5ldD48bmFtZT40PC9uYW1lPjxzaHV0ZG93bi8+PG1vcD48ZW5hYmxlZD5mYWxzZTwvZW5hYmxlZD48c3lzaWQ+ZmFsc2U8L3N5c2lkPjwvbW9wPjxuZWdvdGlhdGlvbiB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1ldGhlcm5ldCI+PGF1dG8+dHJ1ZTwvYXV0bz48L25lZ290aWF0aW9uPjwvR2lnYWJpdEV0aGVybmV0PjxHaWdhYml0RXRoZXJuZXQ+PG5hbWU+NTwvbmFtZT48c2h1dGRvd24vPjxtb3A+PGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+PHN5c2lkPmZhbHNlPC9zeXNpZD48L21vcD48bmVnb3RpYXRpb24geG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtZXRoZXJuZXQiPjxhdXRvPnRydWU8L2F1dG8+PC9uZWdvdGlhdGlvbj48L0dpZ2FiaXRFdGhlcm5ldD48R2lnYWJpdEV0aGVybmV0PjxuYW1lPjY8L25hbWU+PHNodXRkb3duLz48bW9wPjxlbmFibGVkPmZhbHNlPC9lbmFibGVkPjxzeXNpZD5mYWxzZTwvc3lzaWQ+PC9tb3A+PG5lZ290aWF0aW9uIHhtbG5zPSJodHRwOi8vY2lzY28uY29tL25zL3lhbmcvQ2lzY28tSU9TLVhFLWV0aGVybmV0Ij48YXV0bz50cnVlPC9hdXRvPjwvbmVnb3RpYXRpb24+PC9HaWdhYml0RXRoZXJuZXQ+PEdpZ2FiaXRFdGhlcm5ldD48bmFtZT43PC9uYW1lPjxzaHV0ZG93bi8+PG1vcD48ZW5hYmxlZD5mYWxzZTwvZW5hYmxlZD48c3lzaWQ+ZmFsc2U8L3N5c2lkPjwvbW9wPjxuZWdvdGlhdGlvbiB4bWxucz0iaHR0cDovL2Npc2NvLmNvbS9ucy95YW5nL0Npc2NvLUlPUy1YRS1ldGhlcm5ldCI+PGF1dG8+dHJ1ZTwvYXV0bz48L25lZ290aWF0aW9uPjwvR2lnYWJpdEV0aGVybmV0PjxHaWdhYml0RXRoZXJuZXQ+PG5hbWU+ODwvbmFtZT48c2h1dGRvd24vPjxtb3A+PGVuYWJsZWQ+ZmFsc2U8L2VuYWJsZWQ+PHN5c2lkPmZhbHNlPC9zeXNpZD48L21vcD48bmVnb3RpYXRpb24geG1sbnM9Imh0dHA6Ly9jaXNjby5jb20vbnMveWFuZy9DaXNjby1JT1MtWEUtZXRoZXJuZXQiPjxhdXRvPnRydWU8L2F1dG8+PC9uZWdvdGlhdGlvbj48L0dpZ2FiaXRFdGhlcm5ldD48R2lnYWJpdEV0aGVybmV0PjxuYW1lPjk8L25hbWU+PHNodXRkb3duLz48bW9wPjxlbmFibGVkPmZhbHNlPC9lbmFibGVkPgojODMyCjwvaXB2ND48aXB2NiB4bWxucz0idXJuOmlldGY6cGFyYW1zOnhtbDpuczp5YW5nOmlldGYtaXAiPjwvaXB2Nj48L2ludGVyZmFjZT48L2ludGVyZmFjZXM+PG5hY20geG1sbnM9InVybjppZXRmOnBhcmFtczp4bWw6bnM6eWFuZzppZXRmLW5ldGNvbmYtYWNtIj48ZW5hYmxlLW5hY20+dHJ1ZTwvZW5hYmxlLW5hY20+PHJlYWQtZGVmYXVsdD5kZW55PC9yZWFkLWRlZmF1bHQ+PHdyaXRlLWRlZmF1bHQ+ZGVueTwvd3JpdGUtZGVmYXVsdD48ZXhlYy1kZWZhdWx0PmRlbnk8L2V4ZWMtZGVmYXVsdD48ZW5hYmxlLWV4dGVybmFsLWdyb3Vwcz50cnVlPC9lbmFibGUtZXh0ZXJuYWwtZ3JvdXBzPjxydWxlLWxpc3Q+PG5hbWU+YWRtaW48L25hbWU+PGdyb3VwPlBSSVYxNTwvZ3JvdXA+PHJ1bGU+PG5hbWU+cGVybWl0LWFsbDwvbmFtZT48bW9kdWxlLW5hbWU+KjwvbW9kdWxlLW5hbWU+PGFjY2Vzcy1vcGVyYXRpb25zPio8L2FjY2Vzcy1vcGVyYXRpb25zPjxhY3Rpb24+cGVybWl0PC9hY3Rpb24+PC9ydWxlPjwvcnVsZS1saXN0PjwvbmFjbT48cm91dGluZyB4bWxucz0idXJuOmlldGY6cGFyYW1zOnhtbDpuczp5YW5nOmlldGYtcm91dGluZyI+PHJvdXRpbmctaW5zdGFuY2U+PG5hbWU+ZGVmYXVsdDwvbmFtZT48ZGVzY3JpcHRpb24+ZGVmYXVsdC12cmYgW3JlYWQtb25seV08L2Rlc2NyaXB0aW9uPjxyb3V0aW5nLXByb3RvY29scz48cm91dGluZy1wcm90b2NvbD48dHlwZT5zdGF0aWM8L3R5cGU+PG5hbWU+MTwvbmFtZT48L3JvdXRpbmctcHJvdG9jb2w+PC9yb3V0aW5nLXByb3RvY29scz48L3JvdXRpbmctaW5zdGFuY2U+PC9yb3V0aW5nPjwvZGF0YT48L3JwYy1yZXBseT4KIyM=","Result":"\u003crpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"101\"\u003e\u003cdata\u003e\u003cnative xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-native\"\u003e\u003cversion\u003e16.12\u003c/version\u003e\u003cboot-start-marker/\u003e\u003cboot-end-marker/\u003e\u003cmemory\u003e\u003cfree\u003e\u003clow-watermark\u003e\u003cprocessor\u003e72329\u003c/processor\u003e\u003c/low-watermark\u003e\u003c/free\u003e\u003c/memory\u003e\u003ccall-home\u003e\u003ccontact-email-addr xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-call-home\"\u003esch-smart-licensing@cisco.com\u003c/contact-email-addr\u003e\u003cprofile xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-call-home\"\u003e\u003cprofile-name\u003eCiscoTAC-1\u003c/profile-name\u003e\u003cactive\u003etrue\u003c/active\u003e\u003c/profile\u003e\u003c/call-home\u003e\u003cservice\u003e\u003ctimestamps\u003e\u003cdebug\u003e\u003cdatetime\u003e\u003cmsec\u003e\u003c/msec\u003e\u003c/datetime\u003e\u003c/debug\u003e\u003clog\u003e\u003cdatetime\u003e\u003cmsec/\u003e\u003c/datetime\u003e\u003c/log\u003e\u003c/timestamps\u003e\u003ccall-home/\u003e\u003c/service\u003e\u003cplatform\u003e\u003cconsole xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-platform\"\u003e\u003coutput\u003eserial\u003c/output\u003e\u003c/console\u003e\u003cpunt-keepalive xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-platform\"\u003e\u003cdisable-kernel-core\u003etrue\u003c/disable-kernel-core\u003e\u003c/punt-keepalive\u003e\u003c/platform\u003e\u003chostname\u003evr-csr\u003c/hostname\u003e\u003cenable\u003e\u003csecret\u003e\u003ctype\u003e9\u003c/type\u003e\u003csecret\u003e$9$DMRPw.t5RJvNDU$a.T0PIE8re5vNfvDil83cxu24Uuf.wyrbEs.RCsdJYo\u003c/secret\u003e\u003c/secret\u003e\u003c/enable\u003e\u003cusername\u003e\u003cname\u003eadmin\u003c/name\u003e\u003cprivilege\u003e15\u003c/privilege\u003e\u003cpassword\u003e\u003cpassword\u003eadmin\u003c/password\u003e\u003c/password\u003e\u003c/username\u003e\u003cusername\u003e\u003cname\u003eboxen\u003c/name\u003e\u003cprivilege\u003e15\u003c/privilege\u003e\u003cpassword\u003e\u003cencryption\u003e0\u003c/encryption\u003e\u003cpassword\u003eb0x3N-b0x3N\u003c/password\u003e\u003c/password\u003e\u003c/username\u003e\u003cip\u003e\u003cdomain\u003e\u003cname\u003eboxen.box\u003c/name\u003e\u003c/domain\u003e\u003cforward-protocol\u003e\u003cprotocol\u003end\u003c/protocol\u003e\u003c/forward-protocol\u003e\u003cmulticast\u003e\u003croute-limit xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-multicast\"\u003e2147483647\u003c/route-limit\u003e\u003c/multicast\u003e\u003cpim\u003e\u003cautorp-container xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-multicast\"\u003e\u003cautorp\u003efalse\u003c/autorp\u003e\u003c/autorp-container\u003e\u003c/pim\u003e\u003caccess-list\u003e\u003cextended xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-acl\"\u003e\u003cname\u003emeraki-fqdn-dns\u003c/name\u003e\u003c/extended\u003e\u003c/access-list\u003e\u003chttp xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-http\"\u003e\u003cserver\u003efalse\u003c/server\u003e\u003csecure-server\u003etrue\u003c/secure-server\u003e\u003c/http\u003e\u003c/ip\u003e\u003cinterface\u003e\u003cGigabitEthernet\u003e\u003cname\u003e1\u003c/name\u003e\u003cip\u003e\u003caddress\u003e\u003cprimary\u003e\u003caddress\u003e10.0.0.15\u003c/address\u003e\u003cmask\u003e255.255.255.0\u003c/mask\u003e\u003c/primary\u003e\u003c/address\u003e\u003c/ip\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e10\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e2\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e3\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e4\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e5\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e6\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e7\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e8\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003csysid\u003efalse\u003c/sysid\u003e\u003c/mop\u003e\u003cnegotiation xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet\"\u003e\u003cauto\u003etrue\u003c/auto\u003e\u003c/negotiation\u003e\u003c/GigabitEthernet\u003e\u003cGigabitEthernet\u003e\u003cname\u003e9\u003c/name\u003e\u003cshutdown/\u003e\u003cmop\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003c/ipv4\u003e\u003cipv6 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\"\u003e\u003c/ipv6\u003e\u003c/interface\u003e\u003c/interfaces\u003e\u003cnacm xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-acm\"\u003e\u003cenable-nacm\u003etrue\u003c/enable-nacm\u003e\u003cread-default\u003edeny\u003c/read-default\u003e\u003cwrite-default\u003edeny\u003c/write-default\u003e\u003cexec-default\u003edeny\u003c/exec-default\u003e\u003cenable-external-groups\u003etrue\u003c/enable-external-groups\u003e\u003crule-list\u003e\u003cname\u003eadmin\u003c/name\u003e\u003cgroup\u003ePRIV15\u003c/group\u003e\u003crule\u003e\u003cname\u003epermit-all\u003c/name\u003e\u003cmodule-name\u003e*\u003c/module-name\u003e\u003caccess-operations\u003e*\u003c/access-operations\u003e\u003caction\u003epermit\u003c/action\u003e\u003c/rule\u003e\u003c/rule-list\u003e\u003c/nacm\u003e\u003crouting xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\"\u003e\u003crouting-instance\u003e\u003cname\u003edefault\u003c/name\u003e\u003cdescription\u003edefault-vrf [read-only]\u003c/description\u003e\u003crouting-protocols\u003e\u003crouting-protocol\u003e\u003ctype\u003estatic\u003c/type\u003e\u003cname\u003e1\u003c/name\u003e\u003c/routing-protocol\u003e\u003c/routing-protocols\u003e\u003c/routing-instance\u003e\u003c/routing\u003e\u003c/data\u003e\u003c/rpc-reply\u003e","StartTime":"0001-01-01T00:00:00Z","EndTime":"0001-01-01T00:00:00Z","ElapsedTime":0,"FailedWhenContains":["PHJwYy1lcnJvcj4=","PHJwYy1lcnJvcnM+","PC9ycGMtZXJyb3I+","PC9ycGMtZXJyb3JzPg=="],"Failed":null,"StripNamespaces":false,"NetconfVersion":"1.1","ErrorMessages":null,"SubscriptionID":0}
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_iosxe_long_expected b/response/test-fixtures/netconf-output-10.txt
similarity index 93%
rename from test_data/driver/network/expected/cisco_iosxe_long_expected
rename to response/test-fixtures/netconf-output-10.txt
index 35560e4..1363f16 100644
--- a/test_data/driver/network/expected/cisco_iosxe_long_expected
+++ b/response/test-fixtures/netconf-output-10.txt
@@ -1,8 +1,5 @@
-Building configuration...
-
-CONFIG_BYTES_REPLACED
-!
-TIME_STAMP_REPLACED
+!
+TIMESTAMP
!
version 16.12
service timestamps debug datetime msec
@@ -71,7 +68,7 @@ multilink bundle-name authenticated
!
!
!
-CERT_LICENSE_REPLACED
+CERTIFICATES AND LICENSE
diagnostic bootup level minimal
archive
log config
@@ -218,4 +215,4 @@ netconf ssh
!
!
netconf-yang
-end
\ No newline at end of file
+end
\ No newline at end of file
diff --git a/response/test-fixtures/netconf-output-11.txt b/response/test-fixtures/netconf-output-11.txt
new file mode 100644
index 0000000..c71d29d
--- /dev/null
+++ b/response/test-fixtures/netconf-output-11.txt
@@ -0,0 +1,6 @@
+#4117
+
+16.12 72329 sch-smart-licensing@cisco.com CiscoTAC-1 true serial true vr-csr 9 $9$DMRPw.t5RJvNDU$a.T0PIE8re5vNfvDil83cxu24Uuf.wyrbEs.RCsdJYo admin 15 admin boxen 15 0 b0x3N-b0x3N boxen.box nd 2147483647 false meraki-fqdn-dns false true 1 10.0.0.15 255.255.255.0 false false true 10 false false true 2 false false true 3 false false true 4 false false true 5 false false true 6 false false true 7 false false true 8 false false true 9 false
+#832
+true deny deny deny true admin PRIV15 permit-all * * permit default default-vrf [read-only] static 1
+##
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/arista_eos b/test_data/cfg/getconfig/arista_eos
deleted file mode 100644
index 8197b4a..0000000
--- a/test_data/cfg/getconfig/arista_eos
+++ /dev/null
@@ -1,310 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-Last login: Fri Aug 13 22:16:44 2021 from 10.0.0.2
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#show version | i Software image version
-Software image version: 4.22.1F
-localhost#show running-config
-! Command: show running-config
-! device: localhost (vEOS, EOS-4.22.1F)
-!
-! boot system flash:/vEOS-lab.swi
-!
-switchport default mode routed
-!
-transceiver qsfp default-mode 4x10G
-!
-logging console informational
-!
-logging level AAA errors
-logging level ACCOUNTING errors
-logging level ACL errors
-logging level AGENT errors
-logging level ALE errors
-logging level ARP errors
-logging level BFD errors
-logging level BGP errors
-logging level BMP errors
-logging level CAPACITY errors
-logging level CAPI errors
-logging level CLEAR errors
-logging level CVX errors
-logging level DATAPLANE errors
-logging level DHCP errors
-logging level DOT1X errors
-logging level DSCP errors
-logging level ENVMON errors
-logging level ETH errors
-logging level EVENTMON errors
-logging level EXTENSION errors
-logging level FHRP errors
-logging level FLOW errors
-logging level FORWARDING errors
-logging level FRU errors
-logging level FWK errors
-logging level GMP errors
-logging level HARDWARE errors
-logging level HEALTH errors
-logging level HTTPSERVICE errors
-logging level IGMP errors
-logging level IGMPSNOOPING errors
-logging level INT errors
-logging level INTF errors
-logging level IP6ROUTING errors
-logging level IPRIB errors
-logging level IRA errors
-logging level ISIS errors
-logging level KERNELFIB errors
-logging level LACP errors
-logging level LAG errors
-logging level LAUNCHER errors
-logging level LDP errors
-logging level LICENSE errors
-logging level LINEPROTO errors
-logging level LLDP errors
-logging level LOGMGR errors
-logging level LOOPBACK errors
-logging level LOOPPROTECT errors
-logging level MAPREDUCEMONITOR errors
-logging level MIRRORING errors
-logging level MKA errors
-logging level MLAG errors
-logging level MMODE errors
-logging level MROUTE errors
-logging level MRP errors
-logging level MSDP errors
-logging level MSRP errors
-logging level MSSPOLICYMONITOR errors
-logging level MVRP errors
-logging level NAT errors
-logging level OPENCONFIG errors
-logging level OPENFLOW errors
-logging level OSPF errors
-logging level OSPF3 errors
-logging level PACKAGE errors
-logging level PFC errors
-logging level PIMBSR errors
-logging level PORTSECURITY errors
-logging level PTP errors
-logging level PWRMGMT errors
-logging level QOS errors
-logging level QUEUEMONITOR errors
-logging level REDUNDANCY errors
-logging level RIB errors
-logging level ROUTING errors
-logging level SECURITY errors
-logging level SERVERMONITOR errors
-logging level SPANTREE errors
-logging level SSO errors
-logging level STAGEMGR errors
-logging level SYS errors
-logging level SYSDB errors
-logging level TAPAGG errors
-logging level TCP errors
-logging level TRANSCEIVER errors
-logging level TUNNEL errors
-logging level TUNNELINTF errors
-logging level VMTRACERSESS errors
-logging level VMWAREVI errors
-logging level VMWAREVS errors
-logging level VRF errors
-logging level VRRP errors
-logging level VXLAN errors
-logging level XMPP errors
-logging level ZTP informational
-!
-spanning-tree mode mstp
-!
-enable secret sha512 $6$TVJ6KgdC0qsGkKfb$LjGGPd6W3S1iJQVQ.Vvo3Bca4Yt1R3y4nOjWAaaTCsk1NuIoqB/gOMseoigt/HQVFDTvDAJ3IPmsF9sWfxi1V/
-no aaa root
-!
-username boxen role network-admin secret sha512 $6$1ex07TzYfKx23A9z$c02oeDxnr4J7HMgj0PcabYvACZfjdiu3zp4IAgjJQ51IBtkstpvOjm0FYbxSSBhsl.mytIr9Y4PaERp4MDXJM/
-!
-interface Ethernet1
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet2
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet3
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet4
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet5
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet6
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet7
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet8
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet9
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet10
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet11
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet12
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet13
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet14
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet15
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet16
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet17
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet18
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet19
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet20
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Management1
- ip address 10.0.0.15/24
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-no ip routing
-!
-control-plane
- no service-policy input copp-system-policy
-!
-banner login
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-EOF
-!
-management api http-commands
- protocol unix-socket
- no shutdown
-!
-management telnet
- no shutdown
-!
-end
-localhost#
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/arista_eos_expected b/test_data/cfg/getconfig/arista_eos_expected
deleted file mode 100644
index e052236..0000000
--- a/test_data/cfg/getconfig/arista_eos_expected
+++ /dev/null
@@ -1,286 +0,0 @@
-! Command: show running-config
-! device: localhost (vEOS, EOS-4.22.1F)
-!
-! boot system flash:/vEOS-lab.swi
-!
-switchport default mode routed
-!
-transceiver qsfp default-mode 4x10G
-!
-logging console informational
-!
-logging level AAA errors
-logging level ACCOUNTING errors
-logging level ACL errors
-logging level AGENT errors
-logging level ALE errors
-logging level ARP errors
-logging level BFD errors
-logging level BGP errors
-logging level BMP errors
-logging level CAPACITY errors
-logging level CAPI errors
-logging level CLEAR errors
-logging level CVX errors
-logging level DATAPLANE errors
-logging level DHCP errors
-logging level DOT1X errors
-logging level DSCP errors
-logging level ENVMON errors
-logging level ETH errors
-logging level EVENTMON errors
-logging level EXTENSION errors
-logging level FHRP errors
-logging level FLOW errors
-logging level FORWARDING errors
-logging level FRU errors
-logging level FWK errors
-logging level GMP errors
-logging level HARDWARE errors
-logging level HEALTH errors
-logging level HTTPSERVICE errors
-logging level IGMP errors
-logging level IGMPSNOOPING errors
-logging level INT errors
-logging level INTF errors
-logging level IP6ROUTING errors
-logging level IPRIB errors
-logging level IRA errors
-logging level ISIS errors
-logging level KERNELFIB errors
-logging level LACP errors
-logging level LAG errors
-logging level LAUNCHER errors
-logging level LDP errors
-logging level LICENSE errors
-logging level LINEPROTO errors
-logging level LLDP errors
-logging level LOGMGR errors
-logging level LOOPBACK errors
-logging level LOOPPROTECT errors
-logging level MAPREDUCEMONITOR errors
-logging level MIRRORING errors
-logging level MKA errors
-logging level MLAG errors
-logging level MMODE errors
-logging level MROUTE errors
-logging level MRP errors
-logging level MSDP errors
-logging level MSRP errors
-logging level MSSPOLICYMONITOR errors
-logging level MVRP errors
-logging level NAT errors
-logging level OPENCONFIG errors
-logging level OPENFLOW errors
-logging level OSPF errors
-logging level OSPF3 errors
-logging level PACKAGE errors
-logging level PFC errors
-logging level PIMBSR errors
-logging level PORTSECURITY errors
-logging level PTP errors
-logging level PWRMGMT errors
-logging level QOS errors
-logging level QUEUEMONITOR errors
-logging level REDUNDANCY errors
-logging level RIB errors
-logging level ROUTING errors
-logging level SECURITY errors
-logging level SERVERMONITOR errors
-logging level SPANTREE errors
-logging level SSO errors
-logging level STAGEMGR errors
-logging level SYS errors
-logging level SYSDB errors
-logging level TAPAGG errors
-logging level TCP errors
-logging level TRANSCEIVER errors
-logging level TUNNEL errors
-logging level TUNNELINTF errors
-logging level VMTRACERSESS errors
-logging level VMWAREVI errors
-logging level VMWAREVS errors
-logging level VRF errors
-logging level VRRP errors
-logging level VXLAN errors
-logging level XMPP errors
-logging level ZTP informational
-!
-spanning-tree mode mstp
-!
-enable secret sha512 $6$TVJ6KgdC0qsGkKfb$LjGGPd6W3S1iJQVQ.Vvo3Bca4Yt1R3y4nOjWAaaTCsk1NuIoqB/gOMseoigt/HQVFDTvDAJ3IPmsF9sWfxi1V/
-no aaa root
-!
-username boxen role network-admin secret sha512 $6$1ex07TzYfKx23A9z$c02oeDxnr4J7HMgj0PcabYvACZfjdiu3zp4IAgjJQ51IBtkstpvOjm0FYbxSSBhsl.mytIr9Y4PaERp4MDXJM/
-!
-interface Ethernet1
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet2
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet3
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet4
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet5
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet6
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet7
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet8
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet9
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet10
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet11
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet12
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet13
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet14
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet15
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet16
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet17
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet18
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet19
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet20
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Management1
- ip address 10.0.0.15/24
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-no ip routing
-!
-control-plane
- no service-policy input copp-system-policy
-!
-banner login
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-EOF
-!
-management api http-commands
- protocol unix-socket
- no shutdown
-!
-management telnet
- no shutdown
-!
-end
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_iosxr b/test_data/cfg/getconfig/cisco_iosxr
deleted file mode 100644
index 806d217..0000000
--- a/test_data/cfg/getconfig/cisco_iosxr
+++ /dev/null
@@ -1,430 +0,0 @@
-Warning: Permanently added '[localhost]:23022' (RSA) to the list of known hosts.
-Password:
-
-
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Fri Aug 13 22:31:06.873 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Fri Aug 13 22:31:07.017 UTC
-RP/0/RP0/CPU0:ios#show version | i Version
-Fri Aug 13 22:31:07.149 UTC
-Cisco IOS XR Software, Version 6.5.3
- Version : 6.5.3
-RP/0/RP0/CPU0:ios#show running-config
-Fri Aug 13 22:31:07.260 UTC
-Building configuration...
-!! IOS XR Configuration version = 6.5.3
-!! Last configuration change at Fri Aug 13 21:03:33 2021 by boxen
-!
-telnet vrf default ipv4 server max-servers 10
-username boxen
- group root-lr
- group cisco-support
- secret 5 $1$ERrP$IwIygdqbJGkqrXZDtaWVI0
-!
-call-home
- service active
- contact smart-licensing
- profile CiscoTAC-1
- active
- destination transport-method http
- !
-!
-interface MgmtEth0/RP0/CPU0/0
- ipv4 address 10.0.0.15 255.255.255.0
-!
-interface GigabitEthernet0/0/0/0
- shutdown
-!
-interface GigabitEthernet0/0/0/1
- shutdown
-!
-interface GigabitEthernet0/0/0/2
- shutdown
-!
-interface GigabitEthernet0/0/0/3
- shutdown
-!
-interface GigabitEthernet0/0/0/4
- shutdown
-!
-interface GigabitEthernet0/0/0/5
- shutdown
-!
-interface GigabitEthernet0/0/0/6
- shutdown
-!
-interface GigabitEthernet0/0/0/7
- shutdown
-!
-interface GigabitEthernet0/0/0/8
- shutdown
-!
-interface GigabitEthernet0/0/0/9
- shutdown
-!
-interface GigabitEthernet0/0/0/10
- shutdown
-!
-interface GigabitEthernet0/0/0/11
- shutdown
-!
-interface GigabitEthernet0/0/0/12
- shutdown
-!
-interface GigabitEthernet0/0/0/13
- shutdown
-!
-interface GigabitEthernet0/0/0/14
- shutdown
-!
-interface GigabitEthernet0/0/0/15
- shutdown
-!
-interface GigabitEthernet0/0/0/16
- shutdown
-!
-interface GigabitEthernet0/0/0/17
- shutdown
-!
-interface GigabitEthernet0/0/0/18
- shutdown
-!
-interface GigabitEthernet0/0/0/19
- shutdown
-!
-interface GigabitEthernet0/0/0/20
- shutdown
-!
-interface GigabitEthernet0/0/0/21
- shutdown
-!
-interface GigabitEthernet0/0/0/22
- shutdown
-!
-interface GigabitEthernet0/0/0/23
- shutdown
-!
-interface GigabitEthernet0/0/0/24
- shutdown
-!
-interface GigabitEthernet0/0/0/25
- shutdown
-!
-interface GigabitEthernet0/0/0/26
- shutdown
-!
-interface GigabitEthernet0/0/0/27
- shutdown
-!
-interface GigabitEthernet0/0/0/28
- shutdown
-!
-interface GigabitEthernet0/0/0/29
- shutdown
-!
-interface GigabitEthernet0/0/0/30
- shutdown
-!
-interface GigabitEthernet0/0/0/31
- shutdown
-!
-interface GigabitEthernet0/0/0/32
- shutdown
-!
-interface GigabitEthernet0/0/0/33
- shutdown
-!
-interface GigabitEthernet0/0/0/34
- shutdown
-!
-interface GigabitEthernet0/0/0/35
- shutdown
-!
-interface GigabitEthernet0/0/0/36
- shutdown
-!
-interface GigabitEthernet0/0/0/37
- shutdown
-!
-interface GigabitEthernet0/0/0/38
- shutdown
-!
-interface GigabitEthernet0/0/0/39
- shutdown
-!
-interface GigabitEthernet0/0/0/40
- shutdown
-!
-interface GigabitEthernet0/0/0/41
- shutdown
-!
-interface GigabitEthernet0/0/0/42
- shutdown
-!
-interface GigabitEthernet0/0/0/43
- shutdown
-!
-interface GigabitEthernet0/0/0/44
- shutdown
-!
-interface GigabitEthernet0/0/0/45
- shutdown
-!
-interface GigabitEthernet0/0/0/46
- shutdown
-!
-interface GigabitEthernet0/0/0/47
- shutdown
-!
-interface GigabitEthernet0/0/0/48
- shutdown
-!
-interface GigabitEthernet0/0/0/49
- shutdown
-!
-interface GigabitEthernet0/0/0/50
- shutdown
-!
-interface GigabitEthernet0/0/0/51
- shutdown
-!
-interface GigabitEthernet0/0/0/52
- shutdown
-!
-interface GigabitEthernet0/0/0/53
- shutdown
-!
-interface GigabitEthernet0/0/0/54
- shutdown
-!
-interface GigabitEthernet0/0/0/55
- shutdown
-!
-interface GigabitEthernet0/0/0/56
- shutdown
-!
-interface GigabitEthernet0/0/0/57
- shutdown
-!
-interface GigabitEthernet0/0/0/58
- shutdown
-!
-interface GigabitEthernet0/0/0/59
- shutdown
-!
-interface GigabitEthernet0/0/0/60
- shutdown
-!
-interface GigabitEthernet0/0/0/61
- shutdown
-!
-interface GigabitEthernet0/0/0/62
- shutdown
-!
-interface GigabitEthernet0/0/0/63
- shutdown
-!
-interface GigabitEthernet0/0/0/64
- shutdown
-!
-interface GigabitEthernet0/0/0/65
- shutdown
-!
-interface GigabitEthernet0/0/0/66
- shutdown
-!
-interface GigabitEthernet0/0/0/67
- shutdown
-!
-interface GigabitEthernet0/0/0/68
- shutdown
-!
-interface GigabitEthernet0/0/0/69
- shutdown
-!
-interface GigabitEthernet0/0/0/70
- shutdown
-!
-interface GigabitEthernet0/0/0/71
- shutdown
-!
-interface GigabitEthernet0/0/0/72
- shutdown
-!
-interface GigabitEthernet0/0/0/73
- shutdown
-!
-interface GigabitEthernet0/0/0/74
- shutdown
-!
-interface GigabitEthernet0/0/0/75
- shutdown
-!
-interface GigabitEthernet0/0/0/76
- shutdown
-!
-interface GigabitEthernet0/0/0/77
- shutdown
-!
-interface GigabitEthernet0/0/0/78
- shutdown
-!
-interface GigabitEthernet0/0/0/79
- shutdown
-!
-interface GigabitEthernet0/0/0/80
- shutdown
-!
-interface GigabitEthernet0/0/0/81
- shutdown
-!
-interface GigabitEthernet0/0/0/82
- shutdown
-!
-interface GigabitEthernet0/0/0/83
- shutdown
-!
-interface GigabitEthernet0/0/0/84
- shutdown
-!
-interface GigabitEthernet0/0/0/85
- shutdown
-!
-interface GigabitEthernet0/0/0/86
- shutdown
-!
-interface GigabitEthernet0/0/0/87
- shutdown
-!
-interface GigabitEthernet0/0/0/88
- shutdown
-!
-interface GigabitEthernet0/0/0/89
- shutdown
-!
-interface GigabitEthernet0/0/0/90
- shutdown
-!
-interface GigabitEthernet0/0/0/91
- shutdown
-!
-interface GigabitEthernet0/0/0/92
- shutdown
-!
-interface GigabitEthernet0/0/0/93
- shutdown
-!
-interface GigabitEthernet0/0/0/94
- shutdown
-!
-interface GigabitEthernet0/0/0/95
- shutdown
-!
-interface GigabitEthernet0/0/0/96
- shutdown
-!
-interface GigabitEthernet0/0/0/97
- shutdown
-!
-interface GigabitEthernet0/0/0/98
- shutdown
-!
-interface GigabitEthernet0/0/0/99
- shutdown
-!
-interface GigabitEthernet0/0/0/100
- shutdown
-!
-interface GigabitEthernet0/0/0/101
- shutdown
-!
-interface GigabitEthernet0/0/0/102
- shutdown
-!
-interface GigabitEthernet0/0/0/103
- shutdown
-!
-interface GigabitEthernet0/0/0/104
- shutdown
-!
-interface GigabitEthernet0/0/0/105
- shutdown
-!
-interface GigabitEthernet0/0/0/106
- shutdown
-!
-interface GigabitEthernet0/0/0/107
- shutdown
-!
-interface GigabitEthernet0/0/0/108
- shutdown
-!
-interface GigabitEthernet0/0/0/109
- shutdown
-!
-interface GigabitEthernet0/0/0/110
- shutdown
-!
-interface GigabitEthernet0/0/0/111
- shutdown
-!
-interface GigabitEthernet0/0/0/112
- shutdown
-!
-interface GigabitEthernet0/0/0/113
- shutdown
-!
-interface GigabitEthernet0/0/0/114
- shutdown
-!
-interface GigabitEthernet0/0/0/115
- shutdown
-!
-interface GigabitEthernet0/0/0/116
- shutdown
-!
-interface GigabitEthernet0/0/0/117
- shutdown
-!
-interface GigabitEthernet0/0/0/118
- shutdown
-!
-interface GigabitEthernet0/0/0/119
- shutdown
-!
-interface GigabitEthernet0/0/0/120
- shutdown
-!
-interface GigabitEthernet0/0/0/121
- shutdown
-!
-interface GigabitEthernet0/0/0/122
- shutdown
-!
-interface GigabitEthernet0/0/0/123
- shutdown
-!
-interface GigabitEthernet0/0/0/124
- shutdown
-!
-interface GigabitEthernet0/0/0/125
- shutdown
-!
-interface GigabitEthernet0/0/0/126
- shutdown
-!
-interface GigabitEthernet0/0/0/127
- shutdown
-!
-xml agent tty
-!
-netconf-yang agent
- ssh
-!
-ssh server v2
-ssh server netconf vrf default
-end
-
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_iosxr_expected b/test_data/cfg/getconfig/cisco_iosxr_expected
deleted file mode 100644
index 08669cb..0000000
--- a/test_data/cfg/getconfig/cisco_iosxr_expected
+++ /dev/null
@@ -1,414 +0,0 @@
-Fri Aug 13 22:31:07.260 UTC
-Building configuration...
-!! IOS XR Configuration version = 6.5.3
-!! Last configuration change at Fri Aug 13 21:03:33 2021 by boxen
-!
-telnet vrf default ipv4 server max-servers 10
-username boxen
- group root-lr
- group cisco-support
- secret 5 $1$ERrP$IwIygdqbJGkqrXZDtaWVI0
-!
-call-home
- service active
- contact smart-licensing
- profile CiscoTAC-1
- active
- destination transport-method http
- !
-!
-interface MgmtEth0/RP0/CPU0/0
- ipv4 address 10.0.0.15 255.255.255.0
-!
-interface GigabitEthernet0/0/0/0
- shutdown
-!
-interface GigabitEthernet0/0/0/1
- shutdown
-!
-interface GigabitEthernet0/0/0/2
- shutdown
-!
-interface GigabitEthernet0/0/0/3
- shutdown
-!
-interface GigabitEthernet0/0/0/4
- shutdown
-!
-interface GigabitEthernet0/0/0/5
- shutdown
-!
-interface GigabitEthernet0/0/0/6
- shutdown
-!
-interface GigabitEthernet0/0/0/7
- shutdown
-!
-interface GigabitEthernet0/0/0/8
- shutdown
-!
-interface GigabitEthernet0/0/0/9
- shutdown
-!
-interface GigabitEthernet0/0/0/10
- shutdown
-!
-interface GigabitEthernet0/0/0/11
- shutdown
-!
-interface GigabitEthernet0/0/0/12
- shutdown
-!
-interface GigabitEthernet0/0/0/13
- shutdown
-!
-interface GigabitEthernet0/0/0/14
- shutdown
-!
-interface GigabitEthernet0/0/0/15
- shutdown
-!
-interface GigabitEthernet0/0/0/16
- shutdown
-!
-interface GigabitEthernet0/0/0/17
- shutdown
-!
-interface GigabitEthernet0/0/0/18
- shutdown
-!
-interface GigabitEthernet0/0/0/19
- shutdown
-!
-interface GigabitEthernet0/0/0/20
- shutdown
-!
-interface GigabitEthernet0/0/0/21
- shutdown
-!
-interface GigabitEthernet0/0/0/22
- shutdown
-!
-interface GigabitEthernet0/0/0/23
- shutdown
-!
-interface GigabitEthernet0/0/0/24
- shutdown
-!
-interface GigabitEthernet0/0/0/25
- shutdown
-!
-interface GigabitEthernet0/0/0/26
- shutdown
-!
-interface GigabitEthernet0/0/0/27
- shutdown
-!
-interface GigabitEthernet0/0/0/28
- shutdown
-!
-interface GigabitEthernet0/0/0/29
- shutdown
-!
-interface GigabitEthernet0/0/0/30
- shutdown
-!
-interface GigabitEthernet0/0/0/31
- shutdown
-!
-interface GigabitEthernet0/0/0/32
- shutdown
-!
-interface GigabitEthernet0/0/0/33
- shutdown
-!
-interface GigabitEthernet0/0/0/34
- shutdown
-!
-interface GigabitEthernet0/0/0/35
- shutdown
-!
-interface GigabitEthernet0/0/0/36
- shutdown
-!
-interface GigabitEthernet0/0/0/37
- shutdown
-!
-interface GigabitEthernet0/0/0/38
- shutdown
-!
-interface GigabitEthernet0/0/0/39
- shutdown
-!
-interface GigabitEthernet0/0/0/40
- shutdown
-!
-interface GigabitEthernet0/0/0/41
- shutdown
-!
-interface GigabitEthernet0/0/0/42
- shutdown
-!
-interface GigabitEthernet0/0/0/43
- shutdown
-!
-interface GigabitEthernet0/0/0/44
- shutdown
-!
-interface GigabitEthernet0/0/0/45
- shutdown
-!
-interface GigabitEthernet0/0/0/46
- shutdown
-!
-interface GigabitEthernet0/0/0/47
- shutdown
-!
-interface GigabitEthernet0/0/0/48
- shutdown
-!
-interface GigabitEthernet0/0/0/49
- shutdown
-!
-interface GigabitEthernet0/0/0/50
- shutdown
-!
-interface GigabitEthernet0/0/0/51
- shutdown
-!
-interface GigabitEthernet0/0/0/52
- shutdown
-!
-interface GigabitEthernet0/0/0/53
- shutdown
-!
-interface GigabitEthernet0/0/0/54
- shutdown
-!
-interface GigabitEthernet0/0/0/55
- shutdown
-!
-interface GigabitEthernet0/0/0/56
- shutdown
-!
-interface GigabitEthernet0/0/0/57
- shutdown
-!
-interface GigabitEthernet0/0/0/58
- shutdown
-!
-interface GigabitEthernet0/0/0/59
- shutdown
-!
-interface GigabitEthernet0/0/0/60
- shutdown
-!
-interface GigabitEthernet0/0/0/61
- shutdown
-!
-interface GigabitEthernet0/0/0/62
- shutdown
-!
-interface GigabitEthernet0/0/0/63
- shutdown
-!
-interface GigabitEthernet0/0/0/64
- shutdown
-!
-interface GigabitEthernet0/0/0/65
- shutdown
-!
-interface GigabitEthernet0/0/0/66
- shutdown
-!
-interface GigabitEthernet0/0/0/67
- shutdown
-!
-interface GigabitEthernet0/0/0/68
- shutdown
-!
-interface GigabitEthernet0/0/0/69
- shutdown
-!
-interface GigabitEthernet0/0/0/70
- shutdown
-!
-interface GigabitEthernet0/0/0/71
- shutdown
-!
-interface GigabitEthernet0/0/0/72
- shutdown
-!
-interface GigabitEthernet0/0/0/73
- shutdown
-!
-interface GigabitEthernet0/0/0/74
- shutdown
-!
-interface GigabitEthernet0/0/0/75
- shutdown
-!
-interface GigabitEthernet0/0/0/76
- shutdown
-!
-interface GigabitEthernet0/0/0/77
- shutdown
-!
-interface GigabitEthernet0/0/0/78
- shutdown
-!
-interface GigabitEthernet0/0/0/79
- shutdown
-!
-interface GigabitEthernet0/0/0/80
- shutdown
-!
-interface GigabitEthernet0/0/0/81
- shutdown
-!
-interface GigabitEthernet0/0/0/82
- shutdown
-!
-interface GigabitEthernet0/0/0/83
- shutdown
-!
-interface GigabitEthernet0/0/0/84
- shutdown
-!
-interface GigabitEthernet0/0/0/85
- shutdown
-!
-interface GigabitEthernet0/0/0/86
- shutdown
-!
-interface GigabitEthernet0/0/0/87
- shutdown
-!
-interface GigabitEthernet0/0/0/88
- shutdown
-!
-interface GigabitEthernet0/0/0/89
- shutdown
-!
-interface GigabitEthernet0/0/0/90
- shutdown
-!
-interface GigabitEthernet0/0/0/91
- shutdown
-!
-interface GigabitEthernet0/0/0/92
- shutdown
-!
-interface GigabitEthernet0/0/0/93
- shutdown
-!
-interface GigabitEthernet0/0/0/94
- shutdown
-!
-interface GigabitEthernet0/0/0/95
- shutdown
-!
-interface GigabitEthernet0/0/0/96
- shutdown
-!
-interface GigabitEthernet0/0/0/97
- shutdown
-!
-interface GigabitEthernet0/0/0/98
- shutdown
-!
-interface GigabitEthernet0/0/0/99
- shutdown
-!
-interface GigabitEthernet0/0/0/100
- shutdown
-!
-interface GigabitEthernet0/0/0/101
- shutdown
-!
-interface GigabitEthernet0/0/0/102
- shutdown
-!
-interface GigabitEthernet0/0/0/103
- shutdown
-!
-interface GigabitEthernet0/0/0/104
- shutdown
-!
-interface GigabitEthernet0/0/0/105
- shutdown
-!
-interface GigabitEthernet0/0/0/106
- shutdown
-!
-interface GigabitEthernet0/0/0/107
- shutdown
-!
-interface GigabitEthernet0/0/0/108
- shutdown
-!
-interface GigabitEthernet0/0/0/109
- shutdown
-!
-interface GigabitEthernet0/0/0/110
- shutdown
-!
-interface GigabitEthernet0/0/0/111
- shutdown
-!
-interface GigabitEthernet0/0/0/112
- shutdown
-!
-interface GigabitEthernet0/0/0/113
- shutdown
-!
-interface GigabitEthernet0/0/0/114
- shutdown
-!
-interface GigabitEthernet0/0/0/115
- shutdown
-!
-interface GigabitEthernet0/0/0/116
- shutdown
-!
-interface GigabitEthernet0/0/0/117
- shutdown
-!
-interface GigabitEthernet0/0/0/118
- shutdown
-!
-interface GigabitEthernet0/0/0/119
- shutdown
-!
-interface GigabitEthernet0/0/0/120
- shutdown
-!
-interface GigabitEthernet0/0/0/121
- shutdown
-!
-interface GigabitEthernet0/0/0/122
- shutdown
-!
-interface GigabitEthernet0/0/0/123
- shutdown
-!
-interface GigabitEthernet0/0/0/124
- shutdown
-!
-interface GigabitEthernet0/0/0/125
- shutdown
-!
-interface GigabitEthernet0/0/0/126
- shutdown
-!
-interface GigabitEthernet0/0/0/127
- shutdown
-!
-xml agent tty
-!
-netconf-yang agent
- ssh
-!
-ssh server v2
-ssh server netconf vrf default
-end
\ No newline at end of file
diff --git a/test_data/cfg/getconfig/cisco_nxos b/test_data/cfg/getconfig/cisco_nxos
deleted file mode 100644
index c0388a1..0000000
--- a/test_data/cfg/getconfig/cisco_nxos
+++ /dev/null
@@ -1,339 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch# show version | i "NXOS: version"
- NXOS: version 9.2(4)
-switch# show running-config
-
-!Command: show running-config
-!Running configuration last done at: Fri Aug 13 20:36:00 2021
-!Time: Fri Aug 13 22:31:58 2021
-
-version 9.2(4) Bios:version
-vdc switch id 1
- limit-resource vlan minimum 16 maximum 4094
- limit-resource vrf minimum 2 maximum 4096
- limit-resource port-channel minimum 0 maximum 511
- limit-resource u4route-mem minimum 128 maximum 128
- limit-resource u6route-mem minimum 96 maximum 96
- limit-resource m4route-mem minimum 58 maximum 58
- limit-resource m6route-mem minimum 8 maximum 8
-feature telnet
-feature nxapi
-feature scp-server
-
-no password strength-check
-username admin password 5 $5$LOIMHI$hIaO64VM40/x.MTQoeWg8/IAn2iBY5jv4WZyzQbb5q9 role network-admin
-username boxen password 5 $5$rFrywOjz$buvWY6uEPf79GVyfGNO6SGOi5gbxV2VAcsbBtyXDZyB role network-admin
-username boxen passphrase lifetime 99999 warntime 14 gracetime 3
-ip domain-lookup
-copp profile strict
-snmp-server user admin network-admin auth md5 0xd42fc9f6e153a348e1ab40f0f5b84589 priv 0xd42fc9f6e153a348e1ab40f0f5b84589 localizedkey
-snmp-server user boxen network-admin auth md5 0xc168bfc2b500129bd35ee550b6d5d93d priv 0xc168bfc2b500129bd35ee550b6d5d93d localizedkey
-rmon event 1 description FATAL(1) owner PMON@FATAL
-rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
-rmon event 3 description ERROR(3) owner PMON@ERROR
-rmon event 4 description WARNING(4) owner PMON@WARNING
-rmon event 5 description INFORMATION(5) owner PMON@INFO
-
-vlan 1
-
-vrf context management
-
-interface Ethernet1/1
-
-interface Ethernet1/2
-
-interface Ethernet1/3
-
-interface Ethernet1/4
-
-interface Ethernet1/5
-
-interface Ethernet1/6
-
-interface Ethernet1/7
-
-interface Ethernet1/8
-
-interface Ethernet1/9
-
-interface Ethernet1/10
-
-interface Ethernet1/11
-
-interface Ethernet1/12
-
-interface Ethernet1/13
-
-interface Ethernet1/14
-
-interface Ethernet1/15
-
-interface Ethernet1/16
-
-interface Ethernet1/17
-
-interface Ethernet1/18
-
-interface Ethernet1/19
-
-interface Ethernet1/20
-
-interface Ethernet1/21
-
-interface Ethernet1/22
-
-interface Ethernet1/23
-
-interface Ethernet1/24
-
-interface Ethernet1/25
-
-interface Ethernet1/26
-
-interface Ethernet1/27
-
-interface Ethernet1/28
-
-interface Ethernet1/29
-
-interface Ethernet1/30
-
-interface Ethernet1/31
-
-interface Ethernet1/32
-
-interface Ethernet1/33
-
-interface Ethernet1/34
-
-interface Ethernet1/35
-
-interface Ethernet1/36
-
-interface Ethernet1/37
-
-interface Ethernet1/38
-
-interface Ethernet1/39
-
-interface Ethernet1/40
-
-interface Ethernet1/41
-
-interface Ethernet1/42
-
-interface Ethernet1/43
-
-interface Ethernet1/44
-
-interface Ethernet1/45
-
-interface Ethernet1/46
-
-interface Ethernet1/47
-
-interface Ethernet1/48
-
-interface Ethernet1/49
-
-interface Ethernet1/50
-
-interface Ethernet1/51
-
-interface Ethernet1/52
-
-interface Ethernet1/53
-
-interface Ethernet1/54
-
-interface Ethernet1/55
-
-interface Ethernet1/56
-
-interface Ethernet1/57
-
-interface Ethernet1/58
-
-interface Ethernet1/59
-
-interface Ethernet1/60
-
-interface Ethernet1/61
-
-interface Ethernet1/62
-
-interface Ethernet1/63
-
-interface Ethernet1/64
-
-interface Ethernet1/65
-
-interface Ethernet1/66
-
-interface Ethernet1/67
-
-interface Ethernet1/68
-
-interface Ethernet1/69
-
-interface Ethernet1/70
-
-interface Ethernet1/71
-
-interface Ethernet1/72
-
-interface Ethernet1/73
-
-interface Ethernet1/74
-
-interface Ethernet1/75
-
-interface Ethernet1/76
-
-interface Ethernet1/77
-
-interface Ethernet1/78
-
-interface Ethernet1/79
-
-interface Ethernet1/80
-
-interface Ethernet1/81
-
-interface Ethernet1/82
-
-interface Ethernet1/83
-
-interface Ethernet1/84
-
-interface Ethernet1/85
-
-interface Ethernet1/86
-
-interface Ethernet1/87
-
-interface Ethernet1/88
-
-interface Ethernet1/89
-
-interface Ethernet1/90
-
-interface Ethernet1/91
-
-interface Ethernet1/92
-
-interface Ethernet1/93
-
-interface Ethernet1/94
-
-interface Ethernet1/95
-
-interface Ethernet1/96
-
-interface Ethernet1/97
-
-interface Ethernet1/98
-
-interface Ethernet1/99
-
-interface Ethernet1/100
-
-interface Ethernet1/101
-
-interface Ethernet1/102
-
-interface Ethernet1/103
-
-interface Ethernet1/104
-
-interface Ethernet1/105
-
-interface Ethernet1/106
-
-interface Ethernet1/107
-
-interface Ethernet1/108
-
-interface Ethernet1/109
-
-interface Ethernet1/110
-
-interface Ethernet1/111
-
-interface Ethernet1/112
-
-interface Ethernet1/113
-
-interface Ethernet1/114
-
-interface Ethernet1/115
-
-interface Ethernet1/116
-
-interface Ethernet1/117
-
-interface Ethernet1/118
-
-interface Ethernet1/119
-
-interface Ethernet1/120
-
-interface Ethernet1/121
-
-interface Ethernet1/122
-
-interface Ethernet1/123
-
-interface Ethernet1/124
-
-interface Ethernet1/125
-
-interface Ethernet1/126
-
-interface Ethernet1/127
-
-interface Ethernet1/128
-
-interface mgmt0
- vrf member management
- ip address 10.0.0.15/24
-line console
-line vty
-boot nxos bootflash:/nxos.9.2.4.bin
-
-
-
-switch#
\ No newline at end of file
diff --git a/test_data/cfg/getversion/arista_eos b/test_data/cfg/getversion/arista_eos
deleted file mode 100644
index 51f7dfa..0000000
--- a/test_data/cfg/getversion/arista_eos
+++ /dev/null
@@ -1,25 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-Last login: Fri Aug 13 21:54:12 2021 from 10.0.0.2
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#show version | i Software image version
-Software image version: 4.22.1F
-localhost#show version | i Software image version
-Software image version: 4.22.1F
-localhost#
\ No newline at end of file
diff --git a/test_data/cfg/getversion/cisco_iosxe b/test_data/cfg/getversion/cisco_iosxe
deleted file mode 100644
index 7b99da3..0000000
--- a/test_data/cfg/getversion/cisco_iosxe
+++ /dev/null
@@ -1,21 +0,0 @@
-Warning: Permanently added '[localhost]:21022' (RSA) to the list of known hosts.
-Password:
-
-
-
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#show version | i Version
-Cisco IOS XE Software, Version 16.12.03
-Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.12.3, RELEASE SOFTWARE (fc5)
-licensed under the GNU General Public License ("GPL") Version 2.0. The
-software code licensed under GPL Version 2.0 is free software that comes
-GPL code under the terms of GPL Version 2.0. For more details, see the
-csr1000v#show version | i Version
-Cisco IOS XE Software, Version 16.12.03
-Cisco IOS Software [Gibraltar], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 16.12.3, RELEASE SOFTWARE (fc5)
-licensed under the GNU General Public License ("GPL") Version 2.0. The
-software code licensed under GPL Version 2.0 is free software that comes
-GPL code under the terms of GPL Version 2.0. For more details, see the
-csr1000v#
\ No newline at end of file
diff --git a/test_data/cfg/getversion/cisco_iosxr b/test_data/cfg/getversion/cisco_iosxr
deleted file mode 100644
index c8c007e..0000000
--- a/test_data/cfg/getversion/cisco_iosxr
+++ /dev/null
@@ -1,18 +0,0 @@
-Warning: Permanently added '[localhost]:23022' (RSA) to the list of known hosts.
-Password:
-
-
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Fri Aug 13 22:20:29.958 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Fri Aug 13 22:20:30.106 UTC
-RP/0/RP0/CPU0:ios#show version | i Version
-Fri Aug 13 22:20:30.214 UTC
-Cisco IOS XR Software, Version 6.5.3
- Version : 6.5.3
-RP/0/RP0/CPU0:ios#show version | i Version
-Fri Aug 13 22:20:30.316 UTC
-Cisco IOS XR Software, Version 6.5.3
- Version : 6.5.3
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/cfg/getversion/cisco_nxos b/test_data/cfg/getversion/cisco_nxos
deleted file mode 100644
index 63cf7ea..0000000
--- a/test_data/cfg/getversion/cisco_nxos
+++ /dev/null
@@ -1,39 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch# show version | i "NXOS: version"
- NXOS: version 9.2(4)
-switch# show version | i "NXOS: version"
- NXOS: version 9.2(4)
-switch#
\ No newline at end of file
diff --git a/test_data/cfg/getversion/juniper_junos b/test_data/cfg/getversion/juniper_junos
deleted file mode 100644
index bcdd832..0000000
--- a/test_data/cfg/getversion/juniper_junos
+++ /dev/null
@@ -1,25 +0,0 @@
-Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-boxen>
-
-boxen> set cli screen-length 0
-Screen length set to 0
-
-boxen> set cli screen-width 511
-Screen width set to 511
-
-boxen> set cli complete-on-space off
-Disabling complete-on-space
-
-boxen> show version
-Model: srx4100
-Junos: 17.3R2.10
-JUNOS Software Release [17.3R2.10]
-
-boxen> show version
-Model: srx4100
-Junos: 17.3R2.10
-JUNOS Software Release [17.3R2.10]
-
-boxen>
\ No newline at end of file
diff --git a/test_data/channel/getprompt b/test_data/channel/getprompt
deleted file mode 100644
index fb69c9e..0000000
--- a/test_data/channel/getprompt
+++ /dev/null
@@ -1 +0,0 @@
-localhost#
diff --git a/test_data/channel/read b/test_data/channel/read
deleted file mode 100644
index 474d13e..0000000
Binary files a/test_data/channel/read and /dev/null differ
diff --git a/test_data/channel/read_expected b/test_data/channel/read_expected
deleted file mode 100644
index 613ea33..0000000
--- a/test_data/channel/read_expected
+++ /dev/null
@@ -1,35 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#show version
-vEOS
-Hardware version:
-Serial number:
-System MAC address: 5254.001f.e379
-
-Software image version: 4.22.1F
-Architecture: i686
-Internal build version: 4.22.1F-13062802.4221F
-Internal build ID: bb097f5d-d38c-4c32-898b-c20f6e18b00a
-
-Uptime: 0 weeks, 0 days, 0 hours and 15 minutes
-Total memory: 4008840 kB
-Free memory: 3262788 kB
-
-localhost#
\ No newline at end of file
diff --git a/test_data/channel/sendinput b/test_data/channel/sendinput
deleted file mode 100644
index 92e037a..0000000
--- a/test_data/channel/sendinput
+++ /dev/null
@@ -1,16 +0,0 @@
-localhost#show version
-vEOS
-Hardware version:
-Serial number:
-System MAC address: 5254.001f.e379
-
-Software image version: 4.22.1F
-Architecture: i686
-Internal build version: 4.22.1F-13062802.4221F
-Internal build ID: bb097f5d-d38c-4c32-898b-c20f6e18b00a
-
-Uptime: 0 weeks, 0 days, 0 hours and 15 minutes
-Total memory: 4008840 kB
-Free memory: 3262788 kB
-
-localhost#
\ No newline at end of file
diff --git a/test_data/channel/sendinput_expected b/test_data/channel/sendinput_expected
deleted file mode 100644
index 5567737..0000000
--- a/test_data/channel/sendinput_expected
+++ /dev/null
@@ -1,15 +0,0 @@
-vEOS
-Hardware version:
-Serial number:
-System MAC address: 5254.001f.e379
-
-Software image version: 4.22.1F
-Architecture: i686
-Internal build version: 4.22.1F-13062802.4221F
-Internal build ID: bb097f5d-d38c-4c32-898b-c20f6e18b00a
-
-Uptime: 0 weeks, 0 days, 0 hours and 15 minutes
-Total memory: 4008840 kB
-Free memory: 3262788 kB
-
-localhost#
\ No newline at end of file
diff --git a/test_data/channel/sendinteractive b/test_data/channel/sendinteractive
deleted file mode 100644
index 1ec5c5b..0000000
--- a/test_data/channel/sendinteractive
+++ /dev/null
@@ -1,4 +0,0 @@
-localhost#clear logging
-Clear logging buffer [confirm]
-
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/expected/arista_eos_long_expected b/test_data/driver/network/expected/arista_eos_long_expected
deleted file mode 100644
index 628f056..0000000
--- a/test_data/driver/network/expected/arista_eos_long_expected
+++ /dev/null
@@ -1,286 +0,0 @@
-! Command: show running-config
-! device: localhost (vEOS, EOS-4.22.1F)
-!
-! boot system flash:/vEOS-lab.swi
-!
-switchport default mode routed
-!
-transceiver qsfp default-mode 4x10G
-!
-logging console informational
-!
-logging level AAA errors
-logging level ACCOUNTING errors
-logging level ACL errors
-logging level AGENT errors
-logging level ALE errors
-logging level ARP errors
-logging level BFD errors
-logging level BGP errors
-logging level BMP errors
-logging level CAPACITY errors
-logging level CAPI errors
-logging level CLEAR errors
-logging level CVX errors
-logging level DATAPLANE errors
-logging level DHCP errors
-logging level DOT1X errors
-logging level DSCP errors
-logging level ENVMON errors
-logging level ETH errors
-logging level EVENTMON errors
-logging level EXTENSION errors
-logging level FHRP errors
-logging level FLOW errors
-logging level FORWARDING errors
-logging level FRU errors
-logging level FWK errors
-logging level GMP errors
-logging level HARDWARE errors
-logging level HEALTH errors
-logging level HTTPSERVICE errors
-logging level IGMP errors
-logging level IGMPSNOOPING errors
-logging level INT errors
-logging level INTF errors
-logging level IP6ROUTING errors
-logging level IPRIB errors
-logging level IRA errors
-logging level ISIS errors
-logging level KERNELFIB errors
-logging level LACP errors
-logging level LAG errors
-logging level LAUNCHER errors
-logging level LDP errors
-logging level LICENSE errors
-logging level LINEPROTO errors
-logging level LLDP errors
-logging level LOGMGR errors
-logging level LOOPBACK errors
-logging level LOOPPROTECT errors
-logging level MAPREDUCEMONITOR errors
-logging level MIRRORING errors
-logging level MKA errors
-logging level MLAG errors
-logging level MMODE errors
-logging level MROUTE errors
-logging level MRP errors
-logging level MSDP errors
-logging level MSRP errors
-logging level MSSPOLICYMONITOR errors
-logging level MVRP errors
-logging level NAT errors
-logging level OPENCONFIG errors
-logging level OPENFLOW errors
-logging level OSPF errors
-logging level OSPF3 errors
-logging level PACKAGE errors
-logging level PFC errors
-logging level PIMBSR errors
-logging level PORTSECURITY errors
-logging level PTP errors
-logging level PWRMGMT errors
-logging level QOS errors
-logging level QUEUEMONITOR errors
-logging level REDUNDANCY errors
-logging level RIB errors
-logging level ROUTING errors
-logging level SECURITY errors
-logging level SERVERMONITOR errors
-logging level SPANTREE errors
-logging level SSO errors
-logging level STAGEMGR errors
-logging level SYS errors
-logging level SYSDB errors
-logging level TAPAGG errors
-logging level TCP errors
-logging level TRANSCEIVER errors
-logging level TUNNEL errors
-logging level TUNNELINTF errors
-logging level VMTRACERSESS errors
-logging level VMWAREVI errors
-logging level VMWAREVS errors
-logging level VRF errors
-logging level VRRP errors
-logging level VXLAN errors
-logging level XMPP errors
-logging level ZTP informational
-!
-spanning-tree mode mstp
-!
-enable CRYPTO_REPLACED
-no aaa root
-!
-username boxen role network-admin CRYPTO_REPLACED
-!
-interface Ethernet1
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet2
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet3
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet4
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet5
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet6
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet7
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet8
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet9
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet10
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet11
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet12
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet13
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet14
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet15
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet16
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet17
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet18
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet19
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet20
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Management1
- ip address 10.0.0.15/24
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-no ip routing
-!
-control-plane
- no service-policy input copp-system-policy
-!
-banner login
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-EOF
-!
-management api http-commands
- protocol unix-socket
- no shutdown
-!
-management telnet
- no shutdown
-!
-end
\ No newline at end of file
diff --git a/test_data/driver/network/expected/arista_eos_short_expected b/test_data/driver/network/expected/arista_eos_short_expected
deleted file mode 100644
index 85415d4..0000000
--- a/test_data/driver/network/expected/arista_eos_short_expected
+++ /dev/null
@@ -1 +0,0 @@
-logging level ZTP informational
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_iosxe_short_expected b/test_data/driver/network/expected/cisco_iosxe_short_expected
deleted file mode 100644
index f615989..0000000
--- a/test_data/driver/network/expected/cisco_iosxe_short_expected
+++ /dev/null
@@ -1 +0,0 @@
-hostname csr1000v
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_iosxr_long_expected b/test_data/driver/network/expected/cisco_iosxr_long_expected
deleted file mode 100644
index f1f8cd1..0000000
--- a/test_data/driver/network/expected/cisco_iosxr_long_expected
+++ /dev/null
@@ -1,414 +0,0 @@
-TIME_STAMP_REPLACED
-Building configuration...
-!! IOS XR Configuration version = 6.5.3
-TIME_STAMP_REPLACED
-!
-telnet vrf default ipv4 server max-servers 10
-username boxen
- group root-lr
- group cisco-support
-CRYPTO_REPLACED
-!
-call-home
- service active
- contact smart-licensing
- profile CiscoTAC-1
- active
- destination transport-method http
- !
-!
-interface MgmtEth0/RP0/CPU0/0
- ipv4 address 10.0.0.15 255.255.255.0
-!
-interface GigabitEthernet0/0/0/0
- shutdown
-!
-interface GigabitEthernet0/0/0/1
- shutdown
-!
-interface GigabitEthernet0/0/0/2
- shutdown
-!
-interface GigabitEthernet0/0/0/3
- shutdown
-!
-interface GigabitEthernet0/0/0/4
- shutdown
-!
-interface GigabitEthernet0/0/0/5
- shutdown
-!
-interface GigabitEthernet0/0/0/6
- shutdown
-!
-interface GigabitEthernet0/0/0/7
- shutdown
-!
-interface GigabitEthernet0/0/0/8
- shutdown
-!
-interface GigabitEthernet0/0/0/9
- shutdown
-!
-interface GigabitEthernet0/0/0/10
- shutdown
-!
-interface GigabitEthernet0/0/0/11
- shutdown
-!
-interface GigabitEthernet0/0/0/12
- shutdown
-!
-interface GigabitEthernet0/0/0/13
- shutdown
-!
-interface GigabitEthernet0/0/0/14
- shutdown
-!
-interface GigabitEthernet0/0/0/15
- shutdown
-!
-interface GigabitEthernet0/0/0/16
- shutdown
-!
-interface GigabitEthernet0/0/0/17
- shutdown
-!
-interface GigabitEthernet0/0/0/18
- shutdown
-!
-interface GigabitEthernet0/0/0/19
- shutdown
-!
-interface GigabitEthernet0/0/0/20
- shutdown
-!
-interface GigabitEthernet0/0/0/21
- shutdown
-!
-interface GigabitEthernet0/0/0/22
- shutdown
-!
-interface GigabitEthernet0/0/0/23
- shutdown
-!
-interface GigabitEthernet0/0/0/24
- shutdown
-!
-interface GigabitEthernet0/0/0/25
- shutdown
-!
-interface GigabitEthernet0/0/0/26
- shutdown
-!
-interface GigabitEthernet0/0/0/27
- shutdown
-!
-interface GigabitEthernet0/0/0/28
- shutdown
-!
-interface GigabitEthernet0/0/0/29
- shutdown
-!
-interface GigabitEthernet0/0/0/30
- shutdown
-!
-interface GigabitEthernet0/0/0/31
- shutdown
-!
-interface GigabitEthernet0/0/0/32
- shutdown
-!
-interface GigabitEthernet0/0/0/33
- shutdown
-!
-interface GigabitEthernet0/0/0/34
- shutdown
-!
-interface GigabitEthernet0/0/0/35
- shutdown
-!
-interface GigabitEthernet0/0/0/36
- shutdown
-!
-interface GigabitEthernet0/0/0/37
- shutdown
-!
-interface GigabitEthernet0/0/0/38
- shutdown
-!
-interface GigabitEthernet0/0/0/39
- shutdown
-!
-interface GigabitEthernet0/0/0/40
- shutdown
-!
-interface GigabitEthernet0/0/0/41
- shutdown
-!
-interface GigabitEthernet0/0/0/42
- shutdown
-!
-interface GigabitEthernet0/0/0/43
- shutdown
-!
-interface GigabitEthernet0/0/0/44
- shutdown
-!
-interface GigabitEthernet0/0/0/45
- shutdown
-!
-interface GigabitEthernet0/0/0/46
- shutdown
-!
-interface GigabitEthernet0/0/0/47
- shutdown
-!
-interface GigabitEthernet0/0/0/48
- shutdown
-!
-interface GigabitEthernet0/0/0/49
- shutdown
-!
-interface GigabitEthernet0/0/0/50
- shutdown
-!
-interface GigabitEthernet0/0/0/51
- shutdown
-!
-interface GigabitEthernet0/0/0/52
- shutdown
-!
-interface GigabitEthernet0/0/0/53
- shutdown
-!
-interface GigabitEthernet0/0/0/54
- shutdown
-!
-interface GigabitEthernet0/0/0/55
- shutdown
-!
-interface GigabitEthernet0/0/0/56
- shutdown
-!
-interface GigabitEthernet0/0/0/57
- shutdown
-!
-interface GigabitEthernet0/0/0/58
- shutdown
-!
-interface GigabitEthernet0/0/0/59
- shutdown
-!
-interface GigabitEthernet0/0/0/60
- shutdown
-!
-interface GigabitEthernet0/0/0/61
- shutdown
-!
-interface GigabitEthernet0/0/0/62
- shutdown
-!
-interface GigabitEthernet0/0/0/63
- shutdown
-!
-interface GigabitEthernet0/0/0/64
- shutdown
-!
-interface GigabitEthernet0/0/0/65
- shutdown
-!
-interface GigabitEthernet0/0/0/66
- shutdown
-!
-interface GigabitEthernet0/0/0/67
- shutdown
-!
-interface GigabitEthernet0/0/0/68
- shutdown
-!
-interface GigabitEthernet0/0/0/69
- shutdown
-!
-interface GigabitEthernet0/0/0/70
- shutdown
-!
-interface GigabitEthernet0/0/0/71
- shutdown
-!
-interface GigabitEthernet0/0/0/72
- shutdown
-!
-interface GigabitEthernet0/0/0/73
- shutdown
-!
-interface GigabitEthernet0/0/0/74
- shutdown
-!
-interface GigabitEthernet0/0/0/75
- shutdown
-!
-interface GigabitEthernet0/0/0/76
- shutdown
-!
-interface GigabitEthernet0/0/0/77
- shutdown
-!
-interface GigabitEthernet0/0/0/78
- shutdown
-!
-interface GigabitEthernet0/0/0/79
- shutdown
-!
-interface GigabitEthernet0/0/0/80
- shutdown
-!
-interface GigabitEthernet0/0/0/81
- shutdown
-!
-interface GigabitEthernet0/0/0/82
- shutdown
-!
-interface GigabitEthernet0/0/0/83
- shutdown
-!
-interface GigabitEthernet0/0/0/84
- shutdown
-!
-interface GigabitEthernet0/0/0/85
- shutdown
-!
-interface GigabitEthernet0/0/0/86
- shutdown
-!
-interface GigabitEthernet0/0/0/87
- shutdown
-!
-interface GigabitEthernet0/0/0/88
- shutdown
-!
-interface GigabitEthernet0/0/0/89
- shutdown
-!
-interface GigabitEthernet0/0/0/90
- shutdown
-!
-interface GigabitEthernet0/0/0/91
- shutdown
-!
-interface GigabitEthernet0/0/0/92
- shutdown
-!
-interface GigabitEthernet0/0/0/93
- shutdown
-!
-interface GigabitEthernet0/0/0/94
- shutdown
-!
-interface GigabitEthernet0/0/0/95
- shutdown
-!
-interface GigabitEthernet0/0/0/96
- shutdown
-!
-interface GigabitEthernet0/0/0/97
- shutdown
-!
-interface GigabitEthernet0/0/0/98
- shutdown
-!
-interface GigabitEthernet0/0/0/99
- shutdown
-!
-interface GigabitEthernet0/0/0/100
- shutdown
-!
-interface GigabitEthernet0/0/0/101
- shutdown
-!
-interface GigabitEthernet0/0/0/102
- shutdown
-!
-interface GigabitEthernet0/0/0/103
- shutdown
-!
-interface GigabitEthernet0/0/0/104
- shutdown
-!
-interface GigabitEthernet0/0/0/105
- shutdown
-!
-interface GigabitEthernet0/0/0/106
- shutdown
-!
-interface GigabitEthernet0/0/0/107
- shutdown
-!
-interface GigabitEthernet0/0/0/108
- shutdown
-!
-interface GigabitEthernet0/0/0/109
- shutdown
-!
-interface GigabitEthernet0/0/0/110
- shutdown
-!
-interface GigabitEthernet0/0/0/111
- shutdown
-!
-interface GigabitEthernet0/0/0/112
- shutdown
-!
-interface GigabitEthernet0/0/0/113
- shutdown
-!
-interface GigabitEthernet0/0/0/114
- shutdown
-!
-interface GigabitEthernet0/0/0/115
- shutdown
-!
-interface GigabitEthernet0/0/0/116
- shutdown
-!
-interface GigabitEthernet0/0/0/117
- shutdown
-!
-interface GigabitEthernet0/0/0/118
- shutdown
-!
-interface GigabitEthernet0/0/0/119
- shutdown
-!
-interface GigabitEthernet0/0/0/120
- shutdown
-!
-interface GigabitEthernet0/0/0/121
- shutdown
-!
-interface GigabitEthernet0/0/0/122
- shutdown
-!
-interface GigabitEthernet0/0/0/123
- shutdown
-!
-interface GigabitEthernet0/0/0/124
- shutdown
-!
-interface GigabitEthernet0/0/0/125
- shutdown
-!
-interface GigabitEthernet0/0/0/126
- shutdown
-!
-interface GigabitEthernet0/0/0/127
- shutdown
-!
-xml agent tty
-!
-netconf-yang agent
- ssh
-!
-ssh server v2
-ssh server netconf vrf default
-end
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_iosxr_short_expected b/test_data/driver/network/expected/cisco_iosxr_short_expected
deleted file mode 100644
index ae90c0c..0000000
--- a/test_data/driver/network/expected/cisco_iosxr_short_expected
+++ /dev/null
@@ -1,3 +0,0 @@
-TIME_STAMP_REPLACED
-Building configuration...
-interface MgmtEth0/RP0/CPU0/0
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_nxos_short_expected b/test_data/driver/network/expected/cisco_nxos_short_expected
deleted file mode 100644
index 1f241a7..0000000
--- a/test_data/driver/network/expected/cisco_nxos_short_expected
+++ /dev/null
@@ -1 +0,0 @@
-feature scp-server
\ No newline at end of file
diff --git a/test_data/driver/network/expected/juniper_junos_short_expected b/test_data/driver/network/expected/juniper_junos_short_expected
deleted file mode 100644
index 7d9466d..0000000
--- a/test_data/driver/network/expected/juniper_junos_short_expected
+++ /dev/null
@@ -1 +0,0 @@
- address 10.0.0.15/24;
\ No newline at end of file
diff --git a/test_data/driver/network/expected/nokia_sros_classic_long_expected b/test_data/driver/network/expected/nokia_sros_classic_long_expected
deleted file mode 100644
index 8a45265..0000000
--- a/test_data/driver/network/expected/nokia_sros_classic_long_expected
+++ /dev/null
@@ -1,11 +0,0 @@
-===============================================================================
-Interface Table (Router: Base)
-===============================================================================
-Interface-Name Adm Opr(v4/v6) Mode Port/SapId
- IP-Address PfxState
--------------------------------------------------------------------------------
-system Up Down/Down Network system
- - -
--------------------------------------------------------------------------------
-Interfaces : 1
-===============================================================================
\ No newline at end of file
diff --git a/test_data/driver/network/expected/nokia_sros_classic_short_expected b/test_data/driver/network/expected/nokia_sros_classic_short_expected
deleted file mode 100644
index 707f7f7..0000000
--- a/test_data/driver/network/expected/nokia_sros_classic_short_expected
+++ /dev/null
@@ -1,3 +0,0 @@
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
\ No newline at end of file
diff --git a/test_data/driver/network/expected/nokia_sros_long_expected b/test_data/driver/network/expected/nokia_sros_long_expected
deleted file mode 100644
index 8a45265..0000000
--- a/test_data/driver/network/expected/nokia_sros_long_expected
+++ /dev/null
@@ -1,11 +0,0 @@
-===============================================================================
-Interface Table (Router: Base)
-===============================================================================
-Interface-Name Adm Opr(v4/v6) Mode Port/SapId
- IP-Address PfxState
--------------------------------------------------------------------------------
-system Up Down/Down Network system
- - -
--------------------------------------------------------------------------------
-Interfaces : 1
-===============================================================================
\ No newline at end of file
diff --git a/test_data/driver/network/expected/nokia_sros_short_expected b/test_data/driver/network/expected/nokia_sros_short_expected
deleted file mode 100644
index 707f7f7..0000000
--- a/test_data/driver/network/expected/nokia_sros_short_expected
+++ /dev/null
@@ -1,3 +0,0 @@
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
\ No newline at end of file
diff --git a/test_data/driver/network/expected/paloalto_panos_long_expected b/test_data/driver/network/expected/paloalto_panos_long_expected
deleted file mode 100644
index 61a394a..0000000
--- a/test_data/driver/network/expected/paloalto_panos_long_expected
+++ /dev/null
@@ -1,78 +0,0 @@
-==========================================================================
-TemplateStack: lab-palo-4_stack
-
-Serial Hostname IPv4 IPv6 Connected
---------------------------------------------------------------------------
-007251000190935 lab-palo-4b 172.28.87.75 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/11/09 12:44:30
- last template validate all state: none
- last template validate all updated: none
- template md5sum: 6d845a18b87f7498cbed68f2e6a59716(In Sync)
- template version: 395
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-007051000179556 lab-palo-4a 172.28.87.74 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/11/09 12:44:32
- last template validate all state: none
- last template validate all updated: none
- template md5sum: 6d845a18b87f7498cbed68f2e6a59716(In Sync)
- template version: 395
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-==========================================================================
-TemplateStack: lab-palo-test_stack
-
-Serial Hostname IPv4 IPv6 Connected
---------------------------------------------------------------------------
-007251000219929 lab-palo-test 172.28.87.227 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/10/22 14:54:04
- last template validate all state: none
- last template validate all updated: none
- template md5sum: d52b8b0b41977f700d1fb1e692ef4ce2(In Sync)
- template version:
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-==========================================================================
-Template: lab-palo-test
-
-==========================================================================
-Template: global
-
-==========================================================================
-Template: lab-palo-4
\ No newline at end of file
diff --git a/test_data/driver/network/expected/paloalto_panos_short_expected b/test_data/driver/network/expected/paloalto_panos_short_expected
deleted file mode 100644
index 7e7f43e..0000000
--- a/test_data/driver/network/expected/paloalto_panos_short_expected
+++ /dev/null
@@ -1 +0,0 @@
-Sat Nov 20 10:03:53 PST 2021
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/arista_eos_session_short b/test_data/driver/network/sendcommand/arista_eos_session_short
deleted file mode 100644
index 42ed4c7..0000000
--- a/test_data/driver/network/sendcommand/arista_eos_session_short
+++ /dev/null
@@ -1,23 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#show run | i ZTP
-logging level ZTP informational
-
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/cisco_iosxe_session_short b/test_data/driver/network/sendcommand/cisco_iosxe_session_short
deleted file mode 100644
index 55795b1..0000000
--- a/test_data/driver/network/sendcommand/cisco_iosxe_session_short
+++ /dev/null
@@ -1,7 +0,0 @@
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#show run | i hostname
-hostname csr1000v
-
-csr1000v#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/cisco_iosxr_session_short b/test_data/driver/network/sendcommand/cisco_iosxr_session_short
deleted file mode 100644
index ffea391..0000000
--- a/test_data/driver/network/sendcommand/cisco_iosxr_session_short
+++ /dev/null
@@ -1,11 +0,0 @@
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Sun May 16 19:50:16.686 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Sun May 16 19:50:16.842 UTC
-RP/0/RP0/CPU0:ios#show run | i MgmtEth0
-Sun May 16 19:49:14.762 UTC
-Building configuration...
-interface MgmtEth0/RP0/CPU0/0
-
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/cisco_nxos_session_short b/test_data/driver/network/sendcommand/cisco_nxos_session_short
deleted file mode 100644
index 465d6e0..0000000
--- a/test_data/driver/network/sendcommand/cisco_nxos_session_short
+++ /dev/null
@@ -1,38 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch# show run | i scp-server
-feature scp-server
-
-switch#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/juniper_junos_session_short b/test_data/driver/network/sendcommand/juniper_junos_session_short
deleted file mode 100644
index 40a97fb..0000000
--- a/test_data/driver/network/sendcommand/juniper_junos_session_short
+++ /dev/null
@@ -1,18 +0,0 @@
-READ: Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-boxen>
-
-boxen> set cli screen-length 0
-Screen length set to 0
-
-boxen> set cli screen-width 511
-Screen width set to 511
-
-boxen> set cli complete-on-space off
-Disabling complete-on-space
-
-boxen> show configuration | match 10.0.0.15
- address 10.0.0.15/24;
-
-boxen>
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/nokia_sros_classic_session_short b/test_data/driver/network/sendcommand/nokia_sros_classic_session_short
deleted file mode 100644
index 0c2cfb3..0000000
--- a/test_data/driver/network/sendcommand/nokia_sros_classic_session_short
+++ /dev/null
@@ -1,39 +0,0 @@
-Warning: Permanently added 'clab-scrapli-sros-classic,172.20.20.4' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-A:sros-classic#
-A:sros-classic# environment no more
-A:sros-classic# show version
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
-A:sros-classic#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/nokia_sros_session_short b/test_data/driver/network/sendcommand/nokia_sros_session_short
deleted file mode 100644
index 7e2ac09..0000000
--- a/test_data/driver/network/sendcommand/nokia_sros_session_short
+++ /dev/null
@@ -1,53 +0,0 @@
-Warning: Permanently added 'clab-sros01-sr2,172.20.20.2' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-
-[]
-A:admin@sr2#
-
-[]
-A:admin@sr2# environment console width 512
-
-[]
-A:admin@sr2# environment more false
-
-[]
-A:admin@sr2# environment command-completion space false
-
-[]
-A:admin@sr2# show version
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
-
-[]
-A:admin@sr2#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommand/paloalto_panos_session_short b/test_data/driver/network/sendcommand/paloalto_panos_session_short
deleted file mode 100644
index 8f51637..0000000
--- a/test_data/driver/network/sendcommand/paloalto_panos_session_short
+++ /dev/null
@@ -1,18 +0,0 @@
-Warning: Permanently added '1.1.1.1' (RSA) to the list of known hosts.
-Password:
-Last login: Sat Nov 20 09:54:03 2021 from 1.1.1.1
-
-
-
-Number of failed attempts since last successful login: 0
-
-
-admin@lab-panorama>
-admin@lab-panorama> set admin@lab-panorama> set cli admin@lab-panorama> set cli scripting-mode admin@lab-panorama> set cli scripting-mode on
-admin@lab-panorama> set cli pager off
-admin@lab-panorama> show clock
-
-Sat Nov 20 10:03:53 PST 2021
-
-admin@lab-panorama>
-admin@lab-panorama>
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/arista_eos_session b/test_data/driver/network/sendcommands/arista_eos_session
deleted file mode 100644
index 04f20d6..0000000
--- a/test_data/driver/network/sendcommands/arista_eos_session
+++ /dev/null
@@ -1,311 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#show run | i ZTP
-logging level ZTP informational
-
-localhost#show run
-! Command: show running-config
-! device: localhost (vEOS, EOS-4.22.1F)
-!
-! boot system flash:/vEOS-lab.swi
-!
-switchport default mode routed
-!
-transceiver qsfp default-mode 4x10G
-!
-logging console informational
-!
-logging level AAA errors
-logging level ACCOUNTING errors
-logging level ACL errors
-logging level AGENT errors
-logging level ALE errors
-logging level ARP errors
-logging level BFD errors
-logging level BGP errors
-logging level BMP errors
-logging level CAPACITY errors
-logging level CAPI errors
-logging level CLEAR errors
-logging level CVX errors
-logging level DATAPLANE errors
-logging level DHCP errors
-logging level DOT1X errors
-logging level DSCP errors
-logging level ENVMON errors
-logging level ETH errors
-logging level EVENTMON errors
-logging level EXTENSION errors
-logging level FHRP errors
-logging level FLOW errors
-logging level FORWARDING errors
-logging level FRU errors
-logging level FWK errors
-logging level GMP errors
-logging level HARDWARE errors
-logging level HEALTH errors
-logging level HTTPSERVICE errors
-logging level IGMP errors
-logging level IGMPSNOOPING errors
-logging level INT errors
-logging level INTF errors
-logging level IP6ROUTING errors
-logging level IPRIB errors
-logging level IRA errors
-logging level ISIS errors
-logging level KERNELFIB errors
-logging level LACP errors
-logging level LAG errors
-logging level LAUNCHER errors
-logging level LDP errors
-logging level LICENSE errors
-logging level LINEPROTO errors
-logging level LLDP errors
-logging level LOGMGR errors
-logging level LOOPBACK errors
-logging level LOOPPROTECT errors
-logging level MAPREDUCEMONITOR errors
-logging level MIRRORING errors
-logging level MKA errors
-logging level MLAG errors
-logging level MMODE errors
-logging level MROUTE errors
-logging level MRP errors
-logging level MSDP errors
-logging level MSRP errors
-logging level MSSPOLICYMONITOR errors
-logging level MVRP errors
-logging level NAT errors
-logging level OPENCONFIG errors
-logging level OPENFLOW errors
-logging level OSPF errors
-logging level OSPF3 errors
-logging level PACKAGE errors
-logging level PFC errors
-logging level PIMBSR errors
-logging level PORTSECURITY errors
-logging level PTP errors
-logging level PWRMGMT errors
-logging level QOS errors
-logging level QUEUEMONITOR errors
-logging level REDUNDANCY errors
-logging level RIB errors
-logging level ROUTING errors
-logging level SECURITY errors
-logging level SERVERMONITOR errors
-logging level SPANTREE errors
-logging level SSO errors
-logging level STAGEMGR errors
-logging level SYS errors
-logging level SYSDB errors
-logging level TAPAGG errors
-logging level TCP errors
-logging level TRANSCEIVER errors
-logging level TUNNEL errors
-logging level TUNNELINTF errors
-logging level VMTRACERSESS errors
-logging level VMWAREVI errors
-logging level VMWAREVS errors
-logging level VRF errors
-logging level VRRP errors
-logging level VXLAN errors
-logging level XMPP errors
-logging level ZTP informational
-!
-spanning-tree mode mstp
-!
-enable CRYPTO_REPLACED
-no aaa root
-!
-username boxen role network-admin CRYPTO_REPLACED
-!
-interface Ethernet1
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet2
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet3
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet4
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet5
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet6
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet7
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet8
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet9
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet10
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet11
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet12
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet13
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet14
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet15
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet16
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet17
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet18
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet19
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Ethernet20
- speed forced 10000full
- no switchport
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-interface Management1
- ip address 10.0.0.15/24
- ipv6 enable
- ipv6 address auto-config
- ipv6 nd ra rx accept default-route
-!
-no ip routing
-!
-control-plane
- no service-policy input copp-system-policy
-!
-banner login
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-EOF
-!
-management api http-commands
- protocol unix-socket
- no shutdown
-!
-management telnet
- no shutdown
-!
-end
-
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/cisco_iosxe_session b/test_data/driver/network/sendcommands/cisco_iosxe_session
deleted file mode 100644
index cc4ee6d..0000000
--- a/test_data/driver/network/sendcommands/cisco_iosxe_session
+++ /dev/null
@@ -1,230 +0,0 @@
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#show run | i hostname
-hostname csr1000v
-
-csr1000v#show run
-Building configuration...
-
-CONFIG_BYTES_REPLACED
-!
-TIME_STAMP_REPLACED
-!
-version 16.12
-service timestamps debug datetime msec
-service timestamps log datetime msec
-service call-home
-platform qfp utilization monitor load 80
-platform punt-keepalive disable-kernel-core
-platform console serial
-!
-hostname csr1000v
-!
-boot-start-marker
-boot-end-marker
-!
-!
-enable secret 9 $9$xvWnx8Fe35f8xE$E9ijp7GM/V48P5y1Uz3IEPtotXgwkJKYJmN0q3q2E92
-!
-no aaa new-model
-call-home
- ! If contact email address in call-home is configured as sch-smart-licensing@cisco.com
- ! the email address configured in Cisco Smart License Portal will be used as contact email address to send SCH notifications.
- contact-email-addr sch-smart-licensing@cisco.com
- profile "CiscoTAC-1"
- active
- destination transport-method http
- no destination transport-method email
-!
-!
-!
-!
-!
-!
-!
-ip domain name example.com
-!
-!
-!
-login on-success log
-!
-!
-!
-!
-!
-!
-!
-subscriber templating
-!
-!
-!
-!
-!
-!
-multilink bundle-name authenticated
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-CERT_LICENSE_REPLACED
-diagnostic bootup level minimal
-archive
- log config
- logging enable
- path bootflash:
-memory free low-watermark processor 72329
-!
-!
-spanning-tree extend system-id
-!
-username boxen privilege 15 password 0 b0x3N-b0x3N
-!
-redundancy
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-!
-interface GigabitEthernet1
- ip address 10.0.0.15 255.255.255.0
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet2
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet3
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet4
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet5
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet6
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet7
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet8
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet9
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-interface GigabitEthernet10
- no ip address
- shutdown
- negotiation auto
- no mop enabled
- no mop sysid
-!
-!
-virtual-service csr_mgmt
-!
-ip forward-protocol nd
-no ip http server
-no ip http secure-server
-!
-ip ssh pubkey-chain
- username boxen
- key-hash ssh-rsa 5CC74A68B18B026A1709FB09D1F44E2F
-ip scp server enable
-!
-!
-!
-!
-!
-!
-!
-control-plane
-!
-!
-!
-!
-!
-!
-line con 0
- stopbits 1
-line vty 0 4
- login local
- transport input all
-line vty 5 15
- login local
- transport input all
-!
-netconf ssh
-!
-!
-!
-!
-!
-netconf-yang
-end
-
-csr1000v#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/cisco_iosxr_session b/test_data/driver/network/sendcommands/cisco_iosxr_session
deleted file mode 100644
index b271e9d..0000000
--- a/test_data/driver/network/sendcommands/cisco_iosxr_session
+++ /dev/null
@@ -1,427 +0,0 @@
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Sun May 16 19:50:16.686 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Sun May 16 19:50:16.842 UTC
-RP/0/RP0/CPU0:ios#show run | i MgmtEth0
-Sun May 16 19:49:14.762 UTC
-Building configuration...
-interface MgmtEth0/RP0/CPU0/0
-
-RP/0/RP0/CPU0:ios#show run
-TIME_STAMP_REPLACED
-Building configuration...
-!! IOS XR Configuration version = 6.5.3
-TIME_STAMP_REPLACED
-!
-telnet vrf default ipv4 server max-servers 10
-username boxen
- group root-lr
- group cisco-support
-CRYPTO_REPLACED
-!
-call-home
- service active
- contact smart-licensing
- profile CiscoTAC-1
- active
- destination transport-method http
- !
-!
-interface MgmtEth0/RP0/CPU0/0
- ipv4 address 10.0.0.15 255.255.255.0
-!
-interface GigabitEthernet0/0/0/0
- shutdown
-!
-interface GigabitEthernet0/0/0/1
- shutdown
-!
-interface GigabitEthernet0/0/0/2
- shutdown
-!
-interface GigabitEthernet0/0/0/3
- shutdown
-!
-interface GigabitEthernet0/0/0/4
- shutdown
-!
-interface GigabitEthernet0/0/0/5
- shutdown
-!
-interface GigabitEthernet0/0/0/6
- shutdown
-!
-interface GigabitEthernet0/0/0/7
- shutdown
-!
-interface GigabitEthernet0/0/0/8
- shutdown
-!
-interface GigabitEthernet0/0/0/9
- shutdown
-!
-interface GigabitEthernet0/0/0/10
- shutdown
-!
-interface GigabitEthernet0/0/0/11
- shutdown
-!
-interface GigabitEthernet0/0/0/12
- shutdown
-!
-interface GigabitEthernet0/0/0/13
- shutdown
-!
-interface GigabitEthernet0/0/0/14
- shutdown
-!
-interface GigabitEthernet0/0/0/15
- shutdown
-!
-interface GigabitEthernet0/0/0/16
- shutdown
-!
-interface GigabitEthernet0/0/0/17
- shutdown
-!
-interface GigabitEthernet0/0/0/18
- shutdown
-!
-interface GigabitEthernet0/0/0/19
- shutdown
-!
-interface GigabitEthernet0/0/0/20
- shutdown
-!
-interface GigabitEthernet0/0/0/21
- shutdown
-!
-interface GigabitEthernet0/0/0/22
- shutdown
-!
-interface GigabitEthernet0/0/0/23
- shutdown
-!
-interface GigabitEthernet0/0/0/24
- shutdown
-!
-interface GigabitEthernet0/0/0/25
- shutdown
-!
-interface GigabitEthernet0/0/0/26
- shutdown
-!
-interface GigabitEthernet0/0/0/27
- shutdown
-!
-interface GigabitEthernet0/0/0/28
- shutdown
-!
-interface GigabitEthernet0/0/0/29
- shutdown
-!
-interface GigabitEthernet0/0/0/30
- shutdown
-!
-interface GigabitEthernet0/0/0/31
- shutdown
-!
-interface GigabitEthernet0/0/0/32
- shutdown
-!
-interface GigabitEthernet0/0/0/33
- shutdown
-!
-interface GigabitEthernet0/0/0/34
- shutdown
-!
-interface GigabitEthernet0/0/0/35
- shutdown
-!
-interface GigabitEthernet0/0/0/36
- shutdown
-!
-interface GigabitEthernet0/0/0/37
- shutdown
-!
-interface GigabitEthernet0/0/0/38
- shutdown
-!
-interface GigabitEthernet0/0/0/39
- shutdown
-!
-interface GigabitEthernet0/0/0/40
- shutdown
-!
-interface GigabitEthernet0/0/0/41
- shutdown
-!
-interface GigabitEthernet0/0/0/42
- shutdown
-!
-interface GigabitEthernet0/0/0/43
- shutdown
-!
-interface GigabitEthernet0/0/0/44
- shutdown
-!
-interface GigabitEthernet0/0/0/45
- shutdown
-!
-interface GigabitEthernet0/0/0/46
- shutdown
-!
-interface GigabitEthernet0/0/0/47
- shutdown
-!
-interface GigabitEthernet0/0/0/48
- shutdown
-!
-interface GigabitEthernet0/0/0/49
- shutdown
-!
-interface GigabitEthernet0/0/0/50
- shutdown
-!
-interface GigabitEthernet0/0/0/51
- shutdown
-!
-interface GigabitEthernet0/0/0/52
- shutdown
-!
-interface GigabitEthernet0/0/0/53
- shutdown
-!
-interface GigabitEthernet0/0/0/54
- shutdown
-!
-interface GigabitEthernet0/0/0/55
- shutdown
-!
-interface GigabitEthernet0/0/0/56
- shutdown
-!
-interface GigabitEthernet0/0/0/57
- shutdown
-!
-interface GigabitEthernet0/0/0/58
- shutdown
-!
-interface GigabitEthernet0/0/0/59
- shutdown
-!
-interface GigabitEthernet0/0/0/60
- shutdown
-!
-interface GigabitEthernet0/0/0/61
- shutdown
-!
-interface GigabitEthernet0/0/0/62
- shutdown
-!
-interface GigabitEthernet0/0/0/63
- shutdown
-!
-interface GigabitEthernet0/0/0/64
- shutdown
-!
-interface GigabitEthernet0/0/0/65
- shutdown
-!
-interface GigabitEthernet0/0/0/66
- shutdown
-!
-interface GigabitEthernet0/0/0/67
- shutdown
-!
-interface GigabitEthernet0/0/0/68
- shutdown
-!
-interface GigabitEthernet0/0/0/69
- shutdown
-!
-interface GigabitEthernet0/0/0/70
- shutdown
-!
-interface GigabitEthernet0/0/0/71
- shutdown
-!
-interface GigabitEthernet0/0/0/72
- shutdown
-!
-interface GigabitEthernet0/0/0/73
- shutdown
-!
-interface GigabitEthernet0/0/0/74
- shutdown
-!
-interface GigabitEthernet0/0/0/75
- shutdown
-!
-interface GigabitEthernet0/0/0/76
- shutdown
-!
-interface GigabitEthernet0/0/0/77
- shutdown
-!
-interface GigabitEthernet0/0/0/78
- shutdown
-!
-interface GigabitEthernet0/0/0/79
- shutdown
-!
-interface GigabitEthernet0/0/0/80
- shutdown
-!
-interface GigabitEthernet0/0/0/81
- shutdown
-!
-interface GigabitEthernet0/0/0/82
- shutdown
-!
-interface GigabitEthernet0/0/0/83
- shutdown
-!
-interface GigabitEthernet0/0/0/84
- shutdown
-!
-interface GigabitEthernet0/0/0/85
- shutdown
-!
-interface GigabitEthernet0/0/0/86
- shutdown
-!
-interface GigabitEthernet0/0/0/87
- shutdown
-!
-interface GigabitEthernet0/0/0/88
- shutdown
-!
-interface GigabitEthernet0/0/0/89
- shutdown
-!
-interface GigabitEthernet0/0/0/90
- shutdown
-!
-interface GigabitEthernet0/0/0/91
- shutdown
-!
-interface GigabitEthernet0/0/0/92
- shutdown
-!
-interface GigabitEthernet0/0/0/93
- shutdown
-!
-interface GigabitEthernet0/0/0/94
- shutdown
-!
-interface GigabitEthernet0/0/0/95
- shutdown
-!
-interface GigabitEthernet0/0/0/96
- shutdown
-!
-interface GigabitEthernet0/0/0/97
- shutdown
-!
-interface GigabitEthernet0/0/0/98
- shutdown
-!
-interface GigabitEthernet0/0/0/99
- shutdown
-!
-interface GigabitEthernet0/0/0/100
- shutdown
-!
-interface GigabitEthernet0/0/0/101
- shutdown
-!
-interface GigabitEthernet0/0/0/102
- shutdown
-!
-interface GigabitEthernet0/0/0/103
- shutdown
-!
-interface GigabitEthernet0/0/0/104
- shutdown
-!
-interface GigabitEthernet0/0/0/105
- shutdown
-!
-interface GigabitEthernet0/0/0/106
- shutdown
-!
-interface GigabitEthernet0/0/0/107
- shutdown
-!
-interface GigabitEthernet0/0/0/108
- shutdown
-!
-interface GigabitEthernet0/0/0/109
- shutdown
-!
-interface GigabitEthernet0/0/0/110
- shutdown
-!
-interface GigabitEthernet0/0/0/111
- shutdown
-!
-interface GigabitEthernet0/0/0/112
- shutdown
-!
-interface GigabitEthernet0/0/0/113
- shutdown
-!
-interface GigabitEthernet0/0/0/114
- shutdown
-!
-interface GigabitEthernet0/0/0/115
- shutdown
-!
-interface GigabitEthernet0/0/0/116
- shutdown
-!
-interface GigabitEthernet0/0/0/117
- shutdown
-!
-interface GigabitEthernet0/0/0/118
- shutdown
-!
-interface GigabitEthernet0/0/0/119
- shutdown
-!
-interface GigabitEthernet0/0/0/120
- shutdown
-!
-interface GigabitEthernet0/0/0/121
- shutdown
-!
-interface GigabitEthernet0/0/0/122
- shutdown
-!
-interface GigabitEthernet0/0/0/123
- shutdown
-!
-interface GigabitEthernet0/0/0/124
- shutdown
-!
-interface GigabitEthernet0/0/0/125
- shutdown
-!
-interface GigabitEthernet0/0/0/126
- shutdown
-!
-interface GigabitEthernet0/0/0/127
- shutdown
-!
-xml agent tty
-!
-netconf-yang agent
- ssh
-!
-ssh server v2
-ssh server netconf vrf default
-end
-
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/cisco_nxos_session b/test_data/driver/network/sendcommands/cisco_nxos_session
deleted file mode 100644
index 1d0f71c..0000000
--- a/test_data/driver/network/sendcommands/cisco_nxos_session
+++ /dev/null
@@ -1,337 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch# show run | i scp-server
-feature scp-server
-
-switch#show run
-!Command: show running-config
-!Running configuration last done at: TIME_STAMP_REPLACED
-!Time: TIME_STAMP_REPLACED
-
-version 9.2(4) Bios:version
-vdc switch id 1
- limit-resource vlan minimum RESOURCES_REPLACED
- limit-resource vrf minimum RESOURCES_REPLACED
- limit-resource port-channel minimum RESOURCES_REPLACED
- limit-resource u4route-mem minimum RESOURCES_REPLACED
- limit-resource u6route-mem minimum RESOURCES_REPLACED
- limit-resource m4route-mem minimum RESOURCES_REPLACED
- limit-resource m6route-mem minimum RESOURCES_REPLACED
-feature telnet
-feature nxapi
-feature scp-server
-
-no password strength-check
-CRYPTO_REPLACED
-CRYPTO_REPLACED
-username boxen passphrase lifetime 99999 warntime 14 gracetime 3
-ip domain-lookup
-copp profile strict
-CRYPTO_REPLACED
-CRYPTO_REPLACED
-rmon event 1 description FATAL(1) owner PMON@FATAL
-rmon event 2 description CRITICAL(2) owner PMON@CRITICAL
-rmon event 3 description ERROR(3) owner PMON@ERROR
-rmon event 4 description WARNING(4) owner PMON@WARNING
-CRYPTO_REPLACED
-
-vlan 1
-
-vrf context management
-
-interface Ethernet1/1
-
-interface Ethernet1/2
-
-interface Ethernet1/3
-
-interface Ethernet1/4
-
-interface Ethernet1/5
-
-interface Ethernet1/6
-
-interface Ethernet1/7
-
-interface Ethernet1/8
-
-interface Ethernet1/9
-
-interface Ethernet1/10
-
-interface Ethernet1/11
-
-interface Ethernet1/12
-
-interface Ethernet1/13
-
-interface Ethernet1/14
-
-interface Ethernet1/15
-
-interface Ethernet1/16
-
-interface Ethernet1/17
-
-interface Ethernet1/18
-
-interface Ethernet1/19
-
-interface Ethernet1/20
-
-interface Ethernet1/21
-
-interface Ethernet1/22
-
-interface Ethernet1/23
-
-interface Ethernet1/24
-
-interface Ethernet1/25
-
-interface Ethernet1/26
-
-interface Ethernet1/27
-
-interface Ethernet1/28
-
-interface Ethernet1/29
-
-interface Ethernet1/30
-
-interface Ethernet1/31
-
-interface Ethernet1/32
-
-interface Ethernet1/33
-
-interface Ethernet1/34
-
-interface Ethernet1/35
-
-interface Ethernet1/36
-
-interface Ethernet1/37
-
-interface Ethernet1/38
-
-interface Ethernet1/39
-
-interface Ethernet1/40
-
-interface Ethernet1/41
-
-interface Ethernet1/42
-
-interface Ethernet1/43
-
-interface Ethernet1/44
-
-interface Ethernet1/45
-
-interface Ethernet1/46
-
-interface Ethernet1/47
-
-interface Ethernet1/48
-
-interface Ethernet1/49
-
-interface Ethernet1/50
-
-interface Ethernet1/51
-
-interface Ethernet1/52
-
-interface Ethernet1/53
-
-interface Ethernet1/54
-
-interface Ethernet1/55
-
-interface Ethernet1/56
-
-interface Ethernet1/57
-
-interface Ethernet1/58
-
-interface Ethernet1/59
-
-interface Ethernet1/60
-
-interface Ethernet1/61
-
-interface Ethernet1/62
-
-interface Ethernet1/63
-
-interface Ethernet1/64
-
-interface Ethernet1/65
-
-interface Ethernet1/66
-
-interface Ethernet1/67
-
-interface Ethernet1/68
-
-interface Ethernet1/69
-
-interface Ethernet1/70
-
-interface Ethernet1/71
-
-interface Ethernet1/72
-
-interface Ethernet1/73
-
-interface Ethernet1/74
-
-interface Ethernet1/75
-
-interface Ethernet1/76
-
-interface Ethernet1/77
-
-interface Ethernet1/78
-
-interface Ethernet1/79
-
-interface Ethernet1/80
-
-interface Ethernet1/81
-
-interface Ethernet1/82
-
-interface Ethernet1/83
-
-interface Ethernet1/84
-
-interface Ethernet1/85
-
-interface Ethernet1/86
-
-interface Ethernet1/87
-
-interface Ethernet1/88
-
-interface Ethernet1/89
-
-interface Ethernet1/90
-
-interface Ethernet1/91
-
-interface Ethernet1/92
-
-interface Ethernet1/93
-
-interface Ethernet1/94
-
-interface Ethernet1/95
-
-interface Ethernet1/96
-
-interface Ethernet1/97
-
-interface Ethernet1/98
-
-interface Ethernet1/99
-
-interface Ethernet1/100
-
-interface Ethernet1/101
-
-interface Ethernet1/102
-
-interface Ethernet1/103
-
-interface Ethernet1/104
-
-interface Ethernet1/105
-
-interface Ethernet1/106
-
-interface Ethernet1/107
-
-interface Ethernet1/108
-
-interface Ethernet1/109
-
-interface Ethernet1/110
-
-interface Ethernet1/111
-
-interface Ethernet1/112
-
-interface Ethernet1/113
-
-interface Ethernet1/114
-
-interface Ethernet1/115
-
-interface Ethernet1/116
-
-interface Ethernet1/117
-
-interface Ethernet1/118
-
-interface Ethernet1/119
-
-interface Ethernet1/120
-
-interface Ethernet1/121
-
-interface Ethernet1/122
-
-interface Ethernet1/123
-
-interface Ethernet1/124
-
-interface Ethernet1/125
-
-interface Ethernet1/126
-
-interface Ethernet1/127
-
-interface Ethernet1/128
-
-interface mgmt0
- vrf member management
- ip address 10.0.0.15/24
-line console
-line vty
-boot nxos bootflash:/nxos.9.2.4.bin
-
-switch#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/nokia_sros_classic_session b/test_data/driver/network/sendcommands/nokia_sros_classic_session
deleted file mode 100644
index ea69d6d..0000000
--- a/test_data/driver/network/sendcommands/nokia_sros_classic_session
+++ /dev/null
@@ -1,52 +0,0 @@
-Warning: Permanently added 'clab-scrapli-sros-classic,172.20.20.4' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-A:sros-classic#
-A:sros-classic# environment no more
-A:sros-classic# show version
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
-A:sros-classic# show router interface
-
-===============================================================================
-Interface Table (Router: Base)
-===============================================================================
-Interface-Name Adm Opr(v4/v6) Mode Port/SapId
- IP-Address PfxState
--------------------------------------------------------------------------------
-system Up Down/Down Network system
- - -
--------------------------------------------------------------------------------
-Interfaces : 1
-===============================================================================
-A:sros-classic#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/nokia_sros_session b/test_data/driver/network/sendcommands/nokia_sros_session
deleted file mode 100644
index 88d0047..0000000
--- a/test_data/driver/network/sendcommands/nokia_sros_session
+++ /dev/null
@@ -1,68 +0,0 @@
-Warning: Permanently added 'clab-sros01-sr2,172.20.20.2' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-
-[]
-A:admin@sr2#
-
-[]
-A:admin@sr2# environment console width 512
-
-[]
-A:admin@sr2# environment more false
-
-[]
-A:admin@sr2# environment command-completion space false
-
-[]
-A:admin@sr2# show version
-TiMOS-B-20.10.R3 both/x86_64 Nokia 7750 SR Copyright (c) 2000-2021 Nokia.
-All rights reserved. All use subject to applicable license agreements.
-Built on Wed Jan 27 13:21:10 PST 2021 by builder in /builds/c/2010B/R3/panos/main/sros
-
-[]
-A:admin@sr2# show router interface
-
-===============================================================================
-Interface Table (Router: Base)
-===============================================================================
-Interface-Name Adm Opr(v4/v6) Mode Port/SapId
- IP-Address PfxState
--------------------------------------------------------------------------------
-system Up Down/Down Network system
- - -
--------------------------------------------------------------------------------
-Interfaces : 1
-===============================================================================
-
-[]
-A:admin@sr2#
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommands/paloalto_panos_session b/test_data/driver/network/sendcommands/paloalto_panos_session
deleted file mode 100644
index 7d31985..0000000
--- a/test_data/driver/network/sendcommands/paloalto_panos_session
+++ /dev/null
@@ -1,101 +0,0 @@
-Warning: Permanently added '1.1.1.1' (RSA) to the list of known hosts.
-Password:
-Last login: Sat Nov 20 09:54:03 2021 from 1.1.1.1
-
-
-
-Number of failed attempts since last successful login: 0
-
-
-admin@lab-panorama>
-admin@lab-panorama> set admin@lab-panorama> set cli admin@lab-panorama> set cli scripting-mode admin@lab-panorama> set cli scripting-mode on
-admin@lab-panorama> set cli pager off
-admin@lab-panorama> show clock
-
-Sat Nov 20 10:03:53 PST 2021
-
-admin@lab-panorama> show templates
-
-
-==========================================================================
-TemplateStack: lab-palo-4_stack
-
-Serial Hostname IPv4 IPv6 Connected
---------------------------------------------------------------------------
-007251000190935 lab-palo-4b 172.28.87.75 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/11/09 12:44:30
- last template validate all state: none
- last template validate all updated: none
- template md5sum: 6d845a18b87f7498cbed68f2e6a59716(In Sync)
- template version: 395
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-007051000179556 lab-palo-4a 172.28.87.74 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/11/09 12:44:32
- last template validate all state: none
- last template validate all updated: none
- template md5sum: 6d845a18b87f7498cbed68f2e6a59716(In Sync)
- template version: 395
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-==========================================================================
-TemplateStack: lab-palo-test_stack
-
-Serial Hostname IPv4 IPv6 Connected
---------------------------------------------------------------------------
-007251000219929 lab-palo-test 172.28.87.227 unknown yes
-Wildfire Real-time Stream Disablednot deactivated last template commit all state: commit succeeded with warnings
- last template commit all updated: 2021/10/22 14:54:04
- last template validate all state: none
- last template validate all updated: none
- template md5sum: d52b8b0b41977f700d1fb1e692ef4ce2(In Sync)
- template version:
- template md5sum(no content preview):(Out of Sync)
- VPN Disable Mode: no
- Operational Mode: normal
- Certificate Status:
- Certificate subject Name:
- Certificate expiry at:
- Connected at:
- Custom certificate Used:
- Last masterkey push status: Unknown
- Last masterkey push timestamp: none
- Express mode: no
- Device cert present :
- Device cert expiry date : N/A
-==========================================================================
-Template: lab-palo-test
-
-==========================================================================
-Template: global
-
-==========================================================================
-Template: lab-palo-4
-
-
-admin@lab-panorama>
-admin@lab-panorama>
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/arista_eos_commands b/test_data/driver/network/sendcommandsfromfile/arista_eos_commands
deleted file mode 100644
index 541ad10..0000000
--- a/test_data/driver/network/sendcommandsfromfile/arista_eos_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show run | i ZTP
-show run
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/cisco_iosxe_commands b/test_data/driver/network/sendcommandsfromfile/cisco_iosxe_commands
deleted file mode 100644
index 99aaec5..0000000
--- a/test_data/driver/network/sendcommandsfromfile/cisco_iosxe_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show run | i hostname
-show run
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/cisco_iosxr_commands b/test_data/driver/network/sendcommandsfromfile/cisco_iosxr_commands
deleted file mode 100644
index 728cb3a..0000000
--- a/test_data/driver/network/sendcommandsfromfile/cisco_iosxr_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show run | i MgmtEth0
-show run
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/cisco_nxos_commands b/test_data/driver/network/sendcommandsfromfile/cisco_nxos_commands
deleted file mode 100644
index 3faa100..0000000
--- a/test_data/driver/network/sendcommandsfromfile/cisco_nxos_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show run | i scp-server
-show run
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/juniper_junos_commands b/test_data/driver/network/sendcommandsfromfile/juniper_junos_commands
deleted file mode 100644
index 46d9c15..0000000
--- a/test_data/driver/network/sendcommandsfromfile/juniper_junos_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show configuration | match 10.0.0.15
-show configuration
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/nokia_sros_classic_commands b/test_data/driver/network/sendcommandsfromfile/nokia_sros_classic_commands
deleted file mode 100644
index 35e825f..0000000
--- a/test_data/driver/network/sendcommandsfromfile/nokia_sros_classic_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show version
-show router interface
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/nokia_sros_commands b/test_data/driver/network/sendcommandsfromfile/nokia_sros_commands
deleted file mode 100644
index 35e825f..0000000
--- a/test_data/driver/network/sendcommandsfromfile/nokia_sros_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show version
-show router interface
\ No newline at end of file
diff --git a/test_data/driver/network/sendcommandsfromfile/paloalto_panos_commands b/test_data/driver/network/sendcommandsfromfile/paloalto_panos_commands
deleted file mode 100644
index 7dfc21c..0000000
--- a/test_data/driver/network/sendcommandsfromfile/paloalto_panos_commands
+++ /dev/null
@@ -1,2 +0,0 @@
-show clock
-show templates
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/arista_eos b/test_data/driver/network/sendconfigs/arista_eos
deleted file mode 100644
index c926d15..0000000
--- a/test_data/driver/network/sendconfigs/arista_eos
+++ /dev/null
@@ -1,30 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-Last login: Mon May 17 22:39:57 2021 from 10.0.0.2
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#
-localhost#configure terminal
-localhost(config)#
-localhost(config)#interface loopback0
-localhost(config-if-Lo0)#description tacocat
-localhost(config-if-Lo0)#no interface loopback0
-localhost(config)#
-localhost(config)#end
-localhost#
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/cisco_iosxe b/test_data/driver/network/sendconfigs/cisco_iosxe
deleted file mode 100644
index 7fd34f6..0000000
--- a/test_data/driver/network/sendconfigs/cisco_iosxe
+++ /dev/null
@@ -1,12 +0,0 @@
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#configure terminal
-Enter configuration commands, one per line. End with CNTL/Z.
-csr1000v(config)#
-csr1000v(config)#interface loopback0
-csr1000v(config-if)#description tacocat
-csr1000v(config-if)#no interface loopback0
-csr1000v(config)#
-csr1000v(config)#end
-csr1000v#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/cisco_iosxr b/test_data/driver/network/sendconfigs/cisco_iosxr
deleted file mode 100644
index 1e8d0b0..0000000
--- a/test_data/driver/network/sendconfigs/cisco_iosxr
+++ /dev/null
@@ -1,17 +0,0 @@
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Tue May 18 16:49:01.098 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Tue May 18 16:49:01.254 UTC
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#configure terminal
-Tue May 18 16:49:01.357 UTC
-RP/0/RP0/CPU0:ios(config)#
-RP/0/RP0/CPU0:ios(config)#interface loopback0
-RP/0/RP0/CPU0:ios(config-if)#description tacocat
-RP/0/RP0/CPU0:ios(config-if)#no interface loopback0
-RP/0/RP0/CPU0:ios(config)#commit
-Tue May 18 16:49:02.119 UTC
-RP/0/RP0/CPU0:ios(config)#
-RP/0/RP0/CPU0:ios(config)#end
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/cisco_nxos b/test_data/driver/network/sendconfigs/cisco_nxos
deleted file mode 100644
index dc910ea..0000000
--- a/test_data/driver/network/sendconfigs/cisco_nxos
+++ /dev/null
@@ -1,44 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch#
-switch# configure terminal
-Enter configuration commands, one per line. End with CNTL/Z.
-switch(config)#
-switch(config)# interface loopback0
-switch(config-if)# description tacocat
-switch(config-if)# no interface loopback0
-switch(config)#
-switch(config)# end
-switch#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/juniper_junos b/test_data/driver/network/sendconfigs/juniper_junos
deleted file mode 100644
index 17c0273..0000000
--- a/test_data/driver/network/sendconfigs/juniper_junos
+++ /dev/null
@@ -1,40 +0,0 @@
-Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-vrnetlab>
-
-vrnetlab> set cli screen-length 0
-Screen length set to 0
-
-vrnetlab> set cli screen-width 511
-Screen width set to 511
-
-vrnetlab> set cli complete-on-space off
-Disabling complete-on-space
-
-vrnetlab>
-
-vrnetlab> configure
-Entering configuration mode
-
-[edit]
-vrnetlab#
-
-[edit]
-vrnetlab# set interfaces fxp0.0 description tacocat
-
-[edit]
-vrnetlab# delete interfaces fxp0.0 description tacocat
-
-[edit]
-vrnetlab# commit
-commit complete
-
-[edit]
-vrnetlab#
-
-[edit]
-vrnetlab# exit configuration-mode
-Exiting configuration mode
-
-vrnetlab>
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/nokia_sros b/test_data/driver/network/sendconfigs/nokia_sros
deleted file mode 100644
index 700e2d6..0000000
--- a/test_data/driver/network/sendconfigs/nokia_sros
+++ /dev/null
@@ -1,64 +0,0 @@
-Warning: Permanently added 'clab-sros01-sr2,172.20.20.2' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-
-[]
-A:admin@sr2#
-
-[]
-A:admin@sr2# environment console width 512
-
-[]
-A:admin@sr2# environment more false
-
-[]
-A:admin@sr2# environment command-completion space false
-
-[]
-A:admin@sr2# edit-config exclusive
-INFO: CLI #2060: Entering exclusive configuration mode
-INFO: CLI #2061: Uncommitted changes are discarded on configuration mode exit
-
-(ex)[]
-A:admin@sr2# configure router interface "system" description "@ntdvps"
-
-*(ex)[]
-A:admin@sr2# configure system
-
-*(ex)[configure system]
-A:admin@sr2# location wide_internet
-
-*(ex)[configure system]
-A:admin@sr2# commit
-
-(ex)[configure system]
-A:admin@sr2#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/nokia_sros_classic b/test_data/driver/network/sendconfigs/nokia_sros_classic
deleted file mode 100644
index e333491..0000000
--- a/test_data/driver/network/sendconfigs/nokia_sros_classic
+++ /dev/null
@@ -1,40 +0,0 @@
-Warning: Permanently added 'clab-scrapli-sros-classic,172.20.20.4' (RSA) to the list of known hosts.
-
-admin@clab-scrapli-sros-classic's password:
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-A:sros-classic#
-A:sros-classic# environment no more
-A:sros-classic# configure router interface "system" description "@ntdvps"
-*A:sros-classic# configure system
-*A:sros-classic>config>system# location wide_internet
-*A:sros-classic>config>system#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigs/paloalto_panos b/test_data/driver/network/sendconfigs/paloalto_panos
deleted file mode 100644
index 8484a32..0000000
--- a/test_data/driver/network/sendconfigs/paloalto_panos
+++ /dev/null
@@ -1,38 +0,0 @@
-Warning: Permanently added '1.1.1.1' (RSA) to the list of known hosts.
-Password:
-Last login: Sat Nov 20 09:54:03 2021 from 1.1.1.1
-
-
-
-Number of failed attempts since last successful login: 0
-
-
-admin@lab-panorama>
-admin@lab-panorama> set admin@lab-panorama> set cli admin@lab-panorama> set cli scripting-mode admin@lab-panorama> set cli scripting-mode on
-admin@lab-panorama> set cli pager off
-admin@lab-panorama> show clock
-
-Sat Nov 20 10:03:53 PST 2021
-
-admin@lab-panorama> configure
-Entering configuration mode
-[edit]
-admin@lab-panorama#
-[edit]
-admin@lab-panorama# set display-name BLAH
-
-[edit]
-admin@lab-panorama# commit
-Commit job 9468 is in progress. Use Ctrl+C to return to command prompt
-.......55%70%.98%.........100%
-Configuration committed successfully
-External Dynamic List MM-Blocks is configured with no certificate profile. Please select a certificate profile for performing server certificate validation.
-External Dynamic List MM-PDX-Blacklist is configured with no certificate profile. Please select a certificate profile for performing server certificate validation.
-Warning: No valid threat content package exists
-Warning: No valid Antivirus content package exists
-(Module: device)
-
-[edit]
-admin@lab-panorama# exit
-admin@lab-panorama>
-admin@lab-panorama>
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/arista_eos b/test_data/driver/network/sendconfigsfromfile/arista_eos
deleted file mode 100644
index c926d15..0000000
--- a/test_data/driver/network/sendconfigsfromfile/arista_eos
+++ /dev/null
@@ -1,30 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-Last login: Mon May 17 22:39:57 2021 from 10.0.0.2
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#
-localhost#configure terminal
-localhost(config)#
-localhost(config)#interface loopback0
-localhost(config-if-Lo0)#description tacocat
-localhost(config-if-Lo0)#no interface loopback0
-localhost(config)#
-localhost(config)#end
-localhost#
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/arista_eos_configs b/test_data/driver/network/sendconfigsfromfile/arista_eos_configs
deleted file mode 100644
index b9407b4..0000000
--- a/test_data/driver/network/sendconfigsfromfile/arista_eos_configs
+++ /dev/null
@@ -1,3 +0,0 @@
-interface loopback0
-description tacocat
-no interface loopback0
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_iosxe b/test_data/driver/network/sendconfigsfromfile/cisco_iosxe
deleted file mode 100644
index 7fd34f6..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_iosxe
+++ /dev/null
@@ -1,12 +0,0 @@
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#configure terminal
-Enter configuration commands, one per line. End with CNTL/Z.
-csr1000v(config)#
-csr1000v(config)#interface loopback0
-csr1000v(config-if)#description tacocat
-csr1000v(config-if)#no interface loopback0
-csr1000v(config)#
-csr1000v(config)#end
-csr1000v#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_iosxe_configs b/test_data/driver/network/sendconfigsfromfile/cisco_iosxe_configs
deleted file mode 100644
index b9407b4..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_iosxe_configs
+++ /dev/null
@@ -1,3 +0,0 @@
-interface loopback0
-description tacocat
-no interface loopback0
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_iosxr b/test_data/driver/network/sendconfigsfromfile/cisco_iosxr
deleted file mode 100644
index 1e8d0b0..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_iosxr
+++ /dev/null
@@ -1,17 +0,0 @@
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Tue May 18 16:49:01.098 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Tue May 18 16:49:01.254 UTC
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#configure terminal
-Tue May 18 16:49:01.357 UTC
-RP/0/RP0/CPU0:ios(config)#
-RP/0/RP0/CPU0:ios(config)#interface loopback0
-RP/0/RP0/CPU0:ios(config-if)#description tacocat
-RP/0/RP0/CPU0:ios(config-if)#no interface loopback0
-RP/0/RP0/CPU0:ios(config)#commit
-Tue May 18 16:49:02.119 UTC
-RP/0/RP0/CPU0:ios(config)#
-RP/0/RP0/CPU0:ios(config)#end
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_iosxr_configs b/test_data/driver/network/sendconfigsfromfile/cisco_iosxr_configs
deleted file mode 100644
index e9d4f5a..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_iosxr_configs
+++ /dev/null
@@ -1,4 +0,0 @@
-interface loopback0
-description tacocat
-no interface loopback0
-commit
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_nxos b/test_data/driver/network/sendconfigsfromfile/cisco_nxos
deleted file mode 100644
index dc910ea..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_nxos
+++ /dev/null
@@ -1,44 +0,0 @@
-Warning: Permanently added '[localhost]:22022' (RSA) to the list of known hosts.
-User Access Verification
-Password:
-
-Cisco NX-OS Software
-Copyright (c) 2002-2019, Cisco Systems, Inc. All rights reserved.
-Nexus 9000v software ("Nexus 9000v Software") and related documentation,
-files or other reference materials ("Documentation") are
-the proprietary property and confidential information of Cisco
-Systems, Inc. ("Cisco") and are protected, without limitation,
-pursuant to United States and International copyright and trademark
-laws in the applicable jurisdiction which provide civil and criminal
-penalties for copying or distribution without Cisco's authorization.
-
-Any use or disclosure, in whole or in part, of the Nexus 9000v Software
-or Documentation to any third party for any purposes is expressly
-prohibited except as otherwise authorized by Cisco in writing.
-The copyrights to certain works contained herein are owned by other
-third parties and are used and distributed under license. Some parts
-of this software may be covered under the GNU Public License or the
-GNU Lesser General Public License. A copy of each such license is
-available at
-http://www.gnu.org/licenses/gpl.html and
-http://www.gnu.org/licenses/lgpl.html
-***************************************************************************
-* Nexus 9000v is strictly limited to use for evaluation, demonstration *
-* and NX-OS education. Any use or disclosure, in whole or in part of *
-* the Nexus 9000v Software or Documentation to any third party for any *
-* purposes is expressly prohibited except as otherwise authorized by *
-* Cisco in writing. *
-***************************************************************************
-switch#
-switch# terminal length 0
-switch# terminal width 511
-switch#
-switch# configure terminal
-Enter configuration commands, one per line. End with CNTL/Z.
-switch(config)#
-switch(config)# interface loopback0
-switch(config-if)# description tacocat
-switch(config-if)# no interface loopback0
-switch(config)#
-switch(config)# end
-switch#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/cisco_nxos_configs b/test_data/driver/network/sendconfigsfromfile/cisco_nxos_configs
deleted file mode 100644
index b9407b4..0000000
--- a/test_data/driver/network/sendconfigsfromfile/cisco_nxos_configs
+++ /dev/null
@@ -1,3 +0,0 @@
-interface loopback0
-description tacocat
-no interface loopback0
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/juniper_junos b/test_data/driver/network/sendconfigsfromfile/juniper_junos
deleted file mode 100644
index 17c0273..0000000
--- a/test_data/driver/network/sendconfigsfromfile/juniper_junos
+++ /dev/null
@@ -1,40 +0,0 @@
-Warning: Permanently added '[localhost]:25022' (ECDSA) to the list of known hosts.
-Password:
---- JUNOS 17.3R2.10 built 2018-02-08 02:19:07 UTC
-vrnetlab>
-
-vrnetlab> set cli screen-length 0
-Screen length set to 0
-
-vrnetlab> set cli screen-width 511
-Screen width set to 511
-
-vrnetlab> set cli complete-on-space off
-Disabling complete-on-space
-
-vrnetlab>
-
-vrnetlab> configure
-Entering configuration mode
-
-[edit]
-vrnetlab#
-
-[edit]
-vrnetlab# set interfaces fxp0.0 description tacocat
-
-[edit]
-vrnetlab# delete interfaces fxp0.0 description tacocat
-
-[edit]
-vrnetlab# commit
-commit complete
-
-[edit]
-vrnetlab#
-
-[edit]
-vrnetlab# exit configuration-mode
-Exiting configuration mode
-
-vrnetlab>
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/juniper_junos_configs b/test_data/driver/network/sendconfigsfromfile/juniper_junos_configs
deleted file mode 100644
index a27443f..0000000
--- a/test_data/driver/network/sendconfigsfromfile/juniper_junos_configs
+++ /dev/null
@@ -1,3 +0,0 @@
-set interfaces fxp0.0 description tacocat
-delete interfaces fxp0.0 description tacocat
-commit
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/nokia_sros b/test_data/driver/network/sendconfigsfromfile/nokia_sros
deleted file mode 100644
index 700e2d6..0000000
--- a/test_data/driver/network/sendconfigsfromfile/nokia_sros
+++ /dev/null
@@ -1,64 +0,0 @@
-Warning: Permanently added 'clab-sros01-sr2,172.20.20.2' (RSA) to the list of known hosts.
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-
-[]
-A:admin@sr2#
-
-[]
-A:admin@sr2# environment console width 512
-
-[]
-A:admin@sr2# environment more false
-
-[]
-A:admin@sr2# environment command-completion space false
-
-[]
-A:admin@sr2# edit-config exclusive
-INFO: CLI #2060: Entering exclusive configuration mode
-INFO: CLI #2061: Uncommitted changes are discarded on configuration mode exit
-
-(ex)[]
-A:admin@sr2# configure router interface "system" description "@ntdvps"
-
-*(ex)[]
-A:admin@sr2# configure system
-
-*(ex)[configure system]
-A:admin@sr2# location wide_internet
-
-*(ex)[configure system]
-A:admin@sr2# commit
-
-(ex)[configure system]
-A:admin@sr2#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic b/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic
deleted file mode 100644
index e333491..0000000
--- a/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic
+++ /dev/null
@@ -1,40 +0,0 @@
-Warning: Permanently added 'clab-scrapli-sros-classic,172.20.20.4' (RSA) to the list of known hosts.
-
-admin@clab-scrapli-sros-classic's password:
-
- SR OS Software
- Copyright (c) Nokia 2021. All Rights Reserved.
-
- Trademarks
-
- Nokia and the Nokia logo are registered trademarks of Nokia. All other
- trademarks are the property of their respective owners.
-
- IMPORTANT: READ CAREFULLY
-
- The SR OS Software (the "Software") is proprietary to Nokia and is subject
- to and governed by the terms and conditions of the End User License
- Agreement accompanying the product, made available at the time of your order,
- or posted on the Nokia website (collectively, the "EULA"). As set forth
- more fully in the EULA, use of the Software is strictly limited to your
- internal use. Downloading, installing, or using the Software constitutes
- acceptance of the EULA and you are binding yourself and the business entity
- that you represent to the EULA. If you do not agree to all of the terms of
- the EULA, then Nokia is unwilling to license the Software to you and (a) you
- may not download, install or use the Software, and (b) you may return the
- Software as more fully set forth in the EULA.
-
- This product contains cryptographic features and is subject to United States
- and local country laws governing import, export, transfer and use. Delivery
- of Nokia cryptographic products does not imply third-party authority to
- import, export, distribute or use encryption.
-
- If you require further assistance please contact us by sending an email
- to support@nokia.com.
-
-A:sros-classic#
-A:sros-classic# environment no more
-A:sros-classic# configure router interface "system" description "@ntdvps"
-*A:sros-classic# configure system
-*A:sros-classic>config>system# location wide_internet
-*A:sros-classic>config>system#
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic_configs b/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic_configs
deleted file mode 100644
index bc1212c..0000000
--- a/test_data/driver/network/sendconfigsfromfile/nokia_sros_classic_configs
+++ /dev/null
@@ -1,3 +0,0 @@
-configure router interface "system" description "@ntdvps"
-configure system
-location wide_internet
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/nokia_sros_configs b/test_data/driver/network/sendconfigsfromfile/nokia_sros_configs
deleted file mode 100644
index 87bbb5f..0000000
--- a/test_data/driver/network/sendconfigsfromfile/nokia_sros_configs
+++ /dev/null
@@ -1,4 +0,0 @@
-configure router interface "system" description "@ntdvps"
-configure system
-location wide_internet
-commit
\ No newline at end of file
diff --git a/test_data/driver/network/sendconfigsfromfile/paloalto_panos_configs b/test_data/driver/network/sendconfigsfromfile/paloalto_panos_configs
deleted file mode 100644
index cf35339..0000000
--- a/test_data/driver/network/sendconfigsfromfile/paloalto_panos_configs
+++ /dev/null
@@ -1,2 +0,0 @@
-set display-name BLAH
-commit
\ No newline at end of file
diff --git a/test_data/driver/network/sendinteractive/arista_eos b/test_data/driver/network/sendinteractive/arista_eos
deleted file mode 100644
index bfd6d24..0000000
--- a/test_data/driver/network/sendinteractive/arista_eos
+++ /dev/null
@@ -1,24 +0,0 @@
-Warning: Permanently added '[localhost]:24022' (ECDSA) to the list of known hosts.
-No startup-config was found.
-The device is in Zero Touch Provisioning mode and is attempting to
-download the startup-config from a remote system. The device will not
-be fully functional until either a valid startup-config is downloaded
-from a remote system or Zero Touch Provisioning is cancelled.
-To cancel Zero Touch Provisioning, login as admin and type
-'zerotouch cancel' at the CLI. Alternatively, to disable Zero Touch
-Provisioning permanently, type 'zerotouch disable' at the CLI.
-Note: The device will reload when these commands are issued.
-Password:
-Last login: Wed May 19 16:49:45 2021 from 10.0.0.2
-localhost>
-localhost>enable
-Password:
-localhost#
-localhost#terminal length 0
-Pagination disabled.
-localhost#terminal width 32767
-Width set to 32767 columns.
-localhost#clear logging
-localhost#
-
-localhost#
\ No newline at end of file
diff --git a/test_data/driver/network/sendinteractive/cisco_iosxe b/test_data/driver/network/sendinteractive/cisco_iosxe
deleted file mode 100644
index 79e0956..0000000
--- a/test_data/driver/network/sendinteractive/cisco_iosxe
+++ /dev/null
@@ -1,7 +0,0 @@
-csr1000v#
-csr1000v#terminal length 0
-csr1000v#terminal width 512
-csr1000v#clear logging
-Clear logging buffer [confirm]
-
-csr1000v#
\ No newline at end of file
diff --git a/test_data/driver/network/sendinteractive/cisco_iosxr b/test_data/driver/network/sendinteractive/cisco_iosxr
deleted file mode 100644
index 056c60b..0000000
--- a/test_data/driver/network/sendinteractive/cisco_iosxr
+++ /dev/null
@@ -1,14 +0,0 @@
-Warning: Permanently added '[localhost]:23022' (RSA) to the list of known hosts.
-Password:
-
-
-RP/0/RP0/CPU0:ios#
-RP/0/RP0/CPU0:ios#terminal length 0
-Wed May 19 17:25:44.528 UTC
-RP/0/RP0/CPU0:ios#terminal width 512
-Wed May 19 17:25:44.652 UTC
-RP/0/RP0/CPU0:ios#clear logging
-Wed May 19 17:25:44.754 UTC
-Clear logging buffer [confirm] [y/n] :y
-
-RP/0/RP0/CPU0:ios#
\ No newline at end of file
diff --git a/transport/base.go b/transport/base.go
deleted file mode 100644
index f95c7a0..0000000
--- a/transport/base.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package transport
-
-import (
- "errors"
- "time"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-const (
- ReadSize = 65_535
- SystemTransportName = "system"
- StandardTransportName = "standard"
- TelnetTransportName = "telnet"
- // MaxTimeout maximum allowable timeout value -- one day.
- MaxTimeout = 86_400
-)
-
-// SupportedTransports pseudo constant providing slice of supported transport types.
-func SupportedTransports() []string {
- return []string{SystemTransportName, StandardTransportName, TelnetTransportName}
-}
-
-// SupportedNetconfTransports pseudo constant providing slice of supported netconf transport types.
-func SupportedNetconfTransports() []string {
- return []string{SystemTransportName, StandardTransportName}
-}
-
-// ErrTransportFailure error for EOF/failure reading from the transport.
-var ErrTransportFailure = errors.New("error reading from transport, cannot continue")
-
-// ErrUnknownTransport error for when user provides an unknown/unsupported transport name.
-var ErrUnknownTransport = errors.New("unknown transport provided")
-
-// ErrTransportTimeout error for transport operations timing out.
-var ErrTransportTimeout = errors.New("transport operation timed out")
-
-// ErrKeyVerificationFailed ssh key verification failure.
-var ErrKeyVerificationFailed = errors.New("ssh key verification failed")
-
-// ErrUnsupportedOperation error for things like trying to use telnet transport with netconf.
-var ErrUnsupportedOperation = errors.New("unsupported operation for this transport type")
-
-// BaseTransportArgs struct for attributes that are required for any transport type.
-type BaseTransportArgs struct {
- Host string
- Port int
- AuthUsername string
- TimeoutSocket time.Duration
- TimeoutTransport time.Duration
- PtyHeight int
- PtyWidth int
-}
-
-// ReadResult is an object used to return from the read goroutine.
-type ReadResult struct {
- Result []byte
- Error error
-}
-
-// Implementation defines an interface that a transport plugins must implement.
-type Implementation interface {
- Open(baseArgs *BaseTransportArgs) error
- OpenNetconf(baseArgs *BaseTransportArgs) error
- Close() error
- IsAlive() bool
- Read(n int) *ReadResult
- Write([]byte) error
-}
-
-// Transport interface defining required methods for any transport type.
-type Transport struct {
- Impl Implementation
- BaseTransportArgs *BaseTransportArgs
-}
-
-// Open opens the transport in "normal" mode (usually telnet/ssh).
-func (t *Transport) Open() error {
- err := t.Impl.Open(t.BaseTransportArgs)
-
- if err != nil {
- logging.LogError(
- FormatLogMessage(
- t.BaseTransportArgs,
- "error",
- "failed opening transport connection to host",
- ),
- )
- } else {
- logging.LogDebug(
- FormatLogMessage(t.BaseTransportArgs, "debug", "transport connection to host opened"),
- )
- }
-
- return err
-}
-
-// OpenNetconf opens a netconf connection.
-func (t *Transport) OpenNetconf() error {
- err := t.Impl.OpenNetconf(t.BaseTransportArgs)
-
- if err != nil {
- logging.LogError(
- FormatLogMessage(
- t.BaseTransportArgs,
- "error",
- "failed opening netconf transport connection to host",
- ),
- )
- } else {
- logging.LogDebug(
- FormatLogMessage(t.BaseTransportArgs, "debug", "netconf transport connection to host opened"),
- )
- }
-
- return err
-}
-
-// Close closes the transport connection.
-func (t *Transport) Close() error {
- err := t.Impl.Close()
-
- logging.LogDebug(
- FormatLogMessage(t.BaseTransportArgs, "debug", "transport connection to host closed"),
- )
-
- return err
-}
-
-// IsAlive indicates if the transport is alive or not.
-func (t *Transport) IsAlive() bool {
- return t.Impl.IsAlive()
-}
-
-// Read reads bytes from the transport.
-func (t *Transport) Read() ([]byte, error) {
- b, err := t.transportTimeout(
- t.Impl.Read,
- ReadSize,
- )
-
- if err != nil {
- logging.LogError(
- FormatLogMessage(t.BaseTransportArgs, "error", "timed out reading from transport"),
- )
-
- return b, err
- }
-
- return b, nil
-}
-
-// ReadN reads N bytes from the transport.
-func (t *Transport) ReadN(n int) ([]byte, error) {
- b, err := t.transportTimeout(
- t.Impl.Read,
- n,
- )
-
- if err != nil {
- logging.LogError(
- FormatLogMessage(t.BaseTransportArgs, "error", "timed out reading from transport"),
- )
-
- return b, err
- }
-
- return b, nil
-}
-
-// Write writes bytes to the transport.
-func (t *Transport) Write(channelInput []byte) error {
- return t.Impl.Write(channelInput)
-}
-
-func (t *Transport) transportTimeout(
- f func(int) *ReadResult,
- n int,
-) ([]byte, error) {
- c := make(chan *ReadResult)
-
- go func() {
- r := f(n)
- c <- r
- close(c)
- }()
-
- timeout := t.BaseTransportArgs.TimeoutTransport
- if t.BaseTransportArgs.TimeoutTransport <= 0 {
- timeout = MaxTimeout * time.Second
- }
-
- timer := time.NewTimer(timeout)
-
- select {
- case r := <-c:
- return r.Result, r.Error
- case <-timer.C:
- return []byte{}, ErrTransportTimeout
- }
-}
-
-// FormatLogMessage formats log messages for transport level logging.
-func FormatLogMessage(b *BaseTransportArgs, level, msg string) string {
- return logging.FormatLogMessage(level, b.Host, b.Port, msg)
-}
diff --git a/transport/factory.go b/transport/factory.go
new file mode 100644
index 0000000..8237afc
--- /dev/null
+++ b/transport/factory.go
@@ -0,0 +1,79 @@
+package transport
+
+import (
+ "errors"
+ "sync"
+
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
+)
+
+// NewTransport returns an instance of Transport with the requested transport implementation (as
+// defined in transportType) set. Typically, users should not need to call this as the process of
+// Driver creation will handle this for you.
+func NewTransport(
+ l *logging.Instance,
+ host, transportType string,
+ options ...util.Option,
+) (*Transport, error) {
+ var i transportImpl
+
+ var err error
+
+ var args *Args
+
+ args, err = NewArgs(l, host, options...)
+ if err != nil {
+ return nil, err
+ }
+
+ switch transportType {
+ case SystemTransport, StandardTransport:
+ var sshArgs *SSHArgs
+
+ sshArgs, err = NewSSHArgs(options...)
+ if err != nil {
+ return nil, err
+ }
+
+ switch transportType {
+ case SystemTransport:
+ i, err = NewSystemTransport(sshArgs)
+ case StandardTransport:
+ i, err = NewStandardTransport(sshArgs)
+ }
+ case TelnetTransport:
+ var telnetArgs *TelnetArgs
+
+ telnetArgs, err = NewTelnetArgs(options...)
+ if err != nil {
+ return nil, err
+ }
+
+ i, err = NewTelnetTransport(telnetArgs)
+ case FileTransport:
+ i, err = NewFileTransport()
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ for _, option := range options {
+ err = option(i)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ t := &Transport{
+ Args: args,
+ Impl: i,
+ implLock: &sync.Mutex{},
+ timeoutLock: &sync.Mutex{},
+ }
+
+ return t, nil
+}
diff --git a/transport/file.go b/transport/file.go
new file mode 100644
index 0000000..6e3fd61
--- /dev/null
+++ b/transport/file.go
@@ -0,0 +1,87 @@
+package transport
+
+import (
+ "io"
+ "os"
+)
+
+const (
+ // FileTransport transport name.
+ FileTransport = "file"
+)
+
+// NewFileTransport returns an instance of File transport. This is for testing purposes only.
+func NewFileTransport() (*File, error) {
+ t := &File{
+ fd: nil,
+ }
+
+ return t, nil
+}
+
+// File transport is a transport object that "connects" to a file rather than a device, it probably
+// has no use outside of testing.
+type File struct {
+ F string
+ fd *os.File
+
+ content []byte
+
+ Writes [][]byte
+}
+
+// Open opens the File transport.
+func (t *File) Open(a *Args) error {
+ _ = a
+
+ f, err := os.Open(t.F)
+ if err != nil {
+ return err
+ }
+
+ t.fd = f
+
+ t.content, err = io.ReadAll(f)
+ if err != nil {
+ return err
+ }
+
+ _ = t.Close()
+
+ return nil
+}
+
+// Close is a noop for the File transport.
+func (t *File) Close() error {
+ return nil
+}
+
+// IsAlive always returns true for File transport.
+func (t *File) IsAlive() bool {
+ return true
+}
+
+// Read reads n bytes from the transport. File transport ignores EOF errors, see comment below.
+func (t *File) Read(_ int) ([]byte, error) {
+ if len(t.content) == 0 {
+ // we can just sleep here as this is getting called from a goroutine anyway, by blocking
+ // we will stop subsequent reads which means less things for race detector to look at.
+ // it seems to be *moderately* successful in speeding up tests in race mode. we need this
+ // because in unit tests we read *one* byte at a time -- some test data contains >10k bytes
+ // which causes a lot of locks/unlocks and such which makes the race detector have a party.
+ select {}
+ }
+
+ b := t.content[0]
+
+ t.content = t.content[1:]
+
+ return []byte{b}, nil
+}
+
+// Write writes bytes b to the transport.
+func (t *File) Write(b []byte) error {
+ t.Writes = append(t.Writes, b)
+
+ return nil
+}
diff --git a/transport/standard.go b/transport/standard.go
index 0ce122a..2a97deb 100644
--- a/transport/standard.go
+++ b/transport/standard.go
@@ -1,153 +1,123 @@
package transport
import (
- "encoding/base64"
"fmt"
"io"
- "log"
- "net"
"os"
- "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
+
+ "golang.org/x/crypto/ssh/knownhosts"
"golang.org/x/crypto/ssh"
)
-// Standard the "standard" (standard library) transport option for scrapligo.
-type Standard struct {
- StandardTransportArgs *StandardTransportArgs
- client *ssh.Client
- session *ssh.Session
- writer io.WriteCloser
- reader io.Reader
-}
-
-// StandardTransportArgs struct representing attributes required for the Standard transport.
-type StandardTransportArgs struct {
- AuthPassword string
- AuthPrivateKey string
- AuthStrictKey bool
- SSHConfigFile string
- SSHKnownHostsFile string
-}
-
-func keyString(k ssh.PublicKey) string {
- return k.Type() + " " + base64.StdEncoding.EncodeToString(
- k.Marshal(),
- ) // e.g. "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTY...."
-}
+const (
+ // StandardTransport is the standard (crypto/ssh) transport for scrapligo.
+ StandardTransport = "standard"
-// https://stackoverflow.com/questions/44269142/ \
-// golang-ssh-getting-must-specify-hoskeycallback-error-despite-setting-it-to-n
-// basically need to parse ssh config like scrapli does... at some point.
-func trustedHostKeyCallback(trustedKey string) ssh.HostKeyCallback {
- if trustedKey == "" {
- return func(_ string, _ net.Addr, k ssh.PublicKey) error {
- log.Printf(
- "ssh key verification is *NOT* in effect: to fix, add this trustedKey: %q",
- keyString(k),
- )
+ termType = "xterm"
+)
- return nil
- }
+// NewStandardTransport returns an instance of Standard transport.
+func NewStandardTransport(s *SSHArgs) (*Standard, error) {
+ t := &Standard{
+ SSHArgs: s,
+ client: nil,
+ session: nil,
+ writer: nil,
+ reader: nil,
+ ExtraCiphers: make([]string, 0),
+ ExtraKexs: make([]string, 0),
}
- return func(_ string, _ net.Addr, k ssh.PublicKey) error {
- ks := keyString(k)
- if trustedKey != ks {
- return ErrKeyVerificationFailed
- }
+ return t, nil
+}
- return nil
- }
+// Standard is the standard (crypto/ssh) transport object.
+type Standard struct {
+ SSHArgs *SSHArgs
+ client *ssh.Client
+ session *ssh.Session
+ writer io.WriteCloser
+ reader io.Reader
+ ExtraCiphers []string
+ ExtraKexs []string
}
-func (t *Standard) openSession(baseArgs *BaseTransportArgs, cfg *ssh.ClientConfig) error {
+func (t *Standard) openSession(a *Args, cfg *ssh.ClientConfig) error {
var err error
+
t.client, err = ssh.Dial(
- "tcp",
- fmt.Sprintf("%s:%d", baseArgs.Host, baseArgs.Port),
+ tcp,
+ fmt.Sprintf("%s:%d", a.Host, a.Port),
cfg,
)
-
if err != nil {
- logging.LogError(
- FormatLogMessage(
- baseArgs,
- "error",
- fmt.Sprintf("error connecting to host: %v", err),
- ),
- )
+ a.l.Criticalf("error creating crypto/ssh client, error: %s", err)
return err
}
t.session, err = t.client.NewSession()
if err != nil {
- logging.LogError(
- FormatLogMessage(
- baseArgs,
- "error",
- fmt.Sprintf("error allocating session: %v", err),
- ),
- )
+ a.l.Criticalf("error spawning crypto/ssh session, error: %s", err)
return err
}
t.writer, err = t.session.StdinPipe()
if err != nil {
- logging.LogError(
- FormatLogMessage(
- baseArgs,
- "error",
- fmt.Sprintf("error allocating writer: %v", err),
- ),
- )
+ a.l.Criticalf("error spawning crypto/ssh session stdin pipe, error: %s", err)
return err
}
t.reader, err = t.session.StdoutPipe()
if err != nil {
- logging.LogError(
- FormatLogMessage(
- baseArgs,
- "error",
- fmt.Sprintf("error allocating reader: %v", err),
- ),
- )
+ a.l.Criticalf("error spawning crypto/ssh session stdout pipe, error: %s", err)
+
+ return err
}
- return err
+ return nil
}
-func (t *Standard) openBase(baseArgs *BaseTransportArgs) error {
+func (t *Standard) openBase(a *Args) error {
/* #nosec G106 */
- hostKeyCallback := ssh.InsecureIgnoreHostKey()
- if t.StandardTransportArgs.AuthStrictKey {
- // trustedKey will need to be gleaned from known hosts how scrapli does at some point
- hostKeyCallback = trustedHostKeyCallback("")
+ keyCallback := ssh.InsecureIgnoreHostKey()
+
+ if t.SSHArgs.StrictKey {
+ if t.SSHArgs.KnownHostsFile == "" {
+ a.l.Critical("strict host key checking requested, but no known hosts file provided")
+
+ return fmt.Errorf(
+ "%w: strict host key checking requested, but no known hosts file provided",
+ util.ErrBadOption,
+ )
+ }
+
+ knownHosts, err := knownhosts.New(t.SSHArgs.KnownHostsFile)
+ if err != nil {
+ return err
+ }
+
+ keyCallback = knownHosts
}
authMethods := make([]ssh.AuthMethod, 0)
- if t.StandardTransportArgs.AuthPrivateKey != "" {
- key, err := os.ReadFile(t.StandardTransportArgs.AuthPrivateKey)
+ if t.SSHArgs.PrivateKeyPath != "" {
+ k, err := os.ReadFile(t.SSHArgs.PrivateKeyPath)
if err != nil {
+ a.l.Criticalf("error reading ssh key: %s", err)
+
return err
}
- signer, err := ssh.ParsePrivateKey(key)
-
+ signer, err := ssh.ParsePrivateKey(k)
if err != nil {
- logging.LogError(
- FormatLogMessage(
- baseArgs,
- "error",
- fmt.Sprintf("unable to parse private key: %v", err),
- ),
- )
+ a.l.Criticalf("error parsing ssh key: %s", err)
return err
}
@@ -155,13 +125,13 @@ func (t *Standard) openBase(baseArgs *BaseTransportArgs) error {
authMethods = append(authMethods, ssh.PublicKeys(signer))
}
- if t.StandardTransportArgs.AuthPassword != "" {
- authMethods = append(authMethods, ssh.Password(t.StandardTransportArgs.AuthPassword),
+ if a.Password != "" {
+ authMethods = append(authMethods, ssh.Password(a.Password),
ssh.KeyboardInteractive(
func(user, instruction string, questions []string, echos []bool) ([]string, error) {
answers := make([]string, len(questions))
for i := range answers {
- answers[i] = t.StandardTransportArgs.AuthPassword
+ answers[i] = a.Password
}
return answers, nil
@@ -170,47 +140,50 @@ func (t *Standard) openBase(baseArgs *BaseTransportArgs) error {
}
cfg := &ssh.ClientConfig{
- User: baseArgs.AuthUsername,
+ User: a.User,
Auth: authMethods,
- Timeout: baseArgs.TimeoutSocket,
- HostKeyCallback: hostKeyCallback,
+ Timeout: a.TimeoutSocket,
+ HostKeyCallback: keyCallback,
}
- err := t.openSession(baseArgs, cfg)
+ if len(t.ExtraCiphers) > 0 {
+ cfg.Config.Ciphers = append(cfg.Config.Ciphers, t.ExtraCiphers...)
+ }
- return err
+ if len(t.ExtraKexs) > 0 {
+ cfg.Config.KeyExchanges = append(cfg.Config.KeyExchanges, t.ExtraKexs...)
+ }
+
+ return t.openSession(a, cfg)
}
-func (t *Standard) Open(baseArgs *BaseTransportArgs) error {
- err := t.openBase(baseArgs)
+func (t *Standard) open(a *Args) error {
+ err := t.openBase(a)
if err != nil {
return err
}
- // not sure what to do about the tty speeds... figured lets just go fast?
- modes := ssh.TerminalModes{
+ term := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 115200,
ssh.TTY_OP_OSPEED: 115200,
}
err = t.session.RequestPty(
- "xterm",
- baseArgs.PtyHeight,
- baseArgs.PtyWidth,
- modes,
+ termType,
+ a.TermHeight,
+ a.TermWidth,
+ term,
)
if err != nil {
return err
}
- err = t.session.Shell()
-
- return err
+ return t.session.Shell()
}
-func (t *Standard) OpenNetconf(baseArgs *BaseTransportArgs) error {
- err := t.openBase(baseArgs)
+func (t *Standard) openNetconf(a *Args) error {
+ err := t.openBase(a)
if err != nil {
return err
}
@@ -220,6 +193,16 @@ func (t *Standard) OpenNetconf(baseArgs *BaseTransportArgs) error {
return err
}
+// Open opens the Standard transport.
+func (t *Standard) Open(a *Args) error {
+ if t.SSHArgs.NetconfConnection {
+ return t.openNetconf(a)
+ }
+
+ return t.open(a)
+}
+
+// Close closes the Standard transport.
func (t *Standard) Close() error {
err := t.session.Close()
t.session = nil
@@ -227,29 +210,23 @@ func (t *Standard) Close() error {
return err
}
+// IsAlive returns true if the Standard transport session attribute is not nil.
func (t *Standard) IsAlive() bool {
return t.session != nil
}
-func (t *Standard) Read(n int) *ReadResult {
+// Read reads n bytes from the transport.
+func (t *Standard) Read(n int) ([]byte, error) {
b := make([]byte, n)
- _, err := t.reader.Read(b)
- if err != nil {
- return &ReadResult{
- Result: nil,
- Error: ErrTransportFailure,
- }
- }
+ _, err := t.reader.Read(b)
- return &ReadResult{
- Result: b,
- Error: nil,
- }
+ return b, err
}
-func (t *Standard) Write(channelInput []byte) error {
- _, err := t.writer.Write(channelInput)
+// Write writes bytes b to the transport.
+func (t *Standard) Write(b []byte) error {
+ _, err := t.writer.Write(b)
return err
}
diff --git a/transport/system.go b/transport/system.go
index c06835e..3280ba5 100644
--- a/transport/system.go
+++ b/transport/system.go
@@ -9,216 +9,191 @@ import (
"os/exec"
"github.com/creack/pty"
- "github.com/scrapli/scrapligo/logging"
)
-const sshCmd = "ssh"
+const (
+ // SystemTransport is the default "system" (/bin/ssh wrapper) transport for scrapligo.
+ SystemTransport = "system"
-// System the "system" (pty subprocess wrapper) transport option for scrapligo.
-type System struct {
- SystemTransportArgs *SystemTransportArgs
- fileObj *os.File
- OpenCmd []string
- ExecCmd string
-}
+ defaultOpenBin = "ssh"
+)
-// SystemTransport interface describes system transport specific methods.
-type SystemTransport interface {
- SetOpenCmd([]string)
- SetExecCmd(string)
-}
+// NewSystemTransport returns an instance of System transport.
+func NewSystemTransport(a *SSHArgs) (*System, error) {
+ t := &System{
+ SSHArgs: a,
+ openBin: defaultOpenBin,
+ openArgs: make([]string, 0),
+ fd: nil,
+ }
-// SystemTransportArgs struct representing attributes required for the System transport.
-type SystemTransportArgs struct {
- AuthPrivateKey string
- AuthStrictKey bool
- SSHConfigFile string
- SSHKnownHostsFile string
- NetconfForcePty *bool
+ return t, nil
}
-// SetOpenCmd sets the open command string slice; arguments used for opening the connection.
-func (t *System) SetOpenCmd(openCmd []string) {
- t.OpenCmd = openCmd
+// System is the default (/bin/ssh wrapper) transport object.
+type System struct {
+ SSHArgs *SSHArgs
+ ExtraArgs []string
+ openBin string
+ openArgs []string
+ fd *os.File
}
-// SetExecCmd sets the exec command string, binary used for opening the connection.
-func (t *System) SetExecCmd(execCmd string) {
- t.ExecCmd = execCmd
-}
+func (t *System) buildOpenArgs(a *Args) {
+ if len(t.openArgs) > 0 {
+ t.openArgs = []string{}
+ }
-func (t *System) buildOpenCmd(baseArgs *BaseTransportArgs) {
- // base open command arguments; the exec command itself will be passed in open()
- // need to add user arguments could go here at some point
- t.OpenCmd = append(
- t.OpenCmd,
- baseArgs.Host,
+ t.openArgs = []string{
+ a.Host,
"-p",
- fmt.Sprintf("%d", baseArgs.Port),
+ fmt.Sprintf("%d", a.Port),
"-o",
- fmt.Sprintf("ConnectTimeout=%d", int(baseArgs.TimeoutSocket.Seconds())),
+ fmt.Sprintf("ConnectTimeout=%d", int(a.TimeoutSocket.Seconds())),
"-o",
- fmt.Sprintf("ServerAliveInterval=%d", int(baseArgs.TimeoutTransport.Seconds())),
- )
-
- if t.SystemTransportArgs.AuthPrivateKey != "" {
- t.OpenCmd = append(
- t.OpenCmd,
- "-i",
- t.SystemTransportArgs.AuthPrivateKey,
- )
+ fmt.Sprintf("ServerAliveInterval=%d", int(a.TimeoutSocket.Seconds())),
}
- if baseArgs.AuthUsername != "" {
- t.OpenCmd = append(
- t.OpenCmd,
+ if a.User != "" {
+ t.openArgs = append(
+ t.openArgs,
"-l",
- baseArgs.AuthUsername,
+ a.User,
)
}
- if !t.SystemTransportArgs.AuthStrictKey {
- t.OpenCmd = append(
- t.OpenCmd,
- "-o",
- "StrictHostKeyChecking=no",
- "-o",
- "UserKnownHostsFile=/dev/null",
- )
- } else {
- t.OpenCmd = append(
- t.OpenCmd,
+ if t.SSHArgs.StrictKey {
+ t.openArgs = append(
+ t.openArgs,
"-o",
"StrictHostKeyChecking=yes",
)
- if t.SystemTransportArgs.SSHKnownHostsFile != "" {
- t.OpenCmd = append(
- t.OpenCmd,
+ if t.SSHArgs.KnownHostsFile != "" {
+ t.openArgs = append(
+ t.openArgs,
"-o",
- fmt.Sprintf("UserKnownHostsFile=%s", t.SystemTransportArgs.SSHKnownHostsFile),
+ fmt.Sprintf("UserKnownHostsFile=%s", t.SSHArgs.KnownHostsFile),
)
}
+ } else {
+ t.openArgs = append(
+ t.openArgs,
+ "-o",
+ "StrictHostKeyChecking=no",
+ "-o",
+ "UserKnownHostsFile=/dev/null",
+ )
}
- if t.SystemTransportArgs.SSHConfigFile != "" {
- t.OpenCmd = append(
- t.OpenCmd,
+ if t.SSHArgs.ConfigFile != "" {
+ t.openArgs = append(
+ t.openArgs,
"-F",
- t.SystemTransportArgs.SSHConfigFile,
+ t.SSHArgs.ConfigFile,
)
} else {
- t.OpenCmd = append(
- t.OpenCmd,
+ t.openArgs = append(
+ t.openArgs,
"-F",
"/dev/null",
)
}
-}
-func (t *System) Open(baseArgs *BaseTransportArgs) error {
- if t.OpenCmd == nil {
- t.buildOpenCmd(baseArgs)
+ if len(t.ExtraArgs) > 0 {
+ t.openArgs = append(
+ t.openArgs,
+ t.ExtraArgs...,
+ )
}
+}
- if t.ExecCmd == "" {
- t.ExecCmd = sshCmd
+func (t *System) open(a *Args) error {
+ if len(t.openArgs) == 0 {
+ t.buildOpenArgs(a)
}
- logging.LogDebug(
- FormatLogMessage(baseArgs,
- "debug",
- fmt.Sprintf(
- "\"attempting to open transport connection with the following command: %s",
- t.OpenCmd,
- ),
- ),
- )
+ a.l.Debugf("opening system transport with bin '%s' and args '%s'", t.openBin, t.openArgs)
+
+ c := exec.Command(t.openBin, t.openArgs...) //nolint:gosec
- command := exec.Command(t.ExecCmd, t.OpenCmd...) //nolint:gosec
- fileObj, err := pty.StartWithSize(
- command,
+ var err error
+
+ t.fd, err = pty.StartWithSize(
+ c,
&pty.Winsize{
- Rows: uint16(baseArgs.PtyHeight),
- Cols: uint16(baseArgs.PtyWidth),
+ Rows: uint16(a.TermHeight),
+ Cols: uint16(a.TermWidth),
},
)
+ if err != nil {
+ a.l.Criticalf("encountered error spawning pty, error: %s", err)
- if err == nil {
- t.fileObj = fileObj
+ return err
}
- return err
+ return nil
}
-func (t *System) OpenNetconf(baseArgs *BaseTransportArgs) error {
- if t.OpenCmd == nil {
- t.buildOpenCmd(baseArgs)
+func (t *System) openNetconf(a *Args) error {
+ if len(t.openArgs) == 0 {
+ t.buildOpenArgs(a)
+ }
- if t.SystemTransportArgs.NetconfForcePty == nil || *t.SystemTransportArgs.NetconfForcePty {
- t.OpenCmd = append(t.OpenCmd, "-tt")
- }
+ t.openArgs = append(t.openArgs, "-s", "netconf")
- t.OpenCmd = append(t.OpenCmd,
- "-s",
- "netconf",
- )
- }
+ a.l.Debugf("opening system transport with bin '%s' and args '%s'", t.openBin, t.openArgs)
- if t.ExecCmd == "" {
- t.ExecCmd = sshCmd
- }
+ c := exec.Command(t.openBin, t.openArgs...) //nolint:gosec
- logging.LogDebug(
- FormatLogMessage(baseArgs,
- "debug",
- fmt.Sprintf(
- "\"attempting to open netconf transport connection with the following command: %s",
- t.OpenCmd,
- ),
- ),
- )
+ var err error
- command := exec.Command(t.ExecCmd, t.OpenCmd...) //nolint:gosec
- fileObj, err := pty.Start(command)
+ t.fd, err = pty.Start(c)
- if err == nil {
- t.fileObj = fileObj
+ if err != nil {
+ a.l.Criticalf("encountered error spawning pty, error: %s", err)
+
+ return err
}
- return err
+ return nil
+}
+
+// Open opens the System transport.
+func (t *System) Open(a *Args) error {
+ if t.SSHArgs.NetconfConnection {
+ return t.openNetconf(a)
+ }
+
+ return t.open(a)
}
+// Close closes the System transport.
func (t *System) Close() error {
- err := t.fileObj.Close()
- t.fileObj = nil
+ err := t.fd.Close()
+
+ t.fd = nil
return err
}
+// IsAlive returns true if the System transport file descriptor is not nil.
func (t *System) IsAlive() bool {
- return t.fileObj != nil
+ return t.fd != nil
}
-func (t *System) Read(n int) *ReadResult {
+// Read reads n bytes from the transport.
+func (t *System) Read(n int) ([]byte, error) {
b := make([]byte, n)
- _, err := t.fileObj.Read(b)
- if err != nil {
- return &ReadResult{
- Result: nil,
- Error: ErrTransportFailure,
- }
- }
+ _, err := t.fd.Read(b)
- return &ReadResult{
- Result: b,
- Error: nil,
- }
+ return b, err
}
-func (t *System) Write(channelInput []byte) error {
- _, err := t.fileObj.Write(channelInput)
+// Write writes bytes b to the transport.
+func (t *System) Write(b []byte) error {
+ _, err := t.fd.Write(b)
return err
}
diff --git a/transport/telnet.go b/transport/telnet.go
index 2d9eabf..d91e90b 100644
--- a/transport/telnet.go
+++ b/transport/telnet.go
@@ -6,38 +6,44 @@ import (
"time"
"github.com/scrapli/scrapligo/util"
-
- "github.com/scrapli/scrapligo/logging"
)
const (
- IAC = byte(255)
- DONT = byte(254)
- DO = byte(253)
- WONT = byte(252)
- WILL = byte(251)
- SGA = byte(3)
+ // TelnetTransport is the telnet transport for scrapligo.
+ TelnetTransport = "telnet"
+
+ iac = byte(255)
+ dont = byte(254)
+ do = byte(253)
+ wont = byte(252)
+ will = byte(251)
+ sga = byte(3)
)
-// Telnet the telnet transport option for scrapligo.
-type Telnet struct {
- TelnetTransportArgs *TelnetTransportArgs
- Conn net.Conn
- initialBuf []byte
+// NewTelnetTransport returns an instance of Telnet transport.
+func NewTelnetTransport(a *TelnetArgs) (*Telnet, error) {
+ t := &Telnet{
+ TelnetArgs: a,
+ }
+
+ return t, nil
}
-// TelnetTransportArgs struct representing attributes required for the Telnet transport.
-type TelnetTransportArgs struct {
+// Telnet is the telnet transport object.
+type Telnet struct {
+ TelnetArgs *TelnetArgs
+ c net.Conn
+ initialBuf []byte
}
func (t *Telnet) handleControlCharResponse(ctrlBuf []byte, c byte) ([]byte, error) {
if len(ctrlBuf) == 0 { //nolint:nestif
- if c != IAC {
+ if c != iac {
t.initialBuf = append(t.initialBuf, c)
} else {
ctrlBuf = append(ctrlBuf, c)
}
- } else if len(ctrlBuf) == 1 && util.ByteInSlice(c, []byte{DO, DONT, WILL, WONT}) {
+ } else if len(ctrlBuf) == 1 && util.ByteIsAny(c, []byte{do, dont, will, wont}) {
ctrlBuf = append(ctrlBuf, c)
} else if len(ctrlBuf) == 2 { //nolint:gomnd
cmd := ctrlBuf[1:2][0]
@@ -45,14 +51,14 @@ func (t *Telnet) handleControlCharResponse(ctrlBuf []byte, c byte) ([]byte, erro
var writeErr error
- if cmd == DO && c == SGA {
- _, writeErr = t.Conn.Write([]byte{IAC, WILL, c})
- } else if util.ByteInSlice(cmd, []byte{DO, DONT}) {
- _, writeErr = t.Conn.Write([]byte{IAC, WONT, c})
- } else if cmd == WILL {
- _, writeErr = t.Conn.Write([]byte{IAC, DO, c})
- } else if cmd == WONT {
- _, writeErr = t.Conn.Write([]byte{IAC, DONT, c})
+ if cmd == do && c == sga {
+ _, writeErr = t.c.Write([]byte{iac, will, c})
+ } else if util.ByteIsAny(cmd, []byte{do, dont}) {
+ _, writeErr = t.c.Write([]byte{iac, wont, c})
+ } else if cmd == will {
+ _, writeErr = t.c.Write([]byte{iac, do, c})
+ } else if cmd == wont {
+ _, writeErr = t.c.Write([]byte{iac, dont, c})
}
if writeErr != nil {
@@ -63,31 +69,31 @@ func (t *Telnet) handleControlCharResponse(ctrlBuf []byte, c byte) ([]byte, erro
return ctrlBuf, nil
}
-func (t *Telnet) handleControlChars(baseArgs *BaseTransportArgs) error {
- d := baseArgs.TimeoutSocket / 4
+func (t *Telnet) handleControlChars(a *Args) error {
+ d := a.TimeoutSocket / 4
var handleErr error
ctrlBuf := make([]byte, 0)
for {
- setDeadlineErr := t.Conn.SetReadDeadline(time.Now().Add(d))
+ setDeadlineErr := t.c.SetReadDeadline(time.Now().Add(d))
if setDeadlineErr != nil {
return setDeadlineErr
}
// speed up timeout after initial Read
- d = baseArgs.TimeoutSocket / 10
+ d = a.TimeoutSocket / 10
charBuf := make([]byte, 1)
- _, readErr := t.Conn.Read(charBuf)
- if readErr != nil { //nolint:nestif
- if opErr, ok := readErr.(*net.OpError); ok {
+ _, err := t.c.Read(charBuf)
+ if err != nil { //nolint:nestif
+ if opErr, ok := err.(*net.OpError); ok {
if opErr.Timeout() {
// timeout is good -- we want to be done reading control chars, so cancel the
// deadline by setting it to "zero"
- cancelDeadlineErr := t.Conn.SetReadDeadline(time.Time{})
+ cancelDeadlineErr := t.c.SetReadDeadline(time.Time{})
if cancelDeadlineErr != nil {
return cancelDeadlineErr
}
@@ -98,7 +104,7 @@ func (t *Telnet) handleControlChars(baseArgs *BaseTransportArgs) error {
return opErr
}
- return readErr
+ return err
}
ctrlBuf, handleErr = t.handleControlCharResponse(ctrlBuf, charBuf[0])
@@ -108,77 +114,52 @@ func (t *Telnet) handleControlChars(baseArgs *BaseTransportArgs) error {
}
}
-func (t *Telnet) Open(baseArgs *BaseTransportArgs) error {
- var dialErr error
+// Open opens the Telnet connection.
+func (t *Telnet) Open(a *Args) error {
+ var err error
- t.Conn, dialErr = net.Dial(
- "tcp",
- fmt.Sprintf("%s:%d", baseArgs.Host, baseArgs.Port),
- )
- if dialErr != nil {
- return dialErr
+ t.c, err = net.Dial(tcp, fmt.Sprintf("%s:%d", a.Host, a.Port))
+ if err != nil {
+ return err
}
- logging.LogDebug(FormatLogMessage(baseArgs, "debug", "tcp socket to host opened"))
-
- controlCharErr := t.handleControlChars(baseArgs)
- if controlCharErr != nil {
- return controlCharErr
+ err = t.handleControlChars(a)
+ if err != nil {
+ return err
}
- logging.LogDebug(
- FormatLogMessage(baseArgs, "debug", "telnet control characters exchanged"),
- )
-
return nil
}
-func (t *Telnet) OpenNetconf(baseArgs *BaseTransportArgs) error {
- _ = baseArgs
- return ErrUnsupportedOperation
-}
-
+// Close closes the Telnet connection.
func (t *Telnet) Close() error {
- err := t.Conn.Close()
-
- t.Conn = nil
-
- return err
+ return t.c.Close()
}
+// IsAlive returns true if the connection (c) attribute of the Telnet object is not nil.
func (t *Telnet) IsAlive() bool {
- return t.Conn != nil
+ return t.c != nil
}
-func (t *Telnet) Read(n int) *ReadResult {
+// Read reads n bytes from the transport.
+func (t *Telnet) Read(n int) ([]byte, error) {
if len(t.initialBuf) > 0 {
b := t.initialBuf
- t.initialBuf = []byte{}
+ t.initialBuf = nil
- return &ReadResult{
- Result: b,
- Error: nil,
- }
+ return b, nil
}
b := make([]byte, n)
- _, err := t.Conn.Read(b)
- if err != nil {
- return &ReadResult{
- Result: nil,
- Error: ErrTransportFailure,
- }
- }
+ _, err := t.c.Read(b)
- return &ReadResult{
- Result: b,
- Error: nil,
- }
+ return b, err
}
-func (t *Telnet) Write(channelInput []byte) error {
- _, err := t.Conn.Write(channelInput)
+// Write writes bytes b to the transport.
+func (t *Telnet) Write(b []byte) error {
+ _, err := t.c.Write(b)
return err
}
diff --git a/transport/transport.go b/transport/transport.go
new file mode 100644
index 0000000..5297372
--- /dev/null
+++ b/transport/transport.go
@@ -0,0 +1,193 @@
+package transport
+
+import (
+ "errors"
+ "sync"
+ "time"
+
+ "github.com/scrapli/scrapligo/logging"
+ "github.com/scrapli/scrapligo/util"
+)
+
+const (
+ // DefaultTransport is the default transport constant for scrapligo, this defaults to the
+ // "system" transport.
+ DefaultTransport = "system"
+
+ defaultPort = 22
+ defaultTimeoutSocketSeconds = 30
+ defaultReadSize = 65_535
+ defaultTermHeight = 255
+ defaultTermWidth = 80
+ defaultSSHStrictKey = true
+ tcp = "tcp"
+)
+
+// GetTransportNames is returns a slice of available transport type names.
+func GetTransportNames() []string {
+ return []string{SystemTransport, StandardTransport, TelnetTransport}
+}
+
+// GetNetconfTransportNames returns a slice of available NETCONF transport type names.
+func GetNetconfTransportNames() []string {
+ return []string{SystemTransport, StandardTransport}
+}
+
+// NewArgs returns an instance of Args with the logging instance, host, and any provided args
+// set. Users should *generally* not need to call this function as this is called during Transport
+// creation (which is called by the Driver creation).
+func NewArgs(l *logging.Instance, host string, options ...util.Option) (*Args, error) {
+ a := &Args{
+ l: l,
+ Host: host,
+ Port: defaultPort,
+ TimeoutSocket: defaultTimeoutSocketSeconds * time.Second,
+ ReadSize: defaultReadSize,
+ TermHeight: defaultTermHeight,
+ TermWidth: defaultTermWidth,
+ }
+
+ for _, option := range options {
+ err := option(a)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return a, nil
+}
+
+// Args is a struct representing common transport arguments.
+type Args struct {
+ l *logging.Instance
+ Host string
+ Port int
+ User string
+ Password string
+ TimeoutSocket time.Duration
+ ReadSize int
+ TermHeight int
+ TermWidth int
+}
+
+// NewSSHArgs returns an instance of SSH arguments with provided options set. Just like NewArgs,
+// this should generally not be called by users directly.
+func NewSSHArgs(options ...util.Option) (*SSHArgs, error) {
+ a := &SSHArgs{
+ StrictKey: defaultSSHStrictKey,
+ }
+
+ for _, option := range options {
+ err := option(a)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return a, nil
+}
+
+// SSHArgs is a struct representing common transport SSH specific arguments.
+type SSHArgs struct {
+ StrictKey bool
+ PrivateKeyPath string
+ PrivateKeyPassPhrase string
+ ConfigFile string
+ KnownHostsFile string
+ NetconfConnection bool
+}
+
+// NewTelnetArgs returns an instance of TelnetArgs with any provided options set. This should,
+// just like the other NewXArgs functions, not be called directly by users.
+func NewTelnetArgs(options ...util.Option) (*TelnetArgs, error) {
+ a := &TelnetArgs{}
+
+ for _, option := range options {
+ err := option(a)
+ if err != nil {
+ if !errors.Is(err, util.ErrIgnoredOption) {
+ return nil, err
+ }
+ }
+ }
+
+ return a, nil
+}
+
+// TelnetArgs is a struct representing common transport Telnet specific arguments.
+type TelnetArgs struct{}
+
+type transportImpl interface {
+ Open(a *Args) error
+ Close() error
+ IsAlive() bool
+ Read(n int) ([]byte, error)
+ Write(b []byte) error
+}
+
+// Transport is a struct which wraps a transportImpl object and provides a unified interface to any
+// type of transport selected by the user.
+type Transport struct {
+ Args *Args
+ Impl transportImpl
+ implLock *sync.Mutex
+ timeoutLock *sync.Mutex
+}
+
+// Open opens the underlying transportImpl transport object.
+func (t *Transport) Open() error {
+ return t.Impl.Open(t.Args)
+}
+
+// Close closes the underlying transportImpl transport object. force option is required for netconf
+// as there will almost certainly always be a read in progress that we cannot stop and will block,
+// therefore we need a way to bypass the lock.
+func (t *Transport) Close(force bool) error {
+ if !force {
+ t.implLock.Lock()
+ defer t.implLock.Unlock()
+ }
+
+ return t.Impl.Close()
+}
+
+// IsAlive returns true if the underlying transportImpl reports liveness, otherwise false.
+func (t *Transport) IsAlive() bool {
+ return t.Impl.IsAlive()
+}
+
+func (t *Transport) read(n int) ([]byte, error) {
+ t.implLock.Lock()
+ defer t.implLock.Unlock()
+
+ return t.Impl.Read(n)
+}
+
+// Read reads the Transport Args ReadSize bytes from the transportImpl.
+func (t *Transport) Read() ([]byte, error) {
+ return t.read(t.Args.ReadSize)
+}
+
+// ReadN reads n bytes from the transportImpl.
+func (t *Transport) ReadN(n int) ([]byte, error) {
+ return t.read(n)
+}
+
+// Write writes bytes b to the transportImpl.
+func (t *Transport) Write(b []byte) error {
+ return t.Impl.Write(b)
+}
+
+// GetHost is a convenience method to return the Transport Args Host value.
+func (t *Transport) GetHost() string {
+ return t.Args.Host
+}
+
+// GetPort is a convenience method to return the Transport Args Port value.
+func (t *Transport) GetPort() int {
+ return t.Args.Port
+}
diff --git a/transport/transport_test.go b/transport/transport_test.go
new file mode 100644
index 0000000..2a0e53f
--- /dev/null
+++ b/transport/transport_test.go
@@ -0,0 +1,30 @@
+package transport_test
+
+import (
+ "flag"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
diff --git a/util/bytes.go b/util/bytes.go
new file mode 100644
index 0000000..4ef4340
--- /dev/null
+++ b/util/bytes.go
@@ -0,0 +1,47 @@
+package util
+
+import (
+ "bytes"
+ "regexp"
+)
+
+const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?" +
+ "\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
+
+var ansiPattern *regexp.Regexp //nolint: gochecknoglobals
+
+func getAnsiPattern() *regexp.Regexp {
+ if ansiPattern == nil {
+ ansiPattern = regexp.MustCompile(ansi)
+ }
+
+ return ansiPattern
+}
+
+// StripANSI removes ANSI escape codes from the given byte slice b.
+func StripANSI(b []byte) []byte {
+ return getAnsiPattern().ReplaceAll(b, []byte{})
+}
+
+// ByteIsAny checks if byte b is contained in byte slice l.
+func ByteIsAny(b byte, l []byte) bool {
+ for _, ss := range l {
+ if b == ss {
+ return true
+ }
+ }
+
+ return false
+}
+
+// ByteContainsAny checks if byte slice b is contained in any byte slice in the slice of byte
+// slices l.
+func ByteContainsAny(b []byte, l [][]byte) bool {
+ for _, ss := range l {
+ if bytes.Contains(b, ss) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/util/bytes_test.go b/util/bytes_test.go
new file mode 100644
index 0000000..11ea204
--- /dev/null
+++ b/util/bytes_test.go
@@ -0,0 +1,120 @@
+package util_test
+
+import (
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testStripANSI(testName string, in, expected []byte) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if !cmp.Equal(util.StripANSI(in), expected) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ in,
+ expected,
+ )
+ }
+ }
+}
+
+func TestStripANSI(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ in []byte
+ expected []byte
+ }{
+ "strip-ansi-simple": {
+ in: []byte("[admin@CoolDevice.Sea1: \x1b[1m/\x1b[0;0m]$"),
+ expected: []byte("[admin@CoolDevice.Sea1: /]$"),
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStripANSI(testName, testCase.in, testCase.expected)
+
+ t.Run(testName, f)
+ }
+}
+
+func testByteIsAny(testName string, b byte, l []byte, expected bool) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if !cmp.Equal(util.ByteIsAny(b, l), expected) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match",
+ testName,
+ )
+ }
+ }
+}
+
+func TestByteIsAny(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ b byte
+ l []byte
+ expected bool
+ }{
+ "byte-is-any-simple": {
+ b: byte(1),
+ l: []byte{3, 2, 1},
+ expected: true,
+ },
+ "byte-is-any-simple-false": {
+ b: byte(0),
+ l: []byte{3, 2, 1},
+ expected: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testByteIsAny(testName, testCase.b, testCase.l, testCase.expected)
+
+ t.Run(testName, f)
+ }
+}
+
+func testByteContainsAny(testName string, b []byte, l [][]byte, expected bool) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if !cmp.Equal(util.ByteContainsAny(b, l), expected) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match",
+ testName,
+ )
+ }
+ }
+}
+
+func TestByteContainsAny(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ b []byte
+ l [][]byte
+ expected bool
+ }{
+ "byte-contains-any-simple": {
+ b: []byte("one"),
+ l: [][]byte{[]byte("potato"), []byte("one")},
+ expected: true,
+ },
+ "byte-contains-any-simple-false": {
+ b: []byte("one"),
+ l: [][]byte{[]byte("potato"), []byte("two")},
+ expected: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testByteContainsAny(testName, testCase.b, testCase.l, testCase.expected)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/util/constants.go b/util/constants.go
new file mode 100644
index 0000000..c16f1d5
--- /dev/null
+++ b/util/constants.go
@@ -0,0 +1,10 @@
+package util
+
+const (
+ // MaxTimeout is the maximum timeout value used in scrapligo.
+ MaxTimeout = 86_400
+ // All used for test flags for platform/transport.
+ All = "all"
+ // Admin used for functional test user/pass.
+ Admin = "admin"
+)
diff --git a/util/env.go b/util/env.go
new file mode 100644
index 0000000..42e8664
--- /dev/null
+++ b/util/env.go
@@ -0,0 +1,31 @@
+package util
+
+import (
+ "os"
+ "strconv"
+)
+
+// GetEnvIntOrDefault returns the value of the environment variable k as an int *or* the default d
+// if casting fails or the environment variable is not set.
+func GetEnvIntOrDefault(k string, d int) int {
+ if v, ok := os.LookupEnv(k); ok {
+ ev, err := strconv.Atoi(v)
+ if err != nil {
+ return d
+ }
+
+ return ev
+ }
+
+ return d
+}
+
+// GetEnvStrOrDefault returns the value of the environment variable k as a string *or* the default d
+// if casting fails or the environment variable is not set.
+func GetEnvStrOrDefault(k, d string) string {
+ if v, ok := os.LookupEnv(k); ok {
+ return v
+ }
+
+ return d
+}
diff --git a/util/errors.go b/util/errors.go
new file mode 100644
index 0000000..9e6b08d
--- /dev/null
+++ b/util/errors.go
@@ -0,0 +1,30 @@
+package util
+
+import "errors"
+
+var (
+ // ErrIgnoredOption is the error returned when attempting to apply an option to a struct that
+ // is not of the expected type. This error should not be exposed to end users.
+ ErrIgnoredOption = errors.New("errIgnoredOption")
+ // ErrBadOption is returned when a bad value is passed to an option function.
+ ErrBadOption = errors.New("errBadOption")
+ // ErrTimeoutError is returned for any scrapligo timeout issues, meaning socket, transport or
+ // channel (ops) timeouts.
+ ErrTimeoutError = errors.New("errTimeoutError")
+ // ErrAuthError is returned if any authentication errors are returned.
+ ErrAuthError = errors.New("errAuthError")
+ // ErrPrivilegeError is returned if there are any issues acquiring a privilege level or a user
+ // requests an invalid privilege level etc..
+ ErrPrivilegeError = errors.New("errPrivilegeError")
+ // ErrFileNotFoundError is returned if a file cannot be found.
+ ErrFileNotFoundError = errors.New("errFileNotFoundError")
+ // ErrParseError is returned when parsing contents/files fails.
+ ErrParseError = errors.New("errParseError")
+ // ErrPlatformError is returned for any "platform" issues.
+ ErrPlatformError = errors.New("errPlatformError")
+ // ErrNetconfError is a blanket error for NETCONF issues that don't fall into another more
+ // explicit error.
+ ErrNetconfError = errors.New("errNetconfError")
+ // ErrOperationError is returned for any "operation" issues -- mostly meaning ops timeouts.
+ ErrOperationError = errors.New("errOperationError")
+)
diff --git a/util/file.go b/util/file.go
new file mode 100644
index 0000000..fd4fa92
--- /dev/null
+++ b/util/file.go
@@ -0,0 +1,57 @@
+package util
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+)
+
+// ResolveFilePath resolves the qualified path to file string f.
+func ResolveFilePath(f string) (string, error) {
+ _, err := os.Stat(f)
+ if err == nil {
+ return f, nil
+ }
+
+ // if didn't stat a fully qualified file, strip user dir (if exists) and then check there
+ f = strings.TrimPrefix(f, "~/")
+
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return "", err
+ }
+
+ f = fmt.Sprintf("%s/%s", homeDir, f)
+
+ if _, err = os.Stat(f); err == nil {
+ return f, nil
+ }
+
+ return "", ErrFileNotFoundError
+}
+
+// LoadFileLines convenience function to load a file and return slice of strings of lines in that
+// file.
+func LoadFileLines(f string) ([]string, error) {
+ resolvedFile, err := ResolveFilePath(f)
+ if err != nil {
+ return []string{}, err
+ }
+
+ file, err := os.Open(resolvedFile) //nolint: gosec
+ if err != nil {
+ return []string{}, err
+ }
+
+ scanner := bufio.NewScanner(file)
+ scanner.Split(bufio.ScanLines)
+
+ var lines []string
+
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+
+ return lines, nil
+}
diff --git a/util/parse.go b/util/parse.go
new file mode 100644
index 0000000..fb0cddd
--- /dev/null
+++ b/util/parse.go
@@ -0,0 +1,91 @@
+package util
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "strings"
+
+ "github.com/sirikothe/gotextfsm"
+)
+
+// ResolveAtFileOrURL returns the bytes from `path` where path is either a filepath or URL.
+func ResolveAtFileOrURL(path string) ([]byte, error) {
+ var b []byte
+
+ switch {
+ case strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://"):
+ resp, err := http.Get(path) // nolint:gosec,noctx
+ if err != nil {
+ return nil, fmt.Errorf(
+ "%w: failed downloading file at path '%s', error: %s",
+ ErrParseError,
+ path,
+ err,
+ )
+ }
+
+ defer resp.Body.Close() //nolint:errcheck
+
+ b, err = io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf(
+ "%w: failed reading downloaded file at path '%s', error: %s",
+ ErrParseError,
+ path,
+ err,
+ )
+ }
+
+ default: // fall-through to local filesystem
+ var err error
+
+ b, err = os.ReadFile(path) //nolint:gosec
+ if err != nil {
+ return nil, fmt.Errorf(
+ "%w: failed opening provided file at path '%s', error: %s",
+ ErrParseError,
+ path,
+ err,
+ )
+ }
+ }
+
+ return b, nil
+}
+
+// TextFsmParse parses recorded output w/ a provided textfsm template.
+// the argument is interpreted as URL or filesystem path, for example:
+// response.TextFsmParse("http://example.com/textfsm.template") or
+// response.TextFsmParse("./local/textfsm.template").
+func TextFsmParse(s, path string) ([]map[string]interface{}, error) {
+ t, err := ResolveAtFileOrURL(path)
+ if err != nil {
+ return []map[string]interface{}{}, err
+ }
+
+ fsm := gotextfsm.TextFSM{}
+
+ err = fsm.ParseString(string(t))
+ if err != nil {
+ return []map[string]interface{}{}, fmt.Errorf(
+ "%w: failed parsing provided template, gotextfsm error: %s",
+ ErrParseError,
+ err,
+ )
+ }
+
+ parser := gotextfsm.ParserOutput{}
+
+ err = parser.ParseTextString(s, fsm, true)
+ if err != nil {
+ return []map[string]interface{}{}, fmt.Errorf(
+ "%w: failed parsing device output, gotextfsm error: %s",
+ ErrParseError,
+ err,
+ )
+ }
+
+ return parser.Dict, nil
+}
diff --git a/util/parse_test.go b/util/parse_test.go
new file mode 100644
index 0000000..fb8d35f
--- /dev/null
+++ b/util/parse_test.go
@@ -0,0 +1,61 @@
+package util_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testTextFsmParse(testName string) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ s := string(readFile(t, fmt.Sprintf("%s.txt", testName)))
+
+ actualOut, err := util.TextFsmParse(s, "test-fixtures/cisco_ios_show_version.textfsm")
+ if err != nil {
+ t.Fatalf("%s: encountered error parsing with textfsm, error: %s", testName, err)
+ }
+
+ actualOutJSON, err := json.Marshal(actualOut)
+ if err != nil {
+ t.Fatalf(
+ "%s: encountered error dumping textfsm output to json, error: %s",
+ testName,
+ err,
+ )
+ }
+
+ if *update {
+ writeGolden(t, testName, actualOutJSON)
+ }
+
+ expectedOut := readFile(t, fmt.Sprintf("golden/%s-out.txt", testName))
+
+ if !cmp.Equal(actualOutJSON, expectedOut) {
+ t.Fatalf(
+ "%s: actual and expected outputs do not match\nactual: %s\nexpected:%s",
+ testName,
+ actualOut,
+ expectedOut,
+ )
+ }
+ }
+}
+
+func TestTextFsmParse(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ }{
+ "textfsm-parse-simple": {},
+ }
+
+ for testName := range cases {
+ f := testTextFsmParse(testName)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/util/queue.go b/util/queue.go
new file mode 100644
index 0000000..ad40407
--- /dev/null
+++ b/util/queue.go
@@ -0,0 +1,85 @@
+package util
+
+import (
+ "bytes"
+ "sync"
+)
+
+// Queue is a simple queue structure to store and queue/requeue/dequeue bytes.
+type Queue struct {
+ queue [][]byte
+ depth int
+ lock *sync.RWMutex
+}
+
+// NewQueue returns a prepared Queue object.
+func NewQueue() *Queue {
+ return &Queue{
+ lock: &sync.RWMutex{},
+ }
+}
+
+// Requeue prepends some bytes to the front of the queue.
+func (q *Queue) Requeue(b []byte) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ n := [][]byte{b}
+ q.queue = append(n, q.queue...)
+
+ q.depth++
+}
+
+// Enqueue queues some bytes at the end of the queue.
+func (q *Queue) Enqueue(b []byte) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ q.queue = append(q.queue, b)
+ q.depth++
+}
+
+// Dequeue returns the first bytes in the queue.
+func (q *Queue) Dequeue() []byte {
+ // check the depth before acquiring a full read/write lock which can cause deadlocks with tons
+ // of concurrent access to enqueue/deque.
+ if q.GetDepth() == 0 {
+ return nil
+ }
+
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ b := q.queue[0]
+
+ q.queue = q.queue[1:]
+ q.depth--
+
+ return b
+}
+
+// DequeueAll returns all bytes in the queue.
+func (q *Queue) DequeueAll() []byte {
+ if q.GetDepth() == 0 {
+ return nil
+ }
+
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ b := q.queue
+
+ q.queue = nil
+
+ q.depth = 0
+
+ return bytes.Join(b, []byte{})
+}
+
+// GetDepth returns the depth of the queue.
+func (q *Queue) GetDepth() int {
+ q.lock.RLock()
+ defer q.lock.RUnlock()
+
+ return q.depth
+}
diff --git a/util/slices.go b/util/slices.go
new file mode 100644
index 0000000..45dd954
--- /dev/null
+++ b/util/slices.go
@@ -0,0 +1,12 @@
+package util
+
+// StringSliceContains checks if the string slice ss contains the substring s.
+func StringSliceContains(ss []string, s string) bool {
+ for _, sss := range ss {
+ if sss == s {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/util/slices_test.go b/util/slices_test.go
new file mode 100644
index 0000000..6ec9a5a
--- /dev/null
+++ b/util/slices_test.go
@@ -0,0 +1,46 @@
+package util_test
+
+import (
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testStringSliceContains(testName, s string, ss []string, expected bool) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if util.StringSliceContains(ss, s) != expected {
+ t.Fatalf(
+ "%s: actual and expected results differ",
+ testName,
+ )
+ }
+ }
+}
+
+func TestStringSliceContains(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ ss []string
+ s string
+ expected bool
+ }{
+ "string-slice-contains-simple-true": {
+ ss: []string{"taco", "cat", "race", "car"},
+ s: "taco",
+ expected: true,
+ },
+ "string-slice-contains-simple-false": {
+ ss: []string{"taco", "cat", "race", "car"},
+ s: "potato",
+ expected: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStringSliceContains(testName, testCase.s, testCase.ss, testCase.expected)
+
+ t.Run(testName, f)
+ }
+}
diff --git a/util/strings.go b/util/strings.go
new file mode 100644
index 0000000..0303a5e
--- /dev/null
+++ b/util/strings.go
@@ -0,0 +1,27 @@
+package util
+
+import "strings"
+
+// StringContainsAny checks if string s is in the string slice l, this function returns a bool
+// indicating inclusion or not.
+func StringContainsAny(s string, l []string) bool {
+ for _, ss := range l {
+ if strings.Contains(s, ss) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// StringContainsAnySubStrs checks if a string s is in the string slice l and returns the first
+// encountered substring. If no match is encountered an empty string is returned.
+func StringContainsAnySubStrs(s string, l []string) string {
+ for _, ss := range l {
+ if strings.Contains(s, ss) {
+ return ss
+ }
+ }
+
+ return ""
+}
diff --git a/util/strings_test.go b/util/strings_test.go
new file mode 100644
index 0000000..da34c5f
--- /dev/null
+++ b/util/strings_test.go
@@ -0,0 +1,98 @@
+package util_test
+
+import (
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+func testStringContainsAny(
+ testName, s string,
+ contains []string,
+ expected bool,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if util.StringContainsAny(s, contains) != expected {
+ t.Fatalf(
+ "%s: actual and expected results differ",
+ testName,
+ )
+ }
+ }
+}
+
+func TestStringContainsAny(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ s string
+ contains []string
+ expected bool
+ }{
+ "string-contains-any-simple-true": {
+ s: "tacocat",
+ contains: []string{"taco", "cat", "race", "car"},
+ expected: true,
+ },
+ "sstring-contains-any-simple-false": {
+ s: "potato",
+ contains: []string{"taco", "cat", "race", "car"},
+ expected: false,
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStringContainsAny(testName, testCase.s, testCase.contains, testCase.expected)
+
+ t.Run(testName, f)
+ }
+}
+
+func testStringContainsAnySubStrs(
+ testName, s string,
+ contains []string,
+ expected string,
+) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Logf("%s: starting", testName)
+
+ if util.StringContainsAnySubStrs(s, contains) != expected {
+ t.Fatalf(
+ "%s: actual and expected results differ",
+ testName,
+ )
+ }
+ }
+}
+
+func TestStringContainsAnySubStrs(t *testing.T) {
+ cases := map[string]struct {
+ description string
+ s string
+ contains []string
+ expected string
+ }{
+ "string-slice-contains-any-substr-match": {
+ s: "tacocat",
+ contains: []string{"taco", "cat", "race", "car"},
+ expected: "taco",
+ },
+ "string-slice-contains-any-substr-no-match": {
+ s: "potato",
+ contains: []string{"taco", "cat", "race", "car"},
+ expected: "",
+ },
+ }
+
+ for testName, testCase := range cases {
+ f := testStringContainsAnySubStrs(
+ testName,
+ testCase.s,
+ testCase.contains,
+ testCase.expected,
+ )
+
+ t.Run(testName, f)
+ }
+}
diff --git a/test_data/driver/base/response/cisco_ios_show_version.textfsm b/util/test-fixtures/cisco_ios_show_version.textfsm
similarity index 100%
rename from test_data/driver/base/response/cisco_ios_show_version.textfsm
rename to util/test-fixtures/cisco_ios_show_version.textfsm
diff --git a/util/test-fixtures/golden/textfsm-parse-simple-out.txt b/util/test-fixtures/golden/textfsm-parse-simple-out.txt
new file mode 100644
index 0000000..7c2cdf8
--- /dev/null
+++ b/util/test-fixtures/golden/textfsm-parse-simple-out.txt
@@ -0,0 +1 @@
+[{"CONFIG_REGISTER":"0x2102","HARDWARE":["CSR1000V"],"HOSTNAME":"csr1000v","MAC":[],"RELOAD_REASON":"reload","ROMMON":"IOS-XE","RUNNING_IMAGE":"packages.conf","SERIAL":["9MVVU09YZFH"],"UPTIME":"1 hour, 31 minutes","VERSION":"16.12.3"}]
\ No newline at end of file
diff --git a/test_data/driver/network/expected/cisco_iosxe_show_version_textfsm b/util/test-fixtures/textfsm-parse-simple.txt
similarity index 100%
rename from test_data/driver/network/expected/cisco_iosxe_show_version_textfsm
rename to util/test-fixtures/textfsm-parse-simple.txt
diff --git a/util/testhelper/clean.go b/util/testclean.go
similarity index 57%
rename from util/testhelper/clean.go
rename to util/testclean.go
index 7ea08b5..8b820c2 100644
--- a/util/testhelper/clean.go
+++ b/util/testclean.go
@@ -1,7 +1,8 @@
-package testhelper
+package util
import (
"regexp"
+ "strings"
)
func cleanResponseMap() map[string]func(r string) string {
@@ -14,6 +15,10 @@ func cleanResponseMap() map[string]func(r string) string {
}
}
+// GetCleanFunc is only used for testing -- it returns a function that "cleans" output (usually a
+// "show run" type of output) of any data that may change over time -- things like timestamps and
+// password hashes, this allows for comparing the stored "golden" test data against "new" output
+// gleaned from test clab devices.
func GetCleanFunc(platform string) func(r string) string {
cleanFuncs := cleanResponseMap()
@@ -25,11 +30,16 @@ func GetCleanFunc(platform string) func(r string) string {
return cleanFunc
}
+func replaceDoubleNewlines(s string) string {
+ return strings.Replace(s, "\n\n", "\n", -1)
+}
+
func cleanResponseNoop(r string) string { return r }
type aristaEosReplacePatterns struct {
- datetimePattern *regexp.Regexp
- cryptoPattern *regexp.Regexp
+ datetimePattern *regexp.Regexp
+ datetimePatternNetconf *regexp.Regexp
+ cryptoPattern *regexp.Regexp
}
var aristaEosReplacePatternsInstance *aristaEosReplacePatterns //nolint:gochecknoglobals
@@ -42,6 +52,9 @@ func getAristaEosReplacePatterns() *aristaEosReplacePatterns {
`\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)` +
`\s+\d+\s+\d+:\d+:\d+\s+\d+$`,
),
+ datetimePatternNetconf: regexp.MustCompile(
+ `\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z`,
+ ),
cryptoPattern: regexp.MustCompile(`(?im)secret\ssha512\s[\w$./]+$`),
}
}
@@ -52,10 +65,11 @@ func getAristaEosReplacePatterns() *aristaEosReplacePatterns {
func aristaEosCleanResponse(r string) string {
replacePatterns := getAristaEosReplacePatterns()
- r = replacePatterns.datetimePattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.cryptoPattern.ReplaceAllString(r, "CRYPTO_REPLACED")
+ r = replacePatterns.datetimePattern.ReplaceAllString(r, "")
+ r = replacePatterns.datetimePatternNetconf.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoPattern.ReplaceAllString(r, "")
- return r
+ return replaceDoubleNewlines(r)
}
type ciscoIosxrReplacePatterns struct {
@@ -63,6 +77,7 @@ type ciscoIosxrReplacePatterns struct {
cryptoPattern *regexp.Regexp
cfgByPattern *regexp.Regexp
commitInProgressPattern *regexp.Regexp
+ passwordNetconfPattern *regexp.Regexp
}
var ciscoIosxrReplacePatternsInstance *ciscoIosxrReplacePatterns //nolint:gochecknoglobals
@@ -75,11 +90,14 @@ func getCiscoIosxrReplacePatterns() *ciscoIosxrReplacePatterns {
`\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)` +
`\s+\d+\s+\d+:\d+:\d+((\.\d+\s\w+)|\s\d+)`,
),
- cryptoPattern: regexp.MustCompile(`(?im)^\ssecret\s5\s[\w$./]+$`),
+ cryptoPattern: regexp.MustCompile(
+ `(?im)(^\ssecret\s5\s[\w$./]+$)|(^\spassword\s7\s\w+$)`,
+ ),
cfgByPattern: regexp.MustCompile(
`(?im)^!! Last configuration change at TIME_STAMP_REPLACED by (\w+)$`,
),
commitInProgressPattern: regexp.MustCompile(`(?ims)System configuration.*`),
+ passwordNetconfPattern: regexp.MustCompile(`(?im).* `),
}
}
@@ -89,12 +107,13 @@ func getCiscoIosxrReplacePatterns() *ciscoIosxrReplacePatterns {
func ciscoIosxrCleanResponse(r string) string {
replacePatterns := getCiscoIosxrReplacePatterns()
- r = replacePatterns.datetimePattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.cryptoPattern.ReplaceAllString(r, "CRYPTO_REPLACED")
- r = replacePatterns.cfgByPattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
+ r = replacePatterns.datetimePattern.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoPattern.ReplaceAllString(r, "")
+ r = replacePatterns.cfgByPattern.ReplaceAllString(r, "")
r = replacePatterns.commitInProgressPattern.ReplaceAllString(r, "")
+ r = replacePatterns.passwordNetconfPattern.ReplaceAllString(r, "")
- return r
+ return replaceDoubleNewlines(r)
}
type ciscoIosxeReplacePatterns struct {
@@ -104,6 +123,9 @@ type ciscoIosxeReplacePatterns struct {
cfgByPattern *regexp.Regexp
callHomePattern *regexp.Regexp
certLicensePattern *regexp.Regexp
+ serialNetconf *regexp.Regexp
+ macAddrNetconf *regexp.Regexp
+ cryptoNetconf *regexp.Regexp
}
var ciscoIosxeReplacePatternsInstance *ciscoIosxeReplacePatterns //nolint:gochecknoglobals
@@ -116,9 +138,9 @@ func getCiscoIosxeReplacePatterns() *ciscoIosxeReplacePatterns {
`(?im)\d+:\d+:\d+\d+\s+[a-z]{3}\s+(mon|tue|wed|thu|fri|sat|sun)` +
`\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+\d+\s+\d+`,
),
- cryptoPattern: regexp.MustCompile(`(?im)^enable secret 5 (.*$)`),
+ cryptoPattern: regexp.MustCompile(`(?im)^enable secret 9 (.*$)`),
cfgByPattern: regexp.MustCompile(
- `(?im)^! Last configuration change at TIME_STAMP_REPLACED by (\w+)$`,
+ `(?im)^! Last configuration change at ([\w\s]+)?$`,
),
callHomePattern: regexp.MustCompile(
`(?im)^! Call-home is enabled by Smart-Licensing.$`,
@@ -126,6 +148,9 @@ func getCiscoIosxeReplacePatterns() *ciscoIosxeReplacePatterns {
certLicensePattern: regexp.MustCompile(
`(?ims)^crypto pki .*\nlicense udi pid CSR1000V sn \w+$`,
),
+ serialNetconf: regexp.MustCompile(`(?i)\w+ `),
+ macAddrNetconf: regexp.MustCompile(`(?i).* `),
+ cryptoNetconf: regexp.MustCompile(`(?i).* `),
}
}
@@ -135,20 +160,23 @@ func getCiscoIosxeReplacePatterns() *ciscoIosxeReplacePatterns {
func ciscoIosxeCleanResponse(r string) string {
replacePatterns := getCiscoIosxeReplacePatterns()
- r = replacePatterns.configBytesPattern.ReplaceAllString(r, "CONFIG_BYTES_REPLACED")
- r = replacePatterns.datetimePattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.cryptoPattern.ReplaceAllString(r, "CRYPTO_REPLACED")
- r = replacePatterns.cfgByPattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.callHomePattern.ReplaceAllString(r, "CALL_HOME_REPLACED")
- r = replacePatterns.certLicensePattern.ReplaceAllString(r, "CERT_LICENSE_REPLACED")
+ r = replacePatterns.configBytesPattern.ReplaceAllString(r, "")
+ r = replacePatterns.datetimePattern.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoPattern.ReplaceAllString(r, "")
+ r = replacePatterns.cfgByPattern.ReplaceAllString(r, "")
+ r = replacePatterns.callHomePattern.ReplaceAllString(r, "")
+ r = replacePatterns.certLicensePattern.ReplaceAllString(r, "")
+ r = replacePatterns.serialNetconf.ReplaceAllString(r, "")
+ r = replacePatterns.macAddrNetconf.ReplaceAllString(r, "")
- return r
+ return replaceDoubleNewlines(r)
}
type ciscoNxosReplacePatterns struct {
datetimePattern *regexp.Regexp
cryptoPattern *regexp.Regexp
resourcePattern *regexp.Regexp
+ datetimeNetconf *regexp.Regexp
}
var ciscoNxosReplacePatternsInstance *ciscoNxosReplacePatterns //nolint:gochecknoglobals
@@ -164,6 +192,7 @@ func getCiscoNxosReplacePatterns() *ciscoNxosReplacePatterns {
resourcePattern: regexp.MustCompile(
`(?im)\d+\smaximum\s\d+$`,
),
+ datetimeNetconf: regexp.MustCompile(`.* `),
}
}
@@ -173,16 +202,20 @@ func getCiscoNxosReplacePatterns() *ciscoNxosReplacePatterns {
func ciscoNxosCleanResponse(r string) string {
replacePatterns := getCiscoNxosReplacePatterns()
- r = replacePatterns.datetimePattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.cryptoPattern.ReplaceAllString(r, "CRYPTO_REPLACED")
- r = replacePatterns.resourcePattern.ReplaceAllString(r, "RESOURCES_REPLACED")
+ r = replacePatterns.datetimePattern.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoPattern.ReplaceAllString(r, "")
+ r = replacePatterns.resourcePattern.ReplaceAllString(r, "")
- return r
+ return replaceDoubleNewlines(r)
}
type juniperJunosReplacePatterns struct {
- datetimePattern *regexp.Regexp
- cryptoPattern *regexp.Regexp
+ datetimePattern *regexp.Regexp
+ cryptoPattern *regexp.Regexp
+ commitSecNetconfPattern *regexp.Regexp
+ cryptoNetconfPattern *regexp.Regexp
+ datetimeNetconfPattern *regexp.Regexp
+ commitUserNetconfPattern *regexp.Regexp
}
var juniperJunosReplacePatternsInstance *juniperJunosReplacePatterns //nolint:gochecknoglobals
@@ -193,7 +226,19 @@ func getJuniperJunosReplacePatterns() *juniperJunosReplacePatterns {
datetimePattern: regexp.MustCompile(
`(?im)^## Last commit: \d+-\d+-\d+\s\d+:\d+:\d+\s\w+.*$`,
),
- cryptoPattern: regexp.MustCompile(`(?im)^\s+encrypted-password\s"[\w$./]+";\s.*$`),
+ cryptoPattern: regexp.MustCompile(
+ `(?im)^\s+encrypted-password\s"[\w$./]+";\s.*$`,
+ ),
+ commitSecNetconfPattern: regexp.MustCompile(`seconds="\d+"`),
+ cryptoNetconfPattern: regexp.MustCompile(
+ `.* `,
+ ),
+ datetimeNetconfPattern: regexp.MustCompile(
+ `localtime="\d+-\d+-\d+\s\d+:\d+:\d+\s\w+"`,
+ ),
+ commitUserNetconfPattern: regexp.MustCompile(
+ `commit-user="\w+"`,
+ ),
}
}
@@ -203,8 +248,12 @@ func getJuniperJunosReplacePatterns() *juniperJunosReplacePatterns {
func juniperJunosCleanResponse(r string) string {
replacePatterns := getJuniperJunosReplacePatterns()
- r = replacePatterns.datetimePattern.ReplaceAllString(r, "TIME_STAMP_REPLACED")
- r = replacePatterns.cryptoPattern.ReplaceAllString(r, "CRYPTO_REPLACED")
+ r = replacePatterns.datetimePattern.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoPattern.ReplaceAllString(r, "")
+ r = replacePatterns.commitSecNetconfPattern.ReplaceAllString(r, "")
+ r = replacePatterns.cryptoNetconfPattern.ReplaceAllString(r, "")
+ r = replacePatterns.datetimeNetconfPattern.ReplaceAllString(r, "")
+ r = replacePatterns.commitUserNetconfPattern.ReplaceAllString(r, "")
- return r
+ return replaceDoubleNewlines(r)
}
diff --git a/util/testhelper.go b/util/testhelper.go
new file mode 100644
index 0000000..c7e3211
--- /dev/null
+++ b/util/testhelper.go
@@ -0,0 +1,27 @@
+package util
+
+import "strings"
+
+// PlatformOK is only used for testing and checks if the cli flag platforms is either "all" or
+// contains the requested platform name platformName.
+func PlatformOK(platforms *string, platformName string) bool {
+ if *platforms == All {
+ return true
+ }
+
+ p := strings.Split(*platforms, ",")
+
+ return StringSliceContains(p, platformName)
+}
+
+// TransportOK is only used for testing and checks if the cli flag transports is either "all" or
+// contains the requested transport name transportName.
+func TransportOK(transports *string, transportName string) bool {
+ if *transports == All {
+ return true
+ }
+
+ t := strings.Split(*transports, ",")
+
+ return StringSliceContains(t, transportName)
+}
diff --git a/util/testhelper/channel.go b/util/testhelper/channel.go
deleted file mode 100644
index e688da2..0000000
--- a/util/testhelper/channel.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package testhelper
-
-import (
- "fmt"
- "os"
- "regexp"
- "testing"
- "time"
-
- "github.com/scrapli/scrapligo/transport"
-
- "github.com/scrapli/scrapligo/channel"
-)
-
-// NewPatchedChannel create a new channel that is patched with testing transport.
-func NewPatchedChannel(t *testing.T, sessionFile *string) *channel.Channel {
- transportImpl := &TestingTransport{}
-
- if sessionFile != nil {
- finalSessionFile := fmt.Sprintf("../test_data/channel/%s", *sessionFile)
-
- f, err := os.Open(finalSessionFile)
- if err != nil {
- t.Fatalf("failed opening transport session file '%s' err: %v", finalSessionFile, err)
- }
-
- transportImpl.FakeSession = f
- }
-
- tr := &transport.Transport{
- Impl: transportImpl,
- BaseTransportArgs: &transport.BaseTransportArgs{
- Host: "localhost",
- Port: 22,
- TimeoutTransport: 1 * time.Second,
- },
- }
-
- c := &channel.Channel{
- Transport: tr,
- CommsPromptPattern: regexp.MustCompile(`(?im)^localhost#\s?$`),
- CommsReturnChar: "\n",
- CommsPromptSearchDepth: 255,
- TimeoutOps: 30 * time.Second,
- Host: "localhost",
- Port: 22,
- }
-
- return c
-}
-
-// FetchTestTransport fetch the TestTransport object so we can do operations on attributes that only
-// the test transport has.
-func FetchTestTransport(c *channel.Channel, t *testing.T) *TestingTransport {
- testTransp, ok := c.Transport.Impl.(*TestingTransport)
-
- if !ok {
- t.Fatalf("this should not happen; TestingTransport patching failed somehow :(")
- }
-
- return testTransp
-}
-
-// SetTestTransportStandardReadSize set the TestTransport read size to the "normal" value of 65535 -
-// this is living in the channel file as its only necessary to modify for channel test operations.
-func SetTestTransportStandardReadSize(c *channel.Channel, t *testing.T) {
- testTransp, ok := c.Transport.Impl.(*TestingTransport)
-
- if !ok {
- t.Fatalf("this should not happen; TestingTransport patching failed somehow :(")
- }
-
- readSize := 65535
- testTransp.ReadSize = &readSize
-}
diff --git a/util/testhelper/driver.go b/util/testhelper/driver.go
deleted file mode 100644
index 02b1381..0000000
--- a/util/testhelper/driver.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package testhelper
-
-import (
- "testing"
-
- "github.com/scrapli/scrapligo/driver/core"
- "github.com/scrapli/scrapligo/driver/network"
-)
-
-func CreatePatchedDriver(t *testing.T, sessionFile, platform string) *network.Driver {
- d, driverErr := core.NewCoreDriver(
- "localhost",
- platform,
- WithPatchedTransport(sessionFile),
- )
-
- if driverErr != nil {
- t.Fatalf("failed creating test device: %v", driverErr)
- }
-
- return d
-}
diff --git a/util/testhelper/functional_flags.go b/util/testhelper/functional_flags.go
deleted file mode 100644
index e0bc278..0000000
--- a/util/testhelper/functional_flags.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package testhelper
-
-import (
- "flag"
- "strings"
-
- "github.com/scrapli/scrapligo/util"
-)
-
-const all = "all"
-
-var Functional = flag.Bool( //nolint:gochecknoglobals
- "functional", false, "perform functional tests")
-
-var FunctionalPlatform = flag.String( //nolint:gochecknoglobals
- "platform", "all", "list comma sep platform(s) to target")
-
-var FunctionalTransport = flag.String( //nolint:gochecknoglobals
- "transport", "all", "list comma sep transport(s) to target")
-
-func RunPlatform(p string) bool {
- if *FunctionalPlatform == all {
- return true
- }
-
- platformTargetSplit := strings.Split(*FunctionalPlatform, ",")
-
- return util.StrInSlice(p, platformTargetSplit)
-}
-
-func RunTransport(t string) bool {
- if *FunctionalTransport == all {
- return true
- }
-
- platformTargetSplit := strings.Split(*FunctionalTransport, ",")
-
- return util.StrInSlice(t, platformTargetSplit)
-}
diff --git a/util/testhelper/transport.go b/util/testhelper/transport.go
deleted file mode 100644
index 9839ff7..0000000
--- a/util/testhelper/transport.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package testhelper
-
-import (
- "os"
-
- "github.com/scrapli/scrapligo/driver/base"
- "github.com/scrapli/scrapligo/transport"
-)
-
-// TestingTransport patched transport for testing.
-type TestingTransport struct {
- FakeSession *os.File
- CapturedWrites [][]byte
- ReadSize *int
-}
-
-// SetOpenCmd implements SystemTransport SetOpenCmd here for testing purposes.
-func (t *TestingTransport) SetOpenCmd(openCmd []string) {
- _ = openCmd
-}
-
-// SetExecCmd implements SystemTransport SetOpenCmd here for testing purposes.
-func (t *TestingTransport) SetExecCmd(execCmd string) {
- _ = execCmd
-}
-
-// Open do nothing!
-func (t *TestingTransport) Open(baseArgs *transport.BaseTransportArgs) error {
- _ = baseArgs
- return nil
-}
-
-// OpenNetconf do nothing!
-func (t *TestingTransport) OpenNetconf(baseArgs *transport.BaseTransportArgs) error {
- _ = baseArgs
- return nil
-}
-
-// Close do nothing!
-func (t *TestingTransport) Close() error {
- return nil
-}
-
-func (t *TestingTransport) Read(n int) *transport.ReadResult {
- _ = n
- readSize := 1
-
- if t.ReadSize != nil {
- readSize = *t.ReadSize
- }
-
- b := make([]byte, readSize)
- _, err := t.FakeSession.Read(b)
-
- return &transport.ReadResult{Result: b, Error: err}
-}
-
-func (t *TestingTransport) Write(channelInput []byte) error {
- t.CapturedWrites = append(t.CapturedWrites, channelInput)
- return nil
-}
-
-// IsAlive do nothing!
-func (t *TestingTransport) IsAlive() bool {
- return true
-}
-
-// WithPatchedTransport option to use to patch a driver transport.
-func WithPatchedTransport(sessionFile string) base.Option {
- return func(o interface{}) error {
- f, err := os.Open(sessionFile)
- if err != nil {
- return err
- }
-
- d, ok := o.(*base.Driver)
-
- if ok {
- d.Transport = &transport.Transport{
- Impl: &TestingTransport{
- FakeSession: f,
- },
- BaseTransportArgs: &transport.BaseTransportArgs{
- Host: "localhost",
- Port: 22,
- },
- }
- }
-
- return base.ErrIgnoredOption
- }
-}
diff --git a/util/types.go b/util/types.go
new file mode 100644
index 0000000..d850514
--- /dev/null
+++ b/util/types.go
@@ -0,0 +1,11 @@
+package util
+
+// Option is a simple type that accepts an interface and returns an error; this should only be used
+// in the context of applying options to an object.
+type Option func(interface{}) error
+
+// PayloadTestCase is a simple struct used in testing only.
+type PayloadTestCase struct {
+ Description string
+ PayloadFile string
+}
diff --git a/util/util.go b/util/util.go
deleted file mode 100644
index 7028978..0000000
--- a/util/util.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package util
-
-import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "os"
- "regexp"
- "strings"
-
- "github.com/scrapli/scrapligo/logging"
-)
-
-// ErrFileNotFound error for being unable to find requested file.
-var ErrFileNotFound = errors.New("file not found")
-
-const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?" +
- "\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
-
-type ansiPattern struct {
- pattern *regexp.Regexp
-}
-
-var ansiPatternInstance *ansiPattern //nolint:gochecknoglobals
-
-func getAnsiPattern() *ansiPattern {
- if ansiPatternInstance == nil {
- ansiPatternInstance = &ansiPattern{
- pattern: regexp.MustCompile(ansi),
- }
- }
-
- return ansiPatternInstance
-}
-
-// StrContainsAnySubStr checks string `s` for any occurrences of substrings in `l`, returns first
-// found substring if any, otherwise an empty string.
-func StrContainsAnySubStr(s string, l []string) string {
- for _, ss := range l {
- if strings.Contains(s, ss) {
- return s
- }
- }
-
- return ""
-}
-
-// StrInSlice checks for any occurrence of `s` in slice of strings `l`. Returns true if `s` found,
-// otherwise false.
-func StrInSlice(s string, l []string) bool {
- for _, i := range l {
- if s == i {
- return true
- }
- }
-
- return false
-}
-
-// BytesContainsAnySubBytes checks byte `b` for any occurrences of substrings in `l`, returns first
-// found substring if any, otherwise an empty string.
-func BytesContainsAnySubBytes(b []byte, l [][]byte) []byte {
- for _, ss := range l {
- if bytes.Contains(b, ss) {
- return b
- }
- }
-
- return []byte{}
-}
-
-func ByteInSlice(b byte, s []byte) bool {
- for _, i := range s {
- if b == i {
- return true
- }
- }
-
- return false
-}
-
-// StripAnsi strips ansi characters from a byte slice.
-func StripAnsi(b []byte) []byte {
- ap := getAnsiPattern()
- return ap.pattern.ReplaceAll(b, []byte{})
-}
-
-func ResolveFilePath(f string) (string, error) {
- if _, err := os.Stat(f); err == nil {
- return f, nil
- }
-
- // if didn't stat a fully qualified file, strip user dir (if exists) and then check there
- f = strings.TrimPrefix(f, "~/")
- homeDir, err := os.UserHomeDir()
-
- if err != nil {
- logging.LogError(fmt.Sprintf("couldnt determine users home directory: %v", err))
-
- return "", err
- }
-
- f = fmt.Sprintf("%s/%s", homeDir, f)
-
- if _, err = os.Stat(f); err == nil {
- return f, nil
- }
-
- return "", ErrFileNotFound
-}
-
-// LoadFileLines convenience function to load a file and return slice of strings of lines in that
-// file.
-func LoadFileLines(f string) ([]string, error) {
- resolvedFile, err := ResolveFilePath(f)
-
- if err != nil {
- return []string{}, err
- }
-
- file, err := os.Open(resolvedFile)
- if err != nil {
- return []string{}, err
- }
-
- scanner := bufio.NewScanner(file)
- scanner.Split(bufio.ScanLines)
-
- var lines []string
-
- for scanner.Scan() {
- lines = append(lines, scanner.Text())
- }
-
- return lines, nil
-}
diff --git a/util/util_test.go b/util/util_test.go
new file mode 100644
index 0000000..41d0dd7
--- /dev/null
+++ b/util/util_test.go
@@ -0,0 +1,52 @@
+package util_test
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/scrapli/scrapligo/util"
+)
+
+var (
+ update = flag.Bool( //nolint
+ "update",
+ false,
+ "update the golden files",
+ )
+ functional = flag.Bool( //nolint
+ "functional",
+ false,
+ "execute functional tests",
+ )
+ platforms = flag.String( //nolint
+ "platforms",
+ util.All,
+ "comma sep list of platform(s) to target",
+ )
+ transports = flag.String( //nolint
+ "transports",
+ util.All,
+ "comma sep list of transport(s) to target",
+ )
+)
+
+func readFile(t *testing.T, f string) []byte {
+ b, err := os.ReadFile(fmt.Sprintf("./test-fixtures/%s", f))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return b
+}
+
+func writeGolden(t *testing.T, testName string, actualOut []byte) {
+ goldenOut := filepath.Join("test-fixtures", "golden", testName+"-out.txt")
+
+ err := os.WriteFile(goldenOut, actualOut, 0o644) //nolint:gosec
+ if err != nil {
+ t.Fatal(err)
+ }
+}