diff --git a/examples/fabric_resource_policies_port_channel_interface/main.tf b/examples/fabric_resource_policies_port_channel_interface/main.tf new file mode 100644 index 00000000..299a92bd --- /dev/null +++ b/examples/fabric_resource_policies_port_channel_interface/main.tf @@ -0,0 +1,47 @@ +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_port_channel_interface" "example" { + template_id = mso_template.fabric_resource_template.id + name = "example" + description = "example description" + node = "101" + interfaces = ["1/1", "1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.port_channel_interface.id + interface_descriptions { + interface = "1/1" + description = "1/1 description" + } + interface_descriptions { + interface = "1/2" + description = "1/2 description" + } +} \ No newline at end of file diff --git a/mso/datasource_mso_fabric_resource_policies_port_channel_interface.go b/mso/datasource_mso_fabric_resource_policies_port_channel_interface.go new file mode 100644 index 00000000..0dcc468b --- /dev/null +++ b/mso/datasource_mso_fabric_resource_policies_port_channel_interface.go @@ -0,0 +1,91 @@ +package mso + +import ( + "fmt" + "log" + + "github.com/ciscoecosystem/mso-go-client/client" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func datasourceMSOPortChannelInterface() *schema.Resource { + return &schema.Resource{ + Read: dataSourceMSOPortChannelInterfaceRead, + + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "node": { + Type: schema.TypeString, + Computed: true, + }, + "interfaces": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + }, + "interface_policy_group_uuid": { + Type: schema.TypeString, + Computed: true, + }, + "interface_descriptions": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interface": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceMSOPortChannelInterfaceRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Port Channel Interface Data Source - Beginning Read") + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + policyName := d.Get("name").(string) + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "fabricResourceTemplate", "template", "portChannels") + if err != nil { + return err + } + + err = setPortChannelInterfaceData(d, policy, templateId) + if err != nil { + return err + } + + log.Printf("[DEBUG] MSO Port Channel Interface Data Source - Read Complete: %v", d.Id()) + return nil +} diff --git a/mso/datasource_mso_fabric_resource_policies_port_channel_interface_test.go b/mso/datasource_mso_fabric_resource_policies_port_channel_interface_test.go new file mode 100644 index 00000000..9d4f2115 --- /dev/null +++ b/mso/datasource_mso_fabric_resource_policies_port_channel_interface_test.go @@ -0,0 +1,55 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOFabricResourcePortChannelInterfaceDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Port Channel Interface Data Source - With Interface Descriptions") }, + Config: testAccMSOFabricResourcePortChannelInterfaceDataSourceWithInterfaceDescriptions(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "name", msoFabricResourcePortChannelInterfaceName), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "description", "Terraform test Port Channel Interface updated"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "node", "101"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interfaces.#", "2"), + resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "uuid"), + resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "template_id"), + resource.TestCheckResourceAttrSet("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_policy_group_uuid"), + resource.TestCheckResourceAttr("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions.#", "2"), + CustomTestCheckTypeSetElemAttrs("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/1", + "description": "Interface Description 1/1", + }, + ), + CustomTestCheckTypeSetElemAttrs("data.mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/2", + "description": "Interface Description 1/2", + }, + ), + ), + }, + }, + }) +} + +func testAccMSOFabricResourcePortChannelInterfaceDataSourceWithInterfaceDescriptions() string { + return fmt.Sprintf(`%[1]s + data "mso_fabric_resource_policies_port_channel_interface" "%[2]s" { + template_id = mso_template.%[3]s.id + name = mso_fabric_resource_policies_port_channel_interface.%[2]s.name + }`, + testAccMSOFabricResourcePortChannelInterfaceConfigUpdateAddingExtraInterfaceDescription(), + msoFabricResourcePortChannelInterfaceName, + msoFabricResourceTemplateName, + ) +} diff --git a/mso/provider.go b/mso/provider.go index 18d1c7ab..ab309c67 100644 --- a/mso/provider.go +++ b/mso/provider.go @@ -68,155 +68,157 @@ 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_port_channel_interface": resourceMSOPortChannelInterface(), }, 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_port_channel_interface": datasourceMSOPortChannelInterface(), }, ConfigureFunc: configureClient, diff --git a/mso/resource_mso_fabric_resource_policies_port_channel_interface.go b/mso/resource_mso_fabric_resource_policies_port_channel_interface.go new file mode 100644 index 00000000..e2c31560 --- /dev/null +++ b/mso/resource_mso_fabric_resource_policies_port_channel_interface.go @@ -0,0 +1,345 @@ +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 resourceMSOPortChannelInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceMSOPortChannelInterfaceCreate, + Read: resourceMSOPortChannelInterfaceRead, + Update: resourceMSOPortChannelInterfaceUpdate, + Delete: resourceMSOPortChannelInterfaceDelete, + Importer: &schema.ResourceImporter{ + State: resourceMSOPortChannelInterfaceImport, + }, + + CustomizeDiff: customizeDiffPortChannelInterface, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "template_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "node": { + Type: schema.TypeString, + Required: true, + }, + "interfaces": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "interface_policy_group_uuid": { + Type: schema.TypeString, + Required: true, + }, + "interface_descriptions": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interface": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func customizeDiffPortChannelInterface(d *schema.ResourceDiff, m interface{}) error { + interfacesRaw := d.Get("interfaces") + if interfacesRaw == nil { + return nil + } + interfaces := interfacesRaw.(*schema.Set) + if interfaces.Len() == 0 { + return nil + } + + // Create a map for quick lookup + interfaceMap := make(map[string]bool) + for _, memberInterface := range interfaces.List() { + interfaceMap[memberInterface.(string)] = true + } + + interfaceDescriptionsRaw := d.Get("interface_descriptions") + if interfaceDescriptionsRaw == nil { + return nil + } + interfaceDescriptions := interfaceDescriptionsRaw.(*schema.Set) + if interfaceDescriptions.Len() == 0 { + return nil + } + + // Validate each interface_description + for _, interfaceDescriptionRaw := range interfaceDescriptions.List() { + interfaceDescription := interfaceDescriptionRaw.(map[string]interface{}) + interfaceID := interfaceDescription["interface"].(string) + + if !interfaceMap[interfaceID] { + return fmt.Errorf("Interface '%s' specified in 'interface_descriptions' must be defined in the 'interfaces' list", interfaceID) + } + } + + return nil +} + +func getInterfaceDescriptionsPayload(node string, interfaceDescriptions *schema.Set) []map[string]interface{} { + interfaceDescriptionsList := interfaceDescriptions.List() + payload := make([]map[string]interface{}, 0) + for _, interfaceDescription := range interfaceDescriptionsList { + interfaceDescriptionMap := interfaceDescription.(map[string]interface{}) + payload = append(payload, map[string]interface{}{ + "nodeID": node, + "interfaceID": interfaceDescriptionMap["interface"].(string), + "description": interfaceDescriptionMap["description"].(string), + }) + } + return payload +} + +func setPortChannelInterfaceData(d *schema.ResourceData, response *container.Container, templateId string) error { + d.SetId(fmt.Sprintf("templateId/%s/PortChannelInterface/%s", templateId, models.StripQuotes(response.S("name").String()))) + d.Set("template_id", templateId) + d.Set("name", models.StripQuotes(response.S("name").String())) + d.Set("description", models.StripQuotes(response.S("description").String())) + d.Set("uuid", models.StripQuotes(response.S("uuid").String())) + d.Set("node", models.StripQuotes(response.S("node").String())) + d.Set("interfaces", splitCommaString(models.StripQuotes(response.S("memberInterfaces").String()))) + + if response.Exists("policy") { + d.Set("interface_policy_group_uuid", models.StripQuotes(response.S("policy").String())) + } + + interfaceDescriptionsList := make([]map[string]interface{}, 0) + count, err := response.ArrayCount("interfaceDescriptions") + if err == nil { + for i := range count { + descriptionContainer, err := response.ArrayElement(i, "interfaceDescriptions") + if err != nil { + return err + } + interfaceDescriptionsList = append(interfaceDescriptionsList, map[string]interface{}{ + "interface": models.StripQuotes(descriptionContainer.S("interfaceID").String()), + "description": models.StripQuotes(descriptionContainer.S("description").String()), + }) + } + } + + d.Set("interface_descriptions", interfaceDescriptionsList) + + return nil +} + +func resourceMSOPortChannelInterfaceImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Beginning Import: %v", d.Id()) + err := resourceMSOPortChannelInterfaceRead(d, m) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Import Complete: %v", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceMSOPortChannelInterfaceCreate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Beginning Create: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + + payload := map[string]interface{}{ + "name": d.Get("name").(string), + "templateId": templateId, + } + + payload["description"] = d.Get("description").(string) + + payload["node"] = d.Get("node").(string) + payload["memberInterfaces"] = strings.Join(getListOfStringsFromSchemaSet(d, "interfaces"), ",") + payload["policy"] = d.Get("interface_policy_group_uuid").(string) + + if interfaceDescriptions, ok := d.GetOk("interface_descriptions"); ok { + payload["interfaceDescriptions"] = getInterfaceDescriptionsPayload(payload["node"].(string), interfaceDescriptions.(*schema.Set)) + } + + payloadModel := models.GetPatchPayload("add", "/fabricResourceTemplate/template/portChannels/-", payload) + + _, err := msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/PortChannelInterface/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Create Complete: %v", d.Id()) + return resourceMSOPortChannelInterfaceRead(d, m) +} + +func resourceMSOPortChannelInterfaceRead(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Beginning Read: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId, err := GetTemplateIdFromResourceId(d.Id()) + if err != nil { + return err + } + + response, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyName, err := GetPolicyNameFromResourceId(d.Id(), "PortChannelInterface") + if err != nil { + return err + } + + policy, err := GetPolicyByName(response, policyName, "fabricResourceTemplate", "template", "portChannels") + if err != nil { + return err + } + + err = setPortChannelInterfaceData(d, policy, templateId) + if err != nil { + return err + } + + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Read Complete: %v", d.Id()) + return nil +} + +func resourceMSOPortChannelInterfaceUpdate(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Beginning Update: %v", d.Id()) + msoClient := m.(*client.Client) + templateId := d.Get("template_id").(string) + node := d.Get("node").(string) + + templateContainer, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateContainer, "uuid", d.Get("uuid").(string), "fabricResourceTemplate", "template", "portChannels") + if err != nil { + return err + } + + updatePath := fmt.Sprintf("/fabricResourceTemplate/template/portChannels/%d", policyIndex) + + payloadCont := container.New() + payloadCont.Array() + + if d.HasChange("name") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/name", updatePath), d.Get("name").(string)) + if err != nil { + return err + } + } + + if d.HasChange("description") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/description", updatePath), d.Get("description").(string)) + if err != nil { + return err + } + } + + if d.HasChange("node") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/node", updatePath), d.Get("node").(string)) + if err != nil { + return err + } + } + + if d.HasChange("interfaces") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/memberInterfaces", updatePath), strings.Join(getListOfStringsFromSchemaSet(d, "interfaces"), ",")) + if err != nil { + return err + } + } + + if d.HasChange("interface_policy_group_uuid") { + interfacePolicyUUID := d.Get("interface_policy_group_uuid").(string) + if interfacePolicyUUID != "" { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/policy", updatePath), interfacePolicyUUID) + if err != nil { + return err + } + } + } + + if d.HasChange("interface_descriptions") { + err := addPatchPayloadToContainer(payloadCont, "replace", fmt.Sprintf("%s/interfaceDescriptions", updatePath), getInterfaceDescriptionsPayload(node, d.Get("interface_descriptions").(*schema.Set))) + if err != nil { + return err + } + } + + err = doPatchRequest(msoClient, fmt.Sprintf("api/v1/templates/%s", templateId), payloadCont) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("templateId/%s/PortChannelInterface/%s", templateId, d.Get("name").(string))) + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Update Complete: %v", d.Id()) + return resourceMSOPortChannelInterfaceRead(d, m) +} + +func resourceMSOPortChannelInterfaceDelete(d *schema.ResourceData, m interface{}) error { + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Beginning Delete: %v", d.Id()) + msoClient := m.(*client.Client) + + templateId := d.Get("template_id").(string) + + templateContainer, err := msoClient.GetViaURL(fmt.Sprintf("api/v1/templates/%s", templateId)) + if err != nil { + return err + } + + policyIndex, err := GetPolicyIndexByKeyAndValue(templateContainer, "uuid", d.Get("uuid").(string), "fabricResourceTemplate", "template", "portChannels") + if err != nil { + return err + } + + payloadModel := models.GetRemovePatchPayload(fmt.Sprintf("/fabricResourceTemplate/template/portChannels/%d", policyIndex)) + + _, err = msoClient.PatchbyID(fmt.Sprintf("api/v1/templates/%s", templateId), payloadModel) + if err != nil { + return err + } + + d.SetId("") + log.Printf("[DEBUG] MSO Port Channel Interface Resource - Delete Complete: %v", d.Id()) + return nil +} diff --git a/mso/resource_mso_fabric_resource_policies_port_channel_interface_test.go b/mso/resource_mso_fabric_resource_policies_port_channel_interface_test.go new file mode 100644 index 00000000..9b826b66 --- /dev/null +++ b/mso/resource_mso_fabric_resource_policies_port_channel_interface_test.go @@ -0,0 +1,157 @@ +package mso + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccMSOFabricResourcePortChannelInterfaceResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + PreConfig: func() { fmt.Println("Test: Create Port Channel Interface") }, + Config: testAccMSOFabricResourcePortChannelInterfaceConfigCreate(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "name", msoFabricResourcePortChannelInterfaceName), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "description", ""), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "node", "101"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interfaces.#", "2"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "uuid"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "template_id"), + resource.TestCheckResourceAttrSet("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_policy_group_uuid"), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update Port Channel Interface adding interface descriptions") }, + Config: testAccMSOFabricResourcePortChannelInterfaceConfigUpdateAddingInterfaceDescriptions(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "name", msoFabricResourcePortChannelInterfaceName), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "description", "Terraform test Port Channel Interface updated"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "node", "101"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interfaces.#", "2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions.#", "1"), + CustomTestCheckTypeSetElemAttrs("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/1", + "description": "Interface Description 1/1", + }, + ), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update Port Channel Interface adding extra interface description") }, + Config: testAccMSOFabricResourcePortChannelInterfaceConfigUpdateAddingExtraInterfaceDescription(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "name", msoFabricResourcePortChannelInterfaceName), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "description", "Terraform test Port Channel Interface updated"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "node", "101"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interfaces.#", "2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions.#", "2"), + CustomTestCheckTypeSetElemAttrs("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/1", + "description": "Interface Description 1/1", + }, + ), + CustomTestCheckTypeSetElemAttrs("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/2", + "description": "Interface Description 1/2", + }, + ), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Update Port Channel Interface removing extra interface description") }, + Config: testAccMSOFabricResourcePortChannelInterfaceConfigUpdateRemovingExtraInterfaceDescription(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "name", msoFabricResourcePortChannelInterfaceName), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "description", ""), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "node", "101"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interfaces.#", "2"), + resource.TestCheckResourceAttr("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions.#", "1"), + CustomTestCheckTypeSetElemAttrs("mso_fabric_resource_policies_port_channel_interface."+msoFabricResourcePortChannelInterfaceName, "interface_descriptions", + map[string]string{ + "interface": "1/2", + "description": "", + }, + ), + ), + }, + { + PreConfig: func() { fmt.Println("Test: Import Port Channel Interface") }, + ResourceName: "mso_fabric_resource_policies_port_channel_interface." + msoFabricResourcePortChannelInterfaceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + CheckDestroy: testCheckResourceDestroyPolicyWithPathAttributesAndArguments("mso_fabric_resource_policies_port_channel_interface", "fabricResourceTemplate", "template", "portChannels"), + }) +} + +var fabricResourcePortChannelInterfacePreConfig = testFabricResourceTemplateConfig() + testFabricPolicyTemplateConfig() + testFabricPoliciesInterfaceSettingPortChannelConfig() + +func testAccMSOFabricResourcePortChannelInterfaceConfigCreate() string { + return fmt.Sprintf(`%[1]s + resource "mso_fabric_resource_policies_port_channel_interface" "%[2]s" { + template_id = mso_template.%[4]s.id + name = "%[2]s" + node = "101" + interfaces = ["1/1","1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%[3]s_portchannel.uuid + }`, fabricResourcePortChannelInterfacePreConfig, msoFabricResourcePortChannelInterfaceName, msoFabricPolicyTemplateInterfaceSettingName, msoFabricResourceTemplateName) +} + +func testAccMSOFabricResourcePortChannelInterfaceConfigUpdateAddingInterfaceDescriptions() string { + return fmt.Sprintf(`%[1]s + resource "mso_fabric_resource_policies_port_channel_interface" "%[2]s" { + template_id = mso_template.%[4]s.id + name = "%[2]s" + description = "Terraform test Port Channel Interface updated" + node = "101" + interfaces = ["1/1","1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%[3]s_portchannel.uuid + interface_descriptions { + interface = "1/1" + description = "Interface Description 1/1" + } + }`, fabricResourcePortChannelInterfacePreConfig, msoFabricResourcePortChannelInterfaceName, msoFabricPolicyTemplateInterfaceSettingName, msoFabricResourceTemplateName) +} + +func testAccMSOFabricResourcePortChannelInterfaceConfigUpdateAddingExtraInterfaceDescription() string { + return fmt.Sprintf(`%[1]s + resource "mso_fabric_resource_policies_port_channel_interface" "%[2]s" { + template_id = mso_template.%[4]s.id + name = "%[2]s" + description = "Terraform test Port Channel Interface updated" + node = "101" + interfaces = ["1/1","1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%[3]s_portchannel.uuid + interface_descriptions { + interface = "1/1" + description = "Interface Description 1/1" + } + interface_descriptions { + interface = "1/2" + description = "Interface Description 1/2" + } + }`, fabricResourcePortChannelInterfacePreConfig, msoFabricResourcePortChannelInterfaceName, msoFabricPolicyTemplateInterfaceSettingName, msoFabricResourceTemplateName) +} + +func testAccMSOFabricResourcePortChannelInterfaceConfigUpdateRemovingExtraInterfaceDescription() string { + return fmt.Sprintf(`%[1]s + resource "mso_fabric_resource_policies_port_channel_interface" "%[2]s" { + template_id = mso_template.%[4]s.id + name = "%[2]s" + node = "101" + interfaces = ["1/1","1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.%[3]s_portchannel.uuid + interface_descriptions { + interface = "1/2" + } + }`, fabricResourcePortChannelInterfacePreConfig, msoFabricResourcePortChannelInterfaceName, msoFabricPolicyTemplateInterfaceSettingName, msoFabricResourceTemplateName) +} diff --git a/mso/test_constants.go b/mso/test_constants.go index d6ac91e7..d74f09cb 100644 --- a/mso/test_constants.go +++ b/mso/test_constants.go @@ -34,6 +34,9 @@ var msoSchemaTemplateVrfL3MulticastName = acctest.RandStringFromCharSet(10, acct const msoSchemaTemplateAnpEpgSubnetIp = "10.0.0.1/24" +var msoFabricResourceTemplateName = acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) +var msoFabricResourcePortChannelInterfaceName = acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) + func testSiteConfigAnsibleTest() string { return fmt.Sprintf(` data "mso_site" "%[1]s" { @@ -292,3 +295,22 @@ resource "mso_schema_template_anp_epg_subnet" "%[1]s_subnet" { } `, msoSchemaTemplateAnpEpgName, msoSchemaName, msoSchemaTemplateName, msoSchemaTemplateAnpName, msoSchemaTemplateAnpEpgName, msoSchemaTemplateAnpEpgSubnetIp) } + +func testFabricResourceTemplateConfig() string { + return fmt.Sprintf(` +resource "mso_template" "%[1]s" { + template_name = "%[1]s" + template_type = "fabric_resource" +} + `, msoFabricResourceTemplateName) +} + +func testFabricPoliciesInterfaceSettingPortChannelConfig() string { + return fmt.Sprintf(` +resource "mso_fabric_policies_interface_setting" "%[1]s_portchannel" { + template_id = mso_template.%[2]s.id + type = "portchannel" + name = "%[1]s_portchannel" +} +`, msoFabricPolicyTemplateInterfaceSettingName, msoFabricPolicyTemplateName) +} diff --git a/mso/utils.go b/mso/utils.go index 7fc0678c..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 { @@ -540,3 +547,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/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) + } + }) + } +} diff --git a/website/docs/d/fabric_resource_policies_port_channel_interface.html.markdown b/website/docs/d/fabric_resource_policies_port_channel_interface.html.markdown new file mode 100644 index 00000000..f4617718 --- /dev/null +++ b/website/docs/d/fabric_resource_policies_port_channel_interface.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "mso" +page_title: "MSO: mso_fabric_resource_policies_port_channel_interface" +sidebar_current: "docs-mso-data-source-fabric_resource_policies_port_channel_interface" +description: |- + Data source for Fabric Resource Policies Port Channel Interface on Cisco Nexus Dashboard Orchestrator (NDO) +--- + + + +# mso_fabric_resource_policies_port_channel_interface # + +Data source for Fabric Resource Policies Port Channel Interface on Cisco Nexus Dashboard Orchestrator (NDO). This data source is supported in NDO v4.1 or higher. + +## GUI Information ## + +* `Location` - Manage -> Fabric Resource Template -> Fabric Resource Policies -> Port Channel Interfaces + +## Example Usage ## + +```hcl +data "mso_fabric_resource_policies_port_channel_interface" "example" { + template_id = mso_template.fabric_resource_template.id + name = "port_channel_interface_example" +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the Fabric Resource template. +* `name` - (Required) The name of the Port Channel Interface Policy. + +## Attribute Reference ## + +* `id` - (Read-Only) The unique Terraform identifier of the Port Channel Interface Policy. +* `uuid` - (Read-Only) The NDO UUID of the Port Channel Interface Policy. +* `description` - (Read-Only) The description of the Port Channel Interface Policy. +* `node` - (Read-Only) The node ID of the Port Channel Interface Policy. +* `interfaces` - (Read-Only) A list of interfaces where this Port Channel Interface Policy will be applied. +* `interface_policy_group_uuid` - (Read-Only) The UUID of the (Port Channel/Virtual Port Channel) interface settings policy to associate with the Port Channel Interface Policy. +* `interface_descriptions` - (Read-Only) A list of interface descriptions of the Port Channel Interface Policy. + * `interface` - (Read-Only) The interface ID of the member interface. + * `description` - (Read-Only) The description of the member interface. diff --git a/website/docs/r/fabric_resource_policies_port_channel_interface.html.markdown b/website/docs/r/fabric_resource_policies_port_channel_interface.html.markdown new file mode 100644 index 00000000..b928a363 --- /dev/null +++ b/website/docs/r/fabric_resource_policies_port_channel_interface.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "mso" +page_title: "MSO: mso_fabric_resource_policies_port_channel_interface" +sidebar_current: "docs-mso-resource-fabric_resource_policies_port_channel_interface" +description: |- + Manages Fabric Resource Policies Port Channel Interface on Cisco Nexus Dashboard Orchestrator (NDO) +--- + +# mso_fabric_resource_policies_port_channel_interface # + +Manages Fabric Resource Policies Port Channel Interface on Cisco Nexus Dashboard Orchestrator (NDO). This resource is supported in NDO v4.1 or higher. + +## GUI Information ## + +* `Location`: Manage -> Fabric Resource Template -> Fabric Resource Policies -> Port Channel Interfaces + +## Example Usage ## + +```hcl +resource "mso_fabric_resource_policies_port_channel_interface" "example" { + template_id = mso_template.fabric_resource_template.id + name = "example" + description = "example description" + node = "101" + interfaces = ["1/1", "1/2"] + interface_policy_group_uuid = mso_fabric_policies_interface_setting.port_channel_interface.id + interface_descriptions { + interface = "1/1" + description = "1/1 description" + } + interface_descriptions { + interface = "1/2" + description = "1/2 description" + } +} +``` + +## Argument Reference ## + +* `template_id` - (Required) The unique ID of the Fabric Resource template. +* `name` - (Required) The name of the Port Channel Interface Policy. +* `description` - (Optional) The description of the Port Channel Interface Policy. +* `node` - (Required) The node ID of the Port Channel Interface Policy. This is required when creating or updating a Port Channel Interface Policy. +* `interfaces` - (Required) A list of interfaces where this Port Channel Interface Policy will be applied. This is required when creating or updating a Port Channel Interface Policy. +* `interface_policy_group_uuid` - (Required) The UUID of the (Port Channel/Virtual Port Channel) interface settings policy to associate with the Port Channel Interface Policy. This is required when creating a Port Channel Interface Policy. +* `interface_descriptions` - (Optional) A list of interface descriptions of the Port Channel Interface Policy. + * `interface` - (Required) The interface ID of the member interface. Must match an interface defined in the `interfaces` attribute. + * `description` - (Optional) The description of the member interface. + +## Attribute Reference ## + +* `id` - (Read-Only) The unique Terraform identifier of the Port Channel Interface Policy. +* `uuid` - (Read-Only) The NDO UUID of the Port Channel Interface Policy. + +## Importing ## + +An existing MSO Fabric Resource Policies Port Channel Interface Policy can be [imported][docs-import] into this resource via its ID, using the following command: +[docs-import]: https://www.terraform.io/docs/import/index.html + +```bash +terraform import mso_fabric_resource_policies_port_channel_interface.example templateId/{template_id}/PortChannelInterface/{name} +```