From 6b8cfd246d893abefc88ee3933e039f7cf1de508 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 3 Nov 2023 00:13:40 +0300 Subject: [PATCH 01/16] feat: Add MLAG settings Fixes #268 --- .../resources/routeros_bridge_mlag/import.sh | 1 + .../routeros_bridge_mlag/resource.tf | 4 ++ routeros/provider.go | 1 + .../resource_interface_bridge_mlag _test.go | 9 ++++ routeros/resource_interface_bridge_mlag.go | 47 +++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 examples/resources/routeros_bridge_mlag/import.sh create mode 100644 examples/resources/routeros_bridge_mlag/resource.tf create mode 100644 routeros/resource_interface_bridge_mlag _test.go create mode 100644 routeros/resource_interface_bridge_mlag.go diff --git a/examples/resources/routeros_bridge_mlag/import.sh b/examples/resources/routeros_bridge_mlag/import.sh new file mode 100644 index 00000000..62c5831b --- /dev/null +++ b/examples/resources/routeros_bridge_mlag/import.sh @@ -0,0 +1 @@ +terraform import routeros_ip_cloud.test . diff --git a/examples/resources/routeros_bridge_mlag/resource.tf b/examples/resources/routeros_bridge_mlag/resource.tf new file mode 100644 index 00000000..27c7f7e4 --- /dev/null +++ b/examples/resources/routeros_bridge_mlag/resource.tf @@ -0,0 +1,4 @@ +resource "routeros_bridge_mlag" "mlag" { + bridge = "bridge1" + peer_port = "stack-link" +} \ No newline at end of file diff --git a/routeros/provider.go b/routeros/provider.go index 1f8b9e9e..42be79d9 100644 --- a/routeros/provider.go +++ b/routeros/provider.go @@ -119,6 +119,7 @@ func Provider() *schema.Provider { // Aliases for interface objects to retain compatibility between original and fork "routeros_bridge": ResourceInterfaceBridge(), + "routeros_bridge_mlag": ResourceInterfaceBridgeMlag(), "routeros_bridge_port": ResourceInterfaceBridgePort(), "routeros_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_gre": ResourceInterfaceGre(), diff --git a/routeros/resource_interface_bridge_mlag _test.go b/routeros/resource_interface_bridge_mlag _test.go new file mode 100644 index 00000000..81719388 --- /dev/null +++ b/routeros/resource_interface_bridge_mlag _test.go @@ -0,0 +1,9 @@ +package routeros + +import ( + "testing" +) + +func TestAccInterfaceBridgeMlagTest_basic(t *testing.T) { + t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") +} \ No newline at end of file diff --git a/routeros/resource_interface_bridge_mlag.go b/routeros/resource_interface_bridge_mlag.go new file mode 100644 index 00000000..6cc379f1 --- /dev/null +++ b/routeros/resource_interface_bridge_mlag.go @@ -0,0 +1,47 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +/* +{ + "bridge":"bridge1", + "peer-port":"stack-link" +} +*/ + +// https://help.mikrotik.com/docs/display/ROS/Multi-chassis+Link+Aggregation+Group +func ResourceInterfaceBridgeMlag() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/bridge/mlag"), + MetaId: PropId(Id), + + "bridge": { + Type: schema.TypeString, + Required: true, + Description: "The bridge interface where MLAG is being created.", + }, + "peer_port": { + Type: schema.TypeString, + Required: true, + Description: "An interface that will be used as a peer port. Both peer devices are using inter-chassis " + + "communication over these peer ports to establish MLAG and update the host table. Peer port should be " + + "isolated on a different untagged VLAN using a pvid setting. Peer port can be configured as a bonding " + + "interface.", + }, + } + + return &schema.Resource{ + CreateContext: DefaultSystemCreate(resSchema), + ReadContext: DefaultSystemRead(resSchema), + UpdateContext: DefaultSystemUpdate(resSchema), + DeleteContext: DefaultSystemDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} From 162c1da0233e2e909d98dd02f011e89513233c9a Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 3 Nov 2023 00:15:11 +0300 Subject: [PATCH 02/16] feat: Add ethernet switch settings Fixes #285 #282 --- .../import.sh | 3 + .../resource.tf | 4 + routeros/provider.go | 1 + .../resource_interface_ethernet_switch.go | 203 ++++++++++++++++++ ...resource_interface_ethernet_switch_test.go | 9 + 5 files changed, 220 insertions(+) create mode 100644 examples/resources/routeros_interface_ethernet_switch/import.sh create mode 100644 examples/resources/routeros_interface_ethernet_switch/resource.tf create mode 100644 routeros/resource_interface_ethernet_switch.go create mode 100644 routeros/resource_interface_ethernet_switch_test.go diff --git a/examples/resources/routeros_interface_ethernet_switch/import.sh b/examples/resources/routeros_interface_ethernet_switch/import.sh new file mode 100644 index 00000000..53a5fdc3 --- /dev/null +++ b/examples/resources/routeros_interface_ethernet_switch/import.sh @@ -0,0 +1,3 @@ +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/ethernet/switch get [print show-ids]] +terraform import routeros_interface_ethernet_switch.sw0 *0 diff --git a/examples/resources/routeros_interface_ethernet_switch/resource.tf b/examples/resources/routeros_interface_ethernet_switch/resource.tf new file mode 100644 index 00000000..0d1b3429 --- /dev/null +++ b/examples/resources/routeros_interface_ethernet_switch/resource.tf @@ -0,0 +1,4 @@ +resource "routeros_interface_ethernet_switch" "sw0" { + switch_id = 0 # Optional + name = "new switch" +} \ No newline at end of file diff --git a/routeros/provider.go b/routeros/provider.go index 42be79d9..4948acee 100644 --- a/routeros/provider.go +++ b/routeros/provider.go @@ -104,6 +104,7 @@ func Provider() *schema.Provider { "routeros_interface_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_interface_bridge_settings": ResourceInterfaceBridgeSettings(), "routeros_interface_eoip": ResourceInterfaceEoip(), + "routeros_interface_ethernet_switch": ResourceInterfaceEthernetSwitch(), "routeros_interface_gre": ResourceInterfaceGre(), "routeros_interface_vlan": ResourceInterfaceVlan(), "routeros_interface_vrrp": ResourceInterfaceVrrp(), diff --git a/routeros/resource_interface_ethernet_switch.go b/routeros/resource_interface_ethernet_switch.go new file mode 100644 index 00000000..a8ef2262 --- /dev/null +++ b/routeros/resource_interface_ethernet_switch.go @@ -0,0 +1,203 @@ +package routeros + +import ( + "context" + "fmt" + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* +{ + ".id":"*0", + "cpu-flow-control":"true", + "driver-rx-byte":"47500186966", + "driver-rx-packet":"107352097", + "driver-tx-byte":"202118920815", + "driver-tx-packet":"175081916", + "invalid":"false", + "mirror-source":"none", + "mirror-target":"none", + "name":"switch1", + "rx-align-error":"0", + "rx-broadcast":"1049998", + "rx-bytes":"55163404001", + "rx-carrier-error":"0", + "rx-code-error":"0", + "rx-control":"0", + "rx-drop":"0", + "rx-fcs-error":"0", + "rx-fragment":"0", + "rx-jabber":"0", + "rx-length-error":"0", + "rx-multicast":"5101395", + "rx-packet":"145932800", + "rx-pause":"0", + "rx-too-long":"75", + "rx-too-short":"0", + "rx-unknown-op":"0", + "tx-broadcast":"276405", + "tx-bytes":"202693687261", + "tx-control":"0", + "tx-deferred":"0", + "tx-drop":"0", + "tx-excessive-collision":"0", + "tx-excessive-deferred":"0", + "tx-fcs-error":"0", + "tx-fragment":"0", + "tx-jabber":"0", + "tx-late-collision":"0", + "tx-multicast":"737076", + "tx-multiple-collision":"0", + "tx-packet":"175076294", + "tx-pause":"0", + "tx-rx-1024-1518":"169188042", + "tx-rx-128-255":"45589148", + "tx-rx-1519-max":"0", + "tx-rx-256-511":"4444935", + "tx-rx-512-1023":"3890955", + "tx-rx-64":"5245202", + "tx-rx-65-127":"92433137", + "tx-single-collision":"0", + "tx-too-long":"218648", + "tx-too-short":"0", + "tx-total-collision":"0", + "type":"QCA-8337" +} + +[=Oo=@mikrotik]/interface/ethernet/switch/print detail +Flags: I - invalid + 0 name="switch1" type=QCA-8337 mirror-source=none mirror-target=none mirror-egress-target=none cpu-flow-control=yes l3-hw-offloading=no + +*/ + +// https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-CPUFlowControl +// https://help.mikrotik.com/docs/display/ROS/L3+Hardware+Offloading +func ResourceInterfaceEthernetSwitch() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/ethernet/switch"), + MetaId: PropId(Id), + MetaSkipFields: PropSkipFields(`"switch_id","driver_rx_byte","driver_rx_packet","driver_tx_byte","driver_tx_packet",` + + `"rx_align_error","rx_broadcast","rx_bytes","rx_carrier_error","rx_code_error","rx_control","rx_drop",` + + `"rx_fcs_error","rx_fragment","rx_jabber","rx_length_error","rx_multicast","rx_packet","rx_pause",` + + `"rx_too_long","rx_too_short","rx_unknown_op","tx_broadcast","tx_bytes","tx_control","tx_deferred",` + + `"tx_drop","tx_excessive_collision","tx_excessive_deferred","tx_fcs_error","tx_fragment","tx_jabber",` + + `"tx_late_collision","tx_multicast","tx_multiple_collision","tx_packet","tx_pause","tx_rx_1024_1518",` + + `"tx_rx_128_255","tx_rx_1519_max","tx_rx_256_511","tx_rx_512_1023","tx_rx_64","tx_rx_65_127",` + + `"tx_single_collision","tx_too_long","tx_too_short","tx_total_collision"`), + + "cpu_flow_control": { + Type: schema.TypeBool, + Optional: true, + Description: "All switch chips have a special port that is called switchX-cpu, this is the CPU port for a " + + "switch chip, it is meant to forward traffic from a switch chip to the CPU, such a port is required " + + "for management traffic and for routing features. By default the switch chip ensures that this " + + "special CPU port is not congested and sends out Pause Frames when link capacity is exceeded to make " + + "sure the port is not oversaturated, this feature is called CPU Flow Control. Without this feature " + + "packets that might be crucial for routing or management purposes might get dropped.", + }, + KeyInvalid: PropInvalidRo, + "l3_hw_offloading": { + Type: schema.TypeBool, + Optional: true, + Description: "Layer 3 Hardware Offloading (L3HW, otherwise known as IP switching or HW routing) allows to " + + "offload some router features onto the switch chip. This allows reaching wire speeds when routing " + + "packets, which simply would not be possible with the CPU.", + }, + // "mirror_egress_target": { + // Type: schema.TypeString, + // Optional: true, + // Default: "none", + // Description: "Selects a single mirroring egress target port, only available on 88E6393X, 88E6191X and " + + // "88E6190 switch chips. Mirrored packets from mirror-egress (see the property in port menu) will be " + + // "sent to the selected port.", + // }, + "mirror_source": { + Type: schema.TypeString, + Optional: true, + Default: "none", + Description: "Selects a single mirroring source port. Ingress and egress traffic will be sent to the " + + "mirror-target port. Note that mirror-target port has to belong to the same switch (see which port " + + "belongs to which switch in /interface ethernet menu).", + }, + "mirror_target": { + Type: schema.TypeString, + Optional: true, + Default: "none", + Description: "Selects a single mirroring target port. Mirrored packets from mirror-source and mirror " + + "(see the property in rule and host table) will be sent to the selected port.", + }, + KeyName: PropName("Name of the switch."), + "switch_id": { + Type: schema.TypeString, + Optional: true, + Default: "*0", + Description: "Switch-chip id. Default .id = *0", + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^\*\d+$`), + "The string must contain an identifier in MikroTik format: '*0'"), + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Switch-chip type.", + }, + } + + resRead := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + metadata := GetMetadata(resSchema) + + res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) + if err != nil { + ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgGet, err)) + return diag.FromErr(err) + } + + // Resource not found. + if len(*res) == 0 { + d.SetId("") + return nil + } + + id := (*res)[0].GetID(metadata.IdType) + d.SetId(id) + d.Set("switch_id", id) + + return MikrotikResourceDataToTerraform((*res)[0], resSchema, d) + } + + resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + if d.Id() == "" { + d.SetId(d.Get("switch_id").(string)) + } + item, metadata := TerraformResourceDataToMikrotik(resSchema, d) + + var resUrl string + if m.(Client).GetTransport() == TransportREST { + resUrl = "/set" + } + item[".id"] = d.Id() + + err := m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) + if err != nil { + return diag.FromErr(err) + } + + return resRead(ctx, d, m) + } + + return &schema.Resource{ + CreateContext: resCreateUpdate, + ReadContext: resRead, + UpdateContext: resCreateUpdate, + DeleteContext: DefaultSystemDelete(resSchema), + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_interface_ethernet_switch_test.go b/routeros/resource_interface_ethernet_switch_test.go new file mode 100644 index 00000000..0bdb67aa --- /dev/null +++ b/routeros/resource_interface_ethernet_switch_test.go @@ -0,0 +1,9 @@ +package routeros + +import ( + "testing" +) + +func TestAccInterfaceEthernetSwitchTest_basic(t *testing.T) { + t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") +} \ No newline at end of file From 7916c30fcf36899631dfb25a362c378dc3ebbac4 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Fri, 3 Nov 2023 00:16:14 +0300 Subject: [PATCH 03/16] fix(eoip): Fix the resource ID type --- routeros/resource_interface_eoip.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routeros/resource_interface_eoip.go b/routeros/resource_interface_eoip.go index b480fbfb..4686335c 100644 --- a/routeros/resource_interface_eoip.go +++ b/routeros/resource_interface_eoip.go @@ -9,7 +9,7 @@ import ( func ResourceInterfaceEoip() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/eoip"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyArp: PropArpRw, From d5e8f859f3e2a786f96788f3e110565e2b5c9ea9 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 20:48:11 +0300 Subject: [PATCH 04/16] Add logging function with coloring by levels --- routeros/log.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/routeros/log.go b/routeros/log.go index e9a4c659..f6054554 100644 --- a/routeros/log.go +++ b/routeros/log.go @@ -2,9 +2,20 @@ package routeros import ( "context" + "os" + "github.com/fatih/color" "github.com/hashicorp/terraform-plugin-log/tflog" - "os" +) + +type logLevel byte + +const ( + TRACE logLevel = 1 + iota + DEBUG + INFO + WARN + ERROR ) // ColorizedDebug Used to display provider log color messages. @@ -15,3 +26,21 @@ func ColorizedDebug(ctx context.Context, msg string, args ...map[string]interfac } tflog.Debug(ctx, color.GreenString(msg), args...) } + +func ColorizedMessage(ctx context.Context, level logLevel, msg string, args ...map[string]interface{}) { + if _, set := os.LookupEnv("ROS_LOG_COLOR"); set { + color.NoColor = false + } + switch level { + case TRACE: + tflog.Trace(ctx, color.GreenString(msg), args...) + case DEBUG: + tflog.Debug(ctx, color.GreenString(msg), args...) + case INFO: + tflog.Info(ctx, color.HiBlueString(msg), args...) + case WARN: + tflog.Warn(ctx, color.HiRedString(msg), args...) + case ERROR: + tflog.Error(ctx, color.HiRedString(msg), args...) + } +} From 9d7dd8d4e1250efaea1b98a5fd3ddf1b70c19cb4 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 20:51:58 +0300 Subject: [PATCH 05/16] Add state migration function For resources with the "Name" identifier --- routeros/provider_resource_state_migration.go | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 routeros/provider_resource_state_migration.go diff --git a/routeros/provider_resource_state_migration.go b/routeros/provider_resource_state_migration.go new file mode 100644 index 00000000..aecbf914 --- /dev/null +++ b/routeros/provider_resource_state_migration.go @@ -0,0 +1,35 @@ +package routeros + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func stateMigrationNameToId(resourcePath string) schema.StateUpgradeFunc { + return func(ctx context.Context, rawState map[string]interface{}, m interface{}) (map[string]interface{}, error) { + ColorizedMessage(ctx, INFO, fmt.Sprintf("ID attribute before migration: %#v", rawState["id"])) + + if rawState["id"] != nil { + res, err := ReadItems(&ItemId{Name, rawState["id"].(string)}, resourcePath, m.(Client)) + if err != nil { + return nil, err + } + + // Resource not found. + if len(*res) == 0 { + rawState["id"] = "" + ColorizedMessage(ctx, WARN, "No resource found, but the scheme has been updated.", + map[string]interface{}{"path": resourcePath, "id": rawState["id"]}) + return rawState, nil + } + + rawState["id"] = (*res)[0].GetID(Id) + } + + ColorizedMessage(ctx, INFO, fmt.Sprintf("ID attribute after migration: %#v", rawState["id"])) + + return rawState, nil + } +} From fdbd68f21f2ef5284b0e8529095a4cb5a3a76067 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 20:53:31 +0300 Subject: [PATCH 06/16] fix(eoip): Add Name-Id migration --- .../routeros_interface_eoip/import.sh | 5 ++- routeros/resource_interface_eoip.go | 9 ++++ routeros/resource_interface_eoip_v0.go | 43 +++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 routeros/resource_interface_eoip_v0.go diff --git a/examples/resources/routeros_interface_eoip/import.sh b/examples/resources/routeros_interface_eoip/import.sh index 733a2b71..bba87b94 100644 --- a/examples/resources/routeros_interface_eoip/import.sh +++ b/examples/resources/routeros_interface_eoip/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the EoIP interface in case of the example, use `eoip-tunnel1` -terraform import routeros_interface_eoip.eoip_tunnel1 eoip-tunnel1 \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/eoip get [print show-ids]] +terraform import routeros_interface_eoip.eoip_tunnel1 *B \ No newline at end of file diff --git a/routeros/resource_interface_eoip.go b/routeros/resource_interface_eoip.go index 4686335c..2936a81f 100644 --- a/routeros/resource_interface_eoip.go +++ b/routeros/resource_interface_eoip.go @@ -57,6 +57,15 @@ func ResourceInterfaceEoip() *schema.Resource { }), DeleteContext: DefaultDelete(resSchema), + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceEoipV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, diff --git a/routeros/resource_interface_eoip_v0.go b/routeros/resource_interface_eoip_v0.go new file mode 100644 index 00000000..07a2c171 --- /dev/null +++ b/routeros/resource_interface_eoip_v0.go @@ -0,0 +1,43 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceInterfaceEoipV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/eoip"), + MetaId: PropId(Name), + + KeyActualMtu: PropActualMtuRo, + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + KeyAllowFastPath: PropAllowFastPathRw, + KeyClampTcpMss: PropClampTcpMssRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyDontFragment: PropDontFragmentRw, + KeyDscp: PropDscpRw, + KeyIpsecSecret: PropIpsecSecretRw, + KeyKeepalive: PropKeepaliveRw, + KeyL2Mtu: PropL2MtuRo, + KeyLocalAddress: PropLocalAddressRw, + KeyLoopProtect: PropLoopProtectRw, + KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, + KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, + KeyLoopProtectStatus: PropLoopProtectStatusRo, + KeyMacAddress: PropMacAddressRo, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRemoteAddress: PropRemoteAddressRw, + KeyRunning: PropRunningRo, + "tunnel_id": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + Description: "Unique tunnel identifier, which must match the other side of the tunnel.", + }, + }, + } +} From 5d0effa4d24365671dcbf268da66460b4483c25f Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 20:55:20 +0300 Subject: [PATCH 07/16] fix(CAPsMAN): Add Name-Id migration --- .../routeros_capsman_channel/import.sh | 5 +- .../routeros_capsman_configuration/import.sh | 5 +- .../routeros_capsman_datapath/import.sh | 5 +- .../routeros_capsman_rates/import.sh | 5 +- .../routeros_capsman_security/import.sh | 5 +- routeros/resource_capsman_channel.go | 11 +- routeros/resource_capsman_channel_v0.go | 85 +++++++++ routeros/resource_capsman_configuration.go | 11 +- routeros/resource_capsman_configuration_v0.go | 175 ++++++++++++++++++ routeros/resource_capsman_datapath.go | 11 +- routeros/resource_capsman_datapath_v0.go | 87 +++++++++ routeros/resource_capsman_rates.go | 11 +- routeros/resource_capsman_rates_v0.go | 88 +++++++++ routeros/resource_capsman_security.go | 11 +- routeros/resource_capsman_security_v0.go | 95 ++++++++++ 15 files changed, 595 insertions(+), 15 deletions(-) create mode 100644 routeros/resource_capsman_channel_v0.go create mode 100644 routeros/resource_capsman_configuration_v0.go create mode 100644 routeros/resource_capsman_datapath_v0.go create mode 100644 routeros/resource_capsman_rates_v0.go create mode 100644 routeros/resource_capsman_security_v0.go diff --git a/examples/resources/routeros_capsman_channel/import.sh b/examples/resources/routeros_capsman_channel/import.sh index d82e01f1..db915ac1 100644 --- a/examples/resources/routeros_capsman_channel/import.sh +++ b/examples/resources/routeros_capsman_channel/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the CAPsMAN channel configuration in case of the example use test-channel-config -terraform import routeros_capsman_channel.test_channel test-channel-config +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/caps-man/channel get [print show-ids]] +terraform import routeros_capsman_channel.test_channel "*1" \ No newline at end of file diff --git a/examples/resources/routeros_capsman_configuration/import.sh b/examples/resources/routeros_capsman_configuration/import.sh index c21845c8..718ae49c 100644 --- a/examples/resources/routeros_capsman_configuration/import.sh +++ b/examples/resources/routeros_capsman_configuration/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the CAPsMAN configuration in case of the example use test_configuration_name -terraform import routeros_capsman_configuration.test_configuration_2 test_configuration_name \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/caps-man/configuration get [print show-ids]] +terraform import routeros_capsman_configuration.test_configuration_2 "*1" \ No newline at end of file diff --git a/examples/resources/routeros_capsman_datapath/import.sh b/examples/resources/routeros_capsman_datapath/import.sh index 2b100d01..dc061d97 100644 --- a/examples/resources/routeros_capsman_datapath/import.sh +++ b/examples/resources/routeros_capsman_datapath/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the CAPsMAN datapath configuration in case of the example use test-datapath-config -terraform import routeros_capsman_datapath.test_datapath test-datapath-config +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/caps-man/datapath get [print show-ids]] +terraform import routeros_capsman_datapath.test_datapath "*1" diff --git a/examples/resources/routeros_capsman_rates/import.sh b/examples/resources/routeros_capsman_rates/import.sh index 30ff0d4d..6d052134 100644 --- a/examples/resources/routeros_capsman_rates/import.sh +++ b/examples/resources/routeros_capsman_rates/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the CAPsMAN rates configuration in case of the example use test-rates-config -terraform import routeros_capsman_rates.test_rates test-rates-config +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/caps-man/rates get [print show-ids]] +terraform import routeros_capsman_rates.test_rates "*1" \ No newline at end of file diff --git a/examples/resources/routeros_capsman_security/import.sh b/examples/resources/routeros_capsman_security/import.sh index a741c40e..0c689ed2 100644 --- a/examples/resources/routeros_capsman_security/import.sh +++ b/examples/resources/routeros_capsman_security/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the CAPsMAN security profile in case of the example use test-security-config -terraform import routeros_capsman_security.test_security test-security-config +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/caps-man/security get [print show-ids]] +terraform import routeros_capsman_security.test_security "*1" \ No newline at end of file diff --git a/routeros/resource_capsman_channel.go b/routeros/resource_capsman_channel.go index fc8ad3e5..a3e9e5c0 100644 --- a/routeros/resource_capsman_channel.go +++ b/routeros/resource_capsman_channel.go @@ -26,7 +26,7 @@ func ResourceCapsManChannel() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/channel"), - MetaId: PropId(Name), + MetaId: PropId(Id), "band": { Type: schema.TypeString, @@ -109,6 +109,15 @@ func ResourceCapsManChannel() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceCapsManChannelV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_capsman_channel_v0.go b/routeros/resource_capsman_channel_v0.go new file mode 100644 index 00000000..48d5e169 --- /dev/null +++ b/routeros/resource_capsman_channel_v0.go @@ -0,0 +1,85 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceCapsManChannelV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/caps-man/channel"), + MetaId: PropId(Name), + + "band": { + Type: schema.TypeString, + Optional: true, + Description: "Define operational radio frequency band and mode taken from hardware capability of wireless card.", + ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-g/n", "2ghz-onlyg", "2ghz-onlyn", + "5ghz-a", "5ghz-a/n", "5ghz-a/n/ac", "5ghz-n/ac", "5ghz-onlyac", "5ghz-onlyn"}, false), + }, + KeyComment: PropCommentRw, + "control_channel_width": { + Type: schema.TypeString, + Optional: true, + Description: "Control channel width.", + ValidateFunc: validation.StringInSlice([]string{"5mhz", "10mhz", "20mhz", "40mhz-turbo"}, false), + }, + "extension_channel": { + Type: schema.TypeString, + Optional: true, + Description: "Extension channel configuration. (E.g. Ce = extension channel is above Control channel, " + + "eC = extension channel is below Control channel)", + ValidateFunc: validation.StringInSlice([]string{"Ce", "Ceee", "Ceeeeeee", "eC", "eCee", "eCeeeeee", + "eeCe", "eeCeeeee", "eeeC", "eeeCeeee", "eeeeCeee", "eeeeeCee", "eeeeeeCe", "eeeeeeeC", + "xx", "xxxx", "xxxxxxxx", "disabled"}, false), + }, + "frequency": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: "Channel frequency value in MHz on which AP will operate. If left blank, CAPsMAN will " + + "automatically determine the best frequency that is least occupied.", + }, + KeyName: PropNameForceNewRw, + "reselect_interval": { + Type: schema.TypeString, + Optional: true, + Description: "The interval after which the least occupied frequency is chosen, can be defined as a random " + + "interval, ex. as '30m..60m'. Works only if channel.frequency is left blank.", + // We may need to write a custom DiffSuppressFunc. + // DiffSuppressFunc: TimeEquall, not for time ranges + }, + "save_selected": { + Type: schema.TypeBool, + Optional: true, + Description: "If channel frequency is chosen automatically and channel.reselect-interval is used, then " + + "saves the last picked frequency.", + }, + "secondary_frequency": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the second frequency that will be used for 80+80MHz configuration. " + + "Set it to Disabled in order to disable 80+80MHz capability.", + }, + "skip_dfs_channels": { + Type: schema.TypeBool, + Optional: true, + Description: "If channel.frequency is left blank, the selection will skip DFS channels.", + }, + "tx_power": { + Type: schema.TypeInt, + Optional: true, + Description: "TX Power for CAP interface (for the whole interface not for individual chains) in dBm. " + + "It is not possible to set higher than allowed by country regulations or interface. By " + + "default max allowed by country or interface is used.", + ValidateFunc: validation.IntBetween(-30, 40), + }, + "width": { + Type: schema.TypeString, + Optional: true, + Description: "Channel Width in MHz.", + }, + }, + } +} diff --git a/routeros/resource_capsman_configuration.go b/routeros/resource_capsman_configuration.go index ceb81dc2..e27ccaf5 100644 --- a/routeros/resource_capsman_configuration.go +++ b/routeros/resource_capsman_configuration.go @@ -38,7 +38,7 @@ import ( func ResourceCapsManConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/configuration"), - MetaId: PropId(Name), + MetaId: PropId(Id), MetaTransformSet: PropTransformSet(`"channel": "channel.config", "datapath": "datapath.config", "rates": "rates.config", "security": "security.config"`), @@ -211,6 +211,15 @@ func ResourceCapsManConfiguration() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceCapsManConfigurationV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_capsman_configuration_v0.go b/routeros/resource_capsman_configuration_v0.go new file mode 100644 index 00000000..98b28ad3 --- /dev/null +++ b/routeros/resource_capsman_configuration_v0.go @@ -0,0 +1,175 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceCapsManConfigurationV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/caps-man/configuration"), + MetaId: PropId(Name), + MetaTransformSet: PropTransformSet(`"channel": "channel.config", "datapath": "datapath.config", + "rates": "rates.config", "security": "security.config"`), + + "channel": { + Type: schema.TypeMap, + Optional: true, + Description: "Channel inline settings.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + KeyComment: PropCommentRw, + "country": { + Type: schema.TypeString, + Optional: true, + Description: "Limits available bands, frequencies and maximum transmit power for each frequency. Also " + + "specifies default value of scan-list. Value no_country_set is an FCC compliant set of channels.", + }, + "datapath": { + Type: schema.TypeMap, + Optional: true, + Description: "Datapath inline settings.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "disconnect_timeout": { + Type: schema.TypeString, + Optional: true, + Description: "This interval is measured from third sending failure on the lowest data rate. At this point " + + "3 * (hw-retries + 1) frame transmits on the lowest data rate had failed. During disconnect-timeout packet " + + "transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully " + + `during disconnect-timeout, the connection is closed, and this event is logged as "extensive data loss". ` + + "Successful frame transmission resets this timer.", + DiffSuppressFunc: TimeEquall, + }, + "distance": { + Type: schema.TypeString, + Optional: true, + Description: "How long to wait for confirmation of unicast frames (ACKs) before considering transmission " + + "unsuccessful, or in short ACK-Timeout.", + }, + "frame_lifetime": { + Type: schema.TypeString, + Optional: true, + Description: "Discard frames that have been queued for sending longer than frame-lifetime. By default, when " + + "value of this property is 0, frames are discarded only after connection is closed (format: 0.00 sec).", + DiffSuppressFunc: TimeEquall, + }, + "guard_interval": { + Type: schema.TypeString, + Optional: true, + Description: "Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how " + + `this may affect throughput). "any" will use either short or long, depending on data rate, "long" will ` + + "use long.", + ValidateFunc: validation.StringInSlice([]string{"any ", "long"}, false), + }, + "hide_ssid": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "This property has effect only in AP mode. Setting it to yes can remove this network from " + + "the list of wireless networks that are shown by some client software. Changing this setting does not " + + "improve the security of the wireless network, because SSID is included in other frames sent by the AP.", + }, + "hw_protection_mode": { + Type: schema.TypeString, + Optional: true, + Description: "Frame protection support property. " + + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless#Frame_protection_support_(RTS/CTS)).", + ValidateFunc: validation.StringInSlice([]string{"cts-to-self", "none", "rts-cts"}, false), + }, + "hw_retries": { + Type: schema.TypeInt, + Optional: true, + Description: "Number of times sending frame is retried without considering it a transmission failure. " + + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless)", + ValidateFunc: validation.IntBetween(0, 15), + }, + "installation": { + Type: schema.TypeString, + Optional: true, + Description: "Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set.", + ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), + }, + "keepalive_frames": { + Type: schema.TypeString, + Optional: true, + Description: `If a client has not communicated for around 20 seconds, AP sends a "keepalive-frame".`, + ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), + }, + "load_balancing_group": { + Type: schema.TypeString, + Optional: true, + Description: "Tags the interface to the load balancing group. For a client to connect to interface in this " + + "group, the interface should have the same number of already connected clients as all other interfaces " + + "in the group or smaller. Useful in setups where ranges of CAPs mostly overlap.", + }, + "max_sta_count": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum number of associated clients.", + ValidateFunc: validation.IntBetween(1, 2007), + }, + "mode": { + Type: schema.TypeString, + Optional: true, + Description: "Set operational mode. Only **ap** currently supported.", + ValidateFunc: validation.StringInSlice([]string{"ap"}, false), + }, + KeyName: PropNameForceNewRw, + "multicast_helper": { + Type: schema.TypeString, + Optional: true, + Description: "When set to full multicast packets will be sent with unicast destination MAC address, " + + "resolving multicast problem on a wireless link. This option should be enabled only on the access " + + "point, clients should be configured in station-bridge mode.", + ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "full"}, false), + }, + "rates": { + Type: schema.TypeMap, + Optional: true, + Description: "Rates inline settings.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "rx_chains": { + Type: schema.TypeList, + Optional: true, + Description: "Which antennas to use for receive.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 3), + }, + }, + "security": { + Type: schema.TypeMap, + Optional: true, + Description: "Security inline settings.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "ssid": { + Type: schema.TypeString, + Optional: true, + Description: "SSID (service set identifier) is a name broadcast in the beacons that identifies " + + "wireless network.", + ValidateFunc: validation.StringLenBetween(0, 32), + }, + "tx_chains": { + Type: schema.TypeList, + Optional: true, + Description: "Which antennas to use for transmit.", + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 3), + }, + }, + }, + } +} diff --git a/routeros/resource_capsman_datapath.go b/routeros/resource_capsman_datapath.go index 25bfc40b..919113ec 100644 --- a/routeros/resource_capsman_datapath.go +++ b/routeros/resource_capsman_datapath.go @@ -27,7 +27,7 @@ import ( func ResourceCapsManDatapath() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/datapath"), - MetaId: PropId(Name), + MetaId: PropId(Id), "arp": { Type: schema.TypeString, @@ -112,6 +112,15 @@ func ResourceCapsManDatapath() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceCapsManDatapathV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_capsman_datapath_v0.go b/routeros/resource_capsman_datapath_v0.go new file mode 100644 index 00000000..634d983a --- /dev/null +++ b/routeros/resource_capsman_datapath_v0.go @@ -0,0 +1,87 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceCapsManDatapathV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/caps-man/datapath"), + MetaId: PropId(Name), + + "arp": { + Type: schema.TypeString, + Optional: true, + Description: "ARP mode. See [docs](https://wiki.mikrotik.com/wiki/Manual:IP/ARP#ARP_Modes) for info.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "local-proxy-arp", "proxy-arp", + "reply-only"}, false), + }, + "bridge": { + Type: schema.TypeString, + Optional: true, + Description: "Bridge to which particular interface should be automatically added as port. Required " + + "only when local-forwarding is not used.", + }, + "bridge_cost": { + Type: schema.TypeInt, + Optional: true, + Description: "Bridge port cost to use when adding as bridge port.", + }, + "bridge_horizon": { + Type: schema.TypeInt, + Optional: true, + Description: "Bridge horizon to use when adding as bridge port.", + }, + "client_to_client_forwarding": { + Type: schema.TypeBool, + Optional: true, + Description: "Controls if client-to-client forwarding between wireless clients connected to interface " + + "should be allowed, in local forwarding mode this function is performed by CAP, otherwise it is " + + "performed by CAPsMAN.", + }, + KeyComment: PropCommentRw, + "interface_list": { + Type: schema.TypeString, + Optional: true, + Description: "Interface list name.", + }, + "l2mtu": { + Type: schema.TypeInt, + Optional: true, + Description: "Layer2 MTU size.", + }, + "local_forwarding": { + Type: schema.TypeBool, + Optional: true, + Description: "Controls forwarding mode. If disabled, all L2 and L3 data will be forwarded to CAPsMAN, " + + "and further forwarding decisions will be made only then. See [docs](https://wiki.mikrotik.com/wiki/Manual:CAPsMAN#Local_Forwarding_Mode) for info.", + }, + KeyName: PropNameForceNewRw, + "mtu": { + Type: schema.TypeInt, + Optional: true, + Description: "MTU size.", + }, + "openflow_switch": { + Type: schema.TypeString, + Optional: true, + Description: "OpenFlow switch to add interface to, as port when enabled.", + }, + "vlan_id": { + Type: schema.TypeInt, + Optional: true, + Description: "VLAN ID to assign to interface if vlan-mode enables use of VLAN tagging.", + ValidateFunc: validation.IntBetween(1, 4095), + }, + "vlan_mode": { + Type: schema.TypeString, + Optional: true, + Description: "VLAN tagging mode specifies if VLAN tag should be assigned to interface (causes all received " + + "data to get tagged with VLAN tag and allows interface to only send out data tagged with given tag)", + ValidateFunc: validation.StringInSlice([]string{"no-tag", "use-service-tag", "use-tag"}, false), + }, + }, + } +} diff --git a/routeros/resource_capsman_rates.go b/routeros/resource_capsman_rates.go index b8b0adba..c096cf93 100644 --- a/routeros/resource_capsman_rates.go +++ b/routeros/resource_capsman_rates.go @@ -24,7 +24,7 @@ import ( func ResourceCapsManRates() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/rates"), - MetaId: PropId(Name), + MetaId: PropId(Id), "basic": { Type: schema.TypeSet, @@ -108,6 +108,15 @@ func ResourceCapsManRates() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceCapsManRatesV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_capsman_rates_v0.go b/routeros/resource_capsman_rates_v0.go new file mode 100644 index 00000000..a214dbd7 --- /dev/null +++ b/routeros/resource_capsman_rates_v0.go @@ -0,0 +1,88 @@ +package routeros + +import ( + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceCapsManRatesV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/caps-man/rates"), + MetaId: PropId(Name), + + "basic": { + Type: schema.TypeSet, + Optional: true, + Description: "List of basic rates. Client will connect to AP only if it supports all basic " + + "rates announced by the AP. AP will establish WDS link only if it supports all basic " + + "rates of the other AP.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", + "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), + }, + }, + "supported": { + Type: schema.TypeSet, + Optional: true, + Description: "List of supported rates. Two devices will communicate only using rates that " + + "are supported by both devices.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", + "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), + }, + }, + KeyComment: PropCommentRw, + "ht_basic_mcs": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + + "802.11n for MCS specification.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), + `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), + }, + }, + "ht_supported_mcs": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n " + + "for MCS specification.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), + `ht_supported_mcs format is "mcs-[0..23]": "mcs-11"`), + }, + }, + KeyName: PropNameForceNewRw, + "vht_basic_mcs": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream none " + + "- will not use selected Spatial Stream MCS 0-7 - client must support MCS-0 to MCS-7 MCS " + + "0-8 - client must support MCS-0 to MCS-8 MCS 0-9 - client must support MCS-0 to MCS-9", + ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), + }, + "vht_supported_mcs": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to " + + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream none " + + "- will not use selected Spatial Stream MCS 0-7 - devices will advertise as supported " + + "MCS-0 to MCS-7 MCS 0-8 - devices will advertise as supported MCS-0 to MCS-8 MCS 0-9 - " + + "devices will advertise as supported MCS-0 to MCS-9", + ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), + }, + }, + } +} diff --git a/routeros/resource_capsman_security.go b/routeros/resource_capsman_security.go index dbac9e60..0de4c9f5 100644 --- a/routeros/resource_capsman_security.go +++ b/routeros/resource_capsman_security.go @@ -27,7 +27,7 @@ func ResourceCapsManSecurity() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/security"), - MetaId: PropId(Name), + MetaId: PropId(Id), "authentication_types": { Type: schema.TypeSet, @@ -119,6 +119,15 @@ func ResourceCapsManSecurity() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceCapsManSecurityV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_capsman_security_v0.go b/routeros/resource_capsman_security_v0.go new file mode 100644 index 00000000..25c97291 --- /dev/null +++ b/routeros/resource_capsman_security_v0.go @@ -0,0 +1,95 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceCapsManSecurityV0() *schema.Resource { + + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/caps-man/security"), + MetaId: PropId(Name), + + "authentication_types": { + Type: schema.TypeSet, + Optional: true, + Description: "Specify the type of Authentication from wpa-psk, wpa2-psk, wpa-eap or wpa2-eap.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap"}, + false, false), + }, + }, + KeyComment: PropCommentRw, + "disable_pmkid": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to include PMKID into the EAPOL frame sent out by the Access Point. Disabling PMKID " + + "can cause compatibility issues with devices that use the PMKID to connect to an Access Point.", + }, + "eap_methods": { + Type: schema.TypeString, + Optional: true, + Description: "eap-tls - Use built-in EAP TLS authentication; passthrough - Access point will relay " + + "authentication process to the RADIUS server.", + ValidateDiagFunc: ValidationMultiValInSlice([]string{"eap-tls", "passthrough"}, false, false), + }, + "eap_radius_accounting": { + Type: schema.TypeBool, + Optional: true, + Description: "Specifies if RADIUS traffic accounting should be used if RADIUS authentication gets done for " + + "this client", + }, + "encryption": { + Type: schema.TypeSet, + Optional: true, + Description: "Set type of unicast encryption algorithm used.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: ValidationMultiValInSlice([]string{"aes-ccm", "tkip"}, false, false), + }, + }, + "group_encryption": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point advertises one of these ciphers, multiple values can be selected. Access Point " + + "uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access " + + "Points that use one of the specified group ciphers.", + ValidateFunc: validation.StringInSlice([]string{"aes-ccm", "tkip"}, false), + }, + "group_key_update": { + Type: schema.TypeString, + Optional: true, + Description: "Controls how often Access Point updates the group key. This key is used to encrypt all " + + "broadcast and multicast frames. property only has effect for Access Points. (30s..1h)", + DiffSuppressFunc: TimeEquall, + }, + KeyName: PropNameForceNewRw, + // 802.11i specification: + // A pass-phrase is a sequence of between 8 and 63 ASCII-encoded characters. The limit of 63 comes from the + // desire to distinguish between a pass-phrase and a PSK displayed as 64 hexadecimal characters. + "passphrase": { + Type: schema.TypeString, + Optional: true, + Description: "WPA or WPA2 pre-shared key.", + Sensitive: true, + ValidateFunc: validation.StringLenBetween(8, 63), + }, + "tls_certificate": { + Type: schema.TypeString, + Optional: true, + Description: "Access Point always needs a certificate when security.tls-mode is set to value other than " + + "no-certificates.", + }, + "tls_mode": { + Type: schema.TypeString, + Optional: true, + Description: "This property has effect only when security.eap-methods contains eap-tls.", + ValidateFunc: validation.StringInSlice([]string{"verify-certificate", "dont-verify-certificate", + "no-certificates", "verify-certificate-with-crl"}, false), + }, + }, + } +} From 84a7f3c7cafeb700fe7fef8f367208b6d4ba2dc5 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:04:30 +0300 Subject: [PATCH 08/16] fix(bridge): Add Name-Id migration --- .../routeros_interface_bridge/import.sh | 5 +- routeros/resource_interface_bridge.go | 11 +- routeros/resource_interface_bridge_v0.go | 300 ++++++++++++++++++ 3 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_interface_bridge_v0.go diff --git a/examples/resources/routeros_interface_bridge/import.sh b/examples/resources/routeros_interface_bridge/import.sh index 36353750..66b488e7 100644 --- a/examples/resources/routeros_interface_bridge/import.sh +++ b/examples/resources/routeros_interface_bridge/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the bridge in case of the example use bridge -terraform import routeros_interface_bridge.bridge bridge \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/bridge get [print show-ids]] +terraform import routeros_interface_bridge.bridge "*1" diff --git a/routeros/resource_interface_bridge.go b/routeros/resource_interface_bridge.go index 49b15832..63415b85 100644 --- a/routeros/resource_interface_bridge.go +++ b/routeros/resource_interface_bridge.go @@ -9,7 +9,7 @@ import ( func ResourceInterfaceBridge() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, "add_dhcp_option82": { @@ -307,6 +307,15 @@ func ResourceInterfaceBridge() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceBridgeV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_bridge_v0.go b/routeros/resource_interface_bridge_v0.go new file mode 100644 index 00000000..b29b48c1 --- /dev/null +++ b/routeros/resource_interface_bridge_v0.go @@ -0,0 +1,300 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceInterfaceBridgeV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/bridge"), + MetaId: PropId(Name), + + KeyActualMtu: PropActualMtuRo, + "add_dhcp_option82": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to add DHCP Option-82 information (Agent Remote ID and Agent Circuit ID) to DHCP " + + "packets. Can be used together with Option-82 capable DHCP server to assign IP addresses and implement " + + "policies. This property only has effect when dhcp-snooping is set to yes.", + RequiredWith: []string{"dhcp_snooping"}, + }, + "admin_mac": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Static MAC address of the bridge. This property only has effect when auto-mac is set to no.", + }, + "ageing_time": { + Type: schema.TypeString, + Optional: true, + Default: "5m", + Description: "How long a host's information will be kept in the bridge database.", + }, + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + "auto_mac": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Automatically select one MAC address of bridge ports as a bridge MAC address, bridge MAC " + + "will be chosen from the first added bridge port. After a device reboot, the bridge MAC " + + "can change depending on the port-number.", + }, + KeyComment: PropCommentRw, + "dhcp_snooping": { + Type: schema.TypeBool, + Optional: true, + }, + KeyDisabled: PropDisabledRw, + "ether_type": { + Type: schema.TypeString, + Optional: true, + Default: "0x8100", + Description: "This property only has effect when vlan-filtering is set to yes.", + ValidateFunc: validation.StringInSlice([]string{"0x9100", "0x8100", "0x88a8"}, false), + }, + "fast_forward": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "forward_delay": { + Type: schema.TypeString, + Optional: true, + Default: "15s", + Description: "Time which is spent during the initialization phase of the bridge interface " + + "(i.e., after router startup or enabling the interface) in listening/learning state before the " + + "bridge will start functioning normally.", + }, + "frame_types": { + Type: schema.TypeString, + Optional: true, + Default: "admit-all", + Description: "Specifies allowed frame types on a bridge port. This property only has effect when " + + "vlan-filtering is set to yes.", + ValidateFunc: validation.StringInSlice([]string{"admit-all", "admit-only-untagged-and-priority-tagged", + "admit-only-vlan-tagged"}, false), + }, + "igmp_snooping": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Enables multicast group and port learning to prevent multicast traffic from flooding all " + + "interfaces in a bridge.", + }, + "igmp_version": { + Type: schema.TypeInt, + Optional: true, + // Default: "2", + Computed: true, + Description: "Selects the IGMP version in which IGMP general membership queries will be generated. " + + "This property only has effect when igmp-snooping is set to yes.", + ValidateFunc: validation.IntInSlice([]int{2, 3}), + RequiredWith: []string{"igmp_snooping"}, + }, + "ingress_filtering": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Enables or disables VLAN ingress filtering, which checks if the ingress port is a member " + + "of the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if " + + "the ingress traffic should be tagged or untagged. This property only has effect when " + + "vlan-filtering is set to yes.", + RequiredWith: []string{"vlan_filtering"}, + }, + KeyL2Mtu: PropL2MtuRo, + "last_member_interval": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "If a port has fast-leave set to no and a bridge port receives a IGMP Leave message, " + + "then a IGMP Snooping enabled bridge will send a IGMP query to make sure that no devices has " + + "subscribed to a certain multicast stream on a bridge port.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping"}, + }, + "last_member_query_count": { + Type: schema.TypeInt, + Optional: true, + // Default: 2, + Computed: true, + Description: "How many times should last-member-interval pass until a IGMP Snooping bridge will stop " + + "forwarding a certain multicast stream. This property only has effect when igmp-snooping is set to yes.", + RequiredWith: []string{"igmp_snooping"}, + }, + KeyMacAddress: PropMacAddressRo, + "max_hops": { + Type: schema.TypeInt, + Optional: true, + // Default: 20, + Computed: true, + Description: "Bridge count which BPDU can pass in a MSTP enabled network in the same region before BPDU " + + "is being ignored. This property only has effect when protocol-mode is set to mstp.", + ValidateFunc: validation.IntBetween(6, 40), + }, + "max_message_age": { + Type: schema.TypeString, + Optional: true, + Default: "20s", + Description: "Changes the Max Age value in BPDU packets, which is transmitted by the root bridge. " + + "This property only has effect when protocol-mode is set to stp or rstp. Value: 6s..40s", + }, + "membership_interval": { + Type: schema.TypeString, + Optional: true, + // Default: "4m20s", + Computed: true, + Description: "Amount of time after an entry in the Multicast Database (MDB) is removed if a IGMP membership " + + "report is not received on a certain port. This property only has effect when igmp-snooping is set to yes.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping"}, + }, + "mld_version": { + Type: schema.TypeInt, + Optional: true, + // Default: 1, + Computed: true, + Description: "Selects the MLD version. Version 2 adds support for source-specific multicast. This " + + "property only has effect when RouterOS IPv6 package is enabled and igmp-snooping is set to yes.", + ValidateFunc: validation.IntInSlice([]int{1, 2}), + RequiredWith: []string{"igmp_snooping"}, + }, + "mtu": { + Type: schema.TypeString, + Optional: true, + Default: "auto", + Description: "The default bridge MTU value without any bridge ports added is 1500. " + + "The MTU value can be set manually, but it cannot exceed the bridge L2MTU or the lowest bridge " + + "port L2MTU. If a new bridge port is added with L2MTU which is smaller than the actual-mtu " + + "of the bridge (set by the mtu property), then manually set value will be ignored and the bridge " + + "will act as if mtu=auto is set.", + }, + "multicast_querier": { + Type: schema.TypeBool, + Optional: true, + Description: "Multicast querier generates IGMP general membership queries to which all IGMP capable " + + "devices respond with an IGMP membership report, usually a PIM (multicast) router or IGMP proxy " + + "generates these queries. This property only has an effect when igmp-snooping is set to yes. " + + "Additionally, the igmp-snooping should be disabled/enabled after changing multicast-querier property.", + RequiredWith: []string{"igmp_snooping"}, + }, + // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=59277403#BridgeIGMP/MLDsnooping-Configurationoptions + "multicast_router": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "A multicast router port is a port where a multicast router or querier is connected. On " + + "this port, unregistered multicast streams and IGMP/MLD membership reports will be sent. This " + + "setting changes the state of the multicast router for a bridge interface itself. This property can " + + "be used to send IGMP/MLD membership reports and multicast traffic to the bridge interface for further " + + "multicast routing or proxying. This property only has an effect when igmp-snooping is set to yes.", + ValidateFunc: validation.StringInSlice([]string{"disabled", "permanent", "temporary-query"}, false), + RequiredWith: []string{"igmp_snooping"}, + }, + KeyName: PropNameForceNewRw, + "priority": { + Type: schema.TypeString, + Optional: true, + Default: "0x8000", + Description: "Bridge priority, used by STP to determine root bridge, used by MSTP to determine CIST " + + "and IST regional root bridge. This property has no effect when protocol-mode is set to none.", + }, + "protocol_mode": { + Type: schema.TypeString, + Optional: true, + Default: "rstp", + Description: "Select Spanning tree protocol (STP) or Rapid spanning tree protocol (RSTP) to ensure a " + + "loop-free topology for any bridged LAN.", + ValidateFunc: validation.StringInSlice([]string{"none", "rstp", "stp", "mstp"}, false), + }, + "pvid": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. " + + "It applies e.g. to frames sent from bridge IP and destined to a bridge port. " + + "This property only has effect when vlan-filtering is set to yes.", + ValidateFunc: validation.IntBetween(1, 4094), + }, + "querier_interval": { + Type: schema.TypeString, + Optional: true, + // Default: "4m15s", + Computed: true, + Description: "Used to change the interval how often a bridge checks if it is the active multicast " + + "querier. This property only has effect when igmp-snooping and multicast-querier is set to yes.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping", "multicast_querier"}, + }, + "query_interval": { + Type: schema.TypeString, + Optional: true, + // Default: "2m5s", + Computed: true, + Description: "Used to change the interval how often IGMP general membership queries are sent out. " + + "This property only has effect when igmp-snooping and multicast-querier is set to yes.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping", "multicast_querier"}, + }, + "query_response_interval": { + Type: schema.TypeString, + Optional: true, + // Default: "10s", + Computed: true, + Description: "Interval in which a IGMP capable device must reply to a IGMP query with a IGMP membership " + + "report. This property only has effect when igmp-snooping and multicast-querier is set to yes.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping", "multicast_querier"}, + }, + KeyRunning: PropRunningRo, + "region_name": { + Type: schema.TypeString, + Optional: true, + Description: "MSTP region name. This property only has effect when protocol-mode is set to mstp.", + }, + "region_revision": { + Type: schema.TypeInt, + Optional: true, + Description: "MSTP configuration revision number. This property only has effect when protocol-mode is set to mstp.", + ValidateFunc: validation.IntBetween(0, 65535), + }, + "startup_query_count": { + Type: schema.TypeInt, + Optional: true, + // Default: 2, + Computed: true, + Description: "Specifies how many times must startup-query-interval pass until the bridge starts sending " + + "out IGMP general membership queries periodically. This property only has effect when igmp-snooping " + + "and multicast-querier is set to yes.", + RequiredWith: []string{"igmp_snooping", "multicast_querier"}, + }, + "startup_query_interval": { + Type: schema.TypeString, + Optional: true, + // Default: "31s250ms", + Computed: true, + Description: "Used to change the amount of time after a bridge starts sending out IGMP general membership " + + "queries after the bridge is enabled. This property only has effect when igmp-snooping and " + + "multicast-querier is set to yes.", + DiffSuppressFunc: TimeEquall, + RequiredWith: []string{"igmp_snooping", "multicast_querier"}, + }, + "transmit_hold_count": { + Type: schema.TypeInt, + Optional: true, + Default: 6, + Description: "The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate.", + ValidateFunc: validation.IntBetween(1, 10), + }, + "vlan_filtering": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Globally enables or disables VLAN functionality for bridge.", + }, + // Some properties are not implemented, see: https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge + }, + } +} From 6e811c3c3aa054f0c91d554cc9814ed3ff032b62 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:09:48 +0300 Subject: [PATCH 09/16] fix(gre): Add Name-Id migration --- .../routeros_interface_gre/import.sh | 5 ++-- routeros/resource_interface_gre.go | 11 ++++++- routeros/resource_interface_gre_v0.go | 30 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_interface_gre_v0.go diff --git a/examples/resources/routeros_interface_gre/import.sh b/examples/resources/routeros_interface_gre/import.sh index 5c874869..a2a0182e 100644 --- a/examples/resources/routeros_interface_gre/import.sh +++ b/examples/resources/routeros_interface_gre/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the gre interface in case of the example use gre-hq-1 -terraform import routeros_interface_gre.gre_hq gre-hq-1 \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/gre get [print show-ids]] +terraform import routeros_interface_gre.gre_hq "*1" diff --git a/routeros/resource_interface_gre.go b/routeros/resource_interface_gre.go index 336f26fd..1c27eb6a 100644 --- a/routeros/resource_interface_gre.go +++ b/routeros/resource_interface_gre.go @@ -9,7 +9,7 @@ import ( func ResourceInterfaceGre() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/gre"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyAllowFastPath: PropAllowFastPathRw, @@ -48,6 +48,15 @@ func ResourceInterfaceGre() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceGreV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_gre_v0.go b/routeros/resource_interface_gre_v0.go new file mode 100644 index 00000000..501ef1d4 --- /dev/null +++ b/routeros/resource_interface_gre_v0.go @@ -0,0 +1,30 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceInterfaceGreV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/gre"), + MetaId: PropId(Name), + + KeyActualMtu: PropActualMtuRo, + KeyAllowFastPath: PropAllowFastPathRw, + KeyClampTcpMss: PropClampTcpMssRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyDontFragment: PropDontFragmentRw, + KeyDscp: PropDscpRw, + KeyIpsecSecret: PropIpsecSecretRw, + KeyKeepalive: PropKeepaliveRw, + KeyL2Mtu: PropL2MtuRo, + KeyLocalAddress: PropLocalAddressRw, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRemoteAddress: PropRemoteAddressRw, + KeyRunning: PropRunningRo, + }, + } +} From 6b326c0dd62241e7b70307ac6400421f44ff94b1 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:18:21 +0300 Subject: [PATCH 10/16] fix(interface_list): Add Name-Id migration --- .../routeros_interface_list/import.sh | 5 ++-- routeros/resource_interface_list.go | 11 ++++++- routeros/resource_interface_list_v0.go | 30 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_interface_list_v0.go diff --git a/examples/resources/routeros_interface_list/import.sh b/examples/resources/routeros_interface_list/import.sh index f4848f3e..1c9acd9e 100644 --- a/examples/resources/routeros_interface_list/import.sh +++ b/examples/resources/routeros_interface_list/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the interface list in case of the example use my-list -terraform import routeros_interface_list.list my-list \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/list get [print show-ids]] +terraform import routeros_interface_list.list "*2000010" \ No newline at end of file diff --git a/routeros/resource_interface_list.go b/routeros/resource_interface_list.go index 0fe1de18..531f2158 100644 --- a/routeros/resource_interface_list.go +++ b/routeros/resource_interface_list.go @@ -7,7 +7,7 @@ import ( func ResourceInterfaceList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/list"), - MetaId: PropId(Name), + MetaId: PropId(Id), "builtin": { Type: schema.TypeBool, @@ -36,6 +36,15 @@ func ResourceInterfaceList() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceListV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_list_v0.go b/routeros/resource_interface_list_v0.go new file mode 100644 index 00000000..c97e9397 --- /dev/null +++ b/routeros/resource_interface_list_v0.go @@ -0,0 +1,30 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceInterfaceListV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/list"), + MetaId: PropId(Name), + + "builtin": { + Type: schema.TypeBool, + Computed: true, + }, + "comment": PropCommentRw, + "dynamic": PropDynamicRo, + "exclude": { + Type: schema.TypeString, + Optional: true, + }, + "include": { + Type: schema.TypeString, + Optional: true, + }, + "name": PropNameForceNewRw, + }, + } +} From 7592f2f2fba7ea7d3a7875906b71a75126fd0f90 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:23:20 +0300 Subject: [PATCH 11/16] fix(vlan): Add Name-Id migration --- .../routeros_interface_vlan/import.sh | 5 ++- routeros/resource_interface_vlan.go | 13 ++++++- routeros/resource_interface_vlan_v0.go | 38 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 routeros/resource_interface_vlan_v0.go diff --git a/examples/resources/routeros_interface_vlan/import.sh b/examples/resources/routeros_interface_vlan/import.sh index 658ee1e2..da0ecd1b 100644 --- a/examples/resources/routeros_interface_vlan/import.sh +++ b/examples/resources/routeros_interface_vlan/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the vlan interface in case of the example use VLAN_TEST -terraform import routeros_interface_vlan.interface_vlan VLAN_TEST \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/vlan get [print show-ids]] +terraform import routeros_interface_vlan.interface_vlan "*1" \ No newline at end of file diff --git a/routeros/resource_interface_vlan.go b/routeros/resource_interface_vlan.go index 47e8f4fb..19be9be8 100644 --- a/routeros/resource_interface_vlan.go +++ b/routeros/resource_interface_vlan.go @@ -7,8 +7,8 @@ import ( // ResourceInterfaceVlan https://wiki.mikrotik.com/wiki/Manual:Interface/VLAN func ResourceInterfaceVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ - MetaResourcePath: PropResourcePath("/interface/vlan"), - MetaId: PropId(Name), + MetaResourcePath: PropResourcePath("/interface/vlan"), + MetaId: PropId(Id), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, @@ -45,6 +45,15 @@ func ResourceInterfaceVlan() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceVlanV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_vlan_v0.go b/routeros/resource_interface_vlan_v0.go new file mode 100644 index 00000000..d3a10f27 --- /dev/null +++ b/routeros/resource_interface_vlan_v0.go @@ -0,0 +1,38 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceInterfaceVlanV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/vlan"), + MetaId: PropId(Name), + + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + KeyInterface: PropInterfaceRw, + KeyL2Mtu: PropL2MtuRo, + KeyLoopProtect: PropLoopProtectRw, + KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, + KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, + KeyLoopProtectStatus: PropLoopProtectStatusRo, + KeyMacAddress: PropMacAddressRo, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + KeyRunning: PropRunningRo, + "use_service_tag": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "vlan_id": { + Type: schema.TypeInt, + Required: true, + }, + }, + } +} From 30c8f37a004c0b3b67dce85965535a0836b57af7 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:29:20 +0300 Subject: [PATCH 12/16] fix(vrrp): Add Name-Id migration --- routeros/resource_interface_vrrp.go | 11 +- routeros/resource_interface_vrrp_v0.go | 138 +++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 routeros/resource_interface_vrrp_v0.go diff --git a/routeros/resource_interface_vrrp.go b/routeros/resource_interface_vrrp.go index 043f6941..bd78600c 100644 --- a/routeros/resource_interface_vrrp.go +++ b/routeros/resource_interface_vrrp.go @@ -41,7 +41,7 @@ import ( func ResourceInterfaceVrrp() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vrrp"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, @@ -198,6 +198,15 @@ func ResourceInterfaceVrrp() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceVrrpV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_vrrp_v0.go b/routeros/resource_interface_vrrp_v0.go new file mode 100644 index 00000000..0bc6bbd8 --- /dev/null +++ b/routeros/resource_interface_vrrp_v0.go @@ -0,0 +1,138 @@ +package routeros + +import ( + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceInterfaceVrrpV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/vrrp"), + MetaId: PropId(Name), + + KeyArp: PropArpRw, + KeyArpTimeout: PropArpTimeoutRw, + "authentication": { + Type: schema.TypeString, + Optional: true, + Default: "none", + Description: "Authentication method to use for VRRP advertisement packets.", + ValidateFunc: validation.StringInSlice([]string{"ah", "none", "simple"}, false), + }, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "group_master": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group.", + // Maybe this is a bug, but for the 'none' value, the Mikrotik ROS 7.5 returns an empty string. + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == new { + return true + } + + if new == "none" && old == "" { + return true + } + return false + }, + }, + KeyInterface: PropInterfaceRw, + "interval": { + Type: schema.TypeString, + Optional: true, + Default: "1s", + Description: "VRRP update interval in seconds. Defines how often master sends advertisement packets.", + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+(ms|s|M)?)+$`), + "expected hello interval 10ms..4m15s"), + }, + "invalid": { + Type: schema.TypeBool, + Computed: true, + }, + KeyMacAddress: PropMacAddressRo, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + "on_fail": { + Type: schema.TypeString, + Optional: true, + Description: "Script to execute when the node fails.", + }, + "on_backup": { + Type: schema.TypeString, + Optional: true, + Description: "Script to execute when the node is switched to the backup state.", + }, + "on_master": { + Type: schema.TypeString, + Optional: true, + Description: "Script to execute when the node is switched to master state.", + }, + "password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "Password required for authentication. Can be ignored if authentication is not used.", + }, + "preemption_mode": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether the master node always has the priority. When set to 'no' the backup node will not " + + "be elected to be a master until the current master fails, even if the backup node has higher priority " + + "than the current master. This setting is ignored if the owner router becomes available", + }, + "priority": { + Type: schema.TypeInt, + Optional: true, + Default: 100, + Description: "Priority of VRRP node used in Master election algorithm. A higher number means higher " + + "priority. '255' is reserved for the router that owns VR IP and '0' is reserved for the Master router " + + "to indicate that it is releasing responsibility.", + ValidateFunc: validation.IntBetween(1, 254), + }, + "remote_address": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the remote address of the other VRRP router for syncing connection tracking. " + + "If not set, the system autodetects the remote address via VRRP. The remote address is used only if " + + "sync-connection-tracking=yes.Sync connection tracking uses UDP port 8275.", + ValidateFunc: validation.IsIPv4Address, + }, + "running": { + Type: schema.TypeBool, + Computed: true, + }, + "sync_connection_tracking": { + Type: schema.TypeBool, + Optional: true, + Description: "Synchronize connection tracking entries from Master to Backup device.", + }, + "v3_protocol": { + Type: schema.TypeString, + Optional: true, + Default: "ipv4", + Description: "A protocol that will be used by VRRPv3. Valid only if the version is 3.", + ValidateFunc: validation.StringInSlice([]string{"ipv4", "ipv6"}, false), + }, + "version": { + Type: schema.TypeInt, + Optional: true, + Default: 3, + Description: "Which VRRP version to use.", + ValidateFunc: validation.IntBetween(2, 3), + }, + "vrid": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + Description: "Virtual Router identifier. Each Virtual router must have a unique id number.", + ValidateFunc: validation.IntBetween(1, 255), + }, + }, + } +} From d676b8b26c64e114caeb108191ed911319c0f4ab Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:33:04 +0300 Subject: [PATCH 13/16] fix(wg): Add Name-Id migration --- .../routeros_interface_wireguard/import.sh | 5 ++- routeros/resource_interface_wireguard.go | 11 ++++- routeros/resource_interface_wireguard_v0.go | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_interface_wireguard_v0.go diff --git a/examples/resources/routeros_interface_wireguard/import.sh b/examples/resources/routeros_interface_wireguard/import.sh index a791baca..fe30ab17 100644 --- a/examples/resources/routeros_interface_wireguard/import.sh +++ b/examples/resources/routeros_interface_wireguard/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the wireguard interface in case of the example use test_wg_interface -terraform import routeros_interface_wireguard.test_wg_interface test_wg_interface \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/interface/wireguard get [print show-ids]] +terraform import routeros_interface_wireguard.test_wg_interface "*1" \ No newline at end of file diff --git a/routeros/resource_interface_wireguard.go b/routeros/resource_interface_wireguard.go index 5e62fdc7..23865230 100644 --- a/routeros/resource_interface_wireguard.go +++ b/routeros/resource_interface_wireguard.go @@ -22,7 +22,7 @@ import ( func ResourceInterfaceWireguard() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireguard"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, @@ -59,6 +59,15 @@ func ResourceInterfaceWireguard() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceInterfaceWireguardV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_interface_wireguard_v0.go b/routeros/resource_interface_wireguard_v0.go new file mode 100644 index 00000000..87aab08c --- /dev/null +++ b/routeros/resource_interface_wireguard_v0.go @@ -0,0 +1,40 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceInterfaceWireguardV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/interface/wireguard"), + MetaId: PropId(Name), + + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "listen_port": { + Type: schema.TypeInt, + Required: true, + Description: "Port for WireGuard service to listen on for incoming sessions.", + ValidateFunc: validation.IntBetween(1, 65535), + }, + KeyMtu: PropMtuRw(), + KeyName: PropNameForceNewRw, + "private_key": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Sensitive: true, + Description: "A base64 private key. If not specified, it will be automatically " + + "generated upon interface creation.", + }, + "public_key": { + Type: schema.TypeString, + Computed: true, + Description: "A base64 public key is calculated from the private key.", + }, + KeyRunning: PropRunningRo, + }, + } +} From c8c9ff89d6b81253c7e8e8f020aba7cc47e03159 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:39:18 +0300 Subject: [PATCH 14/16] fix(dhcp_server): Add Name-Id migration --- .../routeros_ip_dhcp_server/import.sh | 5 +- routeros/resource_ip_dhcp_server.go | 11 +- routeros/resource_ip_dhcp_server_v0.go | 135 ++++++++++++++++++ 3 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_ip_dhcp_server_v0.go diff --git a/examples/resources/routeros_ip_dhcp_server/import.sh b/examples/resources/routeros_ip_dhcp_server/import.sh index f8926f1c..4fb623f2 100644 --- a/examples/resources/routeros_ip_dhcp_server/import.sh +++ b/examples/resources/routeros_ip_dhcp_server/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the DHCP server in case of the example use bridge_dhcp -terraform import routeros_ip_dhcp_server.server bridge_dhcp \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/ip/dhcp-server get [print show-ids]] +terraform import routeros_ip_dhcp_server.server "*1" \ No newline at end of file diff --git a/routeros/resource_ip_dhcp_server.go b/routeros/resource_ip_dhcp_server.go index df7daa64..dd080537 100644 --- a/routeros/resource_ip_dhcp_server.go +++ b/routeros/resource_ip_dhcp_server.go @@ -9,7 +9,7 @@ import ( func ResourceDhcpServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server"), - MetaId: PropId(Name), + MetaId: PropId(Id), "add_arp": { Type: schema.TypeBool, @@ -140,6 +140,15 @@ func ResourceDhcpServer() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceDhcpServerV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_ip_dhcp_server_v0.go b/routeros/resource_ip_dhcp_server_v0.go new file mode 100644 index 00000000..1ca5aa24 --- /dev/null +++ b/routeros/resource_ip_dhcp_server_v0.go @@ -0,0 +1,135 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceDhcpServerV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/ip/dhcp-server"), + MetaId: PropId(Name), + + "add_arp": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to add dynamic ARP entry. ", + }, + "address_pool": { + Type: schema.TypeString, + Optional: true, + Default: "static-only", + Description: "IP pool, from which to take IP addresses for the clients. If set to static-only, then only " + + "the clients that have a static lease (added in lease submenu) will be allowed.", + }, + "allow_dual_stack_queue": { + Type: schema.TypeBool, + Optional: true, + Description: "Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address and " + + "DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly.", + }, + "always_broadcast": { + Type: schema.TypeBool, + Optional: true, + Description: "Always send replies as broadcasts even if destination IP is known.", + }, + "authoritative": { + Type: schema.TypeString, + Optional: true, + Default: "yes", + Description: "Option changes the way how a server responds to DHCP requests.", + ValidateFunc: validation.StringInSlice([]string{"after-10sec-delay", "after-2sec-delay", "yes", "no"}, false), + }, + "bootp_lease_time": { + Type: schema.TypeString, + Optional: true, + Description: "Accepts two predefined options or time value: * forever - lease never expires " + + "* lease-time - use time from lease-time parameter", + }, + "bootp_support": { + Type: schema.TypeString, + Optional: true, + Default: "static", + Description: "Support for BOOTP clients.", + ValidateFunc: validation.StringInSlice([]string{"none", "static", "dynamic"}, false), + }, + "client_mac_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "Specifies whether to limit specific number of clients per single MAC address.", + }, + "conflict_detection": { + Type: schema.TypeBool, + Optional: true, + Description: "Allows to disable/enable conflict detection. If option is enabled, then whenever server tries " + + "to assign a lease it will send ICMP and ARP messages to detect whether such address in the network " + + "already exist. If any of above get reply address is considered already used. Conflict detection must " + + "be disabled when any kind of DHCP client limitation per port or per mac is used.", + }, + KeyComment: PropCommentRw, + "delay_threshold": { + Type: schema.TypeString, + Optional: true, + Description: "If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored. " + + "If set to none - there is no threshold (all DHCP packets are processed).", + }, + "dhcp_option_set": { + Type: schema.TypeString, + Optional: true, + Description: "Use custom set of DHCP options defined in option sets menu.", + }, + KeyDisabled: PropDisabledRw, + KeyDynamic: PropDynamicRo, + "insert_queue_before": { + Type: schema.TypeString, + Optional: true, + Description: "Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set.", + }, + KeyInterface: PropInterfaceRw, + KeyInvalid: PropInvalidRo, + "lease_script": { + Type: schema.TypeString, + Optional: true, + Description: "A script that will be executed after a lease is assigned or de-assigned.", + }, + "lease_time": { + Type: schema.TypeString, + Optional: true, + Default: "10m", + Description: "The time that a client may use the assigned address. The client will try to renew this " + + "address after half of this time and will request a new address after the time limit expires.", + }, + KeyName: PropNameForceNewRw, + "parent_queue": { + Type: schema.TypeString, + Optional: true, + Description: "", + }, + "relay": { + Type: schema.TypeString, + Optional: true, + Description: "The IP address of the relay this DHCP server.", + ValidateFunc: ValidationIpAddress, + }, + "src_address": { + Type: schema.TypeString, + Optional: true, + Description: "The address which the DHCP client must send requests to in order to renew an IP address lease.", + ValidateFunc: ValidationIpAddress, + }, + "use_framed_as_classless": { + Type: schema.TypeBool, + Optional: true, + Description: "Forward RADIUS Framed-Route as a DHCP Classless-Static-Route to DHCP-client.", + }, + "use_radius": { + Type: schema.TypeString, + Optional: true, + Default: "no", + Description: "Whether to use RADIUS server.", + ValidateFunc: validation.StringInSlice([]string{"yes", "no", "accounting"}, false), + }, + }, + } +} From 98c17c2130e798092a86e2e724b99c7a90980f15 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:45:02 +0300 Subject: [PATCH 15/16] fix(ip_pool): Add Name-Id migration --- examples/resources/routeros_ip_pool/import.sh | 5 +-- routeros/resource_ip_pool.go | 11 ++++++- routeros/resource_ip_pool_v0.go | 33 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 routeros/resource_ip_pool_v0.go diff --git a/examples/resources/routeros_ip_pool/import.sh b/examples/resources/routeros_ip_pool/import.sh index 098d28fe..322a1c25 100644 --- a/examples/resources/routeros_ip_pool/import.sh +++ b/examples/resources/routeros_ip_pool/import.sh @@ -1,2 +1,3 @@ -# Import with the name of the ip pool in case of the example use my_ip_pool -terraform import routeros_ip_pool.pool my_ip_pool \ No newline at end of file +#The ID can be found via API or the terminal +#The command for the terminal is -> :put [/ip/pool get [print show-ids]] +terraform import routeros_ip_pool.pool "*1" \ No newline at end of file diff --git a/routeros/resource_ip_pool.go b/routeros/resource_ip_pool.go index b7b06a55..770628d1 100644 --- a/routeros/resource_ip_pool.go +++ b/routeros/resource_ip_pool.go @@ -8,7 +8,7 @@ import ( func ResourceIPPool() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/pool"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyComment: PropCommentRw, KeyName: PropNameForceNewRw, @@ -38,6 +38,15 @@ func ResourceIPPool() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceIPPoolV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_ip_pool_v0.go b/routeros/resource_ip_pool_v0.go new file mode 100644 index 00000000..43959887 --- /dev/null +++ b/routeros/resource_ip_pool_v0.go @@ -0,0 +1,33 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceIPPoolV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/ip/pool"), + MetaId: PropId(Name), + + KeyComment: PropCommentRw, + KeyName: PropNameForceNewRw, + "next_pool": { + Type: schema.TypeString, + Optional: true, + Description: "When address is acquired from pool that has no free addresses, and next-pool property is set " + + "to another pool, then next IP address will be acquired from next-pool.", + }, + "ranges": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: `IP address list of non-overlapping IP address ranges in form of: ` + + `["from1-to1", "from2-to2", ..., "fromN-toN"]. ` + + `For example, ["10.0.0.1-10.0.0.27", "10.0.0.32-10.0.0.47"]`, + }, + }, + } +} From c34b99483f96eb9d1b49552326883fe9190c3c93 Mon Sep 17 00:00:00 2001 From: Vaerh Date: Mon, 6 Nov 2023 21:51:23 +0300 Subject: [PATCH 16/16] fix(scheduler): Add Name-Id migration --- routeros/resource_system_scheduler.go | 11 +++- routeros/resource_system_scheduler_v0.go | 83 ++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 routeros/resource_system_scheduler_v0.go diff --git a/routeros/resource_system_scheduler.go b/routeros/resource_system_scheduler.go index 03be81a0..c7fe022c 100644 --- a/routeros/resource_system_scheduler.go +++ b/routeros/resource_system_scheduler.go @@ -11,7 +11,7 @@ import ( func ResourceSystemScheduler() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/system/scheduler"), - MetaId: PropId(Name), + MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, @@ -92,6 +92,15 @@ policy = ["ftp", "read", "write"] StateContext: schema.ImportStatePassthroughContext, }, + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: ResourceSystemSchedulerV0().CoreConfigSchema().ImpliedType(), + Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), + Version: 0, + }, + }, + Schema: resSchema, } } diff --git a/routeros/resource_system_scheduler_v0.go b/routeros/resource_system_scheduler_v0.go new file mode 100644 index 00000000..d7edf96e --- /dev/null +++ b/routeros/resource_system_scheduler_v0.go @@ -0,0 +1,83 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceSystemSchedulerV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/system/scheduler"), + MetaId: PropId(Name), + + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "interval": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Interval between two script executions, if time interval is set to zero, the script is only " + + "executed at its start time, otherwise it is executed repeatedly at the time interval is specified.", + }, + KeyName: PropNameForceNewRw, + "next_run": { + Type: schema.TypeString, + Computed: true, + }, + "on_event": { + Type: schema.TypeString, + Required: true, + Description: "Name of the script to execute. It must be presented at /system script.", + }, + "owner": { + Type: schema.TypeString, + Computed: true, + }, + "policy": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"ftp", "reboot", "read", "write", "policy", "test", + "password", "sniff", "sensitive", "romon", "dude"}, false), + }, + Description: `List of applicable policies: + * dude - Policy that grants rights to log in to dude server. + * ftp - Policy that grants full rights to log in remotely via FTP, to read/write/erase files and to transfer files from/to the router. Should be used together with read/write policies. + * password - Policy that grants rights to change the password. + * policy - Policy that grants user management rights. Should be used together with the write policy. Allows also to see global variables created by other users (requires also 'test' policy). + * read - Policy that grants read access to the router's configuration. All console commands that do not alter router's configuration are allowed. Doesn't affect FTP. + * reboot - Policy that allows rebooting the router. + * romon - Policy that grants rights to connect to RoMon server. + * sensitive - Policy that grants rights to change "hide sensitive" option, if this policy is disabled sensitive information is not displayed. + * sniff - Policy that grants rights to use packet sniffer tool. + * test - Policy that grants rights to run ping, traceroute, bandwidth-test, wireless scan, snooper, and other test commands. + * write - Policy that grants write access to the router's configuration, except for user management. This policy does not allow to read the configuration, so make sure to enable read policy as well. +policy = ["ftp", "read", "write"] +`, + }, + "run_count": { + Type: schema.TypeString, + Computed: true, + Description: "This counter is incremented each time the script is executed.", + }, + "start_date": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Date of the first script execution.", + }, + "start_time": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: "Time of the first script execution. If scheduler item has start-time set to startup, it " + + "behaves as if start-time and start-date were set to time 3 seconds after console starts up. " + + "It means that all scripts having start-time is startup and interval is 0 will be executed once each " + + "time router boots. If the interval is set to value other than 0 scheduler will not run at startup.", + }, + }, + } +}