From f6a3e93e17222658feeb80c7b4a0e7d5d8d2b928 Mon Sep 17 00:00:00 2001 From: samitab Date: Sun, 8 Mar 2026 23:21:51 +1000 Subject: [PATCH 01/18] [minor_change] Add mso_fabric_resource_policies_virtual_port_channel_interface resource and datasource. --- ...policies_virtual_port_channel_interface.go | 75 ++++ ...ies_virtual_port_channel_interface_test.go | 70 ++++ mso/provider.go | 2 + ...policies_virtual_port_channel_interface.go | 365 ++++++++++++++++++ ...ies_virtual_port_channel_interface_test.go | 203 ++++++++++ 5 files changed, 715 insertions(+) create mode 100644 mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go create mode 100644 mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go create mode 100644 mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go create mode 100644 mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go new file mode 100644 index 00000000..5f92e93f --- /dev/null +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -0,0 +1,75 @@ +package mso + +import ( + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func dataSourceMSOVirtualPortChannelInterface() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSOFabricResourcePoliciesVirtualPortChannelInterfaceRead, + + SchemaVersion: version, + + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "Fabric Resource template ID.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "Virtual Port Channel Interface name.", + }, + "uuid": {Type: schema.TypeString, Computed: true}, + "description": {Type: schema.TypeString, Computed: true}, + + "node_1": {Type: schema.TypeString, Computed: true}, + "node_2": {Type: schema.TypeString, Computed: true}, + + "node_1_interfaces": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "node_2_interfaces": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "interface_policy_group_uuid": {Type: schema.TypeString, Computed: true}, + + "interface_descriptions": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node": {Type: schema.TypeString, Computed: true}, + "interface": {Type: schema.TypeString, Computed: true}, + "description": {Type: schema.TypeString, Computed: true}, + }, + }, + }, + }, + } +} + +func dataSourceMSOFabricResourcePoliciesVirtualPortChannelInterfaceRead(d *schema.ResourceData, m any) error { + log.Printf("[DEBUG] MSO VPC Interface Data Source - Beginning Read: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + policyName := d.Get("name").(string) + + setVPCInterfaceData(d, msoClient, templateId, policyName) + + log.Printf("[DEBUG] MSO VPC Interface Data Source - Read Complete: %v", d.Id()) + return nil +} \ No newline at end of file diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go new file mode 100644 index 00000000..1c2f130f --- /dev/null +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -0,0 +1,70 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOVirtualPortChannelInterfaceDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: VPC Interface Data Source") }, + Config: testAccMSOVirtualPortChannelInterfaceDataSource(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "name", + "tf_test_vpc_if", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "description", + "Terraform test VPC Interface", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1", + "101", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2", + "102", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1_interfaces.#", + "2", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2_interfaces.#", + "1", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions.#", + "1", + ), + resource.TestCheckResourceAttrSet( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "uuid", + ), + ), + }, + }, + }) +} + +func testAccMSOVirtualPortChannelInterfaceDataSource() string { + return fmt.Sprintf(`%s + data "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if.template_id + name = "tf_test_vpc_if" + }`, testAccMSOVirtualPortChannelInterfaceConfigCreate()) +} diff --git a/mso/provider.go b/mso/provider.go index 18d1c7ab..4ee60e7e 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_fabric_resource_policies_virtual_port_channel_interface": resourceMSOVirtualPortChannelInterface(), }, 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_fabric_resource_policies_virtual_port_channel_interface": dataSourceMSOVirtualPortChannelInterface(), }, ConfigureFunc: configureClient, diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go new file mode 100644 index 00000000..095ab015 --- /dev/null +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -0,0 +1,365 @@ +package mso + +import ( + "fmt" + "log" + "strings" + + "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 resourceMSOVirtualPortChannelInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceMSOVirtualPortChannelInterfaceCreate, + Read: resourceMSOVirtualPortChannelInterfaceRead, + Update: resourceMSOVirtualPortChannelInterfaceUpdate, + Delete: resourceMSOVirtualPortChannelInterfaceDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSOVirtualPortChannelInterfaceImport, + }, + + SchemaVersion: version, + + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "Fabric Resource template ID.", + }, + "name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), + Description: "Virtual Port Channel Interface name.", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Virtual Port Channel Interface UUID.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Virtual Port Channel Interface description.", + }, + "node_1": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "node_2": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "node_1_interfaces": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of interface IDs (or ranges) for node 1.", + }, + "node_2_interfaces": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of interface IDs (or ranges) for node 2.", + }, + "interface_policy_group_uuid": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "UUID of the Port Channel Interface Policy Group.", + }, + "interface_descriptions": { + Type: schema.TypeList, + Optional: true, + Description: "List of interface descriptions; provided list replaces the existing list in NDO.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "interface": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func setVPCInterfaceData(d *schema.ResourceData, msoClient *client.Client, templateId, policyName string) error { + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + vpcCont, err := GetPolicyByName(response, policyName, "fabricResourceTemplate", "template", "virtualPortChannels") + if err != nil { + return err + } + name := models.StripQuotes(vpcCont.S("name").String()) + + d.SetId(fmt.Sprintf("templateId/%s/virtualPortChannelInterface/%s", templateId, name)) + d.Set("template_id", templateId) + + d.Set("name", models.StripQuotes(vpcCont.S("name").String())) + d.Set("description", models.StripQuotes(vpcCont.S("description").String())) + d.Set("uuid", models.StripQuotes(vpcCont.S("uuid").String())) + + d.Set("interface_policy_group_uuid", models.StripQuotes(vpcCont.S("policy").String())) + + d.Set("node_1", models.StripQuotes(vpcCont.S("node1Details", "node").String())) + d.Set("node_2", models.StripQuotes(vpcCont.S("node2Details", "node").String())) + + d.Set("node_1_interfaces", splitCommaString(models.StripQuotes(vpcCont.S("node1Details", "memberInterfaces").String()))) + d.Set("node_2_interfaces", splitCommaString(models.StripQuotes(vpcCont.S("node2Details", "memberInterfaces").String()))) + + if vpcCont.Exists("interfaceDescriptions") { + count, _ := vpcCont.ArrayCount("interfaceDescriptions") + out := make([]any, 0, count) + for i := 0; i < count; i++ { + descCont, err := vpcCont.ArrayElement(i, "interfaceDescriptions") + if err != nil { + return err + } + entry := make(map[string]any) + entry["node"] = models.StripQuotes(descCont.S("nodeID").String()) + entry["interface"] = models.StripQuotes(descCont.S("interfaceID").String()) + entry["description"] = models.StripQuotes(descCont.S("description").String()) + out = append(out, entry) + } + d.Set("interface_descriptions", out) + } + + return nil +} + +func resourceMSOVirtualPortChannelInterfaceImport(d *schema.ResourceData, m any) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO VPC Interface - Beginning Import: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId, err := GetTemplateIdFromResourceId(d.Id()) + if err != nil { + return nil, err + } + name, err := GetPolicyNameFromResourceId(d.Id(), "virtualPortChannelInterface") + if err != nil { + return nil, err + } + + err = setVPCInterfaceData(d, msoClient, templateId, name) + if err != nil { + return nil, err + } + + log.Printf("[DEBUG] MSO VPC Interface - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSOVirtualPortChannelInterfaceCreate(d *schema.ResourceData, m any) error { + log.Printf("[DEBUG] MSO VPC Interface - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + payload := buildFabricResourceVPCInterfacePayload(d) + + payloadModel := models.GetPatchPayload("add", "/fabricResourceTemplate/template/virtualPortChannels/-", payload) + _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + name := d.Get("name").(string) + d.SetId(fmt.Sprintf("templateId/%s/virtualPortChannelInterface/%s", templateId, name)) + + log.Printf("[DEBUG] MSO VPC Interface - Create Complete: %v", d.Id()) + return resourceMSOVirtualPortChannelInterfaceRead(d, m) +} + +func resourceMSOVirtualPortChannelInterfaceRead(d *schema.ResourceData, m any) error { + log.Printf("[DEBUG] MSO VPC Interface - Beginning Read: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + policyName := d.Get("name").(string) + + setVPCInterfaceData(d, msoClient, templateId, policyName) + + log.Printf("[DEBUG] MSO VPC Interface - Read Complete : %v", d.Id()) + return nil +} + +func resourceMSOVirtualPortChannelInterfaceUpdate(d *schema.ResourceData, m any) error { + log.Printf("[DEBUG] MSO VPC Interface - 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 + } + + index, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "fabricResourceTemplate", "template", "virtualPortChannels") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/fabricResourceTemplate/template/virtualPortChannels/%d", index) + + payloadCont := container.New() + payloadCont.Array() + + if d.HasChange("name") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", updatePath), d.Get("name").(string)); err != nil { + return err + } + } + + if d.HasChange("description") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)); err != nil { + return err + } + } + + if d.HasChange("node_1") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/node", updatePath), d.Get("node_1").(string)); err != nil { + return err + } + } + if d.HasChange("node_1_interfaces") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_1_interfaces"), ",")); err != nil { + return err + } + } + + if d.HasChange("node_2") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/node", updatePath), d.Get("node_2").(string)); err != nil { + return err + } + } + + if d.HasChange("node_2_interfaces") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_2_interfaces"), ",")); err != nil { + return err + } + } + + if d.HasChange("interface_policy_group_uuid") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/policy", updatePath), d.Get("interface_policy_group_uuid").(string)); err != nil { + return err + } + } + + if d.HasChange("interface_descriptions") { + if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/interfaceDescriptions", updatePath), buildInterfaceDescriptionsPayload(d)); err != nil { + return err + } + } + + if err := doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont); err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/virtualPortChannelInterface/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO VPC Interface - Update Complete: %v", d.Id()) + return resourceMSOVirtualPortChannelInterfaceRead(d, m) +} + +func resourceMSOVirtualPortChannelInterfaceDelete(d *schema.ResourceData, m any) error { + log.Printf("[DEBUG] MSO VPC Interface - 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 + } + + index, err := GetPolicyIndexByKeyAndValue(templateCont, "uuid", d.Get("uuid").(string), "fabricResourceTemplate", "template", "virtualPortChannels") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/fabricResourceTemplate/template/virtualPortChannels/%d", index)) + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO VPC Interface - Delete Complete: %v", d.Id()) + return nil +} + +// ---- payload + lookup helpers ---- + +func buildFabricResourceVPCInterfacePayload(d *schema.ResourceData) map[string]any { + ifaces1 := getListOfStringsFromSchemaList(d, "node_1_interfaces") + ifaces2 := getListOfStringsFromSchemaList(d, "node_2_interfaces") + + payload := map[string]any{ + "name": d.Get("name").(string), + "description": d.Get("description").(string), + "node1Details": map[string]any{ + "node": d.Get("node_1").(string), + "memberInterfaces": strings.Join(ifaces1, ","), + }, + "node2Details": map[string]any{ + "node": d.Get("node_2").(string), + "memberInterfaces": strings.Join(ifaces2, ","), + }, + "policy": d.Get("interface_policy_group_uuid").(string), + "interfaceDescriptions": buildInterfaceDescriptionsPayload(d), + } + return payload +} + +func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any { + raw := d.Get("interface_descriptions").([]any) + out := make([]map[string]any, 0, len(raw)) + for _, v := range raw { + m := v.(map[string]any) + out = append(out, map[string]any{ + "nodeID": m["node"].(string), + "interfaceID": m["interface"].(string), + "description": m["description"].(string), + }) + } + return out +} + +func splitCommaString(s string) []string { + s = strings.TrimSpace(s) + if s == "" { + return []string{} + } + parts := strings.Split(s, ",") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p != "" { + out = append(out, p) + } + } + return out +} \ No newline at end of file diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go new file mode 100644 index 00000000..9c24acbb --- /dev/null +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -0,0 +1,203 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { + fmt.Println("Test: Virtual Port Channel Interface Resource - Create") + }, + Config: testAccMSOVirtualPortChannelInterfaceConfigCreate(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "name", + "tf_test_vpc_if", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "description", + "Terraform test VPC Interface", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1", + "101", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2", + "102", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1_interfaces.#", + "2", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2_interfaces.#", + "1", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions.#", + "1", + ), + resource.TestCheckResourceAttrSet( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "uuid", + ), + ), + }, + { + PreConfig: func() { + fmt.Println("Test: Virtual Port Channel Interface Resource - Update") + }, + Config: testAccMSOVirtualPortChannelInterfaceConfigUpdate(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "description", + "Terraform test VPC Interface (updated)", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1_interfaces.#", + "3", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2_interfaces.#", + "3", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions.#", + "2", + ), + resource.TestCheckResourceAttrSet( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "uuid", + ), + ), + }, + { + PreConfig: func() { + fmt.Println("Test: Virtual Port Channel Interface Resource - Removed Descriptions") + }, + Config: testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "description", + "Terraform test VPC Interface (removed descriptions)", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_1_interfaces.#", + "3", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "node_2_interfaces.#", + "3", + ), + resource.TestCheckResourceAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions.#", + "0", + ), + resource.TestCheckResourceAttrSet( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "uuid", + ), + ), + }, + { + ResourceName: "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_fabric_resource_policies_virtual_port_channel_interface", "fabricResourceTemplate", "template", "virtualPortChannels"), + }) +} + +func testAccMSOVirtualPortChannelInterfaceConfigCreate() string { + return fmt.Sprintf(`%s +resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource merges (PR #449). + + interface_descriptions { + node = "101" + interface = "1/1" + description = "Terraform test interface description" + } +} +`, testAccMSOTemplateResourceFabricResourceConfig()) +} + +func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { + return fmt.Sprintf(`%s +resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface (updated)" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/2-5", "1/7"] + node_2_interfaces = ["1/2", "1/3", "1/5-7"] + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource merges (PR #449). + + interface_descriptions { + node = "101" + interface = "1/1" + description = "Terraform test interface description 101" + } + + interface_descriptions { + node = "102" + interface = "1/3" + description = "Terraform test interface description 102" + } +} +`, testAccMSOTemplateResourceFabricResourceConfig()) +} + +func testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions() string { + return fmt.Sprintf(`%s +resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface (removed descriptions)" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/2-5", "1/7"] + node_2_interfaces = ["1/2", "1/3", "1/5-7"] + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource lands (PR #449). +} +`, testAccMSOTemplateResourceFabricResourceConfig()) +} From d9763b9c6b61091f73ba22c14ff9ef65f8f167f2 Mon Sep 17 00:00:00 2001 From: samitab Date: Tue, 10 Mar 2026 22:39:25 +1000 Subject: [PATCH 02/18] [ignore] Add vpc interface docs. --- .../main.tf | 51 ++++++++++++++ ...rtual_port_channel_interface.html.markdown | 43 ++++++++++++ ...rtual_port_channel_interface.html.markdown | 70 +++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 examples/fabric_resource_policies_virtual_port_channel_interface/main.tf create mode 100644 website/docs/d/fabric_resource_policies_virtual_port_channel_interface.html.markdown create mode 100644 website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown diff --git a/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf b/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf new file mode 100644 index 00000000..ce1916bf --- /dev/null +++ b/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf @@ -0,0 +1,51 @@ +terraform { + required_providers { + mso = { + source = "CiscoDevNet/mso" + } + } +} + +provider "mso" { + username = "" # + password = "" # + url = "" # + insecure = true +} + +resource "mso_template" "fabric_policy_template" { + template_name = "fabric_policy_template" + template_type = "fabric_policy" +} + +resource "mso_fabric_policies_interface_setting" "port_channel_interface" { + template_id = mso_template.fabric_policy_template.id + type = "portchannel" + name = "port_channel_interface" +} + +resource "mso_template" "fabric_resource_template" { + template_name = "fabric_resource_template" + template_type = "fabric_resource" +} + +resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.fabric_resource_template.id + name = "tf_vpc_if" + description = "Example VPC Interface" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.port_channel_interface.uuid + interface_descriptions { + node = "101" + interface = "1/1" + description = "Interface Description 1/1" + } + interface_descriptions { + node = "102" + interface = "1/10" + description = "Interface Description 1/10" + } +} \ No newline at end of file diff --git a/website/docs/d/fabric_resource_policies_virtual_port_channel_interface.html.markdown b/website/docs/d/fabric_resource_policies_virtual_port_channel_interface.html.markdown new file mode 100644 index 00000000..535b3b9c --- /dev/null +++ b/website/docs/d/fabric_resource_policies_virtual_port_channel_interface.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "mso" +page_title: "MSO: mso_fabric_resource_policies_virtual_port_channel_interface" +sidebar_current: "docs-mso-datasource-fabric_resource_policies_virtual_port_channel_interface" +description: |- + Data source for Virtual Port Channel (VPC) Interfaces on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_fabric_resource_policies_virtual_port_channel_interface # + +Data source for Virtual Port Channel (VPC) Interfaces on Cisco Nexus Dashboard Orchestrator (NDO). + +## GUI Information ## + +* `Location` - Manage -> Fabric Templates -> Fabric Resource Policies -> Virtual Port Channel Interface + +## Example Usage ## + +```hcl +data "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.fabric_resource_template.id + name = "tf_vpc_if" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the Fabric Resource template. +* `name` - (Required) The name of the Virtual Port Channel Interface. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the Virtual Port Channel Interface. +* `description` - (Read-Only) The description of the Virtual Port Channel Interface. +* `node_1` - (Read-Only) The first node ID. +* `node_2` - (Read-Only) The second node ID. +* `node_1_interfaces` - (Read-Only) List of interface IDs (or ranges) for node 1. +* `node_2_interfaces` - (Read-Only) List of interface IDs (or ranges) for node 2. +* `interface_policy_group_uuid` - (Read-Only) UUID of the Port Channel Interface Policy Group. +* `interface_descriptions` - (Read-Only) List of interface description entries. + * `node` - (Read-Only) Node ID. + * `interface` - (Read-Only) Interface ID. + * `description` - (Read-Only) Interface description. diff --git a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown new file mode 100644 index 00000000..1c4d7bd9 --- /dev/null +++ b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown @@ -0,0 +1,70 @@ +--- +layout: "mso" +page_title: "MSO: mso_fabric_resource_policies_virtual_port_channel_interface" +sidebar_current: "docs-mso-resource-fabric_resource_policies_virtual_port_channel_interface" +description: |- + Manages Virtual Port Channel (VPC) Interfaces on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_fabric_resource_policies_virtual_port_channel_interface # + +Manages Virtual Port Channel (VPC) Interfaces on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.3(1) or higher. + +## GUI Information ## + +* `Location` - Manage -> Fabric Templates -> Fabric Resource Policies -> Virtual Port Channel Interface + +## Example Usage ## + +```hcl +resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.fabric_resource_template.id + name = "tf_vpc_if" + description = "Example VPC Interface" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.port_channel_interface.uuid + + interface_descriptions { + node = "101" + interface = "1/1" + description = "Terraform example interface description" + } + + interface_descriptions { + node = "102" + interface = "1/10" + description = "Terraform example interface description" + } +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the Fabric Resource template. +* `name` - (Required) The name of the Virtual Port Channel Interface. +* `description` - (Optional) The description of the Virtual Port Channel Interface. +* `node_1` - (Required) The first node ID. +* `node_2` - (Required) The second node ID. +* `node_1_interfaces` - (Required) List of interface IDs (or ranges) for node 1. +* `node_2_interfaces` - (Optional) List of interface IDs (or ranges) for node 2. +* `interface_policy_group_uuid` - (Required) UUID of the Port Channel Interface Policy Group. +* `interface_descriptions` - (Optional) List of interface description entries. The provided list replaces the existing list in NDO. + * `node` - (Required) Node ID. + * `interface` - (Required) Interface ID. + * `description` - (Optional) Interface description. + +## Attribute Reference ## + +* `uuid` - (Read-Only) The NDO UUID of the Virtual Port Channel Interface. +* `id` - (Read-Only) The unique Terraform identifier of the Virtual Port Channel Interface. + +## Importing ## + +An existing MSO Virtual Port Channel Interface can be [imported][docs-import] into this resource via its ID/path, via the following command: [docs-import]: + +```bash +terraform import mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if templateId/{template_id}/virtualPortChannelInterface/{name} +``` From 6a15570f4f260f8c2e75b74da380fbae76d48fe7 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 14:52:35 +1000 Subject: [PATCH 03/18] [ignore] Fix required node_2_interface. --- ...policies_virtual_port_channel_interface.go | 61 ++++++------------- mso/utils.go | 16 +++++ ...rtual_port_channel_interface.html.markdown | 2 +- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 095ab015..c00841d1 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -68,8 +68,7 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { }, "node_2_interfaces": { Type: schema.TypeList, - Computed: true, - Optional: true, + Required: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interface IDs (or ranges) for node 2.", }, @@ -180,9 +179,26 @@ func resourceMSOVirtualPortChannelInterfaceCreate(d *schema.ResourceData, m any) msoClient := m.(*client.Client) templateId := d.Get("template_id").(string) - payload := buildFabricResourceVPCInterfacePayload(d) + + interfaces1 := getListOfStringsFromSchemaList(d, "node_1_interfaces") + interfaces2 := getListOfStringsFromSchemaList(d, "node_2_interfaces") + payload := map[string]any{ + "name": d.Get("name").(string), + "description": d.Get("description").(string), + "node1Details": map[string]any{ + "node": d.Get("node_1").(string), + "memberInterfaces": strings.Join(interfaces1, ","), + }, + "node2Details": map[string]any{ + "node": d.Get("node_2").(string), + "memberInterfaces": strings.Join(interfaces2, ","), + }, + "policy": d.Get("interface_policy_group_uuid").(string), + "interfaceDescriptions": buildInterfaceDescriptionsPayload(d), + } payloadModel := models.GetPatchPayload("add", "/fabricResourceTemplate/template/virtualPortChannels/-", payload) + _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) if err != nil { return err @@ -311,29 +327,6 @@ func resourceMSOVirtualPortChannelInterfaceDelete(d *schema.ResourceData, m any) return nil } -// ---- payload + lookup helpers ---- - -func buildFabricResourceVPCInterfacePayload(d *schema.ResourceData) map[string]any { - ifaces1 := getListOfStringsFromSchemaList(d, "node_1_interfaces") - ifaces2 := getListOfStringsFromSchemaList(d, "node_2_interfaces") - - payload := map[string]any{ - "name": d.Get("name").(string), - "description": d.Get("description").(string), - "node1Details": map[string]any{ - "node": d.Get("node_1").(string), - "memberInterfaces": strings.Join(ifaces1, ","), - }, - "node2Details": map[string]any{ - "node": d.Get("node_2").(string), - "memberInterfaces": strings.Join(ifaces2, ","), - }, - "policy": d.Get("interface_policy_group_uuid").(string), - "interfaceDescriptions": buildInterfaceDescriptionsPayload(d), - } - return payload -} - func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any { raw := d.Get("interface_descriptions").([]any) out := make([]map[string]any, 0, len(raw)) @@ -346,20 +339,4 @@ func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any }) } return out -} - -func splitCommaString(s string) []string { - s = strings.TrimSpace(s) - if s == "" { - return []string{} - } - parts := strings.Split(s, ",") - out := make([]string, 0, len(parts)) - for _, p := range parts { - p = strings.TrimSpace(p) - if p != "" { - out = append(out, p) - } - } - return out } \ No newline at end of file diff --git a/mso/utils.go b/mso/utils.go index 7fc0678c..f5a75410 100644 --- a/mso/utils.go +++ b/mso/utils.go @@ -540,3 +540,19 @@ func GetDeployedSiteIdsForApplicationTemplate(msoClient *client.Client, schemaId } return siteIds, nil } + +func splitCommaString(s string) []string { + s = strings.TrimSpace(s) + if s == "" { + return []string{} + } + parts := strings.Split(s, ",") + out := make([]string, 0, len(parts)) + for _, part := range parts { + part = strings.TrimSpace(part) + if part != "" { + out = append(out, part) + } + } + return out +} diff --git a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown index 1c4d7bd9..442436a2 100644 --- a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown +++ b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown @@ -49,7 +49,7 @@ resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" * `node_1` - (Required) The first node ID. * `node_2` - (Required) The second node ID. * `node_1_interfaces` - (Required) List of interface IDs (or ranges) for node 1. -* `node_2_interfaces` - (Optional) List of interface IDs (or ranges) for node 2. +* `node_2_interfaces` - (Required) List of interface IDs (or ranges) for node 2. * `interface_policy_group_uuid` - (Required) UUID of the Port Channel Interface Policy Group. * `interface_descriptions` - (Optional) List of interface description entries. The provided list replaces the existing list in NDO. * `node` - (Required) Node ID. From ecc99a195e9497a193a23d35cb098e4c1af3fc3e Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 15:08:46 +1000 Subject: [PATCH 04/18] [ignore] Fix go format --- ...ic_resource_policies_virtual_port_channel_interface.go | 8 ++++---- ...ic_resource_policies_virtual_port_channel_interface.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 5f92e93f..26396842 100644 --- a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -51,9 +51,9 @@ func dataSourceMSOVirtualPortChannelInterface() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "node": {Type: schema.TypeString, Computed: true}, - "interface": {Type: schema.TypeString, Computed: true}, - "description": {Type: schema.TypeString, Computed: true}, + "node": {Type: schema.TypeString, Computed: true}, + "interface": {Type: schema.TypeString, Computed: true}, + "description": {Type: schema.TypeString, Computed: true}, }, }, }, @@ -72,4 +72,4 @@ func dataSourceMSOFabricResourcePoliciesVirtualPortChannelInterfaceRead(d *schem log.Printf("[DEBUG] MSO VPC Interface Data Source - Read Complete: %v", d.Id()) return nil -} \ No newline at end of file +} diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index c00841d1..afab67c5 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -179,7 +179,7 @@ func resourceMSOVirtualPortChannelInterfaceCreate(d *schema.ResourceData, m any) msoClient := m.(*client.Client) templateId := d.Get("template_id").(string) - + interfaces1 := getListOfStringsFromSchemaList(d, "node_1_interfaces") interfaces2 := getListOfStringsFromSchemaList(d, "node_2_interfaces") @@ -333,10 +333,10 @@ func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any for _, v := range raw { m := v.(map[string]any) out = append(out, map[string]any{ - "nodeID": m["node"].(string), + "nodeID": m["node"].(string), "interfaceID": m["interface"].(string), "description": m["description"].(string), }) } return out -} \ No newline at end of file +} From 7aa1da3328abacfa2b21d57098984c210b05c992 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 18:00:07 +1000 Subject: [PATCH 05/18] [ignore] Update tests --- ...ies_virtual_port_channel_interface_test.go | 64 ++--- ...ies_virtual_port_channel_interface_test.go | 263 ++++++++---------- 2 files changed, 145 insertions(+), 182 deletions(-) diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 1c2f130f..e993ffde 100644 --- a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -16,44 +16,25 @@ func TestAccMSOVirtualPortChannelInterfaceDataSource(t *testing.T) { PreConfig: func() { fmt.Println("Test: VPC Interface Data Source") }, Config: testAccMSOVirtualPortChannelInterfaceDataSource(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "name", - "tf_test_vpc_if", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "description", - "Terraform test VPC Interface", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1", - "101", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2", - "102", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1_interfaces.#", - "2", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2_interfaces.#", - "1", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "interface_descriptions.#", - "1", - ), - resource.TestCheckResourceAttrSet( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "uuid", + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", "Terraform test VPC Interface"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "101"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "102"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "2"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/10-11"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "1"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), + resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + customTestCheckResourceTypeSetAttr( + "data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions", + map[string]string{ + "node": "101", + "interface": "1/1", + "description": "Terraform test interface description", + }, ), ), }, @@ -64,7 +45,8 @@ func TestAccMSOVirtualPortChannelInterfaceDataSource(t *testing.T) { func testAccMSOVirtualPortChannelInterfaceDataSource() string { return fmt.Sprintf(`%s data "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { - template_id = mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if.template_id - name = "tf_test_vpc_if" - }`, testAccMSOVirtualPortChannelInterfaceConfigCreate()) + template_id = mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if.template_id + name = "tf_test_vpc_if" + } + `, testAccMSOVirtualPortChannelInterfaceConfigCreate()) } diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 9c24acbb..77df8b5a 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -18,44 +18,25 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { }, Config: testAccMSOVirtualPortChannelInterfaceConfigCreate(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "name", - "tf_test_vpc_if", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "description", - "Terraform test VPC Interface", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1", - "101", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2", - "102", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1_interfaces.#", - "2", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2_interfaces.#", - "1", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "interface_descriptions.#", - "1", - ), - resource.TestCheckResourceAttrSet( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "uuid", + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", "Terraform test VPC Interface"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "101"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "102"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/10-11"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "1"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + customTestCheckResourceTypeSetAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions", + map[string]string{ + "node": "101", + "interface": "1/1", + "description": "Terraform test interface description", + }, ), ), }, @@ -65,29 +46,37 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { }, Config: testAccMSOVirtualPortChannelInterfaceConfigUpdate(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "description", - "Terraform test VPC Interface (updated)", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1_interfaces.#", - "3", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2_interfaces.#", - "3", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "interface_descriptions.#", - "2", - ), - resource.TestCheckResourceAttrSet( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "uuid", + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", "Terraform test VPC Interface (updated)"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "103"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "104"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "3"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/2-5"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.2", "1/7"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "3"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.1", "1/3"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.2", "1/5-7"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "2"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + customTestCheckResourceTypeSetAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions", + map[string]string{ + "node": "103", + "interface": "1/1", + "description": "Terraform test interface description 103", + }, + ), + customTestCheckResourceTypeSetAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions", + map[string]string{ + "node": "104", + "interface": "1/3", + "description": "Terraform test interface description 104", + }, ), ), }, @@ -97,30 +86,11 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { }, Config: testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "description", - "Terraform test VPC Interface (removed descriptions)", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_1_interfaces.#", - "3", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "node_2_interfaces.#", - "3", - ), - resource.TestCheckResourceAttr( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "interface_descriptions.#", - "0", - ), - resource.TestCheckResourceAttrSet( - "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", - "uuid", - ), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", "Terraform test VPC Interface (removed descriptions)"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "3"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "3"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "0"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), ), }, { @@ -129,75 +99,86 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { ImportStateVerify: true, }, }, - CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_fabric_resource_policies_virtual_port_channel_interface", "fabricResourceTemplate", "template", "virtualPortChannels"), + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments( + "mso_fabric_resource_policies_virtual_port_channel_interface", + "fabricResourceTemplate", + "template", + "virtualPortChannels", + ), }) } func testAccMSOVirtualPortChannelInterfaceConfigCreate() string { return fmt.Sprintf(`%s -resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { - template_id = mso_template.template_fabric_resource.id - name = "tf_test_vpc_if" - description = "Terraform test VPC Interface" - node_1 = "101" - node_2 = "102" - node_1_interfaces = ["1/1", "1/10-11"] - node_2_interfaces = ["1/2"] - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource merges (PR #449). - - interface_descriptions { - node = "101" - interface = "1/1" - description = "Terraform test interface description" - } -} -`, testAccMSOTemplateResourceFabricResourceConfig()) + resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface" + node_1 = "101" + node_2 = "102" + + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] + + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource merges (PR #449). + + interface_descriptions { + node = "101" + interface = "1/1" + description = "Terraform test interface description" + } + } + `, testAccMSOTemplateResourceFabricResourceConfig()) } func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { return fmt.Sprintf(`%s -resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { - template_id = mso_template.template_fabric_resource.id - name = "tf_test_vpc_if" - description = "Terraform test VPC Interface (updated)" - node_1 = "101" - node_2 = "102" - node_1_interfaces = ["1/1", "1/2-5", "1/7"] - node_2_interfaces = ["1/2", "1/3", "1/5-7"] - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource merges (PR #449). + resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface (updated)" + node_1 = "103" + node_2 = "104" - interface_descriptions { - node = "101" - interface = "1/1" - description = "Terraform test interface description 101" - } + node_1_interfaces = ["1/1", "1/2-5", "1/7"] + node_2_interfaces = ["1/2", "1/3", "1/5-7"] - interface_descriptions { - node = "102" - interface = "1/3" - description = "Terraform test interface description 102" - } -} -`, testAccMSOTemplateResourceFabricResourceConfig()) + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource merges (PR #449). + + interface_descriptions { + node = "103" + interface = "1/1" + description = "Terraform test interface description 103" + } + + interface_descriptions { + node = "104" + interface = "1/3" + description = "Terraform test interface description 104" + } + } + `, testAccMSOTemplateResourceFabricResourceConfig()) } func testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions() string { return fmt.Sprintf(`%s -resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { - template_id = mso_template.template_fabric_resource.id - name = "tf_test_vpc_if" - description = "Terraform test VPC Interface (removed descriptions)" - node_1 = "101" - node_2 = "102" - node_1_interfaces = ["1/1", "1/2-5", "1/7"] - node_2_interfaces = ["1/2", "1/3", "1/5-7"] - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource lands (PR #449). -} -`, testAccMSOTemplateResourceFabricResourceConfig()) + resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface (removed descriptions)" + node_1 = "103" + node_2 = "104" + + node_1_interfaces = ["1/1", "1/2-5", "1/7"] + node_2_interfaces = ["1/2", "1/3", "1/5-7"] + + interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" + // interface_policy_group_uuid will be validated/required once the + // interface policy group resource lands (PR #449). + } + `, testAccMSOTemplateResourceFabricResourceConfig()) } From 26ec84c79dd1a032286229a00c3f9e1dd0c757a4 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 18:17:38 +1000 Subject: [PATCH 06/18] [ignore] return set data errors --- ...ric_resource_policies_virtual_port_channel_interface.go | 7 +++++-- ...ric_resource_policies_virtual_port_channel_interface.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 26396842..64fa007d 100644 --- a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -66,9 +66,12 @@ func dataSourceMSOFabricResourcePoliciesVirtualPortChannelInterfaceRead(d *schem msoClient := m.(*client.Client) templateId := d.Get("template_id").(string) - policyName := d.Get("name").(string) + name := d.Get("name").(string) - setVPCInterfaceData(d, msoClient, templateId, policyName) + err := setVPCInterfaceData(d, msoClient, templateId, name) + if err != nil { + return err + } log.Printf("[DEBUG] MSO VPC Interface Data Source - Read Complete: %v", d.Id()) return nil diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index afab67c5..36758bef 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -216,9 +216,12 @@ func resourceMSOVirtualPortChannelInterfaceRead(d *schema.ResourceData, m any) e msoClient := m.(*client.Client) templateId := d.Get("template_id").(string) - policyName := d.Get("name").(string) + name := d.Get("name").(string) - setVPCInterfaceData(d, msoClient, templateId, policyName) + err := setVPCInterfaceData(d, msoClient, templateId, name) + if err != nil { + return err + } log.Printf("[DEBUG] MSO VPC Interface - Read Complete : %v", d.Id()) return nil From d85d6a24bdd81911b88aa7407f517d31fb383a23 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 22:51:42 +1000 Subject: [PATCH 07/18] [ignore] Update tests for interface description being unset --- .../main.tf | 2 +- ...c_resource_policies_virtual_port_channel_interface_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf b/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf index ce1916bf..642c4fa0 100644 --- a/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf +++ b/examples/fabric_resource_policies_virtual_port_channel_interface/main.tf @@ -48,4 +48,4 @@ resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" interface = "1/10" description = "Interface Description 1/10" } -} \ No newline at end of file +} diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 77df8b5a..3420db92 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -75,7 +75,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { map[string]string{ "node": "104", "interface": "1/3", - "description": "Terraform test interface description 104", + "description": "", }, ), ), @@ -158,7 +158,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { interface_descriptions { node = "104" interface = "1/3" - description = "Terraform test interface description 104" + // No description on purpose } } `, testAccMSOTemplateResourceFabricResourceConfig()) From 572b8edbde0dfab99792ff121650e2fabe02798c Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 11 Mar 2026 23:13:01 +1000 Subject: [PATCH 08/18] [ignore] Add unit tests for split comma string util --- mso/utils_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 mso/utils_test.go diff --git a/mso/utils_test.go b/mso/utils_test.go new file mode 100644 index 00000000..a480f896 --- /dev/null +++ b/mso/utils_test.go @@ -0,0 +1,33 @@ +package mso + +import ( + "reflect" + "testing" +) + +func TestSplitCommaString(t *testing.T) { + cases := []struct { + name string + input string + expected []string + }{ + {"empty", "", []string{}}, + {"whitespace_only", " \t ", []string{}}, + {"single_token", "1/1", []string{"1/1"}}, + {"single_token_with_spaces", " 1/1 ", []string{"1/1"}}, + {"multiple_tokens", "1/1,1/2", []string{"1/1", "1/2"}}, + {"multiple_tokens_with_spaces", " 1/1 , 1/2 ", []string{"1/1", "1/2"}}, + {"drops_empty_tokens_leading_trailing_commas", ",1/1,1/2,", []string{"1/1", "1/2"}}, + {"drops_empty_tokens_repeated_commas", "1/1,,1/2", []string{"1/1", "1/2"}}, + {"all_empty_tokens", ",,,", []string{}}, + } + + for _, testCase := range cases { + t.Run(testCase.name, func(t *testing.T) { + got := splitCommaString(testCase.input) + if !reflect.DeepEqual(got, testCase.expected) { + t.Fatalf("splitCommaString(%q) = %#v, expected %#v", testCase.input, got, testCase.expected) + } + }) + } +} From a5b7c90634af8cb97c26516d846641c2f0aef3e6 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 18 Mar 2026 06:31:31 +1000 Subject: [PATCH 09/18] [ignore] Add test with interface_policy_group_uuid resource --- ...ies_virtual_port_channel_interface_test.go | 1 + mso/provider.go | 292 +++++++++--------- ...ies_virtual_port_channel_interface_test.go | 33 +- 3 files changed, 159 insertions(+), 167 deletions(-) diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index e993ffde..ecc624c4 100644 --- a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -27,6 +27,7 @@ func TestAccMSOVirtualPortChannelInterfaceDataSource(t *testing.T) { resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), customTestCheckResourceTypeSetAttr( "data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", diff --git a/mso/provider.go b/mso/provider.go index 4ee60e7e..04771567 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -68,156 +68,156 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "mso_schema": resourceMSOSchema(), - "mso_schema_site": resourceMSOSchemaSite(), - "mso_site": resourceMSOSite(), - "mso_remote_location": resourceMSORemoteLocation(), - "mso_user": resourceMSOUser(), - "mso_label": resourceMSOLabel(), - "mso_schema_template": resourceMSOSchemaTemplate(), - "mso_tenant": resourceMSOTenant(), - "mso_schema_template_bd": resourceMSOTemplateBD(), - "mso_schema_template_vrf": resourceMSOSchemaTemplateVrf(), - "mso_schema_template_bd_subnet": resourceMSOTemplateBDSubnet(), - "mso_schema_template_anp": resourceMSOSchemaTemplateAnp(), - "mso_schema_template_anp_epg": resourceMSOSchemaTemplateAnpEpg(), - "mso_schema_template_anp_epg_contract": resourceMSOTemplateAnpEpgContract(), - "mso_schema_template_contract": resourceMSOTemplateContract(), - "mso_schema_template_anp_epg_subnet": resourceMSOSchemaTemplateAnpEpgSubnet(), - "mso_schema_template_l3out": resourceMSOTemplateL3out(), - "mso_schema_template_external_epg": resourceMSOTemplateExtenalepg(), - "mso_schema_template_contract_filter": resourceMSOTemplateContractFilter(), - "mso_schema_template_external_epg_contract": resourceMSOTemplateExternalEpgContract(), - "mso_schema_template_filter_entry": resourceMSOSchemaTemplateFilterEntry(), - "mso_schema_template_external_epg_subnet": resourceMSOTemplateExtenalepgSubnet(), - "mso_schema_site_anp_epg_static_leaf": resourceMSOSchemaSiteAnpEpgStaticleaf(), - "mso_schema_site_anp_epg_static_port": resourceMSOSchemaSiteAnpEpgStaticPort(), - "mso_schema_site_anp_epg_bulk_staticport": resourceMSOSchemaSiteAnpEpgBulkStaticPort(), - "mso_schema_site_bd": resourceMSOSchemaSiteBd(), - "mso_schema_site_anp_epg_subnet": resourceMSOSchemaSiteAnpEpgSubnet(), - "mso_schema_site_anp_epg_domain": resourceMSOSchemaSiteAnpEpgDomain(), - "mso_schema_site_bd_l3out": resourceMSOSchemaSiteBdL3out(), - "mso_schema_site_vrf": resourceMSOSchemaSiteVrf(), - "mso_schema_site_vrf_route_leak": resourceMSOSchemaSiteVrfRouteLeak(), - "mso_schema_site_vrf_region": resourceMSOSchemaSiteVrfRegion(), - "mso_schema_site_bd_subnet": resourceMSOSchemaSiteBdSubnet(), - "mso_rest": resourceMSORest(), - "mso_schema_template_deploy": resourceMSOSchemaTemplateDeploy(), - "mso_schema_template_deploy_ndo": resourceNDOSchemaTemplateDeploy(), - "mso_schema_site_vrf_region_cidr_subnet": resourceMSOSchemaSiteVrfRegionCidrSubnet(), - "mso_schema_site_vrf_region_cidr": resourceMSOSchemaSiteVrfRegionCidr(), - "mso_schema_site_anp": resourceMSOSchemaSiteAnp(), - "mso_schema_site_anp_epg": resourceMSOSchemaSiteAnpEpg(), - "mso_schema_template_anp_epg_selector": resourceMSOSchemaTemplateAnpEpgSelector(), - "mso_schema_site_external_epg": resourceMSOSchemaSiteExternalEpg(), - "mso_schema_template_external_epg_selector": resourceSchemaTemplateExternalEPGSelector(), - "mso_schema_template_anp_epg_useg_attr": resourceMSOSchemaTemplateAnpEpgUsegAttr(), - "mso_schema_site_anp_epg_selector": resourceMSOSchemaSiteAnpEpgSelector(), - "mso_schema_template_vrf_contract": resourceMSOTemplateVRFContract(), - "mso_schema_site_external_epg_selector": resourceMSOSchemaSiteExternalEpgSelector(), - "mso_schema_template_service_graph": resourceMSOSchemaTemplateServiceGraphs(), - "mso_schema_site_service_graph_node": resourceMSOSchemaSiteServiceGraphNode(), - "mso_schema_site_service_graph": resourceMSOSchemaSiteServiceGraph(), - "mso_service_node_type": resourceMSOServiceNodeType(), - "mso_schema_template_contract_service_graph": resourceMSOSchemaTemplateContractServiceGraph(), - "mso_system_config": resourceMSOSystemConfig(), - "mso_schema_site_contract_service_graph": resourceMSOSchemaSiteContractServiceGraph(), - "mso_schema_site_contract_service_graph_listener": resourceMSOSchemaSiteContractServiceGraphListener(), - "mso_template": resourceMSOTemplate(), - "mso_tenant_policies_ipsla_monitoring_policy": resourceMSOIPSLAMonitoringPolicy(), - "mso_tenant_policies_route_map_policy_multicast": resourceMSOMcastRouteMapPolicy(), - "mso_tenant_policies_dhcp_relay_policy": resourceMSOTenantPoliciesDHCPRelayPolicy(), - "mso_fabric_policies_vlan_pool": resourceMSOVlanPool(), - "mso_fabric_policies_physical_domain": resourceMSOPhysicalDomain(), - "mso_service_device_cluster": resourceMSOServiceDeviceCluster(), - "mso_fabric_policies_synce_interface_policy": resourceMSOSyncEInterfacePolicy(), - "mso_fabric_policies_macsec_policy": resourceMSOMacsecPolicy(), - "mso_schema_template_contract_service_chaining": resourceMSOSchemaTemplateContractServiceChaining(), - "mso_tenant_policies_bgp_peer_prefix_policy": resourceMSOBGPPeerPrefixPolicy(), - "mso_fabric_policies_l3_domain": resourceMSOL3Domain(), - "mso_tenant_policies_custom_qos_policy": resourceMSOCustomQoSPolicy(), - "mso_tenant_policies_dhcp_option_policy": resourceMSODHCPOptionPolicy(), - "mso_tenant_policies_mld_snooping_policy": resourceMSOMLDSnoopingPolicy(), - "mso_fabric_policies_mcp_global_policy": resourceMSOMCPGlobalPolicy(), - "mso_tenant_policies_ipsla_track_list": resourceMSOIPSLATrackList(), - "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), - "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), + "mso_schema": resourceMSOSchema(), + "mso_schema_site": resourceMSOSchemaSite(), + "mso_site": resourceMSOSite(), + "mso_remote_location": resourceMSORemoteLocation(), + "mso_user": resourceMSOUser(), + "mso_label": resourceMSOLabel(), + "mso_schema_template": resourceMSOSchemaTemplate(), + "mso_tenant": resourceMSOTenant(), + "mso_schema_template_bd": resourceMSOTemplateBD(), + "mso_schema_template_vrf": resourceMSOSchemaTemplateVrf(), + "mso_schema_template_bd_subnet": resourceMSOTemplateBDSubnet(), + "mso_schema_template_anp": resourceMSOSchemaTemplateAnp(), + "mso_schema_template_anp_epg": resourceMSOSchemaTemplateAnpEpg(), + "mso_schema_template_anp_epg_contract": resourceMSOTemplateAnpEpgContract(), + "mso_schema_template_contract": resourceMSOTemplateContract(), + "mso_schema_template_anp_epg_subnet": resourceMSOSchemaTemplateAnpEpgSubnet(), + "mso_schema_template_l3out": resourceMSOTemplateL3out(), + "mso_schema_template_external_epg": resourceMSOTemplateExtenalepg(), + "mso_schema_template_contract_filter": resourceMSOTemplateContractFilter(), + "mso_schema_template_external_epg_contract": resourceMSOTemplateExternalEpgContract(), + "mso_schema_template_filter_entry": resourceMSOSchemaTemplateFilterEntry(), + "mso_schema_template_external_epg_subnet": resourceMSOTemplateExtenalepgSubnet(), + "mso_schema_site_anp_epg_static_leaf": resourceMSOSchemaSiteAnpEpgStaticleaf(), + "mso_schema_site_anp_epg_static_port": resourceMSOSchemaSiteAnpEpgStaticPort(), + "mso_schema_site_anp_epg_bulk_staticport": resourceMSOSchemaSiteAnpEpgBulkStaticPort(), + "mso_schema_site_bd": resourceMSOSchemaSiteBd(), + "mso_schema_site_anp_epg_subnet": resourceMSOSchemaSiteAnpEpgSubnet(), + "mso_schema_site_anp_epg_domain": resourceMSOSchemaSiteAnpEpgDomain(), + "mso_schema_site_bd_l3out": resourceMSOSchemaSiteBdL3out(), + "mso_schema_site_vrf": resourceMSOSchemaSiteVrf(), + "mso_schema_site_vrf_route_leak": resourceMSOSchemaSiteVrfRouteLeak(), + "mso_schema_site_vrf_region": resourceMSOSchemaSiteVrfRegion(), + "mso_schema_site_bd_subnet": resourceMSOSchemaSiteBdSubnet(), + "mso_rest": resourceMSORest(), + "mso_schema_template_deploy": resourceMSOSchemaTemplateDeploy(), + "mso_schema_template_deploy_ndo": resourceNDOSchemaTemplateDeploy(), + "mso_schema_site_vrf_region_cidr_subnet": resourceMSOSchemaSiteVrfRegionCidrSubnet(), + "mso_schema_site_vrf_region_cidr": resourceMSOSchemaSiteVrfRegionCidr(), + "mso_schema_site_anp": resourceMSOSchemaSiteAnp(), + "mso_schema_site_anp_epg": resourceMSOSchemaSiteAnpEpg(), + "mso_schema_template_anp_epg_selector": resourceMSOSchemaTemplateAnpEpgSelector(), + "mso_schema_site_external_epg": resourceMSOSchemaSiteExternalEpg(), + "mso_schema_template_external_epg_selector": resourceSchemaTemplateExternalEPGSelector(), + "mso_schema_template_anp_epg_useg_attr": resourceMSOSchemaTemplateAnpEpgUsegAttr(), + "mso_schema_site_anp_epg_selector": resourceMSOSchemaSiteAnpEpgSelector(), + "mso_schema_template_vrf_contract": resourceMSOTemplateVRFContract(), + "mso_schema_site_external_epg_selector": resourceMSOSchemaSiteExternalEpgSelector(), + "mso_schema_template_service_graph": resourceMSOSchemaTemplateServiceGraphs(), + "mso_schema_site_service_graph_node": resourceMSOSchemaSiteServiceGraphNode(), + "mso_schema_site_service_graph": resourceMSOSchemaSiteServiceGraph(), + "mso_service_node_type": resourceMSOServiceNodeType(), + "mso_schema_template_contract_service_graph": resourceMSOSchemaTemplateContractServiceGraph(), + "mso_system_config": resourceMSOSystemConfig(), + "mso_schema_site_contract_service_graph": resourceMSOSchemaSiteContractServiceGraph(), + "mso_schema_site_contract_service_graph_listener": resourceMSOSchemaSiteContractServiceGraphListener(), + "mso_template": resourceMSOTemplate(), + "mso_tenant_policies_ipsla_monitoring_policy": resourceMSOIPSLAMonitoringPolicy(), + "mso_tenant_policies_route_map_policy_multicast": resourceMSOMcastRouteMapPolicy(), + "mso_tenant_policies_dhcp_relay_policy": resourceMSOTenantPoliciesDHCPRelayPolicy(), + "mso_fabric_policies_vlan_pool": resourceMSOVlanPool(), + "mso_fabric_policies_physical_domain": resourceMSOPhysicalDomain(), + "mso_service_device_cluster": resourceMSOServiceDeviceCluster(), + "mso_fabric_policies_synce_interface_policy": resourceMSOSyncEInterfacePolicy(), + "mso_fabric_policies_macsec_policy": resourceMSOMacsecPolicy(), + "mso_schema_template_contract_service_chaining": resourceMSOSchemaTemplateContractServiceChaining(), + "mso_tenant_policies_bgp_peer_prefix_policy": resourceMSOBGPPeerPrefixPolicy(), + "mso_fabric_policies_l3_domain": resourceMSOL3Domain(), + "mso_tenant_policies_custom_qos_policy": resourceMSOCustomQoSPolicy(), + "mso_tenant_policies_dhcp_option_policy": resourceMSODHCPOptionPolicy(), + "mso_tenant_policies_mld_snooping_policy": resourceMSOMLDSnoopingPolicy(), + "mso_fabric_policies_mcp_global_policy": resourceMSOMCPGlobalPolicy(), + "mso_tenant_policies_ipsla_track_list": resourceMSOIPSLATrackList(), + "mso_tenant_policies_l3out_interface_routing_policy": resourceMSOL3OutInterfaceRoutingPolicy(), + "mso_fabric_policies_interface_setting": resourceMSOInterfaceSetting(), "mso_fabric_resource_policies_virtual_port_channel_interface": resourceMSOVirtualPortChannelInterface(), }, DataSourcesMap: map[string]*schema.Resource{ - "mso_schema": datasourceMSOSchema(), - "mso_schema_site": datasourceMSOSchemaSite(), - "mso_site": datasourceMSOSite(), - "mso_remote_location": datasourceMSORemoteLocation(), - "mso_role": datasourceMSORole(), - "mso_user": datasourceMSOUser(), - "mso_label": datasourceMSOLabel(), - "mso_schema_template": datasourceMSOSchemaTemplate(), - "mso_tenant": datasourceMSOTenant(), - "mso_schema_template_bd": dataSourceMSOTemplateBD(), - "mso_schema_template_vrf": datasourceMSOSchemaTemplateVrf(), - "mso_schema_template_bd_subnet": dataSourceMSOTemplateSubnetBD(), - "mso_schema_template_anp": datasourceMSOSchemaTemplateAnp(), - "mso_schema_template_anp_epg": datasourceMSOSchemaTemplateAnpEpg(), - "mso_schema_template_anp_epg_contract": dataSourceMSOTemplateAnpEpgContract(), - "mso_schema_template_contract": dataSourceMSOTemplateContract(), - "mso_schema_template_anp_epg_subnet": dataSourceMSOSchemaTemplateAnpEpgSubnet(), - "mso_schema_template_l3out": dataSourceMSOTemplateL3out(), - "mso_schema_template_external_epg": dataSourceMSOTemplateExternalepg(), - "mso_schema_template_contract_filter": dataSourceMSOTemplateContractFilter(), - "mso_schema_template_external_epg_contract": dataSourceMSOTemplateExternalEpgContract(), - "mso_schema_template_filter_entry": dataSourceMSOSchemaTemplateFilterEntry(), - "mso_schema_template_external_epg_subnet": dataSourceMSOTemplateExternalEpgSubnet(), - "mso_schema_site_anp": dataSourceMSOSchemaSiteAnp(), - "mso_schema_site_anp_epg": dataSourceMSOSchemaSiteAnpEpg(), - "mso_schema_site_anp_epg_static_leaf": dataSourceMSOSchemaSiteAnpEpgStaticleaf(), - "mso_schema_site_anp_epg_static_port": datasourceMSOSchemaSiteAnpEpgStaticPort(), - "mso_schema_site_anp_epg_bulk_staticport": datasourceMSOSchemaSiteAnpEpgBulkStaticPort(), - "mso_schema_site_bd": dataSourceMSOSchemaSiteBd(), - "mso_schema_site_anp_epg_subnet": datasourceMSOSchemaSiteAnpEpgSubnet(), - "mso_schema_site_anp_epg_domain": dataSourceMSOSchemaSiteAnpEpgDomain(), - "mso_schema_site_bd_l3out": dataSourceMSOSchemaSiteBdL3out(), - "mso_schema_site_vrf": dataSourceMSOSchemaSiteVrf(), - "mso_schema_site_vrf_region": dataSourceMSOSchemaSiteVrfRegion(), - "mso_schema_site_vrf_route_leak": dataSourceMSOSchemaSiteVrfRouteLeak(), - "mso_schema_site_bd_subnet": dataSourceMSOSchemaSiteBdSubnet(), - "mso_schema_site_vrf_region_cidr_subnet": dataSourceMSOSchemaSiteVrfRegionCidrSubnet(), - "mso_schema_site_vrf_region_cidr": dataSourceMSOSchemaSiteVrfRegionCidr(), - "mso_schema_template_anp_epg_selector": datasourceMSOSchemaTemplateAnpEpgSelector(), - "mso_schema_site_external_epg": dataSourceMSOSchemaSiteExternalEpg(), - "mso_schema_template_external_epg_selector": datasourceSchemaTemplateExternalEPGSelector(), - "mso_schema_template_anp_epg_useg_attr": dataSourceMSOSchemaTemplateAnpEpgUsegAttr(), - "mso_schema_site_anp_epg_selector": datasourceMSOSchemaSiteAnpEpgSelector(), - "mso_schema_template_vrf_contract": dataSourceMSOTemplateVRFContract(), - "mso_schema_site_external_epg_selector": datasourceMSOSchemaSiteExternalEpgSelector(), - "mso_schema_template_service_graph": dataSourceMSOSchemaTemplateServiceGraph(), - "mso_service_node_type": dataSourceMSOServiceNodeType(), - "mso_schema_site_service_graph": datasourceMSOSchemaSiteServiceGraph(), - "mso_schema_template_contract_service_graph": dataSourceMSOSchemaTemplateContractServiceGraph(), - "mso_system_config": dataSourceMSOSystemConfig(), - "mso_rest": datasourceMSORest(), - "mso_schema_site_contract_service_graph": dataSourceMSOSchemaSiteContractServiceGraph(), - "mso_schema_site_contract_service_graph_listener": dataSourceMSOSchemaSiteContractServiceGraphListener(), - "mso_template": datasourceMSOTemplate(), - "mso_tenant_policies_ipsla_monitoring_policy": datasourceMSOIPSLAMonitoringPolicy(), - "mso_tenant_policies_route_map_policy_multicast": datasourceMSOMcastRouteMapPolicy(), - "mso_tenant_policies_dhcp_relay_policy": datasourceMSOTenantPoliciesDHCPRelayPolicy(), - "mso_fabric_policies_vlan_pool": datasourceMSOVlanPool(), - "mso_fabric_policies_physical_domain": datasourceMSOPhysicalDomain(), - "mso_service_device_cluster": datasourceMSOServiceDeviceCluster(), - "mso_fabric_policies_synce_interface_policy": datasourceMSOSyncEInterfacePolicy(), - "mso_fabric_policies_macsec_policy": datasourceMacsecPolicy(), - "mso_schema_template_contract_service_chaining": datasourceMSOSchemaTemplateContractServiceChaining(), - "mso_tenant_policies_bgp_peer_prefix_policy": datasourceMSOBGPPeerPrefixPolicy(), - "mso_fabric_policies_l3_domain": datasourceMSOL3Domain(), - "mso_tenant_policies_custom_qos_policy": datasourceMSOCustomQoSPolicy(), - "mso_tenant_policies_dhcp_option_policy": datasourceMSODHCPOptionPolicy(), - "mso_tenant_policies_mld_snooping_policy": datasourceMSOMLDSnoopingPolicy(), - "mso_fabric_policies_mcp_global_policy": datasourceMSOMCPGlobalPolicy(), - "mso_tenant_policies_ipsla_track_list": datasourceMSOIPSLATrackList(), - "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), - "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), + "mso_schema": datasourceMSOSchema(), + "mso_schema_site": datasourceMSOSchemaSite(), + "mso_site": datasourceMSOSite(), + "mso_remote_location": datasourceMSORemoteLocation(), + "mso_role": datasourceMSORole(), + "mso_user": datasourceMSOUser(), + "mso_label": datasourceMSOLabel(), + "mso_schema_template": datasourceMSOSchemaTemplate(), + "mso_tenant": datasourceMSOTenant(), + "mso_schema_template_bd": dataSourceMSOTemplateBD(), + "mso_schema_template_vrf": datasourceMSOSchemaTemplateVrf(), + "mso_schema_template_bd_subnet": dataSourceMSOTemplateSubnetBD(), + "mso_schema_template_anp": datasourceMSOSchemaTemplateAnp(), + "mso_schema_template_anp_epg": datasourceMSOSchemaTemplateAnpEpg(), + "mso_schema_template_anp_epg_contract": dataSourceMSOTemplateAnpEpgContract(), + "mso_schema_template_contract": dataSourceMSOTemplateContract(), + "mso_schema_template_anp_epg_subnet": dataSourceMSOSchemaTemplateAnpEpgSubnet(), + "mso_schema_template_l3out": dataSourceMSOTemplateL3out(), + "mso_schema_template_external_epg": dataSourceMSOTemplateExternalepg(), + "mso_schema_template_contract_filter": dataSourceMSOTemplateContractFilter(), + "mso_schema_template_external_epg_contract": dataSourceMSOTemplateExternalEpgContract(), + "mso_schema_template_filter_entry": dataSourceMSOSchemaTemplateFilterEntry(), + "mso_schema_template_external_epg_subnet": dataSourceMSOTemplateExternalEpgSubnet(), + "mso_schema_site_anp": dataSourceMSOSchemaSiteAnp(), + "mso_schema_site_anp_epg": dataSourceMSOSchemaSiteAnpEpg(), + "mso_schema_site_anp_epg_static_leaf": dataSourceMSOSchemaSiteAnpEpgStaticleaf(), + "mso_schema_site_anp_epg_static_port": datasourceMSOSchemaSiteAnpEpgStaticPort(), + "mso_schema_site_anp_epg_bulk_staticport": datasourceMSOSchemaSiteAnpEpgBulkStaticPort(), + "mso_schema_site_bd": dataSourceMSOSchemaSiteBd(), + "mso_schema_site_anp_epg_subnet": datasourceMSOSchemaSiteAnpEpgSubnet(), + "mso_schema_site_anp_epg_domain": dataSourceMSOSchemaSiteAnpEpgDomain(), + "mso_schema_site_bd_l3out": dataSourceMSOSchemaSiteBdL3out(), + "mso_schema_site_vrf": dataSourceMSOSchemaSiteVrf(), + "mso_schema_site_vrf_region": dataSourceMSOSchemaSiteVrfRegion(), + "mso_schema_site_vrf_route_leak": dataSourceMSOSchemaSiteVrfRouteLeak(), + "mso_schema_site_bd_subnet": dataSourceMSOSchemaSiteBdSubnet(), + "mso_schema_site_vrf_region_cidr_subnet": dataSourceMSOSchemaSiteVrfRegionCidrSubnet(), + "mso_schema_site_vrf_region_cidr": dataSourceMSOSchemaSiteVrfRegionCidr(), + "mso_schema_template_anp_epg_selector": datasourceMSOSchemaTemplateAnpEpgSelector(), + "mso_schema_site_external_epg": dataSourceMSOSchemaSiteExternalEpg(), + "mso_schema_template_external_epg_selector": datasourceSchemaTemplateExternalEPGSelector(), + "mso_schema_template_anp_epg_useg_attr": dataSourceMSOSchemaTemplateAnpEpgUsegAttr(), + "mso_schema_site_anp_epg_selector": datasourceMSOSchemaSiteAnpEpgSelector(), + "mso_schema_template_vrf_contract": dataSourceMSOTemplateVRFContract(), + "mso_schema_site_external_epg_selector": datasourceMSOSchemaSiteExternalEpgSelector(), + "mso_schema_template_service_graph": dataSourceMSOSchemaTemplateServiceGraph(), + "mso_service_node_type": dataSourceMSOServiceNodeType(), + "mso_schema_site_service_graph": datasourceMSOSchemaSiteServiceGraph(), + "mso_schema_template_contract_service_graph": dataSourceMSOSchemaTemplateContractServiceGraph(), + "mso_system_config": dataSourceMSOSystemConfig(), + "mso_rest": datasourceMSORest(), + "mso_schema_site_contract_service_graph": dataSourceMSOSchemaSiteContractServiceGraph(), + "mso_schema_site_contract_service_graph_listener": dataSourceMSOSchemaSiteContractServiceGraphListener(), + "mso_template": datasourceMSOTemplate(), + "mso_tenant_policies_ipsla_monitoring_policy": datasourceMSOIPSLAMonitoringPolicy(), + "mso_tenant_policies_route_map_policy_multicast": datasourceMSOMcastRouteMapPolicy(), + "mso_tenant_policies_dhcp_relay_policy": datasourceMSOTenantPoliciesDHCPRelayPolicy(), + "mso_fabric_policies_vlan_pool": datasourceMSOVlanPool(), + "mso_fabric_policies_physical_domain": datasourceMSOPhysicalDomain(), + "mso_service_device_cluster": datasourceMSOServiceDeviceCluster(), + "mso_fabric_policies_synce_interface_policy": datasourceMSOSyncEInterfacePolicy(), + "mso_fabric_policies_macsec_policy": datasourceMacsecPolicy(), + "mso_schema_template_contract_service_chaining": datasourceMSOSchemaTemplateContractServiceChaining(), + "mso_tenant_policies_bgp_peer_prefix_policy": datasourceMSOBGPPeerPrefixPolicy(), + "mso_fabric_policies_l3_domain": datasourceMSOL3Domain(), + "mso_tenant_policies_custom_qos_policy": datasourceMSOCustomQoSPolicy(), + "mso_tenant_policies_dhcp_option_policy": datasourceMSODHCPOptionPolicy(), + "mso_tenant_policies_mld_snooping_policy": datasourceMSOMLDSnoopingPolicy(), + "mso_fabric_policies_mcp_global_policy": datasourceMSOMCPGlobalPolicy(), + "mso_tenant_policies_ipsla_track_list": datasourceMSOIPSLATrackList(), + "mso_tenant_policies_l3out_interface_routing_policy": datasourceMSOL3OutInterfaceRoutingPolicy(), + "mso_fabric_policies_interface_setting": datasourceMSOInterfaceSetting(), "mso_fabric_resource_policies_virtual_port_channel_interface": dataSourceMSOVirtualPortChannelInterface(), }, diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 3420db92..8746cf0e 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -29,6 +29,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), customTestCheckResourceTypeSetAttr( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", @@ -60,6 +61,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.2", "1/5-7"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "2"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), customTestCheckResourceTypeSetAttr( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", @@ -91,6 +93,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "3"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "0"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), ), }, { @@ -109,46 +112,38 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { } func testAccMSOVirtualPortChannelInterfaceConfigCreate() string { - return fmt.Sprintf(`%s + return fmt.Sprintf(`%s%s resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { template_id = mso_template.template_fabric_resource.id + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid name = "tf_test_vpc_if" description = "Terraform test VPC Interface" node_1 = "101" node_2 = "102" - node_1_interfaces = ["1/1", "1/10-11"] node_2_interfaces = ["1/2"] - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource merges (PR #449). - interface_descriptions { node = "101" interface = "1/1" description = "Terraform test interface description" } } - `, testAccMSOTemplateResourceFabricResourceConfig()) + `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) } func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { - return fmt.Sprintf(`%s + return fmt.Sprintf(`%s%s resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { template_id = mso_template.template_fabric_resource.id + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid name = "tf_test_vpc_if" description = "Terraform test VPC Interface (updated)" node_1 = "103" node_2 = "104" - node_1_interfaces = ["1/1", "1/2-5", "1/7"] node_2_interfaces = ["1/2", "1/3", "1/5-7"] - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource merges (PR #449). - interface_descriptions { node = "103" interface = "1/1" @@ -161,24 +156,20 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { // No description on purpose } } - `, testAccMSOTemplateResourceFabricResourceConfig()) + `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) } func testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions() string { - return fmt.Sprintf(`%s + return fmt.Sprintf(`%s%s resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { template_id = mso_template.template_fabric_resource.id + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid name = "tf_test_vpc_if" description = "Terraform test VPC Interface (removed descriptions)" node_1 = "103" node_2 = "104" - node_1_interfaces = ["1/1", "1/2-5", "1/7"] node_2_interfaces = ["1/2", "1/3", "1/5-7"] - - interface_policy_group_uuid = "c0e13c44-a1ee-4fe7-8630-c02e9b2520aa" - // interface_policy_group_uuid will be validated/required once the - // interface policy group resource lands (PR #449). } - `, testAccMSOTemplateResourceFabricResourceConfig()) + `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) } From 6755e0a3f8f6a95ad5a933de7ef4c3b0b0c2194e Mon Sep 17 00:00:00 2001 From: samitab Date: Tue, 24 Mar 2026 21:31:58 +1000 Subject: [PATCH 10/18] [ignore] Code review comment fixes --- ...policies_virtual_port_channel_interface.go | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 36758bef..42fcd912 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -120,7 +120,7 @@ func setVPCInterfaceData(d *schema.ResourceData, msoClient *client.Client, templ d.SetId(fmt.Sprintf("templateId/%s/virtualPortChannelInterface/%s", templateId, name)) d.Set("template_id", templateId) - d.Set("name", models.StripQuotes(vpcCont.S("name").String())) + d.Set("name", name) d.Set("description", models.StripQuotes(vpcCont.S("description").String())) d.Set("uuid", models.StripQuotes(vpcCont.S("uuid").String())) @@ -134,17 +134,17 @@ func setVPCInterfaceData(d *schema.ResourceData, msoClient *client.Client, templ if vpcCont.Exists("interfaceDescriptions") { count, _ := vpcCont.ArrayCount("interfaceDescriptions") - out := make([]any, 0, count) + out := make([]any, count) for i := 0; i < count; i++ { descCont, err := vpcCont.ArrayElement(i, "interfaceDescriptions") if err != nil { return err } - entry := make(map[string]any) - entry["node"] = models.StripQuotes(descCont.S("nodeID").String()) - entry["interface"] = models.StripQuotes(descCont.S("interfaceID").String()) - entry["description"] = models.StripQuotes(descCont.S("description").String()) - out = append(out, entry) + out[i] = map[string]any{ + "node": models.StripQuotes(descCont.S("nodeID").String()), + "interface": models.StripQuotes(descCont.S("interfaceID").String()), + "description": models.StripQuotes(descCont.S("description").String()), + } } d.Set("interface_descriptions", out) } @@ -249,53 +249,62 @@ func resourceMSOVirtualPortChannelInterfaceUpdate(d *schema.ResourceData, m any) payloadCont.Array() if d.HasChange("name") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", updatePath), d.Get("name").(string)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", updatePath), d.Get("name").(string)) + if err != nil { return err } } if d.HasChange("description") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)) + if err != nil { return err } } if d.HasChange("node_1") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/node", updatePath), d.Get("node_1").(string)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/node", updatePath), d.Get("node_1").(string)) + if err != nil { return err } } if d.HasChange("node_1_interfaces") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_1_interfaces"), ",")); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_1_interfaces"), ",")) + if err != nil { return err } } if d.HasChange("node_2") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/node", updatePath), d.Get("node_2").(string)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/node", updatePath), d.Get("node_2").(string)) + if err != nil { return err } } if d.HasChange("node_2_interfaces") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_2_interfaces"), ",")); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_2_interfaces"), ",")) + if err != nil { return err } } if d.HasChange("interface_policy_group_uuid") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/policy", updatePath), d.Get("interface_policy_group_uuid").(string)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/policy", updatePath), d.Get("interface_policy_group_uuid").(string)) + if err != nil { return err } } if d.HasChange("interface_descriptions") { - if err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/interfaceDescriptions", updatePath), buildInterfaceDescriptionsPayload(d)); err != nil { + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/interfaceDescriptions", updatePath), buildInterfaceDescriptionsPayload(d)) + if err != nil { return err } } - if err := doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont); err != nil { + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { return err } @@ -332,14 +341,14 @@ func resourceMSOVirtualPortChannelInterfaceDelete(d *schema.ResourceData, m any) func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any { raw := d.Get("interface_descriptions").([]any) - out := make([]map[string]any, 0, len(raw)) - for _, v := range raw { + out := make([]map[string]any, len(raw)) + for i, v := range raw { m := v.(map[string]any) - out = append(out, map[string]any{ + out[i] = map[string]any{ "nodeID": m["node"].(string), "interfaceID": m["interface"].(string), "description": m["description"].(string), - }) + } } return out } From 5971e2c464d08a05b6779c8a1abd2b4cde5b4776 Mon Sep 17 00:00:00 2001 From: samitab Date: Wed, 25 Mar 2026 16:48:02 +1000 Subject: [PATCH 11/18] [ignore] Don't send interface description when unset on update. --- ...ic_resource_policies_virtual_port_channel_interface.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 42fcd912..5616f14f 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -97,6 +97,7 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { "description": { Type: schema.TypeString, Optional: true, + Computed: true, }, }, }, @@ -344,11 +345,14 @@ func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any out := make([]map[string]any, len(raw)) for i, v := range raw { m := v.(map[string]any) - out[i] = map[string]any{ + entry := map[string]any{ "nodeID": m["node"].(string), "interfaceID": m["interface"].(string), - "description": m["description"].(string), } + if desc := m["description"].(string); desc != "" { + entry["description"] = desc + } + out[i] = entry } return out } From 5bcde971a6beb4efca49ee3c4087bb5c28efa880 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 26 Mar 2026 09:18:43 +1000 Subject: [PATCH 12/18] [ignore] Add test for unset interface description --- ...ies_virtual_port_channel_interface_test.go | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 8746cf0e..802b62c0 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -41,6 +41,25 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { ), ), }, + { + PreConfig: func() { + fmt.Println("Test: Virtual Port Channel Interface Resource - Update Unset Interface Description") + }, + Config: testAccMSOVirtualPortChannelInterfaceConfigUpdateUnsetDescription(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), + customTestCheckResourceTypeSetAttr( + "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", + "interface_descriptions", + map[string]string{ + "node": "101", + "interface": "1/1", + "description": "Terraform test interface description", // Remains unchanged + }, + ), + ), + }, { PreConfig: func() { fmt.Println("Test: Virtual Port Channel Interface Resource - Update") @@ -132,6 +151,27 @@ func testAccMSOVirtualPortChannelInterfaceConfigCreate() string { `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) } +func testAccMSOVirtualPortChannelInterfaceConfigUpdateUnsetDescription() string { + return fmt.Sprintf(`%s%s + resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { + template_id = mso_template.template_fabric_resource.id + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid + name = "tf_test_vpc_if" + description = "Terraform test VPC Interface" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] + + interface_descriptions { + node = "101" + interface = "1/1" + // No description, should keep existing state + } + } + `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) +} + func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { return fmt.Sprintf(`%s%s resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { @@ -153,7 +193,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { interface_descriptions { node = "104" interface = "1/3" - // No description on purpose + // No description on new description should be "" } } `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) From 2afea8d087f4b1df30c82b7c59729e413d6f4c54 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 26 Mar 2026 14:40:27 +1000 Subject: [PATCH 13/18] [ignore] Allow name to be updated without ForceNew. --- ...ic_resource_policies_virtual_port_channel_interface.go | 1 - ...source_policies_virtual_port_channel_interface_test.go | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 5616f14f..2eb52a48 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -34,7 +34,6 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { }, "name": { Type: schema.TypeString, - ForceNew: true, Required: true, ValidateFunc: validation.StringLenBetween(1, 128), Description: "Virtual Port Channel Interface name.", diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 802b62c0..4897c6e3 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -66,7 +66,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { }, Config: testAccMSOVirtualPortChannelInterfaceConfigUpdate(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if_new"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", "Terraform test VPC Interface (updated)"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "103"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "104"), @@ -166,7 +166,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdateUnsetDescription() string interface_descriptions { node = "101" interface = "1/1" - // No description, should keep existing state + description = "" } } `, testAccMSOTemplateResourceFabricResourceConfig(), testAccMSOFabricPoliciesInterfaceSettingPortChannelConfigCreate(), msoFabricPolicyTemplateInterfaceSettingName) @@ -177,7 +177,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdate() string { resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { template_id = mso_template.template_fabric_resource.id interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid - name = "tf_test_vpc_if" + name = "tf_test_vpc_if_new" description = "Terraform test VPC Interface (updated)" node_1 = "103" node_2 = "104" @@ -204,7 +204,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigRemoveDescriptions() string { resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { template_id = mso_template.template_fabric_resource.id interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid - name = "tf_test_vpc_if" + name = "tf_test_vpc_if_new" description = "Terraform test VPC Interface (removed descriptions)" node_1 = "103" node_2 = "104" From 52960cf66e64ffca81053dbae9d49c7e5c335c5b Mon Sep 17 00:00:00 2001 From: samitab Date: Fri, 27 Mar 2026 09:27:33 +1000 Subject: [PATCH 14/18] [ignore] Allow descriptions to be cleared. --- ...c_resource_policies_virtual_port_channel_interface.go | 9 ++------- ...ource_policies_virtual_port_channel_interface_test.go | 6 +++--- ...policies_virtual_port_channel_interface.html.markdown | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 2eb52a48..440404a8 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -46,7 +46,6 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { "description": { Type: schema.TypeString, Optional: true, - Computed: true, Description: "Virtual Port Channel Interface description.", }, "node_1": { @@ -96,7 +95,6 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { "description": { Type: schema.TypeString, Optional: true, - Computed: true, }, }, }, @@ -344,14 +342,11 @@ func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any out := make([]map[string]any, len(raw)) for i, v := range raw { m := v.(map[string]any) - entry := map[string]any{ + out[i] = map[string]any{ "nodeID": m["node"].(string), "interfaceID": m["interface"].(string), + "description": m["description"].(string), } - if desc := m["description"].(string); desc != "" { - entry["description"] = desc - } - out[i] = entry } return out } diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 4897c6e3..e40b302c 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -48,14 +48,14 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { Config: testAccMSOVirtualPortChannelInterfaceConfigUpdateUnsetDescription(), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), - resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", ""), customTestCheckResourceTypeSetAttr( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", map[string]string{ "node": "101", "interface": "1/1", - "description": "Terraform test interface description", // Remains unchanged + "description": "", }, ), ), @@ -157,7 +157,7 @@ func testAccMSOVirtualPortChannelInterfaceConfigUpdateUnsetDescription() string template_id = mso_template.template_fabric_resource.id interface_policy_group_uuid = mso_fabric_policies_interface_setting.%s_portchannel.uuid name = "tf_test_vpc_if" - description = "Terraform test VPC Interface" + description = "" node_1 = "101" node_2 = "102" node_1_interfaces = ["1/1", "1/10-11"] diff --git a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown index 442436a2..7f41cded 100644 --- a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown +++ b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown @@ -45,7 +45,7 @@ resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" * `template_id` - (Required) The unique ID of the Fabric Resource template. * `name` - (Required) The name of the Virtual Port Channel Interface. -* `description` - (Optional) The description of the Virtual Port Channel Interface. +* `description` - (Optional) The description of the Virtual Port Channel Interface. If this argument is omitted, the value defaults to an empty string (`""`). * `node_1` - (Required) The first node ID. * `node_2` - (Required) The second node ID. * `node_1_interfaces` - (Required) List of interface IDs (or ranges) for node 1. @@ -54,7 +54,7 @@ resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" * `interface_descriptions` - (Optional) List of interface description entries. The provided list replaces the existing list in NDO. * `node` - (Required) Node ID. * `interface` - (Required) Interface ID. - * `description` - (Optional) Interface description. + * `description` - (Optional) Interface description. If this argument is omitted, the value defaults to an empty string (`""`). ## Attribute Reference ## From 553759f2342ae9feb9f02d286cd897bed8926b7a Mon Sep 17 00:00:00 2001 From: samitab Date: Fri, 27 Mar 2026 16:07:50 +1000 Subject: [PATCH 15/18] [ignore] Change interface description from List to Set in resource. --- ...c_resource_policies_virtual_port_channel_interface.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index 440404a8..abfd7794 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -77,7 +77,7 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { Description: "UUID of the Port Channel Interface Policy Group.", }, "interface_descriptions": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Description: "List of interface descriptions; provided list replaces the existing list in NDO.", Elem: &schema.Resource{ @@ -338,9 +338,10 @@ func resourceMSOVirtualPortChannelInterfaceDelete(d *schema.ResourceData, m any) } func buildInterfaceDescriptionsPayload(d *schema.ResourceData) []map[string]any { - raw := d.Get("interface_descriptions").([]any) - out := make([]map[string]any, len(raw)) - for i, v := range raw { + interfaceDescriptions := d.Get("interface_descriptions").(*schema.Set) + interfaceDescriptionsList := interfaceDescriptions.List() + out := make([]map[string]any, len(interfaceDescriptionsList)) + for i, v := range interfaceDescriptionsList { m := v.(map[string]any) out[i] = map[string]any{ "nodeID": m["node"].(string), From 01ba737d33fc7ccb06063c7d537b3eb6efe131c9 Mon Sep 17 00:00:00 2001 From: samitab Date: Sun, 29 Mar 2026 20:40:55 +1000 Subject: [PATCH 16/18] [ignore] Change node 1 & 2 interfaces to TypeSet --- mso/provider_test.go | 16 ++++++++++++++++ ..._policies_virtual_port_channel_interface.go | 12 ++++++------ ...cies_virtual_port_channel_interface_test.go | 18 +++++++++--------- mso/utils.go | 7 +++++++ 4 files changed, 38 insertions(+), 15 deletions(-) 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_fabric_resource_policies_virtual_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go index abfd7794..35fbda7b 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface.go @@ -59,13 +59,13 @@ func resourceMSOVirtualPortChannelInterface() *schema.Resource { ValidateFunc: validation.StringIsNotEmpty, }, "node_1_interfaces": { - Type: schema.TypeList, + Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interface IDs (or ranges) for node 1.", }, "node_2_interfaces": { - Type: schema.TypeList, + Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interface IDs (or ranges) for node 2.", @@ -178,8 +178,8 @@ func resourceMSOVirtualPortChannelInterfaceCreate(d *schema.ResourceData, m any) templateId := d.Get("template_id").(string) - interfaces1 := getListOfStringsFromSchemaList(d, "node_1_interfaces") - interfaces2 := getListOfStringsFromSchemaList(d, "node_2_interfaces") + interfaces1 := getListOfStringsFromSchemaSet(d, "node_1_interfaces") + interfaces2 := getListOfStringsFromSchemaSet(d, "node_2_interfaces") payload := map[string]any{ "name": d.Get("name").(string), @@ -267,7 +267,7 @@ func resourceMSOVirtualPortChannelInterfaceUpdate(d *schema.ResourceData, m any) } } if d.HasChange("node_1_interfaces") { - err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_1_interfaces"), ",")) + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node1Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaSet(d, "node_1_interfaces"), ",")) if err != nil { return err } @@ -281,7 +281,7 @@ func resourceMSOVirtualPortChannelInterfaceUpdate(d *schema.ResourceData, m any) } if d.HasChange("node_2_interfaces") { - err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaList(d, "node_2_interfaces"), ",")) + err = addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node2Details/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaSet(d, "node_2_interfaces"), ",")) if err != nil { return err } diff --git a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index e40b302c..2b6eac56 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -23,10 +23,10 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "101"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "102"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "2"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/10-11"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces", "1/1"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces", "1/10-11"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "1"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces", "1/2"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), @@ -71,13 +71,13 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "103"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "104"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "3"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/2-5"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.2", "1/7"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces", "1/1"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces", "1/2-5"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces", "1/7"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "3"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.1", "1/3"), - resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.2", "1/5-7"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces", "1/2"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces", "1/3"), + testCheckTypeSetStringElemAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces", "1/5-7"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "2"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), diff --git a/mso/utils.go b/mso/utils.go index f5a75410..9d1204c3 100644 --- a/mso/utils.go +++ b/mso/utils.go @@ -301,6 +301,13 @@ func getListOfStringsFromSchemaList(d *schema.ResourceData, key string) []string return nil } +func getListOfStringsFromSchemaSet(d *schema.ResourceData, key string) []string { + if values, ok := d.GetOk(key); ok { + return convertToListOfStrings(values.(*schema.Set).List()) + } + return nil +} + func convertToListOfStrings(values []interface{}) []string { result := []string{} for _, item := range values { From 06694cc447efc93b03dac6e0b424310efd9c6aa6 Mon Sep 17 00:00:00 2001 From: samitab Date: Thu, 2 Apr 2026 11:59:09 +1000 Subject: [PATCH 17/18] [ignore] Update test check for sets. --- mso/provider_test.go | 2 ++ ...source_policies_virtual_port_channel_interface_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mso/provider_test.go b/mso/provider_test.go index 41040382..a6e9b88b 100644 --- a/mso/provider_test.go +++ b/mso/provider_test.go @@ -174,6 +174,8 @@ func testCheckTypeSetStringElemAttr(resourceName, setKey, value string) resource } } +// Deprecated: This check has a bug because it matches key value pairs across different set elements instead of verifying all attributes +// belong to the same set element, leading to false positives. Use CustomTestCheckTypeSetElemAttrs instead. 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_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index 2b6eac56..81a8e05f 100644 --- a/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/resource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -30,7 +30,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), - customTestCheckResourceTypeSetAttr( + CustomTestCheckTypeSetElemAttrs( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", map[string]string{ @@ -49,7 +49,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "name", "tf_test_vpc_if"), resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "description", ""), - customTestCheckResourceTypeSetAttr( + CustomTestCheckTypeSetElemAttrs( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", map[string]string{ @@ -81,7 +81,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { resource.TestCheckResourceAttr("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "2"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "uuid"), resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_policy_group_uuid"), - customTestCheckResourceTypeSetAttr( + CustomTestCheckTypeSetElemAttrs( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", map[string]string{ @@ -90,7 +90,7 @@ func TestAccMSOVirtualPortChannelInterfaceResource(t *testing.T) { "description": "Terraform test interface description 103", }, ), - customTestCheckResourceTypeSetAttr( + CustomTestCheckTypeSetElemAttrs( "mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions", map[string]string{ From fc8af4774a327cec47156723c0fcdb54d1a2010a Mon Sep 17 00:00:00 2001 From: samitab Date: Tue, 7 Apr 2026 22:45:08 +1000 Subject: [PATCH 18/18] [ignore] Fix test and doc alignment --- ...policies_virtual_port_channel_interface_test.go | 4 ++-- ...es_virtual_port_channel_interface.html.markdown | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go index ecc624c4..938f0616 100644 --- a/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go +++ b/mso/datasource_mso_fabric_resource_policies_virtual_port_channel_interface_test.go @@ -21,8 +21,8 @@ func TestAccMSOVirtualPortChannelInterfaceDataSource(t *testing.T) { resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1", "101"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2", "102"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.#", "2"), - resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/1"), - resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/10-11"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.1", "1/1"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_1_interfaces.0", "1/10-11"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.#", "1"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "node_2_interfaces.0", "1/2"), resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_virtual_port_channel_interface.vpc_if", "interface_descriptions.#", "1"), diff --git a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown index 7f41cded..e4e3f140 100644 --- a/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown +++ b/website/docs/r/fabric_resource_policies_virtual_port_channel_interface.html.markdown @@ -18,13 +18,13 @@ Manages Virtual Port Channel (VPC) Interfaces on Cisco Nexus Dashboard Orchestra ```hcl resource "mso_fabric_resource_policies_virtual_port_channel_interface" "vpc_if" { - template_id = mso_template.fabric_resource_template.id - name = "tf_vpc_if" - description = "Example VPC Interface" - node_1 = "101" - node_2 = "102" - node_1_interfaces = ["1/1", "1/10-11"] - node_2_interfaces = ["1/2"] + template_id = mso_template.fabric_resource_template.id + name = "tf_vpc_if" + description = "Example VPC Interface" + node_1 = "101" + node_2 = "102" + node_1_interfaces = ["1/1", "1/10-11"] + node_2_interfaces = ["1/2"] interface_policy_group_uuid = mso_fabric_policies_interface_setting.port_channel_interface.uuid interface_descriptions {