From f8e373c60e3f1c3db23518daf251684b53819554 Mon Sep 17 00:00:00 2001 From: Vadim Volovik Date: Wed, 19 Nov 2025 14:12:39 +0000 Subject: [PATCH 1/5] add cisco asa to pkg --- pkg/device/asa/asa.go | 58 ++++++++++++++++++++++++++++ pkg/device/asa/asa_test.go | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 pkg/device/asa/asa.go create mode 100644 pkg/device/asa/asa_test.go diff --git a/pkg/device/asa/asa.go b/pkg/device/asa/asa.go new file mode 100644 index 0000000..f449d1a --- /dev/null +++ b/pkg/device/asa/asa.go @@ -0,0 +1,58 @@ +/* +Package cisco implements Cisco ASA CLI using genericcli. +*/ +package ciscoasa + +import ( + "github.com/annetutil/gnetcli/pkg/cmd" + "github.com/annetutil/gnetcli/pkg/device/genericcli" + "github.com/annetutil/gnetcli/pkg/expr" + "github.com/annetutil/gnetcli/pkg/streamer" +) + +const ( + loginExpression = `.*Username:\s?$` + questionExpression = `\n(?P.*Continue\? \[Y/N\]:)$` + promptExpression = `(?P[\w\-.:/_]+(\(conf(ig)?(-[^)]+)*\))?)(>|#)\s*$` + errorExpression = `(` + + `\r\n% Invalid input detected at '\^' marker.\r\n` + + `|^\r? +\^\n(% )?Invalid [\w ()]+ at '\^' marker\.` + + `|ERROR: % Invalid input detected at '\^' marker\.` + + `|ERROR: % Unrecognized command` + + `|(\s+\^\n)?% Ambiguous command(: +\".+\"+| at .+)` + + `|\r?% Permission denied for the role` + + `|\n?% ?Bad (OID|IP address or host name%[\ \w,]+)` + + `|\r?% This command is not authorized` + + `|\r?% Failed to commit .+` + + `|\r?% Specify .+` + + `|^% Invalid input` + + `|Permission denied.+\[Errno \d+\] Permission denied` + + `)` + passwordExpression = `.*password:\s?$` + passwordErrorExpression = `\n\% Authentication failed(\r\n|\n)` + pagerExpression = `<--- More --->` +) + +var autoCommands = []cmd.Cmd{ + cmd.NewCmd("terminal no monitor", cmd.WithErrorIgnore()), // ios, ios-xe + cmd.NewCmd("terminal monitor disable", cmd.WithErrorIgnore()), // ios xr + cmd.NewCmd("terminal length 0", cmd.WithErrorIgnore()), + cmd.NewCmd("terminal width 0", cmd.WithErrorIgnore()), + cmd.NewCmd("enable", cmd.WithErrorIgnore(), cmd.WithAddAnswers(cmd.NewAnswerWithNL("Password: ", ""))), +} + +func NewDevice(connector streamer.Connector, opts ...genericcli.GenericDeviceOption) genericcli.GenericDevice { + cli := genericcli.MakeGenericCLI(expr.NewSimpleExprLast200().FromPattern(promptExpression), expr.NewSimpleExprLast200().FromPattern(errorExpression), + genericcli.WithLoginExprs( + expr.NewSimpleExprLast200().FromPattern(loginExpression), + expr.NewSimpleExprLast200().FromPattern(passwordExpression), + expr.NewSimpleExprLast200().FromPattern(passwordErrorExpression)), + genericcli.WithPager( + expr.NewSimpleExprLast200().FromPattern(pagerExpression)), + genericcli.WithQuestion( + expr.NewSimpleExprLast200().FromPattern(questionExpression)), + genericcli.WithAutoCommands(autoCommands), + genericcli.WithTerminalParams(400, 0), + ) + return genericcli.MakeGenericDevice(cli, connector, opts...) +} diff --git a/pkg/device/asa/asa_test.go b/pkg/device/asa/asa_test.go new file mode 100644 index 0000000..848e497 --- /dev/null +++ b/pkg/device/asa/asa_test.go @@ -0,0 +1,77 @@ +package ciscoasa + +import ( + "testing" + + "github.com/annetutil/gnetcli/pkg/testutils" +) + +func TestErrors(t *testing.T) { + + errorCases := [][]byte{ + []byte(" ^\r\n% Invalid input detected at '^' marker.\r\n\r\n"), + []byte("% Bad IP address or host name% Unknown command or computer name, or unable to find computer address"), + []byte("% Ambiguous command: \"dis clock\""), + []byte(" ^\n% Ambiguous command at '^' marker."), + []byte("% Specify remote-as or peer-group remote AS first"), + []byte("ERROR: % Invalid input detected at '^' marker."), + []byte("ERROR: % Unrecognized command"), + []byte("% Permission denied for the role"), + []byte("\r% Permission denied for the role"), + []byte("% This command is not authorized"), + []byte("\r% This command is not authorized"), + []byte("% Failed to commit one or more configuration commands"), + []byte("\r% Failed to commit the configuration"), + []byte("% Bad OID value"), + []byte("\n% Bad IP address"), + []byte("% Invalid input"), + []byte("Permission denied: Access denied [Errno 13] Permission denied"), + []byte(" ^\n% Ambiguous command at '^' marker."), + []byte(" ^\nInvalid command at '^' marker."), + []byte("% Specify interface name first"), + []byte(" ^\nERROR: % Invalid input detected at '^' marker."), + } + testutils.ExprTester(t, errorCases, errorExpression) +} + +func TestPromptCiscoASA(t *testing.T) { + testCases := [][]byte{ + []byte("\r\ncisco-asa-fw-01#"), + []byte("\r\ncisco-asa-fw-01(config)#"), + []byte("\r\nasa-fw.example.com#"), + []byte("\r\nASA-5520(config)#"), + []byte("\r\nfirewall-asa(config-if)#"), + []byte("\r\ncisco-asa-prod(config-webvpn)#"), + []byte("\r\nmy-asa-device>"), // User mode + []byte("\r\n\rcisco-asa-fw-01# "), + } + testutils.ExprTester(t, testCases, promptExpression) +} + +func TestLogin(t *testing.T) { + errorCases := [][]byte{ + []byte("\r\n\r\nUser Access Verification\r\n\r\nUsername: "), + } + testutils.ExprTester(t, errorCases, loginExpression) +} + +func TestPassword(t *testing.T) { + errorCases := [][]byte{ + []byte("\r\nPassword: "), + } + testutils.ExprTester(t, errorCases, passwordExpression) +} + +func TestErrorPassword(t *testing.T) { + cases := [][]byte{ + []byte("\r\n% Authentication failed\r\n\r\n"), + } + testutils.ExprTester(t, cases, passwordErrorExpression) +} + +func TestQuestion(t *testing.T) { + errorCases := [][]byte{ + []byte("\r\nWarning: The current configuration will be written to the device. Continue? [Y/N]:"), + } + testutils.ExprTester(t, errorCases, questionExpression) +} From bbfde33eebb5f1f36c5c98d129191436523acafe Mon Sep 17 00:00:00 2001 From: Vadim Volovik Date: Wed, 19 Nov 2025 14:15:44 +0000 Subject: [PATCH 2/5] add cisco asa to pkg --- pkg/device/asa/asa.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/device/asa/asa.go b/pkg/device/asa/asa.go index f449d1a..77969bb 100644 --- a/pkg/device/asa/asa.go +++ b/pkg/device/asa/asa.go @@ -21,14 +21,14 @@ const ( `|ERROR: % Unrecognized command` + `|(\s+\^\n)?% Ambiguous command(: +\".+\"+| at .+)` + `|\r?% Permission denied for the role` + - `|\n?% ?Bad (OID|IP address or host name%[\ \w,]+)` + + `|\n?% ?Bad (OID|IP address|host name)(%[\ \w,]+)?` + `|\r?% This command is not authorized` + `|\r?% Failed to commit .+` + `|\r?% Specify .+` + `|^% Invalid input` + `|Permission denied.+\[Errno \d+\] Permission denied` + `)` - passwordExpression = `.*password:\s?$` + passwordExpression = `.*(?i)password:\s?$` passwordErrorExpression = `\n\% Authentication failed(\r\n|\n)` pagerExpression = `<--- More --->` ) From 0d9d065fde2f1022cd129fa43b0bd5eaa9fd95b1 Mon Sep 17 00:00:00 2001 From: Vadim Volovik Date: Wed, 19 Nov 2025 14:44:25 +0000 Subject: [PATCH 3/5] update --- pkg/device/asa/asa.go | 7 ++----- pkg/device/asa/asa_test.go | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/device/asa/asa.go b/pkg/device/asa/asa.go index 77969bb..8ff7044 100644 --- a/pkg/device/asa/asa.go +++ b/pkg/device/asa/asa.go @@ -1,7 +1,7 @@ /* Package cisco implements Cisco ASA CLI using genericcli. */ -package ciscoasa +package asa import ( "github.com/annetutil/gnetcli/pkg/cmd" @@ -34,10 +34,7 @@ const ( ) var autoCommands = []cmd.Cmd{ - cmd.NewCmd("terminal no monitor", cmd.WithErrorIgnore()), // ios, ios-xe - cmd.NewCmd("terminal monitor disable", cmd.WithErrorIgnore()), // ios xr - cmd.NewCmd("terminal length 0", cmd.WithErrorIgnore()), - cmd.NewCmd("terminal width 0", cmd.WithErrorIgnore()), + cmd.NewCmd("terminal no monitor", cmd.WithErrorIgnore()), cmd.NewCmd("enable", cmd.WithErrorIgnore(), cmd.WithAddAnswers(cmd.NewAnswerWithNL("Password: ", ""))), } diff --git a/pkg/device/asa/asa_test.go b/pkg/device/asa/asa_test.go index 848e497..22687b4 100644 --- a/pkg/device/asa/asa_test.go +++ b/pkg/device/asa/asa_test.go @@ -1,4 +1,4 @@ -package ciscoasa +package asa import ( "testing" From 6e087dfc8eab65cab85cf2ee86f6428f92e19951 Mon Sep 17 00:00:00 2001 From: vadvolo Date: Sun, 18 Jan 2026 14:44:51 +0000 Subject: [PATCH 4/5] add asa device into InitDefaultDeviceMapping --- pkg/devconf/devconf.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/devconf/devconf.go b/pkg/devconf/devconf.go index eb935e3..67d1cc5 100644 --- a/pkg/devconf/devconf.go +++ b/pkg/devconf/devconf.go @@ -2,6 +2,7 @@ package devconf import ( "fmt" + "github.com/annetutil/gnetcli/pkg/device/asa" "os" "regexp" "strings" @@ -233,6 +234,7 @@ func InitDefaultDeviceMapping(logger *zap.Logger) map[string]func(streamer.Conne "netconf": netconf.BindDeviceOpts(netconf.NewDevice, netconf.WithLogger(logger)), "aruos": GenericCLIWrapper(aruos.NewDevice, logger), "eltex": GenericCLIWrapper(eltex.NewDevice, logger), + "asa": GenericCLIWrapper(asa.NewDevice, logger), } return deviceMaps } From 4e7dfdc20cd090bd73ef635463bdb120fb0d3931 Mon Sep 17 00:00:00 2001 From: vadvolo Date: Tue, 27 Jan 2026 16:35:17 +0000 Subject: [PATCH 5/5] fix imports sorting --- pkg/devconf/devconf.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/devconf/devconf.go b/pkg/devconf/devconf.go index 67d1cc5..9245166 100644 --- a/pkg/devconf/devconf.go +++ b/pkg/devconf/devconf.go @@ -2,7 +2,6 @@ package devconf import ( "fmt" - "github.com/annetutil/gnetcli/pkg/device/asa" "os" "regexp" "strings" @@ -14,6 +13,7 @@ import ( "github.com/annetutil/gnetcli/pkg/device" "github.com/annetutil/gnetcli/pkg/device/arista" "github.com/annetutil/gnetcli/pkg/device/aruos" + "github.com/annetutil/gnetcli/pkg/device/asa" "github.com/annetutil/gnetcli/pkg/device/bcomos" "github.com/annetutil/gnetcli/pkg/device/cisco" "github.com/annetutil/gnetcli/pkg/device/eltex"