From 403df3d56644b223db3976ebc66b853d5f792335 Mon Sep 17 00:00:00 2001 From: samitab Date: Mon, 16 Mar 2026 17:03:15 +1000 Subject: [PATCH 1/8] [minor_change] Add mso_tenant_policies_netflow_exporter resource and datasource. --- .../tenant_policies_netflow_exporter/main.tf | 40 ++++ ...ce_mso_tenant_policies_netflow_exporter.go | 55 ++++++ ...o_tenant_policies_netflow_exporter_test.go | 33 ++++ mso/provider.go | 2 + ...ce_mso_tenant_policies_netflow_exporter.go | 174 ++++++++++++++++++ ...o_tenant_policies_netflow_exporter_test.go | 56 ++++++ ...nt_policies_netflow_exporter.html.markdown | 34 ++++ ...nt_policies_netflow_exporter.html.markdown | 42 +++++ 8 files changed, 436 insertions(+) create mode 100644 examples/tenant_policies_netflow_exporter/main.tf create mode 100644 mso/datasource_mso_tenant_policies_netflow_exporter.go create mode 100644 mso/datasource_mso_tenant_policies_netflow_exporter_test.go create mode 100644 mso/resource_mso_tenant_policies_netflow_exporter.go create mode 100644 mso/resource_mso_tenant_policies_netflow_exporter_test.go create mode 100644 website/docs/d/tenant_policies_netflow_exporter.html.markdown create mode 100644 website/docs/r/tenant_policies_netflow_exporter.html.markdown diff --git a/examples/tenant_policies_netflow_exporter/main.tf b/examples/tenant_policies_netflow_exporter/main.tf new file mode 100644 index 00000000..52f040b3 --- /dev/null +++ b/examples/tenant_policies_netflow_exporter/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 +} + +# NetFlow Exporter example + +resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.tenant_template.id + name = "netflow_exporter_1" +} + +# NetFlow Exporter data source example + +data "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.tenant_template.id + name = mso_tenant_policies_netflow_exporter.netflow_exporter.name +} \ No newline at end of file diff --git a/mso/datasource_mso_tenant_policies_netflow_exporter.go b/mso/datasource_mso_tenant_policies_netflow_exporter.go new file mode 100644 index 00000000..3a27e0cd --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_exporter.go @@ -0,0 +1,55 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSONetflowExporter() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSONetflowExporterRead, + + 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 NetFlow Exporter.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Exporter.", + }, + }, + } +} + +func dataSourceMSONetflowExporterRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Exporter 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", "netFlowExporters") + if err != nil { + return err + } + + setNetflowExporterData(d, policy, templateId) + log.Printf("[DEBUG] MSO NetFlow Exporter Data Source - Read Complete: %v", d.Id()) + return nil +} \ No newline at end of file diff --git a/mso/datasource_mso_tenant_policies_netflow_exporter_test.go b/mso/datasource_mso_tenant_policies_netflow_exporter_test.go new file mode 100644 index 00000000..42a4018d --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_exporter_test.go @@ -0,0 +1,33 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowExporterDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: NetFlow Exporter Data Source") }, + Config: testAccMSOTenantPoliciesNetflowExporterDataSource(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_exporter.netflow_exporter", "name", "test_netflow_exporter"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_exporter.netflow_exporter", "uuid"), + ), + }, + }, + }) +} + +func testAccMSOTenantPoliciesNetflowExporterDataSource() string { + return fmt.Sprintf(`%s + data "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_tenant_policies_netflow_exporter.netflow_exporter.template_id + name = "test_netflow_exporter" + }`, testAccMSOTenantPoliciesNetflowExporterConfigCreate()) +} \ No newline at end of file diff --git a/mso/provider.go b/mso/provider.go index 18d1c7ab..10a40521 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_netflow_exporter": resourceMSONetflowExporter(), }, 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_netflow_exporter": datasourceMSONetflowExporter(), }, ConfigureFunc: configureClient, diff --git a/mso/resource_mso_tenant_policies_netflow_exporter.go b/mso/resource_mso_tenant_policies_netflow_exporter.go new file mode 100644 index 00000000..fcf2fbdd --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_exporter.go @@ -0,0 +1,174 @@ +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 resourceMSONetflowExporter() *schema.Resource { + return &schema.Resource{ + Create: resourceMSONetflowExporterCreate, + Read: resourceMSONetflowExporterRead, + Update: resourceMSONetflowExporterUpdate, + Delete: resourceMSONetflowExporterDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSONetflowExporterImport, + }, + + 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 NetFlow Exporter.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Exporter.", + }, + }, + } +} + +func setNetflowExporterData(d *schema.ResourceData, response *container.Container, templateId string) error { + d.SetId(fmt.Sprintf("templateId/%s/NetflowExporter/%s", templateId, models.StripQuotes(response.S("name").String()))) + d.Set("template_id", templateId) + d.Set("name", models.StripQuotes(response.S("name").String())) + d.Set("uuid", models.StripQuotes(response.S("uuid").String())) + + return nil +} + +func resourceMSONetflowExporterImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Beginning Import: %v", d.Id()) + resourceMSONetflowExporterRead(d, m) + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSONetflowExporterCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + payload := map[string]interface{}{} + + payload["name"] = d.Get("name").(string) + + payloadModel := models.GetPatchPayload("add", "/tenantPolicyTemplate/template/netFlowExporters/-", 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/NetflowExporter/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Create Complete: %v", d.Id()) + return resourceMSONetflowExporterRead(d, m) +} + +func resourceMSONetflowExporterRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Exporter 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(), "NetflowExporter") + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "netFlowExporters") + if err != nil { + return err + } + + setNetflowExporterData(d, policy, templateId) + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSONetflowExporterUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Exporter 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", "netFlowExporters") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/tenantPolicyTemplate/template/netFlowExporters/%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 + } + } + + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/NetflowExporter/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Update Complete: %v", d.Id()) + return resourceMSONetflowExporterRead(d, m) +} + +func resourceMSONetflowExporterDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Exporter 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", "netFlowExporters") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/tenantPolicyTemplate/template/netFlowExporters/%d", policyIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Delete Complete: %v", d.Id()) + return nil +} \ No newline at end of file diff --git a/mso/resource_mso_tenant_policies_netflow_exporter_test.go b/mso/resource_mso_tenant_policies_netflow_exporter_test.go new file mode 100644 index 00000000..38a89ade --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_exporter_test.go @@ -0,0 +1,56 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowExporterResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Create NetFlow Exporter") }, + Config: testAccMSOTenantPoliciesNetflowExporterConfigCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_exporter.netflow_exporter", "name", "test_netflow_exporter"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_exporter.netflow_exporter", "uuid"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Exporter Name") }, + Config: testAccMSOTenantPoliciesNetflowExporterConfigUpdateName(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_exporter.netflow_exporter", "name", "test_netflow_exporter_updated"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_exporter.netflow_exporter", "uuid"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import NetFlow Exporter") }, + ResourceName: "mso_tenant_policies_netflow_exporter.netflow_exporter", + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_tenant_policies_netflow_exporter", "tenantPolicyTemplate", "template", "netFlowExporters"), + }) +} + +func testAccMSOTenantPoliciesNetflowExporterConfigCreate() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.template_tenant.id + name = "test_netflow_exporter" + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesNetflowExporterConfigUpdateName() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.template_tenant.id + name = "test_netflow_exporter_updated" + }`, testAccMSOTemplateResourceTenantConfig()) +} \ No newline at end of file diff --git a/website/docs/d/tenant_policies_netflow_exporter.html.markdown b/website/docs/d/tenant_policies_netflow_exporter.html.markdown new file mode 100644 index 00000000..2d018c56 --- /dev/null +++ b/website/docs/d/tenant_policies_netflow_exporter.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_exporter" +sidebar_current: "docs-mso-data-source-tenant_policies_netflow_exporter" +description: |- + Data source for NetFlow Exporter Policies. +--- + +# mso_tenant_policies_netflow_exporter # + +Data source for NetFlow Exporter Policies. This data source is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Exporter + +## Example Usage ## + +```hcl +data "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.tenant_template.id + name = "netflow_exporter_1" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the template. +* `name` - (Required) The name of the NetFlow Exporter. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Exporter. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Exporter in the template. diff --git a/website/docs/r/tenant_policies_netflow_exporter.html.markdown b/website/docs/r/tenant_policies_netflow_exporter.html.markdown new file mode 100644 index 00000000..03fcbfe1 --- /dev/null +++ b/website/docs/r/tenant_policies_netflow_exporter.html.markdown @@ -0,0 +1,42 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_exporter" +sidebar_current: "docs-mso-resource-tenant_policies_netflow_exporter" +description: |- + Manages NetFlow Exporter Policies on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_netflow_exporter # + +Manages NetFlow Exporter Policies on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Exporter + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.tenant_template.id + name = "netflow_exporter_1" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the NetFlow Exporter. + +## Attribute Reference ## + +* `uuid` - The NDO UUID of the NetFlow Exporter. +* `id` - The unique terraform identifier of the NetFlow Exporter in the template. + +## Importing ## + +An existing MSO NetFlow Exporter 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_netflow_exporter.netflow_exporter templateId/{template_id}/NetflowExporter/{name} +``` From 013937bc2aa837951186d31733a2edff5adc6f1f Mon Sep 17 00:00:00 2001 From: samitab Date: Tue, 31 Mar 2026 21:16:22 +1000 Subject: [PATCH 2/8] [minor_change] Add mso_tenant_policies_netflow_record resource and datasource. --- .../tenant_policies_netflow_record/main.tf | 42 +++ mso/constants.go | 26 ++ ...urce_mso_tenant_policies_netflow_record.go | 68 +++++ ...mso_tenant_policies_netflow_record_test.go | 37 +++ mso/provider.go | 2 + mso/provider_test.go | 16 ++ ...urce_mso_tenant_policies_netflow_record.go | 247 ++++++++++++++++++ ...mso_tenant_policies_netflow_record_test.go | 107 ++++++++ ...nant_policies_netflow_record.html.markdown | 36 +++ ...nant_policies_netflow_record.html.markdown | 46 ++++ 10 files changed, 627 insertions(+) create mode 100644 examples/tenant_policies_netflow_record/main.tf create mode 100644 mso/datasource_mso_tenant_policies_netflow_record.go create mode 100644 mso/datasource_mso_tenant_policies_netflow_record_test.go create mode 100644 mso/resource_mso_tenant_policies_netflow_record.go create mode 100644 mso/resource_mso_tenant_policies_netflow_record_test.go create mode 100644 website/docs/d/tenant_policies_netflow_record.html.markdown create mode 100644 website/docs/r/tenant_policies_netflow_record.html.markdown diff --git a/examples/tenant_policies_netflow_record/main.tf b/examples/tenant_policies_netflow_record/main.tf new file mode 100644 index 00000000..3d94bad1 --- /dev/null +++ b/examples/tenant_policies_netflow_record/main.tf @@ -0,0 +1,42 @@ +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 +} + +# NetFlow Record example + +resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.tenant_template.id + name = "netflow_record_1" + description = "My NetFlow Record" + match_parameters = ["destination_ip", "source_ip", "ip_protocol"] +} + +# NetFlow Record data source example + +data "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.tenant_template.id + name = mso_tenant_policies_netflow_record.netflow_record.name +} diff --git a/mso/constants.go b/mso/constants.go index a2b6eb46..6325c2e4 100644 --- a/mso/constants.go +++ b/mso/constants.go @@ -48,6 +48,32 @@ var targetDscpMap = map[string]string{ "voiceAdmit": "voice_admit", } +var matchParameterMap = map[string]string{ + "destination_ip": "dstIP", + "destination_ipv4": "dstIPv4", + "destination_ipv6": "dstIPv6", + "destination_mac": "dstMac", + "destination_port": "dstPort", + "ethertype": "ethertype", + "ip_protocol": "proto", + "source_ip": "srcIP", + "source_ipv4": "srcIPv4", + "source_ipv6": "srcIPv6", + "source_mac": "srcMac", + "source_port": "srcPort", + "dstIP": "destination_ip", + "dstIPv4": "destination_ipv4", + "dstIPv6": "destination_ipv6", + "dstMac": "destination_mac", + "dstPort": "destination_port", + "proto": "ip_protocol", + "srcIP": "source_ip", + "srcIPv4": "source_ipv4", + "srcIPv6": "source_ipv6", + "srcMac": "source_mac", + "srcPort": "source_port", +} + var enabledDisabledMap = map[interface{}]interface{}{ "enabled": true, "disabled": false, diff --git a/mso/datasource_mso_tenant_policies_netflow_record.go b/mso/datasource_mso_tenant_policies_netflow_record.go new file mode 100644 index 00000000..adc3ace8 --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_record.go @@ -0,0 +1,68 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSONetflowRecord() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSONetflowRecordRead, + + 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 NetFlow Record.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Record.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the NetFlow Record.", + }, + "match_parameters": { + Type: schema.TypeList, + Computed: true, + Description: "The match parameters of the NetFlow Record.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record 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", "netFlowRecords") + if err != nil { + return err + } + + setNetflowRecordData(d, policy, templateId) + log.Printf("[DEBUG] MSO NetFlow Record Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_netflow_record_test.go b/mso/datasource_mso_tenant_policies_netflow_record_test.go new file mode 100644 index 00000000..69fd104c --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_record_test.go @@ -0,0 +1,37 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowRecordDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: NetFlow Record Data Source") }, + Config: testAccMSOTenantPoliciesNetflowRecordDataSource(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_record.netflow_record", "uuid"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), + testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), + testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), + ), + }, + }, + }) +} + +func testAccMSOTenantPoliciesNetflowRecordDataSource() string { + return fmt.Sprintf(`%s + data "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_tenant_policies_netflow_record.netflow_record.template_id + name = "test_netflow_record" + }`, testAccMSOTenantPoliciesNetflowRecordConfigUpdateMatchParams()) +} diff --git a/mso/provider.go b/mso/provider.go index 10a40521..bf76b834 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -143,6 +143,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), "mso_tenant_policies_netflow_exporter": resourceMSONetflowExporter(), + "mso_tenant_policies_netflow_record": resourceMSONetflowRecord(), }, DataSourcesMap: map[string]*schema.Resource{ @@ -219,6 +220,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), "mso_tenant_policies_netflow_exporter": datasourceMSONetflowExporter(), + "mso_tenant_policies_netflow_record": datasourceMSONetflowRecord(), }, ConfigureFunc: configureClient, diff --git a/mso/provider_test.go b/mso/provider_test.go index 73979b1b..41040382 100644 --- a/mso/provider_test.go +++ b/mso/provider_test.go @@ -158,6 +158,22 @@ func testAccVerifyKeyValue(resourceAttrsMap *map[string]string, resourceAttrRoot } } +func testCheckTypeSetStringElemAttr(resourceName, setKey, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + prefix := setKey + "." + for k, v := range rs.Primary.Attributes { + if strings.HasPrefix(k, prefix) && !strings.HasSuffix(k, ".#") && v == value { + return nil + } + } + return fmt.Errorf("no element with value %q found in set %q", value, setKey) + } +} + func customTestCheckResourceTypeSetAttr(resourceName, resourceAttrRootkey string, resourceAttrsMap map[string]string) resource.TestCheckFunc { return func(is *terraform.State) error { rootModule, err := is.RootModule().Resources[resourceName] diff --git a/mso/resource_mso_tenant_policies_netflow_record.go b/mso/resource_mso_tenant_policies_netflow_record.go new file mode 100644 index 00000000..84df9fe2 --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_record.go @@ -0,0 +1,247 @@ +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 resourceMSONetflowRecord() *schema.Resource { + return &schema.Resource{ + Create: resourceMSONetflowRecordCreate, + Read: resourceMSONetflowRecordRead, + Update: resourceMSONetflowRecordUpdate, + Delete: resourceMSONetflowRecordDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSONetflowRecordImport, + }, + + 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 NetFlow Record.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Record.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the NetFlow Record.", + }, + "match_parameters": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Description: "The match parameters of the NetFlow Record.", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "destination_ip", + "destination_ipv4", + "destination_ipv6", + "destination_mac", + "destination_port", + "ethertype", + "ip_protocol", + "source_ip", + "source_ipv4", + "source_ipv6", + "source_mac", + "source_port", + }, false), + }, + }, + }, + } +} + +func buildMatchParametersPayload(matchParamsRaw interface{}) []string { + matchParamsList := matchParamsRaw.(*schema.Set).List() + matchParams := make([]string, 0, len(matchParamsList)) + for _, item := range matchParamsList { + if param, ok := matchParameterMap[item.(string)]; ok { + matchParams = append(matchParams, param) + } + } + return matchParams +} + +func setNetflowRecordData(d *schema.ResourceData, response *container.Container, templateId string) error { + name := models.StripQuotes(response.S("name").String()) + d.SetId(fmt.Sprintf("templateId/%s/NetflowRecord/%s", templateId, name)) + d.Set("template_id", templateId) + d.Set("name", name) + d.Set("uuid", models.StripQuotes(response.S("uuid").String())) + d.Set("description", models.StripQuotes(response.S("description").String())) + + if response.Exists("match") { + matchCount, _ := response.ArrayCount("match") + matchParams := make([]string, matchCount) + for i := range matchCount { + matchParams[i] = convertValueWithMap(models.StripQuotes(response.S("match").Index(i).String()), matchParameterMap) + } + d.Set("match_parameters", matchParams) + } + + return nil +} + +func resourceMSONetflowRecordImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO NetFlow Record Resource - Beginning Import: %v", d.Id()) + err := resourceMSONetflowRecordRead(d, m) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] MSO NetFlow Record Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSONetflowRecordCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record Resource - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + "description": d.Get("description").(string), + } + + if matchParams, ok := d.GetOk("match_parameters"); ok { + payload["match"] = buildMatchParametersPayload(matchParams) + } + + payloadModel := models.GetPatchPayload("add", "/tenantPolicyTemplate/template/netFlowRecords/-", 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/NetflowRecord/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Record Resource - Create Complete: %v", d.Id()) + return resourceMSONetflowRecordRead(d, m) +} + +func resourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record 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(), "NetflowRecord") + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "tenantPolicyTemplate", "template", "netFlowRecords") + if err != nil { + return err + } + + setNetflowRecordData(d, policy, templateId) + log.Printf("[DEBUG] MSO NetFlow Record Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSONetflowRecordUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record 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", "netFlowRecords") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/tenantPolicyTemplate/template/netFlowRecords/%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("match_parameters") { + matchParams := buildMatchParametersPayload(d.Get("match_parameters")) + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/match", updatePath), matchParams) + 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/NetflowRecord/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Record Resource - Update Complete: %v", d.Id()) + return resourceMSONetflowRecordRead(d, m) +} + +func resourceMSONetflowRecordDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record 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", "netFlowRecords") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/tenantPolicyTemplate/template/netFlowRecords/%d", policyIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO NetFlow Record Resource - Delete Complete: %v", d.Id()) + return nil +} diff --git a/mso/resource_mso_tenant_policies_netflow_record_test.go b/mso/resource_mso_tenant_policies_netflow_record_test.go new file mode 100644 index 00000000..faa5be45 --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_record_test.go @@ -0,0 +1,107 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowRecordResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Create NetFlow Record") }, + Config: testAccMSOTenantPoliciesNetflowRecordConfigCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "description", "Test NetFlow Record"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "0"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_record.netflow_record", "uuid"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Record with Match Parameters") }, + Config: testAccMSOTenantPoliciesNetflowRecordConfigUpdateMatchParams(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_record.netflow_record", "uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), + testCheckTypeSetStringElemAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), + testCheckTypeSetStringElemAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Record Name") }, + Config: testAccMSOTenantPoliciesNetflowRecordConfigUpdateName(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record_updated"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_record.netflow_record", "uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), + testCheckTypeSetStringElemAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), + testCheckTypeSetStringElemAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Record Remove Match Params") }, + Config: testAccMSOTenantPoliciesNetflowRecordConfigRemoveMatch(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record_updated"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record (Removed Match Params)"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_record.netflow_record", "uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "0"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import NetFlow Record") }, + ResourceName: "mso_tenant_policies_netflow_record.netflow_record", + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_tenant_policies_netflow_record", "tenantPolicyTemplate", "template", "netFlowRecords"), + }) +} + +func testAccMSOTenantPoliciesNetflowRecordConfigCreate() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.template_tenant.id + name = "test_netflow_record" + description = "Test NetFlow Record" + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesNetflowRecordConfigUpdateMatchParams() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.template_tenant.id + name = "test_netflow_record" + description = "Updated NetFlow Record" + match_parameters = ["ethertype", "destination_mac"] + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesNetflowRecordConfigUpdateName() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.template_tenant.id + name = "test_netflow_record_updated" + description = "Updated NetFlow Record" + match_parameters = ["ethertype", "destination_mac"] + }`, testAccMSOTemplateResourceTenantConfig()) +} + +func testAccMSOTenantPoliciesNetflowRecordConfigRemoveMatch() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.template_tenant.id + name = "test_netflow_record_updated" + description = "Updated NetFlow Record (Removed Match Params)" + match_parameters = [] + }`, testAccMSOTemplateResourceTenantConfig()) +} \ No newline at end of file diff --git a/website/docs/d/tenant_policies_netflow_record.html.markdown b/website/docs/d/tenant_policies_netflow_record.html.markdown new file mode 100644 index 00000000..04097889 --- /dev/null +++ b/website/docs/d/tenant_policies_netflow_record.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_record" +sidebar_current: "docs-mso-data-source-tenant_policies_netflow_record" +description: |- + Data source for NetFlow Record Policies. +--- + +# mso_tenant_policies_netflow_record # + +Data source for NetFlow Record Policies. This data source is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Record + +## Example Usage ## + +```hcl +data "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.tenant_template.id + name = "netflow_record_1" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the template. +* `name` - (Required) The name of the NetFlow Record. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Record. +* `description` - (Read-Only) The description of the NetFlow Record. +* `match_parameters` - (Read-Only) The match parameters of the NetFlow Record. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Record in the template. diff --git a/website/docs/r/tenant_policies_netflow_record.html.markdown b/website/docs/r/tenant_policies_netflow_record.html.markdown new file mode 100644 index 00000000..76f8d54f --- /dev/null +++ b/website/docs/r/tenant_policies_netflow_record.html.markdown @@ -0,0 +1,46 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_record" +sidebar_current: "docs-mso-resource-tenant_policies_netflow_record" +description: |- + Manages NetFlow Record Policies on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_netflow_record # + +Manages NetFlow Record Policies on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Record + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.tenant_template.id + name = "netflow_record_1" + description = "Test NetFlow Record" + match_parameters = ["destination_ip", "source_ip", "ip_protocol"] +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the NetFlow Record. Maximum 64 characters. +* `description` - (Optional) The description of the NetFlow Record. +* `match_parameters` - (Optional) The set of match parameters for the NetFlow Record. Valid values are: `destination_ip`, `destination_ipv4`, `destination_ipv6`, `destination_mac`, `destination_port`, `ethertype`, `ip_protocol`, `source_ip`, `source_ipv4`, `source_ipv6`, `source_mac`, `source_port`. + +## Attribute Reference ## + +* `uuid` - The NDO UUID of the NetFlow Record. +* `id` - The unique terraform identifier of the NetFlow Record in the template. + +## Importing ## + +An existing MSO NetFlow Record 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_netflow_record.netflow_record templateId/{template_id}/NetflowRecord/{name} +``` From 6055ab8aef3fc7cfa7d55b9799a74d825283f87e Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 2 Apr 2026 09:28:42 +1000 Subject: [PATCH 3/8] [minor_change] Add mso_tenant_policies_netflow_monitor resource and datasource. --- .../tenant_policies_netflow_monitor/main.tf | 57 ++++ ...rce_mso_tenant_policies_netflow_monitor.go | 71 +++++ ...so_tenant_policies_netflow_monitor_test.go | 36 +++ mso/provider.go | 2 + ...rce_mso_tenant_policies_netflow_monitor.go | 252 ++++++++++++++++++ ...so_tenant_policies_netflow_monitor_test.go | 112 ++++++++ ...ant_policies_netflow_monitor.html.markdown | 37 +++ ...ant_policies_netflow_monitor.html.markdown | 48 ++++ 8 files changed, 615 insertions(+) create mode 100644 examples/tenant_policies_netflow_monitor/main.tf create mode 100644 mso/datasource_mso_tenant_policies_netflow_monitor.go create mode 100644 mso/datasource_mso_tenant_policies_netflow_monitor_test.go create mode 100644 mso/resource_mso_tenant_policies_netflow_monitor.go create mode 100644 mso/resource_mso_tenant_policies_netflow_monitor_test.go create mode 100644 website/docs/d/tenant_policies_netflow_monitor.html.markdown create mode 100644 website/docs/r/tenant_policies_netflow_monitor.html.markdown diff --git a/examples/tenant_policies_netflow_monitor/main.tf b/examples/tenant_policies_netflow_monitor/main.tf new file mode 100644 index 00000000..68b0c3d7 --- /dev/null +++ b/examples/tenant_policies_netflow_monitor/main.tf @@ -0,0 +1,57 @@ +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 +} + +# NetFlow Exporter example + +resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.tenant_template.id + name = "netflow_exporter_1" +} + +# NetFlow Record example + +resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.tenant_template.id + name = "netflow_record_1" +} + +# NetFlow Monitor example + +resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.tenant_template.id + name = "netflow_monitor_1" + description = "Test NetFlow Monitor" + netflow_record_uuid = mso_tenant_policies_netflow_record.netflow_record.uuid + netflow_exporter_uuids = [mso_tenant_policies_netflow_exporter.netflow_exporter.uuid] +} + +# NetFlow Monitor data source example + +data "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.tenant_template.id + name = mso_tenant_policies_netflow_monitor.netflow_monitor.name +} diff --git a/mso/datasource_mso_tenant_policies_netflow_monitor.go b/mso/datasource_mso_tenant_policies_netflow_monitor.go new file mode 100644 index 00000000..5412613b --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_monitor.go @@ -0,0 +1,71 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSOTenantPoliciesNetflowMonitor() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSOTenantPoliciesNetflowMonitorRead, + + 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 NetFlow Monitor.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the NetFlow Monitor.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Monitor.", + }, + "netflow_record_uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Record.", + }, + "netflow_exporter_uuids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "The list of NetFlow Exporter UUIDs.", + }, + }, + } +} + +func dataSourceMSOTenantPoliciesNetflowMonitorRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Monitor Data Source - Beginning Read") + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + monitorName := d.Get("name").(string) + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + monitor, err := GetPolicyByName(response, monitorName, "tenantPolicyTemplate", "template", "netFlowMonitors") + if err != nil { + return err + } + + setNetflowMonitorData(d, monitor, templateId) + log.Printf("[DEBUG] MSO NetFlow Monitor Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_netflow_monitor_test.go b/mso/datasource_mso_tenant_policies_netflow_monitor_test.go new file mode 100644 index 00000000..5ea3c85d --- /dev/null +++ b/mso/datasource_mso_tenant_policies_netflow_monitor_test.go @@ -0,0 +1,36 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowMonitorDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: NetFlow Monitor Data Source") }, + Config: testAccMSOTenantPoliciesNetflowMonitorDataSource(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_monitor.netflow_monitor", "name", "test_netflow_monitor"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_monitor.netflow_monitor", "uuid"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_record_uuid"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.#", "1"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.0"), + ), + }, + }, + }) +} + +func testAccMSOTenantPoliciesNetflowMonitorDataSource() string { + return fmt.Sprintf(`%s + data "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_tenant_policies_netflow_monitor.netflow_monitor.template_id + name = "test_netflow_monitor" + }`, testAccMSOTenantPoliciesNetflowMonitorConfigCreate()) +} diff --git a/mso/provider.go b/mso/provider.go index bf76b834..e99247ae 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -143,6 +143,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), "mso_tenant_policies_netflow_exporter": resourceMSONetflowExporter(), + "mso_tenant_policies_netflow_monitor": resourceMSOTenantPoliciesNetflowMonitor(), "mso_tenant_policies_netflow_record": resourceMSONetflowRecord(), }, @@ -220,6 +221,7 @@ func Provider() terraform.ResourceProvider { "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), "mso_tenant_policies_netflow_exporter": datasourceMSONetflowExporter(), + "mso_tenant_policies_netflow_monitor": datasourceMSOTenantPoliciesNetflowMonitor(), "mso_tenant_policies_netflow_record": datasourceMSONetflowRecord(), }, diff --git a/mso/resource_mso_tenant_policies_netflow_monitor.go b/mso/resource_mso_tenant_policies_netflow_monitor.go new file mode 100644 index 00000000..bc02cbb8 --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_monitor.go @@ -0,0 +1,252 @@ +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 resourceMSOTenantPoliciesNetflowMonitor() *schema.Resource { + return &schema.Resource{ + Create: resourceMSOTenantPoliciesNetflowMonitorCreate, + Read: resourceMSOTenantPoliciesNetflowMonitorRead, + Update: resourceMSOTenantPoliciesNetflowMonitorUpdate, + Delete: resourceMSOTenantPoliciesNetflowMonitorDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSOTenantPoliciesNetflowMonitorImport, + }, + + 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 NetFlow Monitor.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description of the NetFlow Monitor.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Monitor.", + }, + "netflow_record_uuid": { + Type: schema.TypeString, + Optional: true, + Description: "The UUID of the NetFlow Record.", + }, + "netflow_exporter_uuids": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "The list of NetFlow Exporter UUIDs.", + }, + }, + } +} + +func buildExporterUUIDsPayload(exportersRaw interface{}) []string { + exportersList := exportersRaw.(*schema.Set).List() + exporters := make([]string, 0, len(exportersList)) + for _, item := range exportersList { + exporters = append(exporters, item.(string)) + } + return exporters +} + +func setNetflowMonitorData(d *schema.ResourceData, response *container.Container, templateId string) error { + d.SetId(fmt.Sprintf("templateId/%s/NetflowMonitor/%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("recordRef") { + d.Set("netflow_record_uuid", models.StripQuotes(response.S("recordRef").String())) + } + + if response.Exists("exporterRefs") { + exporterCount, _ := response.ArrayCount("exporterRefs") + exporters := make([]string, exporterCount) + for i := range exporterCount { + exporters[i] = models.StripQuotes(response.S("exporterRefs").Index(i).String()) + } + d.Set("netflow_exporter_uuids", exporters) + } + + return nil +} + +func resourceMSOTenantPoliciesNetflowMonitorImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Beginning Import: %v", d.Id()) + err := resourceMSOTenantPoliciesNetflowMonitorRead(d, m) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSOTenantPoliciesNetflowMonitorCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Beginning Create: %v", d.Id()) + + msoClient := m.(*client.Client) + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + "description": d.Get("description").(string), + "exporterRefs": buildExporterUUIDsPayload(d.Get("netflow_exporter_uuids")), + } + + if recordUUID, ok := d.GetOk("netflow_record_uuid"); ok { + payload["recordRef"] = recordUUID.(string) + } + + payloadModel := models.GetPatchPayload("add", "/tenantPolicyTemplate/template/netFlowMonitors/-", 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/NetflowMonitor/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Create Complete: %v", d.Id()) + return resourceMSOTenantPoliciesNetflowMonitorRead(d, m) +} + +func resourceMSOTenantPoliciesNetflowMonitorRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Monitor 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 + } + + monitorName, err := GetPolicyNameFromResourceId(d.Id(), "NetflowMonitor") + if err != nil { + return err + } + + monitor, err := GetPolicyByName(response, monitorName, "tenantPolicyTemplate", "template", "netFlowMonitors") + if err != nil { + return err + } + + setNetflowMonitorData(d, monitor, templateId) + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSOTenantPoliciesNetflowMonitorUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Monitor 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 + } + + monitorIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "tenantPolicyTemplate", "template", "netFlowMonitors") + if err != nil { + return err + } + + payloadCont := container.New() + payloadCont.Array() + + monitorPath := fmt.Sprintf("/tenantPolicyTemplate/template/netFlowMonitors/%d", monitorIndex) + + if d.HasChange("name") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", monitorPath), d.Get("name").(string)) + if err != nil { + return err + } + } + + if d.HasChange("description") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", monitorPath), d.Get("description").(string)) + if err != nil { + return err + } + } + + if d.HasChange("netflow_record_uuid") { + recordUUID := d.Get("netflow_record_uuid").(string) + if recordUUID != "" { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/recordRef", monitorPath), recordUUID) + if err != nil { + return err + } + } else { + err := addPatchPayloadToContainer(payloadCont, "remove", fmt.Sprintf("%s/recordRef", monitorPath), nil) + if err != nil { + return err + } + } + } + + if d.HasChange("netflow_exporter_uuids") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/exporterRefs", monitorPath), buildExporterUUIDsPayload(d.Get("netflow_exporter_uuids"))) + 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/NetflowMonitor/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Update Complete: %v", d.Id()) + return resourceMSOTenantPoliciesNetflowMonitorRead(d, m) +} + +func resourceMSOTenantPoliciesNetflowMonitorDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Beginning Delete: %v", d.Id()) + msoClient := m.(*client.Client) + + templateCont, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", d.Get("template_id").(string))) + if err != nil { + return err + } + + monitorIndex, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "tenantPolicyTemplate", "template", "netFlowMonitors") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/tenantPolicyTemplate/template/netFlowMonitors/%d", monitorIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", d.Get("template_id").(string)), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Delete Complete: %v", d.Id()) + return nil +} diff --git a/mso/resource_mso_tenant_policies_netflow_monitor_test.go b/mso/resource_mso_tenant_policies_netflow_monitor_test.go new file mode 100644 index 00000000..07a11060 --- /dev/null +++ b/mso/resource_mso_tenant_policies_netflow_monitor_test.go @@ -0,0 +1,112 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowMonitorResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Create NetFlow Monitor") }, + Config: testAccMSOTenantPoliciesNetflowMonitorConfigCreate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "name", "test_netflow_monitor"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "description", "Test NetFlow Monitor"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_monitor.netflow_monitor", "uuid"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_record_uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.#", "1"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Monitor") }, + Config: testAccMSOTenantPoliciesNetflowMonitorConfigUpdate(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "name", "test_netflow_monitor_updated"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "description", "Updated NetFlow Monitor"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_monitor.netflow_monitor", "uuid"), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_record_uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.#", "2"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update NetFlow Remove Description and Record UUID") }, + Config: testAccMSOTenantPoliciesNetflowMonitorConfigRemoveOptionals(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "name", "test_netflow_monitor_updated"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "description", ""), + resource.TestCheckResourceAttrSet("mso_tenant_policies_netflow_monitor.netflow_monitor", "uuid"), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_record_uuid", ""), + resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.#", "2"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import NetFlow Monitor") }, + ResourceName: "mso_tenant_policies_netflow_monitor.netflow_monitor", + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_tenant_policies_netflow_monitor", "tenantPolicyTemplate", "template", "netFlowMonitors"), + }) +} + +func testAccNeflowMonitorBaseConfig(extra_exporter bool) string { + base := fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { + template_id = mso_template.template_tenant.id + name = "test_netflow_exporter" + } + resource "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_template.template_tenant.id + name = "test_netflow_record" + }`, testAccMSOTemplateResourceTenantConfig()) + + if extra_exporter { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_exporter" "netflow_exporter_2" { + template_id = mso_template.template_tenant.id + name = "test_netflow_exporter_2" + // This is to ensure the exporters are deleted in order. + depends_on = [mso_tenant_policies_netflow_exporter.netflow_exporter] + }`, base) + } + + return base +} + +func testAccMSOTenantPoliciesNetflowMonitorConfigCreate() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.template_tenant.id + name = "test_netflow_monitor" + description = "Test NetFlow Monitor" + netflow_record_uuid = mso_tenant_policies_netflow_record.netflow_record.uuid + netflow_exporter_uuids = [mso_tenant_policies_netflow_exporter.netflow_exporter.uuid] + }`, testAccNeflowMonitorBaseConfig(false)) +} + +func testAccMSOTenantPoliciesNetflowMonitorConfigUpdate() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.template_tenant.id + name = "test_netflow_monitor_updated" + description = "Updated NetFlow Monitor" + netflow_record_uuid = mso_tenant_policies_netflow_record.netflow_record.uuid + netflow_exporter_uuids = [mso_tenant_policies_netflow_exporter.netflow_exporter.uuid, mso_tenant_policies_netflow_exporter.netflow_exporter_2.uuid] + }`, testAccNeflowMonitorBaseConfig(true)) +} + +func testAccMSOTenantPoliciesNetflowMonitorConfigRemoveOptionals() string { + return fmt.Sprintf(`%s + resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.template_tenant.id + name = "test_netflow_monitor_updated" + netflow_exporter_uuids = [mso_tenant_policies_netflow_exporter.netflow_exporter.uuid, mso_tenant_policies_netflow_exporter.netflow_exporter_2.uuid] + }`, testAccNeflowMonitorBaseConfig(true)) +} diff --git a/website/docs/d/tenant_policies_netflow_monitor.html.markdown b/website/docs/d/tenant_policies_netflow_monitor.html.markdown new file mode 100644 index 00000000..cf7238a2 --- /dev/null +++ b/website/docs/d/tenant_policies_netflow_monitor.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_monitor" +sidebar_current: "docs-mso-data-source-tenant_policies_netflow_monitor" +description: |- + Data source for NetFlow Monitor Policies. +--- + +# mso_tenant_policies_netflow_monitor # + +Data source for NetFlow Monitor Policies. This data source is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Monitor + +## Example Usage ## + +```hcl +data "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.tenant_template.id + name = "netflow_monitor_1" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the template. +* `name` - (Required) The name of the NetFlow Monitor. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Monitor. +* `description` - (Read-Only) The description of the NetFlow Monitor. +* `netflow_record_uuid` - (Read-Only) The UUID of the associated NetFlow Record. +* `netflow_exporter_uuids` - (Read-Only) The list of UUIDs of the associated NetFlow Exporters. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Monitor in the template. diff --git a/website/docs/r/tenant_policies_netflow_monitor.html.markdown b/website/docs/r/tenant_policies_netflow_monitor.html.markdown new file mode 100644 index 00000000..8e3b23c1 --- /dev/null +++ b/website/docs/r/tenant_policies_netflow_monitor.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "mso" +page_title: "MSO: mso_tenant_policies_netflow_monitor" +sidebar_current: "docs-mso-resource-tenant_policies_netflow_monitor" +description: |- + Manages NetFlow Monitor Policies on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_tenant_policies_netflow_monitor # + +Manages NetFlow Monitor Policies on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.1 and higher. + +## GUI Information ## + +* `Location` - Manage -> Tenant Template -> Tenant Policies -> NetFlow Monitor + +## Example Usage ## + +```hcl +resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { + template_id = mso_template.tenant_template.id + name = "netflow_monitor_1" + description = "Test NetFlow Monitor" + netflow_record_uuid = mso_tenant_policies_netflow_record.netflow_record.uuid + netflow_exporter_uuids = [mso_tenant_policies_netflow_exporter.netflow_exporter.uuid] +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the tenant policy template. +* `name` - (Required) The name of the NetFlow Monitor. Maximum 64 characters. +* `description` - (Optional) The description of the NetFlow Monitor. +* `netflow_record_uuid` - (Optional) The UUID of the NetFlow Record to associate with this monitor. +* `netflow_exporter_uuids` - (Required) The list of UUIDs of the NetFlow Exporters to associate with this monitor. At least one exporter must be provided. + +## Attribute Reference ## + +* `uuid` - The NDO UUID of the NetFlow Monitor. +* `id` - The unique terraform identifier of the NetFlow Monitor in the template. + +## Importing ## + +An existing MSO NetFlow Monitor 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_netflow_monitor.netflow_monitor templateId/{template_id}/NetflowMonitor/{name} +``` From ba14b42ee743b9b9f5ab383e3dd7fe948421a785 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 2 Apr 2026 09:36:33 +1000 Subject: [PATCH 4/8] [ignore] Fix format --- ...ce_mso_tenant_policies_netflow_exporter.go | 2 +- ...o_tenant_policies_netflow_exporter_test.go | 2 +- ...urce_mso_tenant_policies_netflow_record.go | 136 +++++++++--------- ...mso_tenant_policies_netflow_record_test.go | 74 +++++----- ...ce_mso_tenant_policies_netflow_exporter.go | 2 +- ...o_tenant_policies_netflow_exporter_test.go | 2 +- ...mso_tenant_policies_netflow_record_test.go | 2 +- 7 files changed, 110 insertions(+), 110 deletions(-) diff --git a/mso/datasource_mso_tenant_policies_netflow_exporter.go b/mso/datasource_mso_tenant_policies_netflow_exporter.go index 3a27e0cd..31bcf431 100644 --- a/mso/datasource_mso_tenant_policies_netflow_exporter.go +++ b/mso/datasource_mso_tenant_policies_netflow_exporter.go @@ -52,4 +52,4 @@ func dataSourceMSONetflowExporterRead(d *schema.ResourceData, m interface{}) err setNetflowExporterData(d, policy, templateId) log.Printf("[DEBUG] MSO NetFlow Exporter Data Source - Read Complete: %v", d.Id()) return nil -} \ No newline at end of file +} diff --git a/mso/datasource_mso_tenant_policies_netflow_exporter_test.go b/mso/datasource_mso_tenant_policies_netflow_exporter_test.go index 42a4018d..131abd0b 100644 --- a/mso/datasource_mso_tenant_policies_netflow_exporter_test.go +++ b/mso/datasource_mso_tenant_policies_netflow_exporter_test.go @@ -30,4 +30,4 @@ func testAccMSOTenantPoliciesNetflowExporterDataSource() string { template_id = mso_tenant_policies_netflow_exporter.netflow_exporter.template_id name = "test_netflow_exporter" }`, testAccMSOTenantPoliciesNetflowExporterConfigCreate()) -} \ No newline at end of file +} diff --git a/mso/datasource_mso_tenant_policies_netflow_record.go b/mso/datasource_mso_tenant_policies_netflow_record.go index adc3ace8..3b2203e1 100644 --- a/mso/datasource_mso_tenant_policies_netflow_record.go +++ b/mso/datasource_mso_tenant_policies_netflow_record.go @@ -1,68 +1,68 @@ -package mso - -import ( - "fmt" - "log" - - "github.com/ciscoecosystem/mso-go-client/client" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" -) - -func datasourceMSONetflowRecord() *schema.Resource { - return &schema.Resource{ - Read: dataSourceMSONetflowRecordRead, - - 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 NetFlow Record.", - }, - "uuid": { - Type: schema.TypeString, - Computed: true, - Description: "The UUID of the NetFlow Record.", - }, - "description": { - Type: schema.TypeString, - Computed: true, - Description: "The description of the NetFlow Record.", - }, - "match_parameters": { - Type: schema.TypeList, - Computed: true, - Description: "The match parameters of the NetFlow Record.", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - } -} - -func dataSourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error { - log.Printf("[DEBUG] MSO NetFlow Record 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", "netFlowRecords") - if err != nil { - return err - } - - setNetflowRecordData(d, policy, templateId) - log.Printf("[DEBUG] MSO NetFlow Record Data Source - Read Complete: %v", d.Id()) - return nil -} +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSONetflowRecord() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSONetflowRecordRead, + + 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 NetFlow Record.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "The UUID of the NetFlow Record.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the NetFlow Record.", + }, + "match_parameters": { + Type: schema.TypeList, + Computed: true, + Description: "The match parameters of the NetFlow Record.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO NetFlow Record 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", "netFlowRecords") + if err != nil { + return err + } + + setNetflowRecordData(d, policy, templateId) + log.Printf("[DEBUG] MSO NetFlow Record Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_tenant_policies_netflow_record_test.go b/mso/datasource_mso_tenant_policies_netflow_record_test.go index 69fd104c..c01151a2 100644 --- a/mso/datasource_mso_tenant_policies_netflow_record_test.go +++ b/mso/datasource_mso_tenant_policies_netflow_record_test.go @@ -1,37 +1,37 @@ -package mso - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" -) - -func TestAccMSOTenantPoliciesNetflowRecordDataSource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - PreConfig: func() { fmt.Println("Test: NetFlow Record Data Source") }, - Config: testAccMSOTenantPoliciesNetflowRecordDataSource(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record"), - resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), - resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_record.netflow_record", "uuid"), - resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), - testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), - testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), - ), - }, - }, - }) -} - -func testAccMSOTenantPoliciesNetflowRecordDataSource() string { - return fmt.Sprintf(`%s - data "mso_tenant_policies_netflow_record" "netflow_record" { - template_id = mso_tenant_policies_netflow_record.netflow_record.template_id - name = "test_netflow_record" - }`, testAccMSOTenantPoliciesNetflowRecordConfigUpdateMatchParams()) -} +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOTenantPoliciesNetflowRecordDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: NetFlow Record Data Source") }, + Config: testAccMSOTenantPoliciesNetflowRecordDataSource(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "name", "test_netflow_record"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), + resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_record.netflow_record", "uuid"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), + testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), + testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), + ), + }, + }, + }) +} + +func testAccMSOTenantPoliciesNetflowRecordDataSource() string { + return fmt.Sprintf(`%s + data "mso_tenant_policies_netflow_record" "netflow_record" { + template_id = mso_tenant_policies_netflow_record.netflow_record.template_id + name = "test_netflow_record" + }`, testAccMSOTenantPoliciesNetflowRecordConfigUpdateMatchParams()) +} diff --git a/mso/resource_mso_tenant_policies_netflow_exporter.go b/mso/resource_mso_tenant_policies_netflow_exporter.go index fcf2fbdd..72ce664b 100644 --- a/mso/resource_mso_tenant_policies_netflow_exporter.go +++ b/mso/resource_mso_tenant_policies_netflow_exporter.go @@ -171,4 +171,4 @@ func resourceMSONetflowExporterDelete(d *schema.ResourceData, m interface{}) err d.SetId("") log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Delete Complete: %v", d.Id()) return nil -} \ No newline at end of file +} diff --git a/mso/resource_mso_tenant_policies_netflow_exporter_test.go b/mso/resource_mso_tenant_policies_netflow_exporter_test.go index 38a89ade..dbea409c 100644 --- a/mso/resource_mso_tenant_policies_netflow_exporter_test.go +++ b/mso/resource_mso_tenant_policies_netflow_exporter_test.go @@ -53,4 +53,4 @@ func testAccMSOTenantPoliciesNetflowExporterConfigUpdateName() string { template_id = mso_template.template_tenant.id name = "test_netflow_exporter_updated" }`, testAccMSOTemplateResourceTenantConfig()) -} \ No newline at end of file +} diff --git a/mso/resource_mso_tenant_policies_netflow_record_test.go b/mso/resource_mso_tenant_policies_netflow_record_test.go index faa5be45..93c63aa2 100644 --- a/mso/resource_mso_tenant_policies_netflow_record_test.go +++ b/mso/resource_mso_tenant_policies_netflow_record_test.go @@ -104,4 +104,4 @@ func testAccMSOTenantPoliciesNetflowRecordConfigRemoveMatch() string { description = "Updated NetFlow Record (Removed Match Params)" match_parameters = [] }`, testAccMSOTemplateResourceTenantConfig()) -} \ No newline at end of file +} From eabe648fb56a5a2012513a48028c7c79d3c86f53 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 2 Apr 2026 09:47:25 +1000 Subject: [PATCH 5/8] [ignore] Fix minor issues in netflow resources --- ...source_mso_tenant_policies_netflow_record_test.go | 4 ++-- mso/resource_mso_tenant_policies_netflow_exporter.go | 5 ++++- mso/resource_mso_tenant_policies_netflow_monitor.go | 4 +++- ...ource_mso_tenant_policies_netflow_monitor_test.go | 12 ++++++------ mso/resource_mso_tenant_policies_netflow_record.go | 4 +++- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/mso/datasource_mso_tenant_policies_netflow_record_test.go b/mso/datasource_mso_tenant_policies_netflow_record_test.go index c01151a2..ce8031a7 100644 --- a/mso/datasource_mso_tenant_policies_netflow_record_test.go +++ b/mso/datasource_mso_tenant_policies_netflow_record_test.go @@ -20,8 +20,8 @@ func TestAccMSOTenantPoliciesNetflowRecordDataSource(t *testing.T) { resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "description", "Updated NetFlow Record"), resource.TestCheckResourceAttrSet("data.mso_tenant_policies_netflow_record.netflow_record", "uuid"), resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.#", "2"), - testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "ethertype"), - testCheckTypeSetStringElemAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters", "destination_mac"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.0", "ethertype"), + resource.TestCheckResourceAttr("data.mso_tenant_policies_netflow_record.netflow_record", "match_parameters.1", "destination_mac"), ), }, }, diff --git a/mso/resource_mso_tenant_policies_netflow_exporter.go b/mso/resource_mso_tenant_policies_netflow_exporter.go index 72ce664b..ce262883 100644 --- a/mso/resource_mso_tenant_policies_netflow_exporter.go +++ b/mso/resource_mso_tenant_policies_netflow_exporter.go @@ -54,7 +54,10 @@ func setNetflowExporterData(d *schema.ResourceData, response *container.Containe func resourceMSONetflowExporterImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Beginning Import: %v", d.Id()) - resourceMSONetflowExporterRead(d, m) + err := resourceMSONetflowExporterRead(d, m) + if err != nil { + return nil, err + } log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Import Complete: %v", d.Id()) return []*schema.ResourceData{d}, nil } diff --git a/mso/resource_mso_tenant_policies_netflow_monitor.go b/mso/resource_mso_tenant_policies_netflow_monitor.go index bc02cbb8..e1e15120 100644 --- a/mso/resource_mso_tenant_policies_netflow_monitor.go +++ b/mso/resource_mso_tenant_policies_netflow_monitor.go @@ -73,7 +73,9 @@ func setNetflowMonitorData(d *schema.ResourceData, response *container.Container d.SetId(fmt.Sprintf("templateId/%s/NetflowMonitor/%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())) + if response.Exists("description") { + d.Set("description", models.StripQuotes(response.S("description").String())) + } d.Set("uuid", models.StripQuotes(response.S("uuid").String())) if response.Exists("recordRef") { d.Set("netflow_record_uuid", models.StripQuotes(response.S("recordRef").String())) diff --git a/mso/resource_mso_tenant_policies_netflow_monitor_test.go b/mso/resource_mso_tenant_policies_netflow_monitor_test.go index 07a11060..46d2e243 100644 --- a/mso/resource_mso_tenant_policies_netflow_monitor_test.go +++ b/mso/resource_mso_tenant_policies_netflow_monitor_test.go @@ -34,7 +34,7 @@ func TestAccMSOTenantPoliciesNetflowMonitorResource(t *testing.T) { resource.TestCheckResourceAttr("mso_tenant_policies_netflow_monitor.netflow_monitor", "netflow_exporter_uuids.#", "2"), ), }, - { + { PreConfig: func() { fmt.Println("Test: Update NetFlow Remove Description and Record UUID") }, Config: testAccMSOTenantPoliciesNetflowMonitorConfigRemoveOptionals(), Check: resource.ComposeTestCheckFunc( @@ -57,7 +57,7 @@ func TestAccMSOTenantPoliciesNetflowMonitorResource(t *testing.T) { } func testAccNeflowMonitorBaseConfig(extra_exporter bool) string { - base := fmt.Sprintf(`%s + base := fmt.Sprintf(`%s resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { template_id = mso_template.template_tenant.id name = "test_netflow_exporter" @@ -67,17 +67,17 @@ func testAccNeflowMonitorBaseConfig(extra_exporter bool) string { name = "test_netflow_record" }`, testAccMSOTemplateResourceTenantConfig()) - if extra_exporter { - return fmt.Sprintf(`%s + if extra_exporter { + return fmt.Sprintf(`%s resource "mso_tenant_policies_netflow_exporter" "netflow_exporter_2" { template_id = mso_template.template_tenant.id name = "test_netflow_exporter_2" // This is to ensure the exporters are deleted in order. depends_on = [mso_tenant_policies_netflow_exporter.netflow_exporter] }`, base) - } + } - return base + return base } func testAccMSOTenantPoliciesNetflowMonitorConfigCreate() string { diff --git a/mso/resource_mso_tenant_policies_netflow_record.go b/mso/resource_mso_tenant_policies_netflow_record.go index 84df9fe2..7f9c6439 100644 --- a/mso/resource_mso_tenant_policies_netflow_record.go +++ b/mso/resource_mso_tenant_policies_netflow_record.go @@ -88,7 +88,9 @@ func setNetflowRecordData(d *schema.ResourceData, response *container.Container, d.Set("template_id", templateId) d.Set("name", name) d.Set("uuid", models.StripQuotes(response.S("uuid").String())) - d.Set("description", models.StripQuotes(response.S("description").String())) + if response.Exists("description") { + d.Set("description", models.StripQuotes(response.S("description").String())) + } if response.Exists("match") { matchCount, _ := response.ArrayCount("match") From 70dc5ee314ccf372bd0b1775c5e6b23e33f21228 Mon Sep 17 00:00:00 2001 From: samitab Date: Tue, 7 Apr 2026 22:35:31 +1000 Subject: [PATCH 6/8] [ignore] Add MSO API limitation doc --- mso/resource_mso_tenant_policies_netflow_monitor_test.go | 2 +- website/docs/index.html.markdown | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mso/resource_mso_tenant_policies_netflow_monitor_test.go b/mso/resource_mso_tenant_policies_netflow_monitor_test.go index 46d2e243..7ca54dc0 100644 --- a/mso/resource_mso_tenant_policies_netflow_monitor_test.go +++ b/mso/resource_mso_tenant_policies_netflow_monitor_test.go @@ -72,7 +72,7 @@ func testAccNeflowMonitorBaseConfig(extra_exporter bool) string { resource "mso_tenant_policies_netflow_exporter" "netflow_exporter_2" { template_id = mso_template.template_tenant.id name = "test_netflow_exporter_2" - // This is to ensure the exporters are deleted in order. + // depends_on ensures exporters are deleted sequentially to avoid index conflicts. depends_on = [mso_tenant_policies_netflow_exporter.netflow_exporter] }`, base) } diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index e9b71028..9eff311c 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -11,6 +11,15 @@ MSO Provider Cisco ACI Multi-Site Orchestrator (MSO) is responsible for provisioning, health monitoring, and managing the full lifecycle of Cisco ACI networking policies and tenant policies across Cisco ACI sites around the world. Terraform provider MSO is a Terraform plugin which will be used to manage the MSO Fabric Constructs on the Cisco MSO platform with leveraging advantages of Terraform. The provider needs to be configured with the proper credentials before it can be used. +Limitations +-------------- + +When destroying multiple instances of the same resource type within the same template, use `-parallelism=1` to avoid index conflicts in the MSO API: + +```sh +terraform destroy -parallelism=1 +``` + Authentication -------------- From 4580dd5845ae554864f4c92514e856aba52bbc26 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 9 Apr 2026 08:31:15 +1000 Subject: [PATCH 7/8] [ignore] Update limitation note in the provider docs. --- website/docs/index.html.markdown | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 9eff311c..ab731669 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -14,12 +14,20 @@ Cisco ACI Multi-Site Orchestrator (MSO) is responsible for provisioning, health Limitations -------------- -When destroying multiple instances of the same resource type within the same template, use `-parallelism=1` to avoid index conflicts in the MSO API: +When creating, updating, or destroying multiple instances of the same resource type within the same template, use [`-parallelism=1`](https://developer.hashicorp.com/terraform/cli/commands/apply#parallelism-n) to avoid index conflicts in the MSO API: + +```sh +terraform apply -parallelism=1 +``` + +or ```sh terraform destroy -parallelism=1 ``` +To avoid setting this flag on every command, you can configure it persistently using the [`TF_CLI_ARGS` or `TF_CLI_ARGS_name` environment variables](https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_cli_args-and-tf_cli_args_name). + Authentication -------------- From 5b54ee50882182664bbd6ce8b686233e981ff4fd Mon Sep 17 00:00:00 2001 From: samitab Date: Fri, 10 Apr 2026 13:54:55 +1000 Subject: [PATCH 8/8] [ignore] Add read only prefix and error handling --- mso/datasource_mso_tenant_policies_netflow_exporter.go | 5 ++++- mso/datasource_mso_tenant_policies_netflow_monitor.go | 5 ++++- mso/datasource_mso_tenant_policies_netflow_record.go | 5 ++++- mso/resource_mso_tenant_policies_netflow_exporter.go | 5 ++++- mso/resource_mso_tenant_policies_netflow_monitor.go | 5 ++++- mso/resource_mso_tenant_policies_netflow_record.go | 5 ++++- .../docs/r/tenant_policies_netflow_exporter.html.markdown | 4 ++-- website/docs/r/tenant_policies_netflow_monitor.html.markdown | 4 ++-- website/docs/r/tenant_policies_netflow_record.html.markdown | 4 ++-- 9 files changed, 30 insertions(+), 12 deletions(-) diff --git a/mso/datasource_mso_tenant_policies_netflow_exporter.go b/mso/datasource_mso_tenant_policies_netflow_exporter.go index 31bcf431..2731047a 100644 --- a/mso/datasource_mso_tenant_policies_netflow_exporter.go +++ b/mso/datasource_mso_tenant_policies_netflow_exporter.go @@ -49,7 +49,10 @@ func dataSourceMSONetflowExporterRead(d *schema.ResourceData, m interface{}) err return err } - setNetflowExporterData(d, policy, templateId) + err = setNetflowExporterData(d, policy, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Exporter Data Source - Read Complete: %v", d.Id()) return nil } diff --git a/mso/datasource_mso_tenant_policies_netflow_monitor.go b/mso/datasource_mso_tenant_policies_netflow_monitor.go index 5412613b..cd186be0 100644 --- a/mso/datasource_mso_tenant_policies_netflow_monitor.go +++ b/mso/datasource_mso_tenant_policies_netflow_monitor.go @@ -65,7 +65,10 @@ func dataSourceMSOTenantPoliciesNetflowMonitorRead(d *schema.ResourceData, m int return err } - setNetflowMonitorData(d, monitor, templateId) + err = setNetflowMonitorData(d, monitor, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Monitor Data Source - Read Complete: %v", d.Id()) return nil } diff --git a/mso/datasource_mso_tenant_policies_netflow_record.go b/mso/datasource_mso_tenant_policies_netflow_record.go index 3b2203e1..ce07f7c9 100644 --- a/mso/datasource_mso_tenant_policies_netflow_record.go +++ b/mso/datasource_mso_tenant_policies_netflow_record.go @@ -62,7 +62,10 @@ func dataSourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error return err } - setNetflowRecordData(d, policy, templateId) + err = setNetflowRecordData(d, policy, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Record Data Source - Read Complete: %v", d.Id()) return nil } diff --git a/mso/resource_mso_tenant_policies_netflow_exporter.go b/mso/resource_mso_tenant_policies_netflow_exporter.go index ce262883..29096cc2 100644 --- a/mso/resource_mso_tenant_policies_netflow_exporter.go +++ b/mso/resource_mso_tenant_policies_netflow_exporter.go @@ -107,7 +107,10 @@ func resourceMSONetflowExporterRead(d *schema.ResourceData, m interface{}) error return err } - setNetflowExporterData(d, policy, templateId) + err = setNetflowExporterData(d, policy, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Exporter Resource - Read Complete: %v", d.Id()) return nil } diff --git a/mso/resource_mso_tenant_policies_netflow_monitor.go b/mso/resource_mso_tenant_policies_netflow_monitor.go index e1e15120..f3542515 100644 --- a/mso/resource_mso_tenant_policies_netflow_monitor.go +++ b/mso/resource_mso_tenant_policies_netflow_monitor.go @@ -155,7 +155,10 @@ func resourceMSOTenantPoliciesNetflowMonitorRead(d *schema.ResourceData, m inter return err } - setNetflowMonitorData(d, monitor, templateId) + err = setNetflowMonitorData(d, monitor, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Monitor Resource - Read Complete: %v", d.Id()) return nil } diff --git a/mso/resource_mso_tenant_policies_netflow_record.go b/mso/resource_mso_tenant_policies_netflow_record.go index 7f9c6439..3477c03d 100644 --- a/mso/resource_mso_tenant_policies_netflow_record.go +++ b/mso/resource_mso_tenant_policies_netflow_record.go @@ -164,7 +164,10 @@ func resourceMSONetflowRecordRead(d *schema.ResourceData, m interface{}) error { return err } - setNetflowRecordData(d, policy, templateId) + err = setNetflowRecordData(d, policy, templateId) + if err != nil { + return err + } log.Printf("[DEBUG] MSO NetFlow Record Resource - Read Complete: %v", d.Id()) return nil } diff --git a/website/docs/r/tenant_policies_netflow_exporter.html.markdown b/website/docs/r/tenant_policies_netflow_exporter.html.markdown index 03fcbfe1..cafb8404 100644 --- a/website/docs/r/tenant_policies_netflow_exporter.html.markdown +++ b/website/docs/r/tenant_policies_netflow_exporter.html.markdown @@ -30,8 +30,8 @@ resource "mso_tenant_policies_netflow_exporter" "netflow_exporter" { ## Attribute Reference ## -* `uuid` - The NDO UUID of the NetFlow Exporter. -* `id` - The unique terraform identifier of the NetFlow Exporter in the template. +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Exporter. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Exporter in the template. ## Importing ## diff --git a/website/docs/r/tenant_policies_netflow_monitor.html.markdown b/website/docs/r/tenant_policies_netflow_monitor.html.markdown index 8e3b23c1..1e506ea3 100644 --- a/website/docs/r/tenant_policies_netflow_monitor.html.markdown +++ b/website/docs/r/tenant_policies_netflow_monitor.html.markdown @@ -36,8 +36,8 @@ resource "mso_tenant_policies_netflow_monitor" "netflow_monitor" { ## Attribute Reference ## -* `uuid` - The NDO UUID of the NetFlow Monitor. -* `id` - The unique terraform identifier of the NetFlow Monitor in the template. +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Monitor. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Monitor in the template. ## Importing ## diff --git a/website/docs/r/tenant_policies_netflow_record.html.markdown b/website/docs/r/tenant_policies_netflow_record.html.markdown index 76f8d54f..04571eed 100644 --- a/website/docs/r/tenant_policies_netflow_record.html.markdown +++ b/website/docs/r/tenant_policies_netflow_record.html.markdown @@ -34,8 +34,8 @@ resource "mso_tenant_policies_netflow_record" "netflow_record" { ## Attribute Reference ## -* `uuid` - The NDO UUID of the NetFlow Record. -* `id` - The unique terraform identifier of the NetFlow Record in the template. +* `uuid` - (Read-Only) The NDO UUID of the NetFlow Record. +* `id` - (Read-Only) The unique terraform identifier of the NetFlow Record in the template. ## Importing ##