diff --git a/routeros/provider.go b/routeros/provider.go index 740b3da5..0433cf1d 100644 --- a/routeros/provider.go +++ b/routeros/provider.go @@ -147,7 +147,9 @@ func Provider() *schema.Provider { "routeros_routing_table": ResourceRoutingTable(), // OSPF - "routeros_routing_ospf_instance": ResourceRoutingOspfInstance(), + "routeros_routing_ospf_instance": ResourceRoutingOspfInstance(), + "routeros_routing_ospf_area": ResourceRoutingOspfArea(), + "routeros_routing_ospf_interface_template": ResourceRoutingOspfInterfaceTemplate(), // VPN "routeros_ovpn_server": ResourceOpenVPNServer(), diff --git a/routeros/resource_routing_ospf_area.go b/routeros/resource_routing_ospf_area.go new file mode 100644 index 00000000..d2dbbadc --- /dev/null +++ b/routeros/resource_routing_ospf_area.go @@ -0,0 +1,76 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* + { + ".id": "*54", + "name": "", + "area-id": "", + "default-cost": "", + "instance": "", + "no-summaries": "", + "nssa-translate": "", + "type": "", + } +*/ + +// ResourceRoutingOspfArea https://help.mikrotik.com/docs/display/ROS/OSPF +func ResourceRoutingOspfArea() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/routing/ospf/area"), + MetaId: PropId(Name), + + KeyName: PropNameForceNewRw, + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "area-id": { + Type: schema.TypeString, + Description: "OSPF area identifier.", + }, + "default-cost": { + Type: schema.TypeInt, + Required: false, + Description: "Default cost of injected LSAs into the area.", + }, + "instance": { + Type: schema.TypeString, + Required: true, + Description: "Name of the OSPF instance this area belongs to.", + }, + "no-summaries": { + Type: schema.TypeBool, + Default: false, + Required: false, + Description: "If set then the area will not flood summary LSAs in the stub area.", + }, + "nssa-translate": { + Type: schema.TypeString, + Required: false, + Description: "The parameter indicates which ABR will be used as a translator from type7 to type5 LSA.", + ValidateFunc: validation.StringInSlice([]string{"no", "yes", "candidate"}, false), + }, + "type": { + Type: schema.TypeString, + Required: true, + Default: "default", + Description: "The area type.", + ValidateFunc: validation.StringInSlice([]string{"default", "nssa", "stub"}, true), + }, + } + + return &schema.Resource{ + CreateContext: DefaultCreate(resSchema), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultUpdate(resSchema), + DeleteContext: DefaultDelete(resSchema), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_routing_ospf_area_test.go b/routeros/resource_routing_ospf_area_test.go new file mode 100644 index 00000000..1ec6ac5b --- /dev/null +++ b/routeros/resource_routing_ospf_area_test.go @@ -0,0 +1,66 @@ +package routeros + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +const testRoutingOspfArea = "routeros_routing_ospf_area.test_routing_ospf_area" + +func TestAccRoutingOspfInstanceArea_basic(t *testing.T) { + for _, name := range testNames { + t.Run(name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testSetTransportEnv(t, name) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testCheckResourceDestroy("/routing/ospf/area", "routeros_routing_ospf_area"), + Steps: []resource.TestStep{ + { + Config: testAccCheckRoutingOspfAreaConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoutingOspfAreaExists(testRoutingOspfArea), + resource.TestCheckResourceAttr(testRoutingOspfArea, "name", "test_routing_ospf_area"), + ), + }, + }, + }) + + }) + } +} + +func testAccCheckRoutingOspfAreaExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no id is set") + } + + return nil + } +} + +func testAccCheckRoutingOspfAreaConfig() string { + return ` + +provider "routeros" { + insecure = true +} + +resource "routeros_routing_ospf_area" "test_routing_ospf_area" { + name = "test_routing_ospf_area" + disabled = true +} + +` +} diff --git a/routeros/resource_routing_ospf_instance.go b/routeros/resource_routing_ospf_instance.go index 2b9ad84c..63a6b0b6 100644 --- a/routeros/resource_routing_ospf_instance.go +++ b/routeros/resource_routing_ospf_instance.go @@ -8,7 +8,7 @@ import ( /* { ".id": "*54", - "name": "", + "name": "", "domain-id": "", "domain-tag": "", "in-filter": "", @@ -35,14 +35,9 @@ func ResourceRoutingOspfInstance() *schema.Resource { KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "domain-id": { - Type: schema.TypeString, - Optional: true, - Description: "MPLS-related parameter. Identifies the OSPF domain of the instance. " + - "This value is attached to OSPF routes redistributed in BGP as VPNv4 routes as BGP " + - "extended community attribute and used when BGP VPNv4 routes are " + - "redistributed back to OSPF to determine whether to generate " + - "inter-area or AS-external LSA for that route. " + - "By default Null domain-id is used, as described in RFC 4577.", + Type: schema.TypeString, + Optional: true, + Description: "MPLS-related parameter.", }, "domain-tag": { Type: schema.TypeInt, @@ -57,16 +52,14 @@ func ResourceRoutingOspfInstance() *schema.Resource { Description: "name of the routing filter chain used for incoming prefixes", }, "mpls-te-address": { - Type: schema.TypeString, - Optional: true, - Description: "the area used for MPLS traffic engineering. TE Opaque LSAs are generated in this area. " + - "No more than one OSPF instance can have mpls-te-area configured.", + Type: schema.TypeString, + Optional: true, + Description: "the area used for MPLS traffic engineering.", }, "mpls-te-area": { - Type: schema.TypeString, - Optional: true, - Description: "the area used for MPLS traffic engineering. TE Opaque LSAs are generated in this area. " + - "No more than one OSPF instance can have mpls-te-area configured.", + Type: schema.TypeString, + Optional: true, + Description: "the area used for MPLS traffic engineering.", }, "originate-default": { Type: schema.TypeString, @@ -74,16 +67,14 @@ func ResourceRoutingOspfInstance() *schema.Resource { Description: "Specifies default route (0.0.0.0/0) distribution method.", }, "out-filter-chain": { - Type: schema.TypeString, - Optional: true, - Description: "name of the routing filter chain used for outgoing prefixes filtering. " + - "Output operates only with \"external\" routes.", + Type: schema.TypeString, + Optional: true, + Description: "name of the routing filter chain used for outgoing prefixes filtering.", }, "out-filter-select": { - Type: schema.TypeString, - Optional: true, - Description: "name of the routing filter select chain, used for output selection. " + - "Output operates only with \"external\" routes.", + Type: schema.TypeString, + Optional: true, + Description: "name of the routing filter select chain, used for output selection.", }, "redistribute": { Type: schema.TypeString, diff --git a/routeros/resource_routing_ospf_interface_template.go b/routeros/resource_routing_ospf_interface_template.go new file mode 100644 index 00000000..5400363c --- /dev/null +++ b/routeros/resource_routing_ospf_interface_template.go @@ -0,0 +1,146 @@ +package routeros + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +/* + { + ".id": "*54", + "interfaces": "", + "network": "", + "area": "", + "auth": "", + "auth-id": "", + "authentication-key": "", + "comment": "", + "cost": "", + "dead-interval": "", + "disabled": "", + "hello-interval": "", + "instance-id": "", + "passive": "", + "prefix-list": "", + "priority": "", + "retransmit-interval": "", + "transmit-delay": "", + "type": "", + "vlink-neighbor-id": "", + "vlink-transit-area": "", + } +*/ + +// ResourceRoutingOspfInterfaceTemplate https://help.mikrotik.com/docs/display/ROS/OSPF +func ResourceRoutingOspfInterfaceTemplate() *schema.Resource { + resSchema := map[string]*schema.Schema{ + MetaResourcePath: PropResourcePath("/routing/ospf/interface-template"), + MetaId: PropId(Name), + + KeyComment: PropCommentRw, + KeyDisabled: PropDisabledRw, + "interfaces": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "Interfaces to match.", + }, + "network": { + Type: schema.TypeString, + Description: "The network prefix associated with the area.", + }, + "area": { + Type: schema.TypeString, + Required: true, + Description: "The OSPF area to which the matching interface will be associated.", + }, + "auth": { + Type: schema.TypeString, + Description: "Specifies authentication method for OSPF protocol messages.", + ValidateFunc: validation.StringInSlice([]string{"simple", "md5", "sha1", "sha256", "sha384", "sha512"}, true), + }, + "auth-id": { + Type: schema.TypeInt, + Description: "The key id is used to calculate message digest (used when MD5 or SHA authentication is enabled).", + }, + "authentication-key": { + Type: schema.TypeString, + Description: "The authentication key to be used, should match on all the neighbors of the network segment.", + }, + "cost": { + Type: schema.TypeInt, + Default: 1, + Description: "Interface cost expressed as link state metric.", + ValidateFunc: validation.IntBetween(0, 65535), + }, + "dead-interval": { + Type: schema.TypeString, + Default: "00:00:40", + Description: "Specifies the interval after which a neighbor is declared dead.", + }, + "hello-interval": { + Type: schema.TypeString, + Default: "00:00:10", + Description: "The interval between HELLO packets that the router sends out this interface.", + }, + "instance-id": { + Type: schema.TypeInt, + Description: "Interface cost expressed as link state metric.", + Default: 0, + ValidateFunc: validation.IntBetween(0, 255), + }, + "passive": { + Type: schema.TypeBool, + Default: false, + Description: "If enabled, then do not send or receive OSPF traffic on the matching interfaces", + }, + "prefix-list": { + Type: schema.TypeString, + Description: "Name of the address list containing networks that should be advertised to the v3 interface.", + }, + "priority": { + Type: schema.TypeInt, + Description: "Router's priority. Used to determine the designated router in a broadcast network.", + Default: 128, + ValidateFunc: validation.IntBetween(0, 255), + }, + "retransmit-interval": { + Type: schema.TypeString, + Default: "00:00:05", + Description: "Time interval the lost link state advertisement will be resent.", + }, + "transmit-delay": { + Type: schema.TypeString, + Default: "00:00:01", + Description: "Link-state transmit delay is the estimated time it takes to transmit a link-state update packet on the interface.", + }, + "type": { + Type: schema.TypeString, + Description: "The OSPF network type on this interface.", + Default: "broadcast", + ValidateFunc: validation.StringInSlice([]string{"broadcast", "nbma", "ptp", "ptmp", "ptp-unnumbered", "virtual-link"}, true), + }, + "vlink-neighbor-id": { + Type: schema.TypeString, + Description: "Specifies the router-id of the neighbor which should be connected over the virtual link.", + }, + "vlink-transit-area": { + Type: schema.TypeString, + Description: "A non-backbone area the two routers have in common over which the virtual link will be established.", + }, + } + + return &schema.Resource{ + CreateContext: DefaultCreate(resSchema), + ReadContext: DefaultRead(resSchema), + UpdateContext: DefaultUpdate(resSchema), + DeleteContext: DefaultDelete(resSchema), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: resSchema, + } +} diff --git a/routeros/resource_routing_ospf_interface_template_test.go b/routeros/resource_routing_ospf_interface_template_test.go new file mode 100644 index 00000000..c76c38dc --- /dev/null +++ b/routeros/resource_routing_ospf_interface_template_test.go @@ -0,0 +1,70 @@ +package routeros + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +const testRoutingOspfInterfaceTemplate = "routeros_routing_ospf_interface_template.test_routing_ospf_interface_template" + +func TestAccRoutingOspfInterfaceTemplateTest_basic(t *testing.T) { + for _, name := range testNames { + t.Run(name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testSetTransportEnv(t, name) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testCheckResourceDestroy("/routing/ospf/interface-template", "routeros_routing_ospf_interface_template"), + Steps: []resource.TestStep{ + { + Config: testAccCheckRoutingOspfInterfaceTemplateConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoutingOspfInterfaceTemplateExists(testRoutingOspfInterfaceTemplate), + resource.TestCheckResourceAttr(testRoutingOspfInterfaceTemplate, "name", "test_routing_ospf_interface_template"), + ), + }, + }, + }) + + }) + } +} + +func testAccCheckRoutingOspfInterfaceTemplateExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no id is set") + } + + return nil + } +} + +func testAccCheckRoutingOspfInterfaceTemplateConfig() string { + return ` + +provider "routeros" { + insecure = true +} + +resource "routeros_routing_ospf_area" "test_routing_ospf_area" { + name = "test_routing_ospf_area" + disabled = true +} + +resource "routeros_routing_ospf_interface_template" "test_routing_ospf_interface_template" { + area = routeros_routing_ospf_area.test_routing_ospf_area.name +} + +` +}