Skip to content

Commit

Permalink
query resource fields from remote system
Browse files Browse the repository at this point in the history
  • Loading branch information
maksym-nazarenko committed Nov 13, 2023
1 parent a78d341 commit ba99fef
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 16 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ type (

// Item represents inspected console items.
Item struct {
NodeType NodeType
Type Type
Name string
NodeType NodeType `mikrotik:"node-type"`
Type Type `mikrotik:"type"`
Name string `mikrotik:"name"`
}

// ConsoleItem represents inspected console item with extracted commands, arguments, etc.
Expand Down
42 changes: 42 additions & 0 deletions client/console_inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package client

import (
"strings"

consoleinspected "github.com/ddelnano/terraform-provider-mikrotik/client/console-inspected"
)

func (c Mikrotik) InspectConsoleCommand(command string) (consoleinspected.ConsoleItem, error) {
client, err := c.getMikrotikClient()
if err != nil {
return consoleinspected.ConsoleItem{}, err
}
normalizedCommand := strings.ReplaceAll(command[1:], "/", ",")
cmd := []string{"/console/inspect", "as-value", "=path=" + normalizedCommand, "=request=child"}
reply, err := client.RunArgs(cmd)
if err != nil {
return consoleinspected.ConsoleItem{}, err
}
var items []consoleinspected.Item
var result consoleinspected.ConsoleItem
if err := Unmarshal(*reply, &items); err != nil {
return consoleinspected.ConsoleItem{}, err
}

for _, v := range items {
if v.Type == consoleinspected.TypeSelf {
result.Self = v
continue
}
switch v.NodeType {
case consoleinspected.NodeTypeArg:
result.Arguments = append(result.Arguments, v)
case consoleinspected.NodeTypeCommand:
result.Commands = append(result.Commands, v.Name)
case consoleinspected.NodeTypeDir:
result.Subcommands = append(result.Subcommands, v.Name)
}
}

return result, nil
}
36 changes: 26 additions & 10 deletions cmd/mikrotik-codegen/internal/codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ where

`commandBase` - base path to craft commands for CRUD operations.

It is also possible to pre-fill list of fields using `-inspect-definition-file` (see [Experimental](#experimental) section)
It is also possible to pre-fill list of fields using either `-inspect-definition-file` argument
```sh
$ go run ./cmd/mikrotik-codegen mikrotik -name BridgeVlan -commandBase "/interface/bridge/vlan" -inspect-definition-file ./inspect_vlan.txt
```

or `-query-definition` flag (requires valid credentials in evironment)
```sh
$ go run ./cmd/mikrotik-codegen mikrotik -name BridgeVlan -commandBase "/interface/bridge/vlan" -query-definition
```
For details, see [Experimental](#experimental) section.

## Terraform resource
Just add a `codegen` tag key to struct fields:
```go
Expand Down Expand Up @@ -58,7 +64,7 @@ $ go run ./cmd/mikrotik-codegen terraform -src client/resource.go -struct Mikrot

This section contains documentation for experimental and non-stable features.

### Generate Mikrotik resource using /console/inspect definition file
### Generate Mikrotik resource using /console/inspect definition

Modern RouterOS versions (>7.x) provide new `/console/inspect` command to query hierarchy or syntax of particular command.

Expand Down Expand Up @@ -101,13 +107,15 @@ syntax set explanation 1 no Change item properties

Using that information, it is possible to query (even recursively) information about all menu items and sub-commands, starting from the root `/` command.

Since this feature is recent, trying to call it with our client package results in `terminal crush`, so fully integrating `/console/inspect` into codegen binary is left for the future releases.
:Warning: Since this feature is recent, trying to call it with our client package sometimes results in `terminal crush`.

In general, to pre-fill list of fields during code generation, one needs:
#### Using definition in file

For this case, one needs:
1. Machine-readable data about available fields
2. Pass this data as `-inspect-definition-file` argument.

For step #1, we'll use this command:
To get resource definition, run the following command on remote system:
```
$ :put [/console/inspect path=interface,list,add request=child as-value]
```
Expand All @@ -117,11 +125,7 @@ which produces:
name=add;node-type=cmd;type=self;name=comment;node-type=arg;type=child;name=copy-from;node-type=arg;type=child;name=exclude;node-type=arg;type=child;name=include;node-type=arg;type=child;name=name;node-type=arg;type=child
```

Note, that we used `interface,list,add` as argument to `path`. The terminal equivalent would be `/interface/list/add` (not sure why it works that way, you can check [forum topic](https://forum.mikrotik.com/viewtopic.php?t=199139#p1024410))

The reason we used `add` command and not `/interface/list` menu itself, is that we need only args (fields) of `add` command - not information about possible commands for `/interface/list`

If you have `ssh` access to the Mikrotik, the following command will be helpful to get this data:
If you have `ssh` access to the Mikrotik, the following command will produce the same string:
```shell
$ ssh -o Port=XXXX -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@board ":put [/console inspect as-value request=child path=interface,list,add]" > inspect_definition.txt
```
Expand All @@ -131,3 +135,15 @@ After getting the definition file, just generate Mikrotik resource as usual with
$ go run ./cmd/mikrotik-codegen mikrotik -name InterfaceList -commandBase "/interface/list" -inspect-definition-file ./inspect_definition.txt
```
and all fields for the struct will be created.


Note, that we used `interface,list,add` as argument to `path`. The terminal equivalent would be `/interface/list/add` (not sure why it works that way, you can check [forum topic](https://forum.mikrotik.com/viewtopic.php?t=199139#p1024410))

The reason we used `add` command and not `/interface/list` menu itself, is that we need only args (fields) of `add` command - not information about possible commands for `/interface/list`
#### Query resource definition automatically

To use this method, current environment must contain valid credentials.

```shell
$ go run cmd/mikrotik-codegen/main.go mikrotik -commandBase /ip/dhcp-server -name DhcpServer -query-definition
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"io"
"text/template"

consoleinspected "github.com/ddelnano/terraform-provider-mikrotik/cmd/mikrotik-codegen/internal/codegen/console-inspected"
consoleinspected "github.com/ddelnano/terraform-provider-mikrotik/client/console-inspected"
"github.com/ddelnano/terraform-provider-mikrotik/cmd/mikrotik-codegen/internal/utils"
)

Expand Down
21 changes: 19 additions & 2 deletions cmd/mikrotik-codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import (
"os"
"strconv"

"github.com/ddelnano/terraform-provider-mikrotik/client"
consoleinspected "github.com/ddelnano/terraform-provider-mikrotik/client/console-inspected"
"github.com/ddelnano/terraform-provider-mikrotik/cmd/mikrotik-codegen/internal/codegen"
consoleinspected "github.com/ddelnano/terraform-provider-mikrotik/cmd/mikrotik-codegen/internal/codegen/console-inspected"
)

type (
MikrotikConfiguration struct {
CommandBasePath string
ResourceName string
InspectDefinitionFile string
QueryDefinition bool
}

TerraformConfiguration struct {
Expand Down Expand Up @@ -105,9 +107,15 @@ func realMain(args []string) error {
fs.StringVar(&config.ResourceName, "name", "", "Name of the resource to generate.")
fs.StringVar(&config.CommandBasePath, "commandBase", "/", "The command base path in MikroTik.")
fs.StringVar(&config.InspectDefinitionFile, "inspect-definition-file", "",
"[EXPERIMENTAL] File with command definition. Ooutput of '/console/inspect' command (see README)")
"[EXPERIMENTAL] File with command definition. Conflicts with query-definition.")
fs.BoolVar(&config.QueryDefinition, "query-definition", false,
"[EXPERIMENTAL] Query remote MikroTik device to fetch resource fields. Conflicts with inspect-definition-file.")
_ = fs.Parse(args)

if config.InspectDefinitionFile != "" && config.QueryDefinition {
return errors.New("only one of inspect-definition-file or query-definition can be used")
}

consoleCommandDefinition := consoleinspected.ConsoleItem{}
if config.InspectDefinitionFile != "" {
fileBytes, err := os.ReadFile(config.InspectDefinitionFile)
Expand All @@ -119,8 +127,17 @@ func realMain(args []string) error {
if err != nil {
return err
}
}

if config.QueryDefinition {
var err error
c := client.NewClient(client.GetConfigFromEnv())
consoleCommandDefinition, err = c.InspectConsoleCommand(config.CommandBasePath + "/add")
if err != nil {
return err
}
}

generator = func() GeneratorFunc {
return func(w io.Writer) error {
return codegen.GenerateMikrotikResource(config.ResourceName, config.CommandBasePath, consoleCommandDefinition, w)
Expand Down

0 comments on commit ba99fef

Please sign in to comment.