From 2da3b53e6104ce6965466d9cb515c840628fe86c Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 22 Jan 2026 10:27:16 -0500 Subject: [PATCH 1/4] [minor_change] Addition of resource and data source for tenant_policies_l3out_node_routing_policy --- .../main.tf | 50 ++ ...nant_policies_l3out_node_routing_policy.go | 128 +++++ ...policies_l3out_node_routing_policy_test.go | 49 ++ mso/provider.go | 2 + ...nant_policies_l3out_node_routing_policy.go | 487 ++++++++++++++++++ ...policies_l3out_node_routing_policy_test.go | 295 +++++++++++ ...es_l3out_node_routing_policy.html.markdown | 47 ++ ...es_l3out_node_routing_policy.html.markdown | 72 +++ 8 files changed, 1130 insertions(+) create mode 100644 examples/tenant_policies_l3out_node_routing_policy/main.tf create mode 100644 mso/datasource_mso_tenant_policies_l3out_node_routing_policy.go create mode 100644 mso/datasource_mso_tenant_policies_l3out_node_routing_policy_test.go create mode 100644 mso/resource_mso_tenant_policies_l3out_node_routing_policy.go create mode 100644 mso/resource_mso_tenant_policies_l3out_node_routing_policy_test.go create mode 100644 website/docs/d/tenant_policies_l3out_node_routing_policy.html.markdown create mode 100644 website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown diff --git a/examples/tenant_policies_l3out_node_routing_policy/main.tf b/examples/tenant_policies_l3out_node_routing_policy/main.tf new file mode 100644 index 00000000..8d5bb897 --- /dev/null +++ b/examples/tenant_policies_l3out_node_routing_policy/main.tf @@ -0,0 +1,50 @@ +terraform { + required_providers { + mso = { + source = "CiscoDevNet/mso" + } + } +} + +provider "mso" { + username = "" # + password = "" # + url = "" # + insecure = true +} + +data "mso_tenant" "example_tenant" { + name = "example_tenant" +} + +# Tenant template example + +resource "mso_template" "tenant_template" { + template_name = "tenant_template" + template_type = "tenant" + tenant_id = data.mso_tenant.example_tenant.id +} + +# L3Out Node Routing Policy example with all configuration blocks + +resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy_full" { + template_id = mso_template.tenant_template.id + name = "production_node_routing_policy" + description = "Production L3Out Node Routing Policy with BFD and BGP" + as_path_multipath_relax = true + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 3 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = true + keep_alive_interval = 60 + hold_interval = 180 + stale_interval = 300 + max_as_limit = 0 + } +} diff --git a/mso/datasource_mso_tenant_policies_l3out_node_routing_policy.go b/mso/datasource_mso_tenant_policies_l3out_node_routing_policy.go new file mode 100644 index 00000000..2e7a2694 --- /dev/null +++ b/mso/datasource_mso_tenant_policies_l3out_node_routing_policy.go @@ -0,0 +1,128 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSOL3OutNodeRoutingPolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSOL3OutNodeRoutingPolicyRead, + + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + Description: "The ID of the tenant policy template.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the L3Out Node Routing Policy.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the L3Out Node Routing Policy.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the L3Out Node Routing Policy.", + }, + "as_path_multipath_relax": { + Type: schema.TypeBool, + Computed: true, + Description: "BGP Best Path Control - AS path multipath relax.", + }, + "bfd_multi_hop_settings": { + Type: schema.TypeList, + Computed: true, + Description: "BFD multi-hop configuration.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "admin_state": { + Type: schema.TypeString, + Computed: true, + Description: "Administrative state.", + }, + "detection_multiplier": { + Type: schema.TypeInt, + Computed: true, + Description: "Detection multiplier.", + }, + "min_receive_interval": { + Type: schema.TypeInt, + Computed: true, + Description: "Minimum receive interval in microseconds.", + }, + "min_transmit_interval": { + Type: schema.TypeInt, + Computed: true, + Description: "Minimum transmit interval in microseconds.", + }, + }, + }, + }, + "bgp_node_settings": { + Type: schema.TypeList, + Computed: true, + Description: "BGP node configuration.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "graceful_restart_helper": { + Type: schema.TypeBool, + Computed: true, + Description: "Graceful restart helper mode.", + }, + "keep_alive_interval": { + Type: schema.TypeInt, + Computed: true, + Description: "BGP keepalive interval in seconds.", + }, + "hold_interval": { + Type: schema.TypeInt, + Computed: true, + Description: "BGP hold interval in seconds.", + }, + "stale_interval": { + Type: schema.TypeInt, + Computed: true, + Description: "BGP stale interval in seconds.", + }, + "max_as_limit": { + Type: schema.TypeInt, + Computed: true, + Description: "Maximum AS path limit.", + }, + }, + }, + }, + }, + } +} + +func dataSourceMSOL3OutNodeRoutingPolicyRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Data Source - Beginning Read") + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + policyName := d.Get("name").(string) + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "l3OutNodePolGroups") + if err != nil { + return err + } + + setL3OutNodeRoutingPolicyData(d, policy, templateId) + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_l3out_node_routing_policy_test.go b/mso/datasource_mso_tenant_policies_l3out_node_routing_policy_test.go new file mode 100644 index 00000000..f27bbe3a --- /dev/null +++ b/mso/datasource_mso_tenant_policies_l3out_node_routing_policy_test.go @@ -0,0 +1,49 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesL3OutNodeRoutingPolicyDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: L3Out Node Routing Policy Data Source") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyDataSource(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "description", "Test L3Out Node Routing Policy"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "uuid"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "template_id"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "as_path_multipath_relax", "false"), + + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.#", "1"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.admin_state", "enabled"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.detection_multiplier", "3"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_receive_interval", "250"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_transmit_interval", "250"), + + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.#", "1"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.graceful_restart_helper", "true"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.keep_alive_interval", "60"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.hold_interval", "180"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.stale_interval", "300"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.max_as_limit", "0"), + ), + }, + }, + }) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyDataSource() string { + return fmt.Sprintf(`%s + data "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_tenant_policies_l3out_node_routing_policy.node_policy.template_id + name = "test_node_routing_policy" + }`, testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigCreate()) +} diff --git a/mso/provider.go b/mso/provider.go index 18d1c7ab..8a7dc3c6 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -142,6 +142,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_ipsla_track_list": resourceMSOIPSLATrackList(), "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), + "mso_tenant_policies_l3out_node_routing_policy": resourceMSOL3OutNodeRoutingPolicy(), }, DataSourcesMap: map[string]*schema.Resource{ @@ -217,6 +218,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_ipsla_track_list": datasourceMSOIPSLATrackList(), "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), + "mso_tenant_policies_l3out_node_routing_policy": datasourceMSOL3OutNodeRoutingPolicy(), }, ConfigureFunc: configureClient, diff --git a/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go b/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go new file mode 100644 index 00000000..67fd223a --- /dev/null +++ b/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go @@ -0,0 +1,487 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/ciscoecosystem/mso-go-client/container" + "github.com/ciscoecosystem/mso-go-client/models" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceMSOL3OutNodeRoutingPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceMSOL3OutNodeRoutingPolicyCreate, + Read: resourceMSOL3OutNodeRoutingPolicyRead, + Update: resourceMSOL3OutNodeRoutingPolicyUpdate, + Delete: resourceMSOL3OutNodeRoutingPolicyDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSOL3OutNodeRoutingPolicyImport, + }, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + Description: "The ID of the tenant policy template.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + Description: "The name of the L3Out Node Routing Policy.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The description of the L3Out Node Routing Policy.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the L3Out Node Routing Policy.", + }, + "as_path_multipath_relax": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "BGP Best Path Control - AS path multipath relax. Allows load balancing across paths with different AS paths.", + }, + "bfd_multi_hop_settings": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "BFD multi-hop configuration.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "admin_state": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "enabled", "disabled", + }, false), + Description: "Administrative state. Default: enabled when unset during creation.", + }, + "detection_multiplier": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 50), + Description: "Detection multiplier. Default: 3 when unset during creation. Range: 1-50.", + }, + "min_receive_interval": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(250, 999), + Description: "Minimum receive interval in microseconds. Default: 250 when unset during creation. Range: 250-999.", + }, + "min_transmit_interval": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(250, 999), + Description: "Minimum transmit interval in microseconds. Default: 250 when unset during creation. Range: 250-999.", + }, + }, + }, + }, + "bgp_node_settings": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "BGP node configuration.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "graceful_restart_helper": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Enable graceful restart helper mode. Default: true (enabled) when unset during creation.", + }, + "keep_alive_interval": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(0, 3600), + Description: "BGP keepalive interval in seconds. Default: 60 when unset during creation. Range: 0-3600.", + }, + "hold_interval": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntAtLeast(0), + Description: "BGP hold interval in seconds. Default: 180 when unset during creation. Must be 0 or between 3-3600.", + }, + "stale_interval": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 3600), + Description: "BGP stale interval in seconds for graceful restart. Default: 300 when unset during creation. Range: 1-3600.", + }, + "max_as_limit": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(0, 2000), + Description: "Maximum AS path limit. Default: 0 (no limit) when unset during creation. Range: 0-2000.", + }, + }, + }, + }, + }, + } +} + +func buildNodeBFDMultiHopPayload(bfdMultiHop []interface{}) map[string]interface{} { + if len(bfdMultiHop) == 0 { + return nil + } + + settings := bfdMultiHop[0].(map[string]interface{}) + payload := make(map[string]interface{}) + + if adminState, ok := settings["admin_state"].(string); ok && adminState != "" { + payload["adminState"] = adminState + } + if detectionMultiplier, ok := settings["detection_multiplier"].(int); ok && detectionMultiplier != 0 { + payload["detectionMultiplier"] = detectionMultiplier + } + if minRxInterval, ok := settings["min_receive_interval"].(int); ok && minRxInterval != 0 { + payload["minRxInterval"] = minRxInterval + } + if minTxInterval, ok := settings["min_transmit_interval"].(int); ok && minTxInterval != 0 { + payload["minTxInterval"] = minTxInterval + } + + return payload +} + +func buildBGPNodeSettingsPayload(bgpSettings []interface{}) map[string]interface{} { + if len(bgpSettings) == 0 { + return nil + } + + settings := bgpSettings[0].(map[string]interface{}) + payload := make(map[string]interface{}) + + if gracefulRestart, ok := settings["graceful_restart_helper"].(bool); ok { + payload["gracefulRestartHelper"] = gracefulRestart + } + if keepAlive, ok := settings["keep_alive_interval"].(int); ok && keepAlive != 0 { + payload["keepAliveInterval"] = keepAlive + } + if holdInterval, ok := settings["hold_interval"].(int); ok { + payload["holdInterval"] = holdInterval + } + if staleInterval, ok := settings["stale_interval"].(int); ok && staleInterval != 0 { + payload["staleInterval"] = staleInterval + } + if maxAsLimit, ok := settings["max_as_limit"].(int); ok { + payload["maxAslimit"] = maxAsLimit + } + + return payload +} + +func setL3OutNodeRoutingPolicyData(d *schema.ResourceData, response *container.Container, templateId string) error { + d.SetId(fmt.Sprintf("templateId/%s/L3OutNodeRoutingPolicy/%s", templateId, models.StripQuotes(response.S("name").String()))) + d.Set("template_id", templateId) + d.Set("name", models.StripQuotes(response.S("name").String())) + d.Set("description", models.StripQuotes(response.S("description").String())) + d.Set("uuid", models.StripQuotes(response.S("uuid").String())) + + if response.Exists("asPathPol") && response.S("asPathPol").Exists("asPathMultipathRelax") { + if val, ok := response.S("asPathPol").S("asPathMultipathRelax").Data().(bool); ok { + d.Set("as_path_multipath_relax", val) + } + } else { + d.Set("as_path_multipath_relax", nil) + } + + if response.Exists("bfdMultiHopPol") { + bfdMultiHop := response.S("bfdMultiHopPol") + bfdMultiHopMap := map[string]interface{}{} + + if bfdMultiHop.Exists("adminState") { + bfdMultiHopMap["admin_state"] = models.StripQuotes(bfdMultiHop.S("adminState").String()) + } + if bfdMultiHop.Exists("detectionMultiplier") { + if val, ok := bfdMultiHop.S("detectionMultiplier").Data().(float64); ok { + bfdMultiHopMap["detection_multiplier"] = int(val) + } + } + if bfdMultiHop.Exists("minRxInterval") { + if val, ok := bfdMultiHop.S("minRxInterval").Data().(float64); ok { + bfdMultiHopMap["min_receive_interval"] = int(val) + } + } + if bfdMultiHop.Exists("minTxInterval") { + if val, ok := bfdMultiHop.S("minTxInterval").Data().(float64); ok { + bfdMultiHopMap["min_transmit_interval"] = int(val) + } + } + + d.Set("bfd_multi_hop_settings", []interface{}{bfdMultiHopMap}) + } else { + d.Set("bfd_multi_hop_settings", []interface{}{}) + } + + if response.Exists("bgpTimerPol") { + bgpTimer := response.S("bgpTimerPol") + bgpMap := map[string]interface{}{} + + if bgpTimer.Exists("gracefulRestartHelper") { + if val, ok := bgpTimer.S("gracefulRestartHelper").Data().(bool); ok { + bgpMap["graceful_restart_helper"] = val + } + } + if bgpTimer.Exists("keepAliveInterval") { + if val, ok := bgpTimer.S("keepAliveInterval").Data().(float64); ok { + bgpMap["keep_alive_interval"] = int(val) + } + } + if bgpTimer.Exists("holdInterval") { + if val, ok := bgpTimer.S("holdInterval").Data().(float64); ok { + bgpMap["hold_interval"] = int(val) + } + } + if bgpTimer.Exists("staleInterval") { + if val, ok := bgpTimer.S("staleInterval").Data().(float64); ok { + bgpMap["stale_interval"] = int(val) + } + } + if bgpTimer.Exists("maxAslimit") { + if val, ok := bgpTimer.S("maxAslimit").Data().(float64); ok { + bgpMap["max_as_limit"] = int(val) + } + } + + d.Set("bgp_node_settings", []interface{}{bgpMap}) + } else { + d.Set("bgp_node_settings", []interface{}{}) + } + + return nil +} + +func resourceMSOL3OutNodeRoutingPolicyImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Beginning Import: %v", d.Id()) + resourceMSOL3OutNodeRoutingPolicyRead(d, m) + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSOL3OutNodeRoutingPolicyCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + hasBFDMultiHop := len(d.Get("bfd_multi_hop_settings").([]interface{})) > 0 + hasBGP := len(d.Get("bgp_node_settings").([]interface{})) > 0 + hasASPath := d.Get("as_path_multipath_relax") != nil + + if !hasBFDMultiHop && !hasBGP && !hasASPath { + return fmt.Errorf("at least one of 'bfd_multi_hop_settings', 'bgp_node_settings', or 'as_path_multipath_relax' must be specified") + } + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + } + + if description, ok := d.GetOk("description"); ok { + payload["description"] = description.(string) + } + + if asPath, ok := d.GetOk("as_path_multipath_relax"); ok { + payload["asPathPol"] = map[string]interface{}{ + "asPathMultipathRelax": asPath.(bool), + } + } + + if bfdMultiHopRaw, ok := d.GetOk("bfd_multi_hop_settings"); ok { + if bfdMultiHopPayload := buildNodeBFDMultiHopPayload(bfdMultiHopRaw.([]interface{})); bfdMultiHopPayload != nil { + payload["bfdMultiHopPol"] = bfdMultiHopPayload + } + } + + if bgpRaw, ok := d.GetOk("bgp_node_settings"); ok { + if bgpPayload := buildBGPNodeSettingsPayload(bgpRaw.([]interface{})); bgpPayload != nil { + payload["bgpTimerPol"] = bgpPayload + } + } + + payloadModel := models.GetPatchPayload("add", "/tenantPolicyTemplate/template/l3OutNodePolGroups/-", payload) + templateId := d.Get("template_id").(string) + + _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/L3OutNodeRoutingPolicy/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Create Complete: %v", d.Id()) + return resourceMSOL3OutNodeRoutingPolicyRead(d, m) +} + +func resourceMSOL3OutNodeRoutingPolicyRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Beginning Read: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId, err := GetTemplateIdFromResourceId(d.Id()) + if err != nil { + return err + } + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyName, err := GetPolicyNameFromResourceId(d.Id(), "L3OutNodeRoutingPolicy") + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "l3OutNodePolGroups") + if err != nil { + return err + } + + setL3OutNodeRoutingPolicyData(d, policy, templateId) + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSOL3OutNodeRoutingPolicyUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Beginning Update: %v", d.Id()) + msoClient := m.(*client.Client) + templateId := d.Get("template_id").(string) + + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "tenantPolicyTemplate", "template", "l3OutNodePolGroups") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/tenantPolicyTemplate/template/l3OutNodePolGroups/%d", policyIndex) + + payloadCont := container.New() + payloadCont.Array() + + if d.HasChange("name") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", updatePath), d.Get("name").(string)) + if err != nil { + return err + } + } + + if d.HasChange("description") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)) + if err != nil { + return err + } + } + + if d.HasChange("as_path_multipath_relax") { + _, newVal := d.GetChange("as_path_multipath_relax") + + if newVal == nil { + err := addPatchPayloadToContainer(payloadCont, "remove", fmt.Sprintf("%s/asPathPol", updatePath), nil) + if err != nil { + return err + } + } else { + asPathPayload := map[string]interface{}{ + "asPathMultipathRelax": newVal.(bool), + } + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/asPathPol", updatePath), asPathPayload) + if err != nil { + return err + } + } + } + + if d.HasChange("bfd_multi_hop_settings") { + _, newVal := d.GetChange("bfd_multi_hop_settings") + bfdMultiHopRaw := newVal.([]interface{}) + + if len(bfdMultiHopRaw) == 0 { + err := addPatchPayloadToContainer(payloadCont, "remove", fmt.Sprintf("%s/bfdMultiHopPol", updatePath), nil) + if err != nil { + return err + } + } else { + if bfdMultiHopPayload := buildNodeBFDMultiHopPayload(bfdMultiHopRaw); bfdMultiHopPayload != nil { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/bfdMultiHopPol", updatePath), bfdMultiHopPayload) + if err != nil { + return err + } + } + } + } + + if d.HasChange("bgp_node_settings") { + _, newVal := d.GetChange("bgp_node_settings") + bgpRaw := newVal.([]interface{}) + + if len(bgpRaw) == 0 { + err := addPatchPayloadToContainer(payloadCont, "remove", fmt.Sprintf("%s/bgpTimerPol", updatePath), nil) + if err != nil { + return err + } + } else { + if bgpPayload := buildBGPNodeSettingsPayload(bgpRaw); bgpPayload != nil { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/bgpTimerPol", updatePath), bgpPayload) + if err != nil { + return err + } + } + } + } + + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/L3OutNodeRoutingPolicy/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Update Complete: %v", d.Id()) + return resourceMSOL3OutNodeRoutingPolicyRead(d, m) +} + +func resourceMSOL3OutNodeRoutingPolicyDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Beginning Delete: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "tenantPolicyTemplate", "template", "l3OutNodePolGroups") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/tenantPolicyTemplate/template/l3OutNodePolGroups/%d", policyIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO L3Out Node Routing Policy Resource - Delete Complete: %v", d.Id()) + return nil +} diff --git a/mso/resource_mso_tenant_policies_l3out_node_routing_policy_test.go b/mso/resource_mso_tenant_policies_l3out_node_routing_policy_test.go new file mode 100644 index 00000000..b292a0eb --- /dev/null +++ b/mso/resource_mso_tenant_policies_l3out_node_routing_policy_test.go @@ -0,0 +1,295 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesL3OutNodeRoutingPolicyResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Create L3Out Node Routing Policy with BFD and BGP") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "description", "Test L3Out Node Routing Policy"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_l3out_node_routing_policy.node_policy", "uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "as_path_multipath_relax", "false"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.#", "1"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.admin_state", "enabled"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.detection_multiplier", "3"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_receive_interval", "250"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_transmit_interval", "250"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.#", "1"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.graceful_restart_helper", "true"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.keep_alive_interval", "60"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.hold_interval", "180"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.stale_interval", "300"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.max_as_limit", "0"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Modify BGP Settings") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigUpdateBGP(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "description", "Updated BGP settings"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.graceful_restart_helper", "false"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.keep_alive_interval", "30"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.hold_interval", "90"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.stale_interval", "180"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.max_as_limit", "10"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Enable AS Path Multipath Relax") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigEnableASPath(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "as_path_multipath_relax", "true"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Remove BFD Multi-Hop") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigRemoveBFD(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.#", "0"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.#", "1"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Remove All Settings") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigRemoveAll(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.#", "0"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.#", "0"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Maximum Values") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigMaxValues(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy_max"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.detection_multiplier", "50"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_receive_interval", "999"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_transmit_interval", "999"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.keep_alive_interval", "3599"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.hold_interval", "3600"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.stale_interval", "3600"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.max_as_limit", "2000"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy - Minimum Values") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigMinValues(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy_min"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.detection_multiplier", "1"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_receive_interval", "250"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bfd_multi_hop_settings.0.min_transmit_interval", "250"), + + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.keep_alive_interval", "1"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.hold_interval", "3"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.stale_interval", "1"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "bgp_node_settings.0.max_as_limit", "0"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update L3Out Node Routing Policy Name") }, + Config: testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigUpdateName(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "name", "test_node_routing_policy_renamed"), + resource.TestCheckResourceAttr("mso_tenant_policies_l3out_node_routing_policy.node_policy", "description", "Renamed Policy"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import L3Out Node Routing Policy") }, + ResourceName: "mso_tenant_policies_l3out_node_routing_policy.node_policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithArguments("mso_tenant_policies_l3out_node_routing_policy", "l3OutNodePolGroup"), + }) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigCreate() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy" + description = "Test L3Out Node Routing Policy" + as_path_multipath_relax = false + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 3 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = true + keep_alive_interval = 60 + hold_interval = 180 + stale_interval = 300 + max_as_limit = 0 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigUpdateBGP() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy" + description = "Updated BGP settings" + as_path_multipath_relax = false + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 3 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = false + keep_alive_interval = 30 + hold_interval = 90 + stale_interval = 180 + max_as_limit = 10 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigEnableASPath() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy" + description = "AS Path Multipath Relax Enabled" + as_path_multipath_relax = true + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 3 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = false + keep_alive_interval = 30 + hold_interval = 90 + stale_interval = 180 + max_as_limit = 10 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigRemoveBFD() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy" + description = "BFD Multi-Hop Removed" + as_path_multipath_relax = true + + bgp_node_settings { + graceful_restart_helper = false + keep_alive_interval = 30 + hold_interval = 90 + stale_interval = 180 + max_as_limit = 10 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigRemoveAll() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy" + description = "All Settings Removed" + as_path_multipath_relax = true + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigMaxValues() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy_max" + description = "Maximum Values Test" + as_path_multipath_relax = true + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 50 + min_receive_interval = 999 + min_transmit_interval = 999 + } + + bgp_node_settings { + graceful_restart_helper = true + keep_alive_interval = 3599 + hold_interval = 3600 + stale_interval = 3600 + max_as_limit = 2000 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigMinValues() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy_min" + description = "Minimum Values Test" + as_path_multipath_relax = false + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 1 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = false + keep_alive_interval = 1 + hold_interval = 3 + stale_interval = 1 + max_as_limit = 0 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesL3OutNodeRoutingPolicyConfigUpdateName() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "test_node_routing_policy_renamed" + description = "Renamed Policy" + as_path_multipath_relax = true + + bgp_node_settings { + graceful_restart_helper = true + keep_alive_interval = 60 + hold_interval = 180 + } + }`, testAccMSOTemplateResourceTenantConfig()) +} diff --git a/website/docs/d/tenant_policies_l3out_node_routing_policy.html.markdown b/website/docs/d/tenant_policies_l3out_node_routing_policy.html.markdown new file mode 100644 index 00000000..82b5d0ba --- /dev/null +++ b/website/docs/d/tenant_policies_l3out_node_routing_policy.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_l3out_node_routing_policy" +sidebar_current: "docs-mso-data-source-tenant_policies_l3out_node_routing_policy" +description: |- + Data source for L3Out Node Routing Policy. +--- + +# mso_tenant_policies_l3out_node_routing_policy # + +Data source for Layer 3 Outside (L3Out) Node Routing Policy. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> L3Out Node Routing Policy + +## Example Usage ## + +```hcl +data "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "production_node_routing_policy" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the L3Out Node Routing Policy to retrieve. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The UUID of the L3Out Node Routing Policy. +* `id` - (Read-Only) The unique Terraform identifier of the L3Out Node Routing Policy in the template. +* `description` - (Read-Only) The description of the L3Out Node Routing Policy. +* `as_path_multipath_relax` - (Read-Only) BGP Best Path Control AS path multipath relax setting. +* `bfd_multi_hop_settings` - (Read-Only) A list containing BFD multi-hop configuration. Empty list if not configured. When present, contains a single element with: + * `admin_state` - (Read-Only) Administrative state. + * `detection_multiplier` - (Read-Only) The number of consecutive BFD packets that must be missed before the session is declared down. + * `min_receive_interval` - (Read-Only) The minimum interval in microseconds between received BFD packets. + * `min_transmit_interval` - (Read-Only) The minimum interval in microseconds between transmitted BFD packets. +* `bgp_node_settings` - (Read-Only) A list containing BGP node configuration. Empty list if not configured. When present, contains a single element with: + * `graceful_restart_helper` - (Read-Only) BGP graceful restart helper mode. + * `keep_alive_interval` - (Read-Only) The BGP keepalive interval in seconds. + * `hold_interval` - (Read-Only) The BGP hold interval in seconds. + * `stale_interval` - (Read-Only) The BGP stale interval in seconds for graceful restart. + * `max_as_limit` - (Read-Only) Maximum AS path limit to prevent routing loops. diff --git a/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown new file mode 100644 index 00000000..902ba35a --- /dev/null +++ b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown @@ -0,0 +1,72 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_l3out_node_routing_policy" +sidebar_current: "docs-mso-resource-tenant_policies_l3out_node_routing_policy" +description: |- + Manages L3Out Node Routing Policies on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_l3out_node_routing_policy # + +Manages Layer 3 Outside (L3Out) Node Routing Policies on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.3 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> L3Out Node Routing Policy + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { + template_id = mso_template.template_tenant.id + name = "production_node_routing_policy" + description = "Production L3Out Node Routing Policy" + as_path_multipath_relax = true + + bfd_multi_hop_settings { + admin_state = "enabled" + detection_multiplier = 3 + min_receive_interval = 250 + min_transmit_interval = 250 + } + + bgp_node_settings { + graceful_restart_helper = true + keep_alive_interval = 60 + hold_interval = 180 + stale_interval = 300 + max_as_limit = 0 + } +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the L3Out Node Routing Policy. +* `description` - (Optional) The description of the L3Out Node Routing Policy. When unset during creation, no description is applied. +* `as_path_multipath_relax` - (Optional) BGP Best Path Control - enables AS path multipath relaxation to allow load balancing across paths with different AS paths. When unset during creation, this setting is not configured. +* `bfd_multi_hop_settings` - (Optional) BFD multi-hop configuration block. Omitting this block will remove BFD multi-hop settings if they exist. +* `admin_state` - (Optional) Administrative state. Default: `enabled` when unset during creation. Valid values: `enabled`, `disabled`. +* `detection_multiplier` - (Optional) The number of consecutive BFD packets that must be missed before the session is declared down. Default: 3 when unset during creation. Valid range: 1-50. +* `min_receive_interval` - (Optional) The minimum interval in microseconds between received BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. +* `min_transmit_interval` - (Optional) The minimum interval in microseconds between transmitted BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. +* `bgp_node_settings` - (Optional) BGP node configuration block. Omitting this block will remove BGP node settings if they exist. +* `graceful_restart_helper` - (Optional) Enable or disable BGP graceful restart helper mode, allowing the router to assist peers during restart. Default: `true` (enabled) when unset during creation. +* `keep_alive_interval` - (Optional) The BGP keepalive interval in seconds. Keepalive messages maintain the BGP session. Default: 60 when unset during creation. Valid range: 0-3600 seconds. +* `hold_interval` - (Optional) The BGP hold interval in seconds. If no message is received within this time, the session is terminated. Default: 180 when unset during creation. Must be 0 or between 3-3600 seconds. +* `stale_interval` - (Optional) The BGP stale interval in seconds for graceful restart. Routes are marked stale after this period. Default: 300 when unset during creation. Valid range: 1-3600 seconds. +* `max_as_limit` - (Optional) Maximum AS path limit to prevent routing loops. A value of 0 means no limit. Default: 0 (no limit) when unset during creation. Valid range: 0-2000. + +## Attribute Reference ## + +* `uuid` - The NDO UUID of the L3Out Node Routing Policy. +* `id` - The unique Terraform identifier of the L3Out Node Routing Policy in the template. + +## Importing ## + +An existing MSO L3Out Node Routing Policy can be [imported][docs-import] into this resource via its ID/path, via the following command: [docs-import]: https://www.terraform.io/docs/import/index.html + +```bash +terraform import mso_tenant_policies_l3out_node_routing_policy.node_policy templateId/{template_id}/L3OutNodeRoutingPolicy/{name} +``` From bc837aa9f370d6937cba616857ab4c0359ad34d4 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 1 Feb 2026 20:29:39 -0500 Subject: [PATCH 2/4] [ignore] Modified sub-section of a few block attributes in the docs for tenant_policies_l3out_node_routing_policy --- ...ies_l3out_node_routing_policy.html.markdown | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown index 902ba35a..972d45a5 100644 --- a/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown +++ b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown @@ -47,16 +47,16 @@ resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { * `description` - (Optional) The description of the L3Out Node Routing Policy. When unset during creation, no description is applied. * `as_path_multipath_relax` - (Optional) BGP Best Path Control - enables AS path multipath relaxation to allow load balancing across paths with different AS paths. When unset during creation, this setting is not configured. * `bfd_multi_hop_settings` - (Optional) BFD multi-hop configuration block. Omitting this block will remove BFD multi-hop settings if they exist. -* `admin_state` - (Optional) Administrative state. Default: `enabled` when unset during creation. Valid values: `enabled`, `disabled`. -* `detection_multiplier` - (Optional) The number of consecutive BFD packets that must be missed before the session is declared down. Default: 3 when unset during creation. Valid range: 1-50. -* `min_receive_interval` - (Optional) The minimum interval in microseconds between received BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. -* `min_transmit_interval` - (Optional) The minimum interval in microseconds between transmitted BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. + * `admin_state` - (Optional) Administrative state. Default: `enabled` when unset during creation. Valid values: `enabled`, `disabled`. + * `detection_multiplier` - (Optional) The number of consecutive BFD packets that must be missed before the session is declared down. Default: 3 when unset during creation. Valid range: 1-50. + * `min_receive_interval` - (Optional) The minimum interval in microseconds between received BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. + * `min_transmit_interval` - (Optional) The minimum interval in microseconds between transmitted BFD packets. Default: 250 when unset during creation. Valid range: 250-999 microseconds. * `bgp_node_settings` - (Optional) BGP node configuration block. Omitting this block will remove BGP node settings if they exist. -* `graceful_restart_helper` - (Optional) Enable or disable BGP graceful restart helper mode, allowing the router to assist peers during restart. Default: `true` (enabled) when unset during creation. -* `keep_alive_interval` - (Optional) The BGP keepalive interval in seconds. Keepalive messages maintain the BGP session. Default: 60 when unset during creation. Valid range: 0-3600 seconds. -* `hold_interval` - (Optional) The BGP hold interval in seconds. If no message is received within this time, the session is terminated. Default: 180 when unset during creation. Must be 0 or between 3-3600 seconds. -* `stale_interval` - (Optional) The BGP stale interval in seconds for graceful restart. Routes are marked stale after this period. Default: 300 when unset during creation. Valid range: 1-3600 seconds. -* `max_as_limit` - (Optional) Maximum AS path limit to prevent routing loops. A value of 0 means no limit. Default: 0 (no limit) when unset during creation. Valid range: 0-2000. + * `graceful_restart_helper` - (Optional) Enable or disable BGP graceful restart helper mode, allowing the router to assist peers during restart. Default: `true` (enabled) when unset during creation. + * `keep_alive_interval` - (Optional) The BGP keepalive interval in seconds. Keepalive messages maintain the BGP session. Default: 60 when unset during creation. Valid range: 0-3600 seconds. + * `hold_interval` - (Optional) The BGP hold interval in seconds. If no message is received within this time, the session is terminated. Default: 180 when unset during creation. Must be 0 or between 3-3600 seconds. + * `stale_interval` - (Optional) The BGP stale interval in seconds for graceful restart. Routes are marked stale after this period. Default: 300 when unset during creation. Valid range: 1-3600 seconds. + * `max_as_limit` - (Optional) Maximum AS path limit to prevent routing loops. A value of 0 means no limit. Default: 0 (no limit) when unset during creation. Valid range: 0-2000. ## Attribute Reference ## From 02f95387392a7f01427bc63cd805440713e096a5 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 4 Mar 2026 09:34:33 -0500 Subject: [PATCH 3/4] [ignore] Addition of valid values for as_path_multipath_relax in the docs for tenant_policies_l3out_node_routing_policy --- .../r/tenant_policies_l3out_node_routing_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown index 972d45a5..ef1d43aa 100644 --- a/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown +++ b/website/docs/r/tenant_policies_l3out_node_routing_policy.html.markdown @@ -45,7 +45,7 @@ resource "mso_tenant_policies_l3out_node_routing_policy" "node_policy" { * `template_id` - (Required) The unique ID of the tenant policy template. * `name` - (Required) The name of the L3Out Node Routing Policy. * `description` - (Optional) The description of the L3Out Node Routing Policy. When unset during creation, no description is applied. -* `as_path_multipath_relax` - (Optional) BGP Best Path Control - enables AS path multipath relaxation to allow load balancing across paths with different AS paths. When unset during creation, this setting is not configured. +* `as_path_multipath_relax` - (Optional) BGP Best Path Control - enables AS path multipath relaxation to allow load balancing across paths with different AS paths. When unset during creation, this setting is not configured. Valid values: `true`, `false`. * `bfd_multi_hop_settings` - (Optional) BFD multi-hop configuration block. Omitting this block will remove BFD multi-hop settings if they exist. * `admin_state` - (Optional) Administrative state. Default: `enabled` when unset during creation. Valid values: `enabled`, `disabled`. * `detection_multiplier` - (Optional) The number of consecutive BFD packets that must be missed before the session is declared down. Default: 3 when unset during creation. Valid range: 1-50. From d3baac31ee0ff3e3284851ba622275d6875ce0f1 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 29 Mar 2026 21:31:52 -0400 Subject: [PATCH 4/4] [ignore] Added ability to delete a value for description from the TF configuration by making computed false --- mso/resource_mso_tenant_policies_l3out_node_routing_policy.go | 1 - 1 file changed, 1 deletion(-) diff --git a/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go b/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go index 67fd223a..3cb4432e 100644 --- a/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go +++ b/mso/resource_mso_tenant_policies_l3out_node_routing_policy.go @@ -37,7 +37,6 @@ func resourceMSOL3OutNodeRoutingPolicy() *schema.Resource { "description": { Type: schema.TypeString, Optional: true, - Computed: true, Description: "The description of the L3Out Node Routing Policy.", }, "uuid": {