From ba99fefebac4170ea0b1afbca60db91361165f8d Mon Sep 17 00:00:00 2001 From: Maksym Nazarenko Date: Mon, 13 Nov 2023 23:44:07 +0200 Subject: [PATCH] query resource fields from remote system --- .../console-inspected/parse.go | 0 .../console-inspected/parse_test.go | 0 .../console-inspected/split_strategy.go | 0 .../console-inspected/types.go | 6 +-- client/console_inspect.go | 42 +++++++++++++++++++ .../internal/codegen/README.md | 36 +++++++++++----- .../internal/codegen/generator_mikrotik.go | 2 +- cmd/mikrotik-codegen/main.go | 21 +++++++++- 8 files changed, 91 insertions(+), 16 deletions(-) rename {cmd/mikrotik-codegen/internal/codegen => client}/console-inspected/parse.go (100%) rename {cmd/mikrotik-codegen/internal/codegen => client}/console-inspected/parse_test.go (100%) rename {cmd/mikrotik-codegen/internal/codegen => client}/console-inspected/split_strategy.go (100%) rename {cmd/mikrotik-codegen/internal/codegen => client}/console-inspected/types.go (93%) create mode 100644 client/console_inspect.go diff --git a/cmd/mikrotik-codegen/internal/codegen/console-inspected/parse.go b/client/console-inspected/parse.go similarity index 100% rename from cmd/mikrotik-codegen/internal/codegen/console-inspected/parse.go rename to client/console-inspected/parse.go diff --git a/cmd/mikrotik-codegen/internal/codegen/console-inspected/parse_test.go b/client/console-inspected/parse_test.go similarity index 100% rename from cmd/mikrotik-codegen/internal/codegen/console-inspected/parse_test.go rename to client/console-inspected/parse_test.go diff --git a/cmd/mikrotik-codegen/internal/codegen/console-inspected/split_strategy.go b/client/console-inspected/split_strategy.go similarity index 100% rename from cmd/mikrotik-codegen/internal/codegen/console-inspected/split_strategy.go rename to client/console-inspected/split_strategy.go diff --git a/cmd/mikrotik-codegen/internal/codegen/console-inspected/types.go b/client/console-inspected/types.go similarity index 93% rename from cmd/mikrotik-codegen/internal/codegen/console-inspected/types.go rename to client/console-inspected/types.go index d190bef..ba15f07 100644 --- a/cmd/mikrotik-codegen/internal/codegen/console-inspected/types.go +++ b/client/console-inspected/types.go @@ -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. diff --git a/client/console_inspect.go b/client/console_inspect.go new file mode 100644 index 0000000..d0063ed --- /dev/null +++ b/client/console_inspect.go @@ -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 +} diff --git a/cmd/mikrotik-codegen/internal/codegen/README.md b/cmd/mikrotik-codegen/internal/codegen/README.md index 88da584..f1d719e 100644 --- a/cmd/mikrotik-codegen/internal/codegen/README.md +++ b/cmd/mikrotik-codegen/internal/codegen/README.md @@ -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 @@ -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. @@ -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] ``` @@ -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 ``` @@ -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 +``` diff --git a/cmd/mikrotik-codegen/internal/codegen/generator_mikrotik.go b/cmd/mikrotik-codegen/internal/codegen/generator_mikrotik.go index 1c8b666..5cd9201 100644 --- a/cmd/mikrotik-codegen/internal/codegen/generator_mikrotik.go +++ b/cmd/mikrotik-codegen/internal/codegen/generator_mikrotik.go @@ -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" ) diff --git a/cmd/mikrotik-codegen/main.go b/cmd/mikrotik-codegen/main.go index 76bf2de..bb00307 100644 --- a/cmd/mikrotik-codegen/main.go +++ b/cmd/mikrotik-codegen/main.go @@ -10,8 +10,9 @@ 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 ( @@ -19,6 +20,7 @@ type ( CommandBasePath string ResourceName string InspectDefinitionFile string + QueryDefinition bool } TerraformConfiguration struct { @@ -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) @@ -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)