diff --git a/examples/tenant_policies_route_map_policy_route_control/main.tf b/examples/tenant_policies_route_map_policy_route_control/main.tf new file mode 100644 index 00000000..0922613a --- /dev/null +++ b/examples/tenant_policies_route_map_policy_route_control/main.tf @@ -0,0 +1,32 @@ +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 +} + +resource "mso_tenant_policies_route_map_policy_route_control" "route_map_policy" { + template_id = mso_template.tenant_template.id + name = "example_route_map_policy" + description = "Example Route Map Policy" +} diff --git a/examples/tenant_policies_route_map_policy_route_control_context/main.tf b/examples/tenant_policies_route_map_policy_route_control_context/main.tf new file mode 100644 index 00000000..096729e3 --- /dev/null +++ b/examples/tenant_policies_route_map_policy_route_control_context/main.tf @@ -0,0 +1,40 @@ +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 +} + +resource "mso_tenant_policies_route_map_policy_route_control" "route_map_policy" { + template_id = mso_template.tenant_template.id + name = "example_route_map_policy" + description = "Example Route Map Policy" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.route_map_policy.id + name = "example_context" + description = "Example Route Control Context" + order = 1 + action = "permit" +} diff --git a/mso/datasource_mso_tenant_policies_route_map_policy_route_control.go b/mso/datasource_mso_tenant_policies_route_map_policy_route_control.go new file mode 100644 index 00000000..0759f3a2 --- /dev/null +++ b/mso/datasource_mso_tenant_policies_route_map_policy_route_control.go @@ -0,0 +1,61 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSORouteMapPolicy() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSORouteMapPolicyRead, + + 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 Route Map Policy.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the Route Map Policy.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the Route Map Policy.", + }, + }, + } +} + +func dataSourceMSORouteMapPolicyRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map 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", "routeMapPolicies") + if err != nil { + return err + } + + setRouteMapPolicyData(d, policy, templateId) + + log.Printf("[DEBUG] MSO Route Map Policy Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context.go b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context.go new file mode 100644 index 00000000..e03645d2 --- /dev/null +++ b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context.go @@ -0,0 +1,141 @@ +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" +) + +func datasourceMSORouteMapPolicyContext() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSORouteMapPolicyContextRead, + + Schema: map[string]*schema.Schema{ + "parent_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "action": { + Type: schema.TypeString, + Computed: true, + }, + "order": { + Type: schema.TypeInt, + Computed: true, + }, + "match_rule_uuids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "set_rule_uuid": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceMSORouteMapPolicyContextRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Context Data Source - Beginning Read") + msoClient := m.(*client.Client) + parentId := d.Get("parent_id").(string) + contextName := d.Get("name").(string) + + templateId, err := GetTemplateIdFromResourceId(parentId) + if err != nil { + return err + } + policyName, err := GetPolicyNameFromResourceId(parentId, "RouteMapPolicy") + if err != nil { + return err + } + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + contexts := policy.S("contexts") + count, _ := contexts.ArrayCount() + + var match *container.Container + for i := 0; i < count; i++ { + c := contexts.Index(i) + if models.StripQuotes(c.S("name").String()) == contextName { + match = c + break + } + } + + if match == nil { + return fmt.Errorf("Route Map Policy Context '%s' not found in parent policy '%s'", contextName, policyName) + } + + d.SetId(fmt.Sprintf("%s/context/%s", parentId, models.StripQuotes(match.S("name").String()))) + d.Set("parent_id", parentId) + d.Set("name", contextName) + + if match.Exists("description") { + descValue := models.StripQuotes(match.S("description").String()) + if descValue == "{}" { + d.Set("description", "") + } else { + d.Set("description", descValue) + } + } else { + d.Set("description", "") + } + + if match.Exists("order") { + d.Set("order", int(match.S("order").Data().(float64))) + } + + if match.Exists("action") { + d.Set("action", models.StripQuotes(match.S("action").String())) + } + + if match.Exists("setRuleRef") { + setRuleValue := models.StripQuotes(match.S("setRuleRef").String()) + if setRuleValue == "{}" { + d.Set("set_rule_uuid", "") + } else { + d.Set("set_rule_uuid", setRuleValue) + } + } else { + d.Set("set_rule_uuid", "") + } + + if match.Exists("matchRules") { + apiList, _ := match.S("matchRules").Data().([]interface{}) + uuids := make([]string, len(apiList)) + for i, uuid := range apiList { + uuids[i] = models.StripQuotes(fmt.Sprintf("%v", uuid)) + } + d.Set("match_rule_uuids", uuids) + } else { + d.Set("match_rule_uuids", []string{}) + } + + log.Printf("[DEBUG] MSO Route Map Policy Context Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context_test.go b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context_test.go new file mode 100644 index 00000000..a360059c --- /dev/null +++ b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_context_test.go @@ -0,0 +1,74 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSORouteMapPolicyContextDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Task: Create Resource and Query via Data Source") }, + Config: testAccMSORouteMapPolicyContextDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "parent_id", + "mso_tenant_policies_route_map_policy_route_control_context.test", "parent_id", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "name", + "mso_tenant_policies_route_map_policy_route_control_context.test", "name", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "description", + "mso_tenant_policies_route_map_policy_route_control_context.test", "description", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "action", + "mso_tenant_policies_route_map_policy_route_control_context.test", "action", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "order", + "mso_tenant_policies_route_map_policy_route_control_context.test", "order", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "set_rule_uuid", + "mso_tenant_policies_route_map_policy_route_control_context.test", "set_rule_uuid", + ), + resource.TestCheckResourceAttrPair( + "data.mso_tenant_policies_route_map_policy_route_control_context.test", "match_rules.#", + "mso_tenant_policies_route_map_policy_route_control_context.test", "match_rules.#", + ), + ), + }, + }, + }) +} + +func testAccMSORouteMapPolicyContextDataSourceConfig() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control" "test" { + template_id = mso_template.template_tenant.id + name = "test_route_map_ds" + description = "Route Map for data source test" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "test" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test.id + name = "ctx_ds_1" + description = "Context for data source test" + action = "permit" + order = 1 +} + +data "mso_tenant_policies_route_map_policy_route_control_context" "test" { + parent_id = mso_tenant_policies_route_map_policy_route_control_context.test.parent_id + name = "ctx_ds_1" +} +`, testAccMSOTemplateResourceTenantConfig()) +} diff --git a/mso/datasource_mso_tenant_policies_route_map_policy_route_control_test.go b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_test.go new file mode 100644 index 00000000..518c6ecc --- /dev/null +++ b/mso/datasource_mso_tenant_policies_route_map_policy_route_control_test.go @@ -0,0 +1,47 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSORouteMapPolicyDataSource(t *testing.T) { + resourceName := "mso_tenant_policies_route_map_policy_route_control.test_policy" + dataSourceName := "data.mso_tenant_policies_route_map_policy_route_control.test_ds" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Task: Create Resource and Query via Data Source") }, + Config: testAccMSORouteMapPolicyDataSourceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(dataSourceName, "template_id", resourceName, "template_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "uuid", resourceName, "uuid"), + resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttr(dataSourceName, "name", "test_ds_policy"), + resource.TestCheckResourceAttr(dataSourceName, "description", "Data source test description"), + ), + }, + }, + }) +} + +func testAccMSORouteMapPolicyDataSourceConfig() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control" "test_policy" { + template_id = mso_template.template_tenant.id + name = "test_ds_policy" + description = "Data source test description" +} + +data "mso_tenant_policies_route_map_policy_route_control" "test_ds" { + template_id = mso_tenant_policies_route_map_policy_route_control.test_policy.template_id + name = mso_tenant_policies_route_map_policy_route_control.test_policy.name +} +`, testAccMSOTemplateResourceTenantConfig()) +} diff --git a/mso/provider.go b/mso/provider.go index 18d1c7ab..0c038f2d 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -68,155 +68,159 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "mso_schema": resourceMSOSchema(), - "mso_schema_site": resourceMSOSchemaSite(), - "mso_site": resourceMSOSite(), - "mso_remote_location": resourceMSORemoteLocation(), - "mso_user": resourceMSOUser(), - "mso_label": resourceMSOLabel(), - "mso_schema_template": resourceMSOSchemaTemplate(), - "mso_tenant": resourceMSOTenant(), - "mso_schema_template_bd": resourceMSOTemplateBD(), - "mso_schema_template_vrf": resourceMSOSchemaTemplateVrf(), - "mso_schema_template_bd_subnet": resourceMSOTemplateBDSubnet(), - "mso_schema_template_anp": resourceMSOSchemaTemplateAnp(), - "mso_schema_template_anp_epg": resourceMSOSchemaTemplateAnpEpg(), - "mso_schema_template_anp_epg_contract": resourceMSOTemplateAnpEpgContract(), - "mso_schema_template_contract": resourceMSOTemplateContract(), - "mso_schema_template_anp_epg_subnet": resourceMSOSchemaTemplateAnpEpgSubnet(), - "mso_schema_template_l3out": resourceMSOTemplateL3out(), - "mso_schema_template_external_epg": resourceMSOTemplateExtenalepg(), - "mso_schema_template_contract_filter": resourceMSOTemplateContractFilter(), - "mso_schema_template_external_epg_contract": resourceMSOTemplateExternalEpgContract(), - "mso_schema_template_filter_entry": resourceMSOSchemaTemplateFilterEntry(), - "mso_schema_template_external_epg_subnet": resourceMSOTemplateExtenalepgSubnet(), - "mso_schema_site_anp_epg_static_leaf": resourceMSOSchemaSiteAnpEpgStaticleaf(), - "mso_schema_site_anp_epg_static_port": resourceMSOSchemaSiteAnpEpgStaticPort(), - "mso_schema_site_anp_epg_bulk_staticport": resourceMSOSchemaSiteAnpEpgBulkStaticPort(), - "mso_schema_site_bd": resourceMSOSchemaSiteBd(), - "mso_schema_site_anp_epg_subnet": resourceMSOSchemaSiteAnpEpgSubnet(), - "mso_schema_site_anp_epg_domain": resourceMSOSchemaSiteAnpEpgDomain(), - "mso_schema_site_bd_l3out": resourceMSOSchemaSiteBdL3out(), - "mso_schema_site_vrf": resourceMSOSchemaSiteVrf(), - "mso_schema_site_vrf_route_leak": resourceMSOSchemaSiteVrfRouteLeak(), - "mso_schema_site_vrf_region": resourceMSOSchemaSiteVrfRegion(), - "mso_schema_site_bd_subnet": resourceMSOSchemaSiteBdSubnet(), - "mso_rest": resourceMSORest(), - "mso_schema_template_deploy": resourceMSOSchemaTemplateDeploy(), - "mso_schema_template_deploy_ndo": resourceNDOSchemaTemplateDeploy(), - "mso_schema_site_vrf_region_cidr_subnet": resourceMSOSchemaSiteVrfRegionCidrSubnet(), - "mso_schema_site_vrf_region_cidr": resourceMSOSchemaSiteVrfRegionCidr(), - "mso_schema_site_anp": resourceMSOSchemaSiteAnp(), - "mso_schema_site_anp_epg": resourceMSOSchemaSiteAnpEpg(), - "mso_schema_template_anp_epg_selector": resourceMSOSchemaTemplateAnpEpgSelector(), - "mso_schema_site_external_epg": resourceMSOSchemaSiteExternalEpg(), - "mso_schema_template_external_epg_selector": resourceSchemaTemplateExternalEPGSelector(), - "mso_schema_template_anp_epg_useg_attr": resourceMSOSchemaTemplateAnpEpgUsegAttr(), - "mso_schema_site_anp_epg_selector": resourceMSOSchemaSiteAnpEpgSelector(), - "mso_schema_template_vrf_contract": resourceMSOTemplateVRFContract(), - "mso_schema_site_external_epg_selector": resourceMSOSchemaSiteExternalEpgSelector(), - "mso_schema_template_service_graph": resourceMSOSchemaTemplateServiceGraphs(), - "mso_schema_site_service_graph_node": resourceMSOSchemaSiteServiceGraphNode(), - "mso_schema_site_service_graph": resourceMSOSchemaSiteServiceGraph(), - "mso_service_node_type": resourceMSOServiceNodeType(), - "mso_schema_template_contract_service_graph": resourceMSOSchemaTemplateContractServiceGraph(), - "mso_system_config": resourceMSOSystemConfig(), - "mso_schema_site_contract_service_graph": resourceMSOSchemaSiteContractServiceGraph(), - "mso_schema_site_contract_service_graph_listener": resourceMSOSchemaSiteContractServiceGraphListener(), - "mso_template": resourceMSOTemplate(), - "mso_tenant_policies_ipsla_monitoring_policy": resourceMSOIPSLAMonitoringPolicy(), - "mso_tenant_policies_route_map_policy_multicast": resourceMSOMcastRouteMapPolicy(), - "mso_tenant_policies_dhcp_relay_policy": resourceMSOTenantPoliciesDHCPRelayPolicy(), - "mso_fabric_policies_vlan_pool": resourceMSOVlanPool(), - "mso_fabric_policies_physical_domain": resourceMSOPhysicalDomain(), - "mso_service_device_cluster": resourceMSOServiceDeviceCluster(), - "mso_fabric_policies_synce_interface_policy": resourceMSOSyncEInterfacePolicy(), - "mso_fabric_policies_macsec_policy": resourceMSOMacsecPolicy(), - "mso_schema_template_contract_service_chaining": resourceMSOSchemaTemplateContractServiceChaining(), - "mso_tenant_policies_bgp_peer_prefix_policy": resourceMSOBGPPeerPrefixPolicy(), - "mso_fabric_policies_l3_domain": resourceMSOL3Domain(), - "mso_tenant_policies_custom_qos_policy": resourceMSOCustomQoSPolicy(), - "mso_tenant_policies_dhcp_option_policy": resourceMSODHCPOptionPolicy(), - "mso_tenant_policies_mld_snooping_policy": resourceMSOMLDSnoopingPolicy(), - "mso_fabric_policies_mcp_global_policy": resourceMSOMCPGlobalPolicy(), - "mso_tenant_policies_ipsla_track_list": resourceMSOIPSLATrackList(), - "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), - "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), + "mso_schema": resourceMSOSchema(), + "mso_schema_site": resourceMSOSchemaSite(), + "mso_site": resourceMSOSite(), + "mso_remote_location": resourceMSORemoteLocation(), + "mso_user": resourceMSOUser(), + "mso_label": resourceMSOLabel(), + "mso_schema_template": resourceMSOSchemaTemplate(), + "mso_tenant": resourceMSOTenant(), + "mso_schema_template_bd": resourceMSOTemplateBD(), + "mso_schema_template_vrf": resourceMSOSchemaTemplateVrf(), + "mso_schema_template_bd_subnet": resourceMSOTemplateBDSubnet(), + "mso_schema_template_anp": resourceMSOSchemaTemplateAnp(), + "mso_schema_template_anp_epg": resourceMSOSchemaTemplateAnpEpg(), + "mso_schema_template_anp_epg_contract": resourceMSOTemplateAnpEpgContract(), + "mso_schema_template_contract": resourceMSOTemplateContract(), + "mso_schema_template_anp_epg_subnet": resourceMSOSchemaTemplateAnpEpgSubnet(), + "mso_schema_template_l3out": resourceMSOTemplateL3out(), + "mso_schema_template_external_epg": resourceMSOTemplateExtenalepg(), + "mso_schema_template_contract_filter": resourceMSOTemplateContractFilter(), + "mso_schema_template_external_epg_contract": resourceMSOTemplateExternalEpgContract(), + "mso_schema_template_filter_entry": resourceMSOSchemaTemplateFilterEntry(), + "mso_schema_template_external_epg_subnet": resourceMSOTemplateExtenalepgSubnet(), + "mso_schema_site_anp_epg_static_leaf": resourceMSOSchemaSiteAnpEpgStaticleaf(), + "mso_schema_site_anp_epg_static_port": resourceMSOSchemaSiteAnpEpgStaticPort(), + "mso_schema_site_anp_epg_bulk_staticport": resourceMSOSchemaSiteAnpEpgBulkStaticPort(), + "mso_schema_site_bd": resourceMSOSchemaSiteBd(), + "mso_schema_site_anp_epg_subnet": resourceMSOSchemaSiteAnpEpgSubnet(), + "mso_schema_site_anp_epg_domain": resourceMSOSchemaSiteAnpEpgDomain(), + "mso_schema_site_bd_l3out": resourceMSOSchemaSiteBdL3out(), + "mso_schema_site_vrf": resourceMSOSchemaSiteVrf(), + "mso_schema_site_vrf_route_leak": resourceMSOSchemaSiteVrfRouteLeak(), + "mso_schema_site_vrf_region": resourceMSOSchemaSiteVrfRegion(), + "mso_schema_site_bd_subnet": resourceMSOSchemaSiteBdSubnet(), + "mso_rest": resourceMSORest(), + "mso_schema_template_deploy": resourceMSOSchemaTemplateDeploy(), + "mso_schema_template_deploy_ndo": resourceNDOSchemaTemplateDeploy(), + "mso_schema_site_vrf_region_cidr_subnet": resourceMSOSchemaSiteVrfRegionCidrSubnet(), + "mso_schema_site_vrf_region_cidr": resourceMSOSchemaSiteVrfRegionCidr(), + "mso_schema_site_anp": resourceMSOSchemaSiteAnp(), + "mso_schema_site_anp_epg": resourceMSOSchemaSiteAnpEpg(), + "mso_schema_template_anp_epg_selector": resourceMSOSchemaTemplateAnpEpgSelector(), + "mso_schema_site_external_epg": resourceMSOSchemaSiteExternalEpg(), + "mso_schema_template_external_epg_selector": resourceSchemaTemplateExternalEPGSelector(), + "mso_schema_template_anp_epg_useg_attr": resourceMSOSchemaTemplateAnpEpgUsegAttr(), + "mso_schema_site_anp_epg_selector": resourceMSOSchemaSiteAnpEpgSelector(), + "mso_schema_template_vrf_contract": resourceMSOTemplateVRFContract(), + "mso_schema_site_external_epg_selector": resourceMSOSchemaSiteExternalEpgSelector(), + "mso_schema_template_service_graph": resourceMSOSchemaTemplateServiceGraphs(), + "mso_schema_site_service_graph_node": resourceMSOSchemaSiteServiceGraphNode(), + "mso_schema_site_service_graph": resourceMSOSchemaSiteServiceGraph(), + "mso_service_node_type": resourceMSOServiceNodeType(), + "mso_schema_template_contract_service_graph": resourceMSOSchemaTemplateContractServiceGraph(), + "mso_system_config": resourceMSOSystemConfig(), + "mso_schema_site_contract_service_graph": resourceMSOSchemaSiteContractServiceGraph(), + "mso_schema_site_contract_service_graph_listener": resourceMSOSchemaSiteContractServiceGraphListener(), + "mso_template": resourceMSOTemplate(), + "mso_tenant_policies_ipsla_monitoring_policy": resourceMSOIPSLAMonitoringPolicy(), + "mso_tenant_policies_route_map_policy_multicast": resourceMSOMcastRouteMapPolicy(), + "mso_tenant_policies_dhcp_relay_policy": resourceMSOTenantPoliciesDHCPRelayPolicy(), + "mso_fabric_policies_vlan_pool": resourceMSOVlanPool(), + "mso_fabric_policies_physical_domain": resourceMSOPhysicalDomain(), + "mso_service_device_cluster": resourceMSOServiceDeviceCluster(), + "mso_fabric_policies_synce_interface_policy": resourceMSOSyncEInterfacePolicy(), + "mso_fabric_policies_macsec_policy": resourceMSOMacsecPolicy(), + "mso_schema_template_contract_service_chaining": resourceMSOSchemaTemplateContractServiceChaining(), + "mso_tenant_policies_bgp_peer_prefix_policy": resourceMSOBGPPeerPrefixPolicy(), + "mso_fabric_policies_l3_domain": resourceMSOL3Domain(), + "mso_tenant_policies_custom_qos_policy": resourceMSOCustomQoSPolicy(), + "mso_tenant_policies_dhcp_option_policy": resourceMSODHCPOptionPolicy(), + "mso_tenant_policies_mld_snooping_policy": resourceMSOMLDSnoopingPolicy(), + "mso_fabric_policies_mcp_global_policy": resourceMSOMCPGlobalPolicy(), + "mso_tenant_policies_ipsla_track_list": resourceMSOIPSLATrackList(), + "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), + "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), + "mso_tenant_policies_route_map_policy_route_control": resourceMSORouteMapPolicy(), + "mso_tenant_policies_route_map_policy_route_control_context": resourceMSORouteMapPolicyContext(), }, DataSourcesMap: map[string]*schema.Resource{ - "mso_schema": datasourceMSOSchema(), - "mso_schema_site": datasourceMSOSchemaSite(), - "mso_site": datasourceMSOSite(), - "mso_remote_location": datasourceMSORemoteLocation(), - "mso_role": datasourceMSORole(), - "mso_user": datasourceMSOUser(), - "mso_label": datasourceMSOLabel(), - "mso_schema_template": datasourceMSOSchemaTemplate(), - "mso_tenant": datasourceMSOTenant(), - "mso_schema_template_bd": dataSourceMSOTemplateBD(), - "mso_schema_template_vrf": datasourceMSOSchemaTemplateVrf(), - "mso_schema_template_bd_subnet": dataSourceMSOTemplateSubnetBD(), - "mso_schema_template_anp": datasourceMSOSchemaTemplateAnp(), - "mso_schema_template_anp_epg": datasourceMSOSchemaTemplateAnpEpg(), - "mso_schema_template_anp_epg_contract": dataSourceMSOTemplateAnpEpgContract(), - "mso_schema_template_contract": dataSourceMSOTemplateContract(), - "mso_schema_template_anp_epg_subnet": dataSourceMSOSchemaTemplateAnpEpgSubnet(), - "mso_schema_template_l3out": dataSourceMSOTemplateL3out(), - "mso_schema_template_external_epg": dataSourceMSOTemplateExternalepg(), - "mso_schema_template_contract_filter": dataSourceMSOTemplateContractFilter(), - "mso_schema_template_external_epg_contract": dataSourceMSOTemplateExternalEpgContract(), - "mso_schema_template_filter_entry": dataSourceMSOSchemaTemplateFilterEntry(), - "mso_schema_template_external_epg_subnet": dataSourceMSOTemplateExternalEpgSubnet(), - "mso_schema_site_anp": dataSourceMSOSchemaSiteAnp(), - "mso_schema_site_anp_epg": dataSourceMSOSchemaSiteAnpEpg(), - "mso_schema_site_anp_epg_static_leaf": dataSourceMSOSchemaSiteAnpEpgStaticleaf(), - "mso_schema_site_anp_epg_static_port": datasourceMSOSchemaSiteAnpEpgStaticPort(), - "mso_schema_site_anp_epg_bulk_staticport": datasourceMSOSchemaSiteAnpEpgBulkStaticPort(), - "mso_schema_site_bd": dataSourceMSOSchemaSiteBd(), - "mso_schema_site_anp_epg_subnet": datasourceMSOSchemaSiteAnpEpgSubnet(), - "mso_schema_site_anp_epg_domain": dataSourceMSOSchemaSiteAnpEpgDomain(), - "mso_schema_site_bd_l3out": dataSourceMSOSchemaSiteBdL3out(), - "mso_schema_site_vrf": dataSourceMSOSchemaSiteVrf(), - "mso_schema_site_vrf_region": dataSourceMSOSchemaSiteVrfRegion(), - "mso_schema_site_vrf_route_leak": dataSourceMSOSchemaSiteVrfRouteLeak(), - "mso_schema_site_bd_subnet": dataSourceMSOSchemaSiteBdSubnet(), - "mso_schema_site_vrf_region_cidr_subnet": dataSourceMSOSchemaSiteVrfRegionCidrSubnet(), - "mso_schema_site_vrf_region_cidr": dataSourceMSOSchemaSiteVrfRegionCidr(), - "mso_schema_template_anp_epg_selector": datasourceMSOSchemaTemplateAnpEpgSelector(), - "mso_schema_site_external_epg": dataSourceMSOSchemaSiteExternalEpg(), - "mso_schema_template_external_epg_selector": datasourceSchemaTemplateExternalEPGSelector(), - "mso_schema_template_anp_epg_useg_attr": dataSourceMSOSchemaTemplateAnpEpgUsegAttr(), - "mso_schema_site_anp_epg_selector": datasourceMSOSchemaSiteAnpEpgSelector(), - "mso_schema_template_vrf_contract": dataSourceMSOTemplateVRFContract(), - "mso_schema_site_external_epg_selector": datasourceMSOSchemaSiteExternalEpgSelector(), - "mso_schema_template_service_graph": dataSourceMSOSchemaTemplateServiceGraph(), - "mso_service_node_type": dataSourceMSOServiceNodeType(), - "mso_schema_site_service_graph": datasourceMSOSchemaSiteServiceGraph(), - "mso_schema_template_contract_service_graph": dataSourceMSOSchemaTemplateContractServiceGraph(), - "mso_system_config": dataSourceMSOSystemConfig(), - "mso_rest": datasourceMSORest(), - "mso_schema_site_contract_service_graph": dataSourceMSOSchemaSiteContractServiceGraph(), - "mso_schema_site_contract_service_graph_listener": dataSourceMSOSchemaSiteContractServiceGraphListener(), - "mso_template": datasourceMSOTemplate(), - "mso_tenant_policies_ipsla_monitoring_policy": datasourceMSOIPSLAMonitoringPolicy(), - "mso_tenant_policies_route_map_policy_multicast": datasourceMSOMcastRouteMapPolicy(), - "mso_tenant_policies_dhcp_relay_policy": datasourceMSOTenantPoliciesDHCPRelayPolicy(), - "mso_fabric_policies_vlan_pool": datasourceMSOVlanPool(), - "mso_fabric_policies_physical_domain": datasourceMSOPhysicalDomain(), - "mso_service_device_cluster": datasourceMSOServiceDeviceCluster(), - "mso_fabric_policies_synce_interface_policy": datasourceMSOSyncEInterfacePolicy(), - "mso_fabric_policies_macsec_policy": datasourceMacsecPolicy(), - "mso_schema_template_contract_service_chaining": datasourceMSOSchemaTemplateContractServiceChaining(), - "mso_tenant_policies_bgp_peer_prefix_policy": datasourceMSOBGPPeerPrefixPolicy(), - "mso_fabric_policies_l3_domain": datasourceMSOL3Domain(), - "mso_tenant_policies_custom_qos_policy": datasourceMSOCustomQoSPolicy(), - "mso_tenant_policies_dhcp_option_policy": datasourceMSODHCPOptionPolicy(), - "mso_tenant_policies_mld_snooping_policy": datasourceMSOMLDSnoopingPolicy(), - "mso_fabric_policies_mcp_global_policy": datasourceMSOMCPGlobalPolicy(), - "mso_tenant_policies_ipsla_track_list": datasourceMSOIPSLATrackList(), - "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), - "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), + "mso_schema": datasourceMSOSchema(), + "mso_schema_site": datasourceMSOSchemaSite(), + "mso_site": datasourceMSOSite(), + "mso_remote_location": datasourceMSORemoteLocation(), + "mso_role": datasourceMSORole(), + "mso_user": datasourceMSOUser(), + "mso_label": datasourceMSOLabel(), + "mso_schema_template": datasourceMSOSchemaTemplate(), + "mso_tenant": datasourceMSOTenant(), + "mso_schema_template_bd": dataSourceMSOTemplateBD(), + "mso_schema_template_vrf": datasourceMSOSchemaTemplateVrf(), + "mso_schema_template_bd_subnet": dataSourceMSOTemplateSubnetBD(), + "mso_schema_template_anp": datasourceMSOSchemaTemplateAnp(), + "mso_schema_template_anp_epg": datasourceMSOSchemaTemplateAnpEpg(), + "mso_schema_template_anp_epg_contract": dataSourceMSOTemplateAnpEpgContract(), + "mso_schema_template_contract": dataSourceMSOTemplateContract(), + "mso_schema_template_anp_epg_subnet": dataSourceMSOSchemaTemplateAnpEpgSubnet(), + "mso_schema_template_l3out": dataSourceMSOTemplateL3out(), + "mso_schema_template_external_epg": dataSourceMSOTemplateExternalepg(), + "mso_schema_template_contract_filter": dataSourceMSOTemplateContractFilter(), + "mso_schema_template_external_epg_contract": dataSourceMSOTemplateExternalEpgContract(), + "mso_schema_template_filter_entry": dataSourceMSOSchemaTemplateFilterEntry(), + "mso_schema_template_external_epg_subnet": dataSourceMSOTemplateExternalEpgSubnet(), + "mso_schema_site_anp": dataSourceMSOSchemaSiteAnp(), + "mso_schema_site_anp_epg": dataSourceMSOSchemaSiteAnpEpg(), + "mso_schema_site_anp_epg_static_leaf": dataSourceMSOSchemaSiteAnpEpgStaticleaf(), + "mso_schema_site_anp_epg_static_port": datasourceMSOSchemaSiteAnpEpgStaticPort(), + "mso_schema_site_anp_epg_bulk_staticport": datasourceMSOSchemaSiteAnpEpgBulkStaticPort(), + "mso_schema_site_bd": dataSourceMSOSchemaSiteBd(), + "mso_schema_site_anp_epg_subnet": datasourceMSOSchemaSiteAnpEpgSubnet(), + "mso_schema_site_anp_epg_domain": dataSourceMSOSchemaSiteAnpEpgDomain(), + "mso_schema_site_bd_l3out": dataSourceMSOSchemaSiteBdL3out(), + "mso_schema_site_vrf": dataSourceMSOSchemaSiteVrf(), + "mso_schema_site_vrf_region": dataSourceMSOSchemaSiteVrfRegion(), + "mso_schema_site_vrf_route_leak": dataSourceMSOSchemaSiteVrfRouteLeak(), + "mso_schema_site_bd_subnet": dataSourceMSOSchemaSiteBdSubnet(), + "mso_schema_site_vrf_region_cidr_subnet": dataSourceMSOSchemaSiteVrfRegionCidrSubnet(), + "mso_schema_site_vrf_region_cidr": dataSourceMSOSchemaSiteVrfRegionCidr(), + "mso_schema_template_anp_epg_selector": datasourceMSOSchemaTemplateAnpEpgSelector(), + "mso_schema_site_external_epg": dataSourceMSOSchemaSiteExternalEpg(), + "mso_schema_template_external_epg_selector": datasourceSchemaTemplateExternalEPGSelector(), + "mso_schema_template_anp_epg_useg_attr": dataSourceMSOSchemaTemplateAnpEpgUsegAttr(), + "mso_schema_site_anp_epg_selector": datasourceMSOSchemaSiteAnpEpgSelector(), + "mso_schema_template_vrf_contract": dataSourceMSOTemplateVRFContract(), + "mso_schema_site_external_epg_selector": datasourceMSOSchemaSiteExternalEpgSelector(), + "mso_schema_template_service_graph": dataSourceMSOSchemaTemplateServiceGraph(), + "mso_service_node_type": dataSourceMSOServiceNodeType(), + "mso_schema_site_service_graph": datasourceMSOSchemaSiteServiceGraph(), + "mso_schema_template_contract_service_graph": dataSourceMSOSchemaTemplateContractServiceGraph(), + "mso_system_config": dataSourceMSOSystemConfig(), + "mso_rest": datasourceMSORest(), + "mso_schema_site_contract_service_graph": dataSourceMSOSchemaSiteContractServiceGraph(), + "mso_schema_site_contract_service_graph_listener": dataSourceMSOSchemaSiteContractServiceGraphListener(), + "mso_template": datasourceMSOTemplate(), + "mso_tenant_policies_ipsla_monitoring_policy": datasourceMSOIPSLAMonitoringPolicy(), + "mso_tenant_policies_route_map_policy_multicast": datasourceMSOMcastRouteMapPolicy(), + "mso_tenant_policies_dhcp_relay_policy": datasourceMSOTenantPoliciesDHCPRelayPolicy(), + "mso_fabric_policies_vlan_pool": datasourceMSOVlanPool(), + "mso_fabric_policies_physical_domain": datasourceMSOPhysicalDomain(), + "mso_service_device_cluster": datasourceMSOServiceDeviceCluster(), + "mso_fabric_policies_synce_interface_policy": datasourceMSOSyncEInterfacePolicy(), + "mso_fabric_policies_macsec_policy": datasourceMacsecPolicy(), + "mso_schema_template_contract_service_chaining": datasourceMSOSchemaTemplateContractServiceChaining(), + "mso_tenant_policies_bgp_peer_prefix_policy": datasourceMSOBGPPeerPrefixPolicy(), + "mso_fabric_policies_l3_domain": datasourceMSOL3Domain(), + "mso_tenant_policies_custom_qos_policy": datasourceMSOCustomQoSPolicy(), + "mso_tenant_policies_dhcp_option_policy": datasourceMSODHCPOptionPolicy(), + "mso_tenant_policies_mld_snooping_policy": datasourceMSOMLDSnoopingPolicy(), + "mso_fabric_policies_mcp_global_policy": datasourceMSOMCPGlobalPolicy(), + "mso_tenant_policies_ipsla_track_list": datasourceMSOIPSLATrackList(), + "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), + "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), + "mso_tenant_policies_route_map_policy_route_control": datasourceMSORouteMapPolicy(), + "mso_tenant_policies_route_map_policy_route_control_context": datasourceMSORouteMapPolicyContext(), }, ConfigureFunc: configureClient, diff --git a/mso/provider_test.go b/mso/provider_test.go index 73979b1b..7e95e048 100644 --- a/mso/provider_test.go +++ b/mso/provider_test.go @@ -107,6 +107,51 @@ func testCheckResourceDestroyPolicy(s *terraform.State, resource, policyType str return nil } +func testCheckResourceDestroyPolicyChildWithArguments(resource, parentPolicyType, childKey string) func(s *terraform.State) error { + return func(s *terraform.State) error { + return testCheckResourceDestroyPolicyChild(s, resource, parentPolicyType, childKey) + } +} + +func testCheckResourceDestroyPolicyChild(s *terraform.State, resource, parentPolicyType, childKey string) error { + msoClient := testAccPreCheck(nil) + for name, rs := range s.RootModule().Resources { + if rs.Type == resource { + parentID := rs.Primary.Attributes["parent_id"] + childName := rs.Primary.Attributes["name"] + + parentParts := strings.Split(parentID, "/") + if len(parentParts) < 4 { + return fmt.Errorf("error parsing parent_id '%s' for resource '%s'", parentID, name) + } + parentUUID := parentParts[1] + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/objects?type=%s&uuid=%s", parentPolicyType, parentUUID)) + if err != nil { + if response != nil && response.S("code").Data().(float64) == 404 { + continue + } + return fmt.Errorf("error fetching parent policy for resource '%s' with ID '%s': %s", name, rs.Primary.ID, err) + } + + children, childErr := response.S(childKey).Children() + if childErr != nil { + continue + } + + for _, child := range children { + if child.S("name").Data().(string) == childName { + return fmt.Errorf( + "terraform destroy was unsuccessful. The child resource '%s' with name '%s' still exists under parent '%s'", + name, childName, parentID, + ) + } + } + } + } + return nil +} + func testCheckResourceDestroyPolicyWithPathAttributesAndArguments(resource string, objectPath ...string) func(s *terraform.State) error { return func(s *terraform.State) error { return testCheckResourceDestroyPolicyWithPathAttributes(s, resource, objectPath...) diff --git a/mso/resource_mso_tenant_policies_route_map_policy_route_control.go b/mso/resource_mso_tenant_policies_route_map_policy_route_control.go new file mode 100644 index 00000000..de80da9a --- /dev/null +++ b/mso/resource_mso_tenant_policies_route_map_policy_route_control.go @@ -0,0 +1,195 @@ +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 resourceMSORouteMapPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceMSORouteMapPolicyCreate, + Read: resourceMSORouteMapPolicyRead, + Update: resourceMSORouteMapPolicyUpdate, + Delete: resourceMSORouteMapPolicyDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSORouteMapPolicyImport, + }, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + ForceNew: 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 Route Map Policy.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the Route Map Policy.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the Route Map Policy.", + }, + }, + } +} + +func setRouteMapPolicyData(d *schema.ResourceData, response *container.Container, templateId string) error { + d.SetId(fmt.Sprintf("templateId/%s/RouteMapPolicy/%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())) + + return nil +} + +func resourceMSORouteMapPolicyImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO Route Map Policy Resource - Beginning Import: %v", d.Id()) + err := resourceMSORouteMapPolicyRead(d, m) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] MSO Route Map Policy Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSORouteMapPolicyCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Resource - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + } + + if description, ok := d.GetOk("description"); ok { + payload["description"] = description.(string) + } + + payloadModel := models.GetPatchPayload("add", "/tenantPolicyTemplate/template/routeMapPolicies/-", 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/RouteMapPolicy/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO Route Map Policy Resource - Create Complete: %v", d.Id()) + return resourceMSORouteMapPolicyRead(d, m) +} + +func resourceMSORouteMapPolicyRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map 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(), "RouteMapPolicy") + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + setRouteMapPolicyData(d, policy, templateId) + log.Printf("[DEBUG] MSO Route Map Policy Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSORouteMapPolicyUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map 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", "routeMapPolicies") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/tenantPolicyTemplate/template/routeMapPolicies/%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 + } + } + + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/RouteMapPolicy/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO Route Map Policy Resource - Update Complete: %v", d.Id()) + return resourceMSORouteMapPolicyRead(d, m) +} + +func resourceMSORouteMapPolicyDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map 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", "routeMapPolicies") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/tenantPolicyTemplate/template/routeMapPolicies/%d", policyIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO Route Map Policy Resource - Delete Complete: %v", d.Id()) + return nil +} diff --git a/mso/resource_mso_tenant_policies_route_map_policy_route_control_context.go b/mso/resource_mso_tenant_policies_route_map_policy_route_control_context.go new file mode 100644 index 00000000..116198c4 --- /dev/null +++ b/mso/resource_mso_tenant_policies_route_map_policy_route_control_context.go @@ -0,0 +1,355 @@ +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 resourceMSORouteMapPolicyContext() *schema.Resource { + return &schema.Resource{ + Create: resourceMSORouteMapPolicyContextCreate, + Read: resourceMSORouteMapPolicyContextRead, + Update: resourceMSORouteMapPolicyContextUpdate, + Delete: resourceMSORouteMapPolicyContextDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSORouteMapPolicyContextImport, + }, + + Schema: map[string]*schema.Schema{ + "parent_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The Terraform ID of the parent Route Map Policy.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The name of the Route Control context.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the context.", + }, + // Default is explicitly set because the API requires 'order' in the payload + // but does not assign a default value if omitted. + "order": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntBetween(0, 9), + Description: "The order of the context. Range: 0-9.", + }, + // Default is explicitly set because the API requires 'action' in the payload + // but does not assign a default value if omitted. + "action": { + Type: schema.TypeString, + Optional: true, + Default: "permit", + ValidateFunc: validation.StringInSlice([]string{ + "permit", "deny", + }, false), + Description: "The action of the context.", + }, + "set_rule_uuid": { + Type: schema.TypeString, + Optional: true, + Description: "The UUID of the Set Rule Policy (max one).", + }, + "match_rule_uuids": { + Type: schema.TypeSet, + Optional: true, + Description: "A set of Match Rule Policy UUIDs.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +func buildMatchRuleUUIDsPayload(matchRulesRaw interface{}) []string { + if matchRulesRaw == nil { + return nil + } + set := matchRulesRaw.(*schema.Set).List() + uuids := make([]string, len(set)) + for i, item := range set { + uuids[i] = item.(string) + } + return uuids +} + +func resourceMSORouteMapPolicyContextImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Beginning Import: %v", d.Id()) + parentId, contextName, err := ParseChildResourceId(d.Id(), "/context/") + if err != nil { + return nil, err + } + d.Set("parent_id", parentId) + d.Set("name", contextName) + + err = resourceMSORouteMapPolicyContextRead(d, m) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSORouteMapPolicyContextCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Beginning Create") + msoClient := m.(*client.Client) + parentId := d.Get("parent_id").(string) + + templateId, err := GetTemplateIdFromResourceId(parentId) + if err != nil { + return err + } + policyName, err := GetPolicyNameFromResourceId(parentId, "RouteMapPolicy") + if err != nil { + return err + } + + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "name", policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + "description": d.Get("description").(string), + "order": d.Get("order").(int), + "action": d.Get("action").(string), + } + + if v, ok := d.GetOk("set_rule_uuid"); ok { + payload["setRuleRef"] = v.(string) + } + + if v, ok := d.GetOk("match_rule_uuids"); ok { + payload["matchRules"] = buildMatchRuleUUIDsPayload(v) + } + + path := fmt.Sprintf("/tenantPolicyTemplate/template/routeMapPolicies/%d/contexts/-", policyIndex) + payloadModel := models.GetPatchPayload("add", path, payload) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s/context/%s", parentId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Create Complete: %v", d.Id()) + return resourceMSORouteMapPolicyContextRead(d, m) +} + +func resourceMSORouteMapPolicyContextRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Beginning Read") + msoClient := m.(*client.Client) + parentId := d.Get("parent_id").(string) + + templateId, err := GetTemplateIdFromResourceId(parentId) + if err != nil { + return err + } + policyName, err := GetPolicyNameFromResourceId(parentId, "RouteMapPolicy") + if err != nil { + return err + } + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + contextName := d.Get("name").(string) + contexts := policy.S("contexts") + count, _ := contexts.ArrayCount() + + var match *container.Container + for i := 0; i < count; i++ { + c := contexts.Index(i) + if models.StripQuotes(c.S("name").String()) == contextName { + match = c + break + } + } + + if match == nil { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Context '%s' not found in parent policy, clearing ID", contextName) + d.SetId("") + return nil + } + + if match.Exists("description") { + descValue := models.StripQuotes(match.S("description").String()) + if descValue == "{}" { + d.Set("description", "") + } else { + d.Set("description", descValue) + } + } else { + d.Set("description", "") + } + + if match.Exists("order") { + d.Set("order", int(match.S("order").Data().(float64))) + } + + if match.Exists("action") { + d.Set("action", models.StripQuotes(match.S("action").String())) + } + + if match.Exists("setRuleRef") { + setRuleValue := models.StripQuotes(match.S("setRuleRef").String()) + if setRuleValue == "{}" { + d.Set("set_rule_uuid", "") + } else { + d.Set("set_rule_uuid", setRuleValue) + } + } else { + d.Set("set_rule_uuid", "") + } + + if match.Exists("matchRules") { + apiList, _ := match.S("matchRules").Data().([]interface{}) + uuids := make([]string, len(apiList)) + for i, uuid := range apiList { + uuids[i] = models.StripQuotes(fmt.Sprintf("%v", uuid)) + } + d.Set("match_rule_uuids", uuids) + } else { + d.Set("match_rule_uuids", []string{}) + } + + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSORouteMapPolicyContextUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Beginning Update: %v", d.Id()) + msoClient := m.(*client.Client) + parentId := d.Get("parent_id").(string) + + templateId, err := GetTemplateIdFromResourceId(parentId) + if err != nil { + return err + } + policyName, err := GetPolicyNameFromResourceId(parentId, "RouteMapPolicy") + if err != nil { + return err + } + + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "name", policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + policy := templateCont.S("tenantPolicyTemplate", "template", "routeMapPolicies").Index(policyIndex) + contextIndex, err := GetPolicyIndexByKeyAndValue(policy, "name", d.Get("name").(string), "contexts") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/tenantPolicyTemplate/template/routeMapPolicies/%d/contexts/%d", policyIndex, contextIndex) + payloadCont := container.New() + payloadCont.Array() + + if d.HasChange("description") { + addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)) + } + if d.HasChange("order") { + addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/order", updatePath), d.Get("order").(int)) + } + if d.HasChange("action") { + addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/action", updatePath), d.Get("action").(string)) + } + if d.HasChange("set_rule_uuid") { + addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/setRuleRef", updatePath), d.Get("set_rule_uuid").(string)) + } + if d.HasChange("match_rule_uuids") { + _, newVal := d.GetChange("match_rule_uuids") + uuids := buildMatchRuleUUIDsPayload(newVal) + + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/matchRules", updatePath), uuids) + if err != nil { + return err + } + } + + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { + return err + } + + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Update Complete: %v", d.Id()) + return resourceMSORouteMapPolicyContextRead(d, m) +} + +func resourceMSORouteMapPolicyContextDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Beginning Delete: %v", d.Id()) + msoClient := m.(*client.Client) + parentId := d.Get("parent_id").(string) + + templateId, err := GetTemplateIdFromResourceId(parentId) + if err != nil { + return err + } + policyName, err := GetPolicyNameFromResourceId(parentId, "RouteMapPolicy") + if err != nil { + return err + } + + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "name", policyName, "tenantPolicyTemplate", "template", "routeMapPolicies") + if err != nil { + return err + } + + policy := templateCont.S("tenantPolicyTemplate", "template", "routeMapPolicies").Index(policyIndex) + contextIndex, err := GetPolicyIndexByKeyAndValue(policy, "name", d.Get("name").(string), "contexts") + if err != nil { + return err + } + + path := fmt.Sprintf("/tenantPolicyTemplate/template/routeMapPolicies/%d/contexts/%d", policyIndex, contextIndex) + payloadModel := models.GetRemovePatchPayload(path) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO Route Map Policy Context Resource - Delete Complete") + return nil +} diff --git a/mso/resource_mso_tenant_policies_route_map_policy_route_control_context_test.go b/mso/resource_mso_tenant_policies_route_map_policy_route_control_context_test.go new file mode 100644 index 00000000..77ee5586 --- /dev/null +++ b/mso/resource_mso_tenant_policies_route_map_policy_route_control_context_test.go @@ -0,0 +1,290 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSORouteMapPolicyContextResource(t *testing.T) { + resourceName := "mso_tenant_policies_route_map_policy_route_control_context.test_context" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Task: Creating Route Map Policy Context") }, + Config: testAccMSORouteMapPolicyContextConfig_Create(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "ctx_1"), + resource.TestCheckResourceAttr(resourceName, "action", "permit"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Updating Context Attributes") }, + Config: testAccMSORouteMapPolicyContextConfig_UpdateAttrs(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "description", "Updated description"), + resource.TestCheckResourceAttr(resourceName, "order", "2"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Adding Match and Set Rules") }, + Config: testAccMSORouteMapPolicyContextConfig_WithRules(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "set_rule_uuid", "a7f90577-fcd9-4c55-8bf4-628592922fc2"), + resource.TestCheckResourceAttr(resourceName, "match_rule_uuids.#", "2"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Removing Rules") }, + Config: testAccMSORouteMapPolicyContextConfig_Create(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "set_rule_uuid", ""), + resource.TestCheckResourceAttr(resourceName, "match_rule_uuids.#", "0"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Importing Context") }, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyChildWithArguments( + "mso_tenant_policies_route_map_policy_route_control_context", + "routeMap", + "ctxs", + ), + }) +} + +func TestAccMSORouteMapPolicyRouteControlContext_MultipleContextIndexing(t *testing.T) { + resourceName1 := "mso_tenant_policies_route_map_policy_route_control_context.ctx_1" + resourceName2 := "mso_tenant_policies_route_map_policy_route_control_context.ctx_2" + resourceName3 := "mso_tenant_policies_route_map_policy_route_control_context.ctx_3" + resourceName4 := "mso_tenant_policies_route_map_policy_route_control_context.ctx_4" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Task: Creating 3 Route Control Contexts") }, + Config: testAccRouteControlContextMultipleCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName1, "name", "ctx_1"), + resource.TestCheckResourceAttr(resourceName1, "description", "First context"), + resource.TestCheckResourceAttr(resourceName1, "order", "0"), + resource.TestCheckResourceAttr(resourceName1, "action", "permit"), + resource.TestCheckResourceAttr(resourceName2, "name", "ctx_2"), + resource.TestCheckResourceAttr(resourceName2, "description", "Second context"), + resource.TestCheckResourceAttr(resourceName2, "order", "1"), + resource.TestCheckResourceAttr(resourceName2, "action", "deny"), + resource.TestCheckResourceAttr(resourceName3, "name", "ctx_3"), + resource.TestCheckResourceAttr(resourceName3, "description", "Third context"), + resource.TestCheckResourceAttr(resourceName3, "order", "2"), + resource.TestCheckResourceAttr(resourceName3, "action", "permit"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Deleting Middle Context (ctx_2)") }, + Config: testAccRouteControlContextMiddleDeleted(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName1, "name", "ctx_1"), + resource.TestCheckResourceAttr(resourceName1, "description", "First context"), + resource.TestCheckResourceAttr(resourceName1, "order", "0"), + resource.TestCheckResourceAttr(resourceName1, "action", "permit"), + resource.TestCheckResourceAttr(resourceName3, "name", "ctx_3"), + resource.TestCheckResourceAttr(resourceName3, "description", "Third context"), + resource.TestCheckResourceAttr(resourceName3, "order", "2"), + resource.TestCheckResourceAttr(resourceName3, "action", "permit"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Updating Remaining Contexts After Middle Deletion") }, + Config: testAccRouteControlContextUpdateAfterDeletion(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName1, "name", "ctx_1"), + resource.TestCheckResourceAttr(resourceName1, "description", "First context updated"), + resource.TestCheckResourceAttr(resourceName1, "order", "0"), + resource.TestCheckResourceAttr(resourceName1, "action", "deny"), + resource.TestCheckResourceAttr(resourceName3, "name", "ctx_3"), + resource.TestCheckResourceAttr(resourceName3, "description", "Third context updated"), + resource.TestCheckResourceAttr(resourceName3, "order", "5"), + resource.TestCheckResourceAttr(resourceName3, "action", "deny"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Re-adding New Context (ctx_4) Alongside Existing Contexts") }, + Config: testAccRouteControlContextReaddNew(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName1, "name", "ctx_1"), + resource.TestCheckResourceAttr(resourceName1, "description", "First context updated"), + resource.TestCheckResourceAttr(resourceName1, "order", "0"), + resource.TestCheckResourceAttr(resourceName1, "action", "deny"), + resource.TestCheckResourceAttr(resourceName3, "name", "ctx_3"), + resource.TestCheckResourceAttr(resourceName3, "description", "Third context updated"), + resource.TestCheckResourceAttr(resourceName3, "order", "5"), + resource.TestCheckResourceAttr(resourceName3, "action", "deny"), + resource.TestCheckResourceAttr(resourceName4, "name", "ctx_4"), + resource.TestCheckResourceAttr(resourceName4, "description", "Fourth context new"), + resource.TestCheckResourceAttr(resourceName4, "order", "3"), + resource.TestCheckResourceAttr(resourceName4, "action", "permit"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Importing Context (ctx_3) After Index Shifts") }, + ResourceName: resourceName3, + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyChildWithArguments( + "mso_tenant_policies_route_map_policy_route_control_context", + "routeMap", + "ctxs", + ), + }) +} + +func testAccMSORouteMapPolicyContextConfig_Create() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "test_context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + order = 1 + action = "permit" +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccMSORouteMapPolicyContextConfig_UpdateAttrs() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "test_context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + description = "Updated description" + order = 2 + action = "permit" +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccMSORouteMapPolicyContextConfig_WithRules() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "test_context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + order = 1 + action = "permit" + + set_rule_uuid = "a7f90577-fcd9-4c55-8bf4-628592922fc2" + match_rule_uuids = ["f016b945-83c3-49a8-ab1f-c0c69ed58ec8", "50f1e25e-b5b3-4a06-a1d8-a7e41120bf4e"] +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccRouteControlContextMultipleCreate() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_1" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + description = "First context" + order = 0 + action = "permit" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_2" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_2" + description = "Second context" + order = 1 + action = "deny" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_1] +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_3" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_3" + description = "Third context" + order = 2 + action = "permit" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_2] +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccRouteControlContextMiddleDeleted() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_1" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + description = "First context" + order = 0 + action = "permit" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_3" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_3" + description = "Third context" + order = 2 + action = "permit" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_1] +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccRouteControlContextUpdateAfterDeletion() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_1" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + description = "First context updated" + order = 0 + action = "deny" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_3" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_3" + description = "Third context updated" + order = 5 + action = "deny" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_1] +} +`, testAccMSORouteMapPolicyConfigCreate()) +} + +func testAccRouteControlContextReaddNew() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_1" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_1" + description = "First context updated" + order = 0 + action = "deny" +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_3" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_3" + description = "Third context updated" + order = 5 + action = "deny" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_1] +} + +resource "mso_tenant_policies_route_map_policy_route_control_context" "ctx_4" { + parent_id = mso_tenant_policies_route_map_policy_route_control.test_policy.id + name = "ctx_4" + description = "Fourth context new" + order = 3 + action = "permit" + depends_on = [mso_tenant_policies_route_map_policy_route_control_context.ctx_3] +} +`, testAccMSORouteMapPolicyConfigCreate()) +} diff --git a/mso/resource_mso_tenant_policies_route_map_policy_route_control_test.go b/mso/resource_mso_tenant_policies_route_map_policy_route_control_test.go new file mode 100644 index 00000000..e389b26f --- /dev/null +++ b/mso/resource_mso_tenant_policies_route_map_policy_route_control_test.go @@ -0,0 +1,79 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSORouteMapPolicyResource(t *testing.T) { + resourceName := "mso_tenant_policies_route_map_policy_route_control.test_policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Task: Create Route Map Policy") }, + Config: testAccMSORouteMapPolicyConfigCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "test_policy"), + resource.TestCheckResourceAttr(resourceName, "description", "Initial description"), + resource.TestCheckResourceAttrSet(resourceName, "uuid"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Update Route Map Policy Description") }, + Config: testAccMSORouteMapPolicyConfigUpdateDescription(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "description", "Updated description"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Update Route Map Policy Name") }, + Config: testAccMSORouteMapPolicyConfigUpdateName(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "test_policy_renamed"), + ), + }, + { + PreConfig: func() { fmt.Println("Task: Import Route Map Policy") }, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithArguments("mso_tenant_policies_route_map_policy_route_control", "routeMapP"), + }) +} + +func testAccMSORouteMapPolicyConfigCreate() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control" "test_policy" { + template_id = mso_template.template_tenant.id + name = "test_policy" + description = "Initial description" +} +`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSORouteMapPolicyConfigUpdateDescription() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control" "test_policy" { + template_id = mso_template.template_tenant.id + name = "test_policy" + description = "Updated description" +} +`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSORouteMapPolicyConfigUpdateName() string { + return fmt.Sprintf(`%s +resource "mso_tenant_policies_route_map_policy_route_control" "test_policy" { + template_id = mso_template.template_tenant.id + name = "test_policy_renamed" + description = "Updated description" +} +`, testAccMSOTemplateResourceTenantConfig()) +} diff --git a/mso/utils.go b/mso/utils.go index 7fc0678c..f17faf1f 100644 --- a/mso/utils.go +++ b/mso/utils.go @@ -540,3 +540,11 @@ func GetDeployedSiteIdsForApplicationTemplate(msoClient *client.Client, schemaId } return siteIds, nil } + +func ParseChildResourceId(importId, separator string) (parentId string, childName string, err error) { + parts := strings.SplitN(importId, separator, 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("invalid import ID format '%s', expected format: %s", importId, separator) + } + return parts[0], parts[1], nil +} diff --git a/mso/utils_test.go b/mso/utils_test.go new file mode 100644 index 00000000..1b411a1b --- /dev/null +++ b/mso/utils_test.go @@ -0,0 +1,94 @@ +package mso + +import ( + "testing" +) + +func TestParseChildResourceId(t *testing.T) { + cases := []struct { + name string + importId string + separator string + expectedParentId string + expectedChild string + expectError bool + }{ + { + name: "valid_context_separator", + importId: "templateId/abc123/RouteMapPolicy/test/context/ctx_1", + separator: "/context/", + expectedParentId: "templateId/abc123/RouteMapPolicy/test", + expectedChild: "ctx_1", + expectError: false, + }, + { + name: "valid_with_different_separator", + importId: "parentId/entry/myEntry", + separator: "/entry/", + expectedParentId: "parentId", + expectedChild: "myEntry", + expectError: false, + }, + { + name: "child_name_contains_separator_characters", + importId: "templateId/abc123/RouteMapPolicy/test/context/ctx/with/slashes", + separator: "/context/", + expectedParentId: "templateId/abc123/RouteMapPolicy/test", + expectedChild: "ctx/with/slashes", + expectError: false, + }, + { + name: "missing_separator", + importId: "templateId/abc123/RouteMapPolicy/test", + separator: "/context/", + expectError: true, + }, + { + name: "empty_import_id", + importId: "", + separator: "/context/", + expectError: true, + }, + { + name: "empty_parent_id", + importId: "/context/ctx_1", + separator: "/context/", + expectError: true, + }, + { + name: "empty_child_name", + importId: "templateId/abc123/RouteMapPolicy/test/context/", + separator: "/context/", + expectError: true, + }, + { + name: "separator_only", + importId: "/context/", + separator: "/context/", + expectError: true, + }, + } + + for _, testCase := range cases { + t.Run(testCase.name, func(t *testing.T) { + parentId, childName, err := ParseChildResourceId(testCase.importId, testCase.separator) + + if testCase.expectError { + if err == nil { + t.Fatalf("ParseChildResourceId(%q, %q) expected error, got parentId=%q childName=%q", testCase.importId, testCase.separator, parentId, childName) + } + return + } + + if err != nil { + t.Fatalf("ParseChildResourceId(%q, %q) unexpected error: %s", testCase.importId, testCase.separator, err) + } + if parentId != testCase.expectedParentId { + t.Fatalf("ParseChildResourceId(%q, %q) parentId = %q, expected %q", testCase.importId, testCase.separator, parentId, testCase.expectedParentId) + } + if childName != testCase.expectedChild { + t.Fatalf("ParseChildResourceId(%q, %q) childName = %q, expected %q", testCase.importId, testCase.separator, childName, testCase.expectedChild) + } + }) + } +} diff --git a/website/docs/d/tenant_policies_route_map_policy_route_control.html.markdown b/website/docs/d/tenant_policies_route_map_policy_route_control.html.markdown new file mode 100644 index 00000000..7f12ec0f --- /dev/null +++ b/website/docs/d/tenant_policies_route_map_policy_route_control.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_route_map_policy_route_control" +sidebar_current: "docs-mso-data-source-route_map_policy_route_control" +description: |- + Data source for Route Map Policy. +--- + +# mso_tenant_policies_route_map_policy_route_control # + +Data source for Route Map Policies for Route Control on Cisco Nexus Dashboard Orchestrator (NDO). This data source is supported in NDO v4.3 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> Route Map Policy + +## Example Usage ## + +```hcl +data "mso_tenant_policies_route_map_policy_route_control" "route_map_policy" { + template_id = mso_template.template_tenant.id + name = "example_route_map_policy" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the Route Map Policy. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the Route Map Policy. +* `id` - (Read-Only) The unique Terraform identifier of the Route Map Policy in the template. +* `description` - (Read-Only) The description of the Route Map Policy. diff --git a/website/docs/d/tenant_policies_route_map_policy_route_control_context.html.markdown b/website/docs/d/tenant_policies_route_map_policy_route_control_context.html.markdown new file mode 100644 index 00000000..fcea94ce --- /dev/null +++ b/website/docs/d/tenant_policies_route_map_policy_route_control_context.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_route_map_policy_route_control_context" +sidebar_current: "docs-mso-data-source-route_map_policy_route_control_context" +description: |- + Data source for Route Map Policy Route Control Context. +--- + +# mso_tenant_policies_route_map_policy_route_control_context # + +Data source for Route Control Contexts within a Route Map Policy on Cisco Nexus Dashboard Orchestrator (NDO). This data source is supported in NDO v4.3 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> Route Map Policy -> Route Control Context + +## Example Usage ## + +```hcl +data "mso_tenant_policies_route_map_policy_route_control_context" "context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.route_map_policy.id + name = "example_context" +} +``` + +## Argument Reference ## + +* `parent_id` - (Required) The Terraform ID of the parent Route Map Policy resource. +* `name` - (Required) The name of the Route Control Context. + +## Attribute Reference ## + +* `id` - (Read-Only) The unique Terraform identifier of the Route Control Context. +* `description` - (Read-Only) The description of the Route Control Context. +* `action` - (Read-Only) The action of the Route Control Context. +* `order` - (Read-Only) The order of the Route Control Context. +* `set_rule_uuid` - (Read-Only) The UUID of the Set Rule Policy associated with this context. +* `match_rules_uuids` - (Read-Only) A set of Match Rule Policy UUIDs associated with this context. diff --git a/website/docs/r/tenant_policies_route_map_policy_route_control.html.markdown b/website/docs/r/tenant_policies_route_map_policy_route_control.html.markdown new file mode 100644 index 00000000..3e5953d9 --- /dev/null +++ b/website/docs/r/tenant_policies_route_map_policy_route_control.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_route_map_policy_route_control" +sidebar_current: "docs-mso-resource-route_map_policy_route_control" +description: |- + Manages Route Map Policies on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_route_map_policy_route_control # + +Manages Route Map Policies for Route Control on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.3 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> Route Map Policy + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_route_map_policy_route_control" "route_map_policy" { + template_id = mso_template.template_tenant.id + name = "example_route_map_policy" + description = "Example Route Map Policy" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the Route Map Policy. +* `description` - (Optional) The description of the Route Map Policy. When unset during creation, no description is applied. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the Route Map Policy. +* `id` - (Read-Only) The unique Terraform identifier of the Route Map Policy in the template. + +## Importing ## + +An existing MSO Route Map 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_route_map_policy_route_control.route_map_policy templateId/{template_id}/RouteMapPolicy/{name} +``` diff --git a/website/docs/r/tenant_policies_route_map_policy_route_control_context.html.markdown b/website/docs/r/tenant_policies_route_map_policy_route_control_context.html.markdown new file mode 100644 index 00000000..3064f405 --- /dev/null +++ b/website/docs/r/tenant_policies_route_map_policy_route_control_context.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_route_map_policy_route_control_context" +sidebar_current: "docs-mso-resource-route_map_policy_route_control_context" +description: |- + Manages Route Map Policy Route Control Contexts on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_route_map_policy_route_control_context # + +Manages Route Control Contexts within a Route Map Policy on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.3 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> Route Map Policy -> Route Control Context + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_route_map_policy_route_control_context" "context" { + parent_id = mso_tenant_policies_route_map_policy_route_control.route_map_policy.id + name = "example_context" + description = "Example Route Control Context" + order = 1 + action = "permit" +} +``` + +## Argument Reference ## + +* `parent_id` - (Required) The Terraform ID of the parent Route Map Policy resource. +* `name` - (Required) The name of the Route Control Context. +* `description` - (Optional) The description of the Route Control Context. +* `order` - (Optional) The order of the Route Control Context. Valid range: 0-9. Default: `0`. +* `action` - (Optional) The action of the Route Control Context. Allowed values: `permit`, `deny`. Default: `permit`. +* `set_rule_uuid` - (Optional) The UUID of the Set Rule Policy to associate with this context. +* `match_rules_uuids` - (Optional) A set of Match Rule Policy UUIDs to associate with this context. + +## Attribute Reference ## + +* `id` - (Read-Only) The unique Terraform identifier of the Route Control Context. + +## Importing ## + +An existing MSO Route Map Policy Route Control Context 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_route_map_policy_route_control_context.context templateId/{template_id}/RouteMapPolicy/{policy_name}/context/{context_name} +```