Skip to content

Commit

Permalink
Merge branch 'master' into docker-routeros
Browse files Browse the repository at this point in the history
  • Loading branch information
maksym-nazarenko committed Jun 22, 2024
2 parents d9531b2 + bcaea5c commit 9646ee8
Show file tree
Hide file tree
Showing 39 changed files with 1,447 additions and 394 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
go: ["1.18"]
os: [ubuntu-latest]
# Test against latest stable release, v6 beta and v7 beta
routeros: ["6.48.3", "6.49beta54"]
routeros: ["6.48.3", "6.49beta54", "7.1beta6"]
include:
- experimental: true
go: 1.18
Expand Down
12 changes: 8 additions & 4 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ func Marshal(c string, s interface{}) []string {
}

switch value.Kind() {
case reflect.Int:
intValue := elem.Field(i).Interface().(int)
cmd = append(cmd, fmt.Sprintf("=%s=%d", mikrotikPropName, intValue))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
cmd = append(cmd, fmt.Sprintf("=%s=%d", mikrotikPropName, elem.Field(i).Interface()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
cmd = append(cmd, fmt.Sprintf("=%s=%d", mikrotikPropName, elem.Field(i).Interface()))
case reflect.String:
stringValue := elem.Field(i).Interface().(string)
cmd = append(cmd, fmt.Sprintf("=%s=%s", mikrotikPropName, stringValue))
Expand Down Expand Up @@ -253,9 +254,12 @@ func parseStruct(v *reflect.Value, sentence proto.Sentence) {
case reflect.Bool:
b, _ := strconv.ParseBool(pair.Value)
field.SetBool(b)
case reflect.Int:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intValue, _ := strconv.Atoi(pair.Value)
field.SetInt(int64(intValue))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uintValue, _ := strconv.ParseUint(pair.Value, 10, 0)
field.SetUint(uint64(uintValue))
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions client/client_crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ func (client Mikrotik) Delete(d Resource) error {
cmd := []string{d.ActionToCommand(Delete), "=" + deleteField + "=" + deleteFieldValue}
log.Printf("[INFO] Running the mikrotik command: `%s`", cmd)
_, err = c.RunArgs(cmd)
if rosErr, ok := err.(*routeros.DeviceError); ok {
if rosErr.Sentence.Map["message"] == "no such item" {
return NewNotFound(rosErr.Sentence.Map["message"])
}
}
if eh, ok := d.(ErrorHandler); ok {
err = eh.HandleError(err)
}
Expand Down
86 changes: 84 additions & 2 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ func TestUnmarshal(t *testing.T) {
Name string
NotNamedOwner string `mikrotik:"owner"`
RunCount int `mikrotik:"run-count"`
Allowed bool
Schedule types.MikrotikList
RunCount8 int8 `mikrotik:"run-count8"`
RunCount16 int16 `mikrotik:"run-count16"`
RunCount32 int32 `mikrotik:"run-count32"`
RunCount64 int64 `mikrotik:"run-count64"`
CountUint uint `mikrotik:"run-count-uint"`
CountUint8 uint8 `mikrotik:"run-count-uint8"`
CountUint16 uint16 `mikrotik:"run-count-uint16"`
CountUint32 uint32 `mikrotik:"run-count-uint32"`
CountUint64 uint64 `mikrotik:"run-count-uint64"`

Allowed bool
Schedule types.MikrotikList
}

testCases := []struct {
Expand All @@ -43,6 +53,42 @@ func TestUnmarshal(t *testing.T) {
Key: "run-count",
Value: "3",
},
{
Key: "run-count8",
Value: "-3",
},
{
Key: "run-count16",
Value: "12000",
},
{
Key: "run-count32",
Value: "12000000",
},
{
Key: "run-count64",
Value: "12000000000000",
},
{
Key: "run-count-uint",
Value: "500",
},
{
Key: "run-count-uint8",
Value: "5",
},
{
Key: "run-count-uint16",
Value: "15000",
},
{
Key: "run-count-uint32",
Value: "15000000",
},
{
Key: "run-count-uint64",
Value: "15000000000000000",
},
{
Key: "allowed",
Value: "true",
Expand All @@ -55,6 +101,15 @@ func TestUnmarshal(t *testing.T) {
Name: "testing script",
NotNamedOwner: "admin",
RunCount: 3,
RunCount8: -3,
RunCount16: 12000,
RunCount32: 12000000,
RunCount64: 12000000000000,
CountUint: 500,
CountUint8: 5,
CountUint16: 15000,
CountUint32: 15000000,
CountUint64: 15000000000000000,
Allowed: true,
},
},
Expand Down Expand Up @@ -234,19 +289,46 @@ func TestMarshal(t *testing.T) {
Name string `mikrotik:"name"`
NotNamedOwner string `mikrotik:"owner,extraTagNotUsed"`
RunCount int `mikrotik:"run-count"`
RunCount8 int8 `mikrotik:"run-count8"`
RunCount16 int16 `mikrotik:"run-count16"`
RunCount32 int32 `mikrotik:"run-count32"`
RunCount64 int64 `mikrotik:"run-count64"`
CountUint uint `mikrotik:"run-count-uint"`
CountUint8 uint8 `mikrotik:"run-count-uint8"`
CountUint16 uint16 `mikrotik:"run-count-uint16"`
CountUint32 uint32 `mikrotik:"run-count-uint32"`
CountUint64 uint64 `mikrotik:"run-count-uint64"`
ReadOnlyProp bool `mikrotik:"read-only-prop,readonly"`
Allowed bool `mikrotik:"allowed-or-not"`
}{
Name: "test owner",
NotNamedOwner: "admin",
RunCount: 3,
RunCount8: 10,
RunCount16: 12000,
RunCount32: -12_000_000,
RunCount64: 12_000_000_000_000_000,
CountUint: 15000,
CountUint8: 250,
CountUint16: 15000,
CountUint32: 15_000_000,
CountUint64: 15_000_000_000_000_000,
Allowed: true,
},
expectedCmd: []string{
"/test/owner/add",
"=name=test owner",
"=owner=admin",
"=run-count=3",
"=run-count8=10",
"=run-count16=12000",
"=run-count32=-12000000",
"=run-count64=12000000000000000",
"=run-count-uint=15000",
"=run-count-uint8=250",
"=run-count-uint16=15000",
"=run-count-uint32=15000000",
"=run-count-uint64=15000000000000000",
"=allowed-or-not=yes",
},
},
Expand Down
62 changes: 62 additions & 0 deletions client/console-inspected/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package consoleinspected

import (
"fmt"
"strings"
)

// Parse parses definition of inspected console item and extracts items using splitStrategy.
//
// It returns console item struct with its subcommands, commands, arguments, etc.
func Parse(input string, splitStrategy ItemsDefinitionSplitStrategy) (ConsoleItem, error) {
chunks, err := splitStrategy.Split(input)
if err != nil {
return ConsoleItem{}, err
}

var result ConsoleItem
result.Self = Item{}

for _, v := range chunks {
item, err := parseItem(v)
if err != nil {
return ConsoleItem{}, err
}
if item.Type == TypeSelf {
result.Self = item
continue
}
switch t := item.NodeType; t {
case NodeTypeDir:
result.Subcommands = append(result.Subcommands, item.Name)
case NodeTypeArg:
result.Arguments = append(result.Arguments, item)
case NodeTypeCommand:
result.Commands = append(result.Commands, item.Name)
default:
return ConsoleItem{}, fmt.Errorf("unknown node type %q", t)
}
}

return result, nil
}

func parseItem(input string) (Item, error) {
result := Item{}
for _, v := range strings.Split(input, ";") {
if strings.TrimSpace(v) == "" {
continue
}
if strings.HasPrefix(v, "name=") {
result.Name = strings.TrimPrefix(v, "name=")
}
if strings.HasPrefix(v, "node-type=") {
result.NodeType = NodeType(strings.TrimPrefix(v, "node-type="))
}
if strings.HasPrefix(v, "type=") {
result.Type = Type(strings.TrimPrefix(v, "type="))
}
}

return result, nil
}
65 changes: 65 additions & 0 deletions client/console-inspected/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package consoleinspected

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestParse(t *testing.T) {
testCases := []struct {
name string
input string
expected ConsoleItem
expectedError bool
}{
{
name: "simple command",
input: "name=add;node-type=cmd;type=self;name=comment;node-type=arg;type=child;name=copy-from;node-type=arg;type=child;",
expected: ConsoleItem{
Self: Item{
Name: "add",
NodeType: NodeTypeCommand,
Type: TypeSelf,
},
Arguments: []Item{
{Name: "comment", NodeType: NodeTypeArg, Type: TypeChild},
{Name: "copy-from", NodeType: NodeTypeArg, Type: TypeChild},
},
},
},
{
name: "command with subcommands",
input: "name=list;node-type=dir;type=self;name=add;node-type=cmd;type=child;name=comment;node-type=cmd;type=child;name=edit;node-type=cmd;type=child;name=export;node-type=cmd;type=child;name=find;node-type=cmd;type=child;name=get;node-type=cmd;type=child;name=member;node-type=dir;type=child;name=print;node-type=cmd;type=child;name=remove;node-type=cmd;type=child;name=reset;node-type=cmd;type=child;name=set;node-type=cmd;type=child",
expected: ConsoleItem{
Self: Item{
Name: "list",
NodeType: NodeTypeDir,
Type: TypeSelf,
},
Commands: []string{
"add",
"comment",
"edit",
"export",
"find",
"get",
"print",
"remove",
"reset",
"set",
},
Subcommands: []string{"member"},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
item, err := Parse(tc.input, DefaultSplitStrategy)
if !assert.Equal(t, !tc.expectedError, err == nil) || tc.expectedError {
return
}
assert.Equal(t, tc.expected, item)
})
}
}
38 changes: 38 additions & 0 deletions client/console-inspected/split_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package consoleinspected

import "strings"

var DefaultSplitStrategy = splitStrategyFunc(orderedSplit)

type splitStrategyFunc func(string) ([]string, error)

func (f splitStrategyFunc) Split(in string) ([]string, error) {
return f(in)
}

// orderedSplit splits items definition using order of fields.
//
// Each 'name=' key starts a new item definition.
func orderedSplit(in string) ([]string, error) {
result := []string{}

buf := strings.Builder{}
for _, v := range strings.Split(in, ";") {
if strings.TrimSpace(v) == "" {
continue
}
if strings.HasPrefix(v, "name=") {
if buf.Len() > 0 {
result = append(result, buf.String())
}
buf.Reset()
}
buf.WriteString(v)
buf.WriteString(";")
}
if buf.Len() > 0 {
result = append(result, buf.String())
}

return result, nil
}
Loading

0 comments on commit 9646ee8

Please sign in to comment.