From 952b07e663727d93fe3faa825bdc4a632ad7c11b Mon Sep 17 00:00:00 2001 From: Kobi Samoray Date: Wed, 6 Sep 2023 11:43:43 +0300 Subject: [PATCH] Implemnt host transport node collection resource Signed-off-by: Kobi Samoray --- nsxt/provider.go | 1 + ...t_policy_host_transport_node_collection.go | 277 ++++++++++++++++++ nsxt/resource_nsxt_policy_transport_zone.go | 2 +- ...st_transport_node_collection.html.markdown | 62 ++++ 4 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 nsxt/resource_nsxt_policy_host_transport_node_collection.go create mode 100644 website/docs/r/policy_host_transport_node_collection.html.markdown diff --git a/nsxt/provider.go b/nsxt/provider.go index 455d2541f..735fbfb15 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -431,6 +431,7 @@ func Provider() *schema.Provider { "nsxt_failure_domain": resourceNsxtFailureDomain(), "nsxt_cluster_virtual_ip": resourceNsxtClusterVirualIP(), "nsxt_policy_host_transport_node_profile": resourceNsxtPolicyHostTransportNodeProfile(), + "nsxt_policy_host_transport_node_collection": resourceNsxtPolicyHostTransportNodeCollection(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_policy_host_transport_node_collection.go b/nsxt/resource_nsxt_policy_host_transport_node_collection.go new file mode 100644 index 000000000..f5b9e07c4 --- /dev/null +++ b/nsxt/resource_nsxt_policy_host_transport_node_collection.go @@ -0,0 +1,277 @@ +/* Copyright © 2023 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" +) + +func resourceNsxtPolicyHostTransportNodeCollection() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtPolicyHostTransportNodeCollectionCreate, + Read: resourceNsxtPolicyHostTransportNodeCollectionRead, + Update: resourceNsxtPolicyHostTransportNodeCollectionUpdate, + Delete: resourceNsxtPolicyHostTransportNodeCollectionDelete, + Importer: &schema.ResourceImporter{ + State: nsxtPolicyPathResourceImporter, + }, + + Schema: map[string]*schema.Schema{ + "nsx_id": getNsxIDSchema(), + "path": getPathSchema(), + "display_name": getDisplayNameSchema(), + "description": getDescriptionSchema(), + "revision": getRevisionSchema(), + "tag": getTagsSchema(), + "site_path": { + Type: schema.TypeString, + Description: "Path to the site this Transport Zone belongs to", + Optional: true, + ForceNew: true, + Default: defaultInfraSitePath, + ValidateFunc: validatePolicyPath(), + }, + "enforcement_point": { + Type: schema.TypeString, + Description: "ID of the enforcement point this Transport Zone belongs to", + Optional: true, + ForceNew: true, + Computed: true, + }, + "compute_collection_id": { + Type: schema.TypeString, + Required: true, + Description: "Compute collection id", + }, + "sub_cluster_config": { + Type: schema.TypeList, + Optional: true, + Description: "List of sub-cluster configuration", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_switch_config_source": { + Type: schema.TypeList, + Required: true, + Description: "List of overridden HostSwitch configuration", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "host_switch_id": { + Type: schema.TypeString, + Required: true, + Description: "HostSwitch Id", + }, + "transport_node_profile_sub_config_name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the TransportNodeProfile sub configuration to be used", + }, + }, + }, + }, + "sub_cluster_id": { + Type: schema.TypeString, + Required: true, + Description: "sub-cluster Id", + }, + }, + }, + }, + "transport_node_profile_path": { + Type: schema.TypeString, + Optional: true, + Description: "Transport Node Profile Path", + }, + }, + } +} + +func resourceNsxtPolicyHostTransportNodeCollectionExists(siteID, epID, id string, connector client.Connector) (bool, error) { + // Check site existence first + siteClient := infra.NewSitesClient(connector) + _, err := siteClient.Get(siteID) + if err != nil { + msg := fmt.Sprintf("Failed to read site %s", siteID) + return false, logAPIError(msg, err) + } + + client := enforcement_points.NewTransportNodeCollectionsClient(connector) + _, err = client.Get(siteID, epID, id) + if err == nil { + return true, nil + } + + if isNotFoundError(err) { + return false, nil + } + + return false, logAPIError("Error retrieving resource", err) +} + +func policyHostTransportNodeCollectionUpdate(siteID, epID, id string, applyProfileParam bool, d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + displayName := d.Get("display_name").(string) + description := d.Get("description").(string) + tags := getPolicyTagsFromSchema(d) + + computeCollectionID := d.Get("compute_collection_id").(string) + transportNodeProfileID := d.Get("transport_node_profile_path").(string) + var subClusterConfigs []model.SubClusterConfig + for _, scc := range d.Get("sub_cluster_config").([]interface{}) { + subClusterConfig := scc.(map[string]interface{}) + var hostSwitchConfigSources []model.HostSwitchConfigSource + for _, hss := range subClusterConfig["host_switch_config_source"].([]interface{}) { + hsSource := hss.(map[string]interface{}) + hostSwitchID := hsSource["host_switch_id"].(string) + transportNodeProfileSubConfigName := hsSource["transport_node_profile_sub_config_name"].(string) + elem := model.HostSwitchConfigSource{ + HostSwitchId: &hostSwitchID, + TransportNodeProfileSubConfigName: &transportNodeProfileSubConfigName, + } + hostSwitchConfigSources = append(hostSwitchConfigSources, elem) + } + subClusterID := subClusterConfig["sub_cluster_id"].(string) + elem := model.SubClusterConfig{ + SubClusterId: &subClusterID, + HostSwitchConfigSources: hostSwitchConfigSources, + } + subClusterConfigs = append(subClusterConfigs, elem) + } + obj := model.HostTransportNodeCollection{ + DisplayName: &displayName, + Description: &description, + Tags: tags, + ComputeCollectionId: &computeCollectionID, + TransportNodeProfileId: &transportNodeProfileID, + SubClusterConfig: subClusterConfigs, + } + + client := enforcement_points.NewTransportNodeCollectionsClient(connector) + _, err := client.Update(siteID, epID, id, obj, &applyProfileParam, nil) + + return err +} + +func resourceNsxtPolicyHostTransportNodeCollectionCreate(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + id := d.Get("nsx_id").(string) + if id == "" { + id = newUUID() + } + sitePath := d.Get("site_path").(string) + siteID := getResourceIDFromResourcePath(sitePath, "sites") + if siteID == "" { + return fmt.Errorf("error obtaining Site ID from site path %s", sitePath) + } + epID := d.Get("enforcement_point").(string) + if epID == "" { + epID = getPolicyEnforcementPoint(m) + } + + exists, err := resourceNsxtPolicyHostTransportNodeCollectionExists(siteID, epID, id, connector) + if err != nil { + return err + } + if exists { + return fmt.Errorf("resource with ID %s already exists", id) + } + + // Create the resource using PATCH + log.Printf("[INFO] Creating HostTransportNodeCollection with ID %s under site %s enforcement point %s", id, siteID, epID) + err = policyHostTransportNodeCollectionUpdate(siteID, epID, id, true, d, m) + if err != nil { + return handleCreateError("HostTransportNodeCollection", id, err) + } + + d.SetId(id) + d.Set("nsx_id", id) + + return resourceNsxtPolicyHostTransportNodeCollectionRead(d, m) +} + +func resourceNsxtPolicyHostTransportNodeCollectionRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + client := enforcement_points.NewTransportNodeCollectionsClient(connector) + // (TODO) Reusing this code here - maybe worthwhile renaming this func as it's usable for other resources + id, siteID, epID, err := policyTransportZoneIDTuple(d, m) + if err != nil { + return err + } + obj, err := client.Get(siteID, epID, id) + if err != nil { + return handleReadError(d, "HostTransportNodeCollection", id, err) + } + + d.Set("enforcement_point", epID) + d.Set("display_name", obj.DisplayName) + d.Set("description", obj.Description) + setPolicyTagsInSchema(d, obj.Tags) + d.Set("nsx_id", id) + d.Set("path", obj.Path) + d.Set("revision", obj.Revision) + d.Set("compute_collection_id", obj.ComputeCollectionId) + if obj.SubClusterConfig != nil { + var sccList []map[string]interface{} + for _, cfg := range obj.SubClusterConfig { + scc := make(map[string]interface{}) + + if cfg.HostSwitchConfigSources != nil { + var hscsList []map[string]interface{} + for _, src := range cfg.HostSwitchConfigSources { + hscs := make(map[string]interface{}) + hscs["host_switch_id"] = src.HostSwitchId + hscs["transport_node_profile_sub_config_name"] = src.TransportNodeProfileSubConfigName + hscsList = append(hscsList, hscs) + } + scc["host_switch_config_source"] = hscsList + } + + scc["sub_cluster_id"] = cfg.SubClusterId + sccList = append(sccList, scc) + } + d.Set("sub_cluster_config", sccList) + } + d.Set("transport_node_profile_path", obj.TransportNodeProfileId) + return nil +} + +func resourceNsxtPolicyHostTransportNodeCollectionUpdate(d *schema.ResourceData, m interface{}) error { + id, siteID, epID, err := policyTransportZoneIDTuple(d, m) + if err != nil { + return err + } + + log.Printf("[INFO] Updating HostTransportNodeCollection with ID %s", id) + err = policyHostTransportNodeCollectionUpdate(siteID, epID, id, false, d, m) + + if err != nil { + return handleUpdateError("HostTransportNodeCollection", id, err) + } + + return resourceNsxtPolicyHostTransportNodeCollectionRead(d, m) +} + +func resourceNsxtPolicyHostTransportNodeCollectionDelete(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + client := enforcement_points.NewTransportNodeCollectionsClient(connector) + id, siteID, epID, err := policyTransportZoneIDTuple(d, m) + if err != nil { + return err + } + + log.Printf("[INFO] Deleting HostTransportNodeCollection with ID %s", id) + err = client.Delete(siteID, epID, id) + if err != nil { + return handleDeleteError("HostTransportNodeCollection", id, err) + } + + return nil +} diff --git a/nsxt/resource_nsxt_policy_transport_zone.go b/nsxt/resource_nsxt_policy_transport_zone.go index 55730d7ef..763598f81 100644 --- a/nsxt/resource_nsxt_policy_transport_zone.go +++ b/nsxt/resource_nsxt_policy_transport_zone.go @@ -243,7 +243,7 @@ func resourceNsxtPolicyTransportZoneUpdate(d *schema.ResourceData, m interface{} return err } - log.Printf("[INFO] Updateing TransportZone with ID %s", id) + log.Printf("[INFO] Updating TransportZone with ID %s", id) err = policyTransportZonePatch(siteID, epID, id, d, m) if err != nil { return handleUpdateError("TransportZone", id, err) diff --git a/website/docs/r/policy_host_transport_node_collection.html.markdown b/website/docs/r/policy_host_transport_node_collection.html.markdown new file mode 100644 index 000000000..676c41953 --- /dev/null +++ b/website/docs/r/policy_host_transport_node_collection.html.markdown @@ -0,0 +1,62 @@ +--- +subcategory: "Fabric" +layout: "nsxt" +page_title: "NSXT: nsxt_policy_host_transport_node_collection" +description: A resource to configure Policy Host Transport Node Collection. +--- + +# nsxt_policy_host_transport_node_collection + +This resource provides a method for the management of a Host Transport Node Collection. + +# Example Usage + +```hcl +data "vsphere_compute_cluster" "compute_cluster" { + provider = vsphere.vsphere-nsxcfg-data + name = "Compute-Cluster" + datacenter_id = data.vsphere_datacenter.datacenter.id +} + +resource "nsxt_policy_host_transport_node_collection" "htnc1" { + display_name = "HostTransportNodeCollection1" + compute_collection_id = data.vsphere_compute_cluster.compute_cluster.id + transport_node_profile_path = nsxt_policy_host_transport_node_profile.tnp.path + tag { + scope = "color" + tag = "red" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `display_name` - (Optional) The Display Name of the Transport Zone. +* `description` - (Optional) Description of the Transport Zone. +* `tag` - (Optional) A list of scope + tag pairs to associate with this resource. +* `nsx_id` - (Optional) The NSX ID of this resource. If set, this ID will be used to create the policy resource. +* `compute_collection_id` - (Required) Compute collection id. +* `sub_cluster_config` - (Optional) List of sub-cluster configuration. + * `host_switch_config_source` - (Required) List of overridden HostSwitch configuration. + * `host_switch_id` - (Required) HostSwitch ID. + * `transport_node_profile_sub_config_name` - (Required) Name of the TransportNodeProfile sub configuration to be used. + * `sub_cluster_id` - (Required) sub-cluster ID. +* `transport_node_profile_path` - (Optional) Transport Node Profile Path. + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `id` - ID of the resource. +* `revision` - Indicates current revision number of the object as seen by NSX-T API server. This attribute can be useful for debugging. +* `path` - The NSX path of the policy resource. + +## Importing + +An existing policy Host Transport Node Collection can be [imported][docs-import] into this resource, via the following command: + +terraform import nsxt_policy_host_transport_node_collection.test POLICY_PATH + +The above command imports Policy Host Transport Node Collection named test with NSX policy path POLICY_PATH. \ No newline at end of file