diff --git a/nsxt/provider.go b/nsxt/provider.go index 975060f27..a9b05c779 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -478,6 +478,7 @@ func Provider() *schema.Provider { "nsxt_upgrade_prepare": resourceNsxtUpgradePrepare(), "nsxt_upgrade_precheck_acknowledge": resourceNsxtUpgradePrecheckAcknowledge(), "nsxt_policy_vtep_ha_host_switch_profile": resourceNsxtVtepHAHostSwitchProfile(), + "nsxt_policy_site": resourceNsxtPolicySite(), }, ConfigureFunc: providerConfigure, diff --git a/nsxt/resource_nsxt_policy_site.go b/nsxt/resource_nsxt_policy_site.go new file mode 100644 index 000000000..c3607e0c7 --- /dev/null +++ b/nsxt/resource_nsxt_policy_site.go @@ -0,0 +1,268 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-gm/global_infra" + gm_model "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-gm/model" +) + +var siteTypeValues = []string{ + gm_model.Site_SITE_TYPE_ONPREM_LM, + gm_model.Site_SITE_TYPE_SDDC_LM, +} + +func resourceNsxtPolicySite() *schema.Resource { + return &schema.Resource{ + Create: resourceNsxtPolicySiteCreate, + Read: resourceNsxtPolicySiteRead, + Update: resourceNsxtPolicySiteUpdate, + Delete: resourceNsxtPolicySiteDelete, + Importer: &schema.ResourceImporter{ + State: nsxtPolicyPathResourceImporter, + }, + + Schema: map[string]*schema.Schema{ + "nsx_id": getNsxIDSchema(), + "path": getPathSchema(), + "display_name": getDisplayNameSchema(), + "description": getDescriptionSchema(), + "revision": getRevisionSchema(), + "tag": getTagsSchema(), + "fail_if_rtep_misconfigured": { + Type: schema.TypeBool, + Optional: true, + Description: "Fail onboarding if RTEPs misconfigured", + Default: true, + }, + "fail_if_rtt_exceeded": { + Type: schema.TypeBool, + Optional: true, + Description: "Fail onboarding if maximum RTT exceeded", + Default: true, + }, + "maximum_rtt": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum acceptable packet round trip time (RTT)", + Default: 250, + ValidateFunc: validation.IntBetween(0, 1000), + }, + "site_connection_info": getConnectionInfoSchema(), + "site_type": { + Type: schema.TypeString, + Required: true, + Description: "Persistent Site Type", + ValidateFunc: validation.StringInSlice(siteTypeValues, false), + }, + }, + } +} + +func getConnectionInfoSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Description: "Connection information", + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fqdn": { + Type: schema.TypeString, + Optional: true, + Description: "Fully Qualified Domain Name of the Management Node", + }, + "password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "Password", + }, + "site_uuid": { + Type: schema.TypeString, + Optional: true, + Description: "id of Site", + }, + "thumbprint": { + Type: schema.TypeString, + Optional: true, + Description: "Thumbprint of Enforcement Point", + }, + "username": { + Type: schema.TypeString, + Optional: true, + Description: "Username", + }, + }, + }, + } +} + +func getConnectionInfosFromSchema(d *schema.ResourceData, key string) []gm_model.SiteNodeConnectionInfo { + var connectionInfos []gm_model.SiteNodeConnectionInfo + connectionInfoList := d.Get(key).([]interface{}) + for _, sci := range connectionInfoList { + elem := sci.(map[string]interface{}) + fqdn := elem["fqdn"].(string) + password := elem["password"].(string) + siteUUID := elem["site_uuid"].(string) + thumbprint := elem["thumbprint"].(string) + username := elem["username"].(string) + + connectionInfo := gm_model.SiteNodeConnectionInfo{ + Fqdn: &fqdn, + Password: &password, + SiteUuid: &siteUUID, + Thumbprint: &thumbprint, + Username: &username, + } + connectionInfos = append(connectionInfos, connectionInfo) + } + return connectionInfos +} + +func setConnectionInfosInSchema(d *schema.ResourceData, connectionInfo []gm_model.SiteNodeConnectionInfo, key string) { + var connectionInfos []interface{} + for _, sci := range connectionInfo { + elem := make(map[string]interface{}) + elem["fqdn"] = sci.Fqdn + elem["password"] = sci.Password + elem["site_uuid"] = sci.SiteUuid + elem["thumbprint"] = sci.Thumbprint + elem["username"] = sci.Username + + connectionInfos = append(connectionInfos, elem) + } + d.Set(key, connectionInfos) +} + +func getSiteFromSchema(d *schema.ResourceData) gm_model.Site { + displayName := d.Get("display_name").(string) + description := d.Get("description").(string) + tags := getGMTagsFromSchema(d) + failIfRtepMisconfigured := d.Get("fail_if_rtep_misconfigured").(bool) + failIfRttExceeded := d.Get("fail_if_rtt_exceeded").(bool) + maximumRtt := int64(d.Get("maximum_rtt").(int)) + siteConnectionInfos := getConnectionInfosFromSchema(d, "site_connection_info") + siteType := d.Get("site_type").(string) + + return gm_model.Site{ + DisplayName: &displayName, + Description: &description, + Tags: tags, + FailIfRtepMisconfigured: &failIfRtepMisconfigured, + FailIfRttExceeded: &failIfRttExceeded, + MaximumRtt: &maximumRtt, + SiteConnectionInfo: siteConnectionInfos, + SiteType: &siteType, + } +} + +func resourceNsxtPolicySiteExists(id string, connector client.Connector, isGlobal bool) (bool, error) { + client := global_infra.NewSitesClient(connector) + _, err := client.Get(id) + if err == nil { + return true, nil + } + + if isNotFoundError(err) { + return false, nil + } + + return false, logAPIError("Error retrieving resource", err) +} + +func resourceNsxtPolicySiteCreate(d *schema.ResourceData, m interface{}) error { + if !isPolicyGlobalManager(m) { + return globalManagerOnlyError() + } + id, err := getOrGenerateID(d, m, resourceNsxtPolicySiteExists) + if err != nil { + return err + } + + connector := getPolicyConnector(m) + client := global_infra.NewSitesClient(connector) + site := getSiteFromSchema(d) + + err = client.Patch(id, site) + if err != nil { + return handleCreateError("Site", id, err) + } + + d.SetId(id) + d.Set("nsx_id", id) + + return resourceNsxtPolicySiteRead(d, m) +} + +func resourceNsxtPolicySiteRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining Site ID") + } + client := global_infra.NewSitesClient(connector) + obj, err := client.Get(id) + if err != nil { + return handleReadError(d, "Site", id, err) + } + d.Set("display_name", obj.DisplayName) + d.Set("description", obj.Description) + setGMTagsInSchema(d, obj.Tags) + d.Set("nsx_id", id) + d.Set("path", obj.Path) + d.Set("revision", obj.Revision) + + d.Set("fail_if_rtep_misconfigured", obj.FailIfRtepMisconfigured) + d.Set("fail_if_rtt_exceeded", obj.FailIfRttExceeded) + d.Set("maximum_rtt", obj.MaximumRtt) + + setConnectionInfosInSchema(d, obj.SiteConnectionInfo, "site_connection_info") + d.Set("site_type", obj.SiteType) + + return nil +} + +func resourceNsxtPolicySiteUpdate(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining Site ID") + } + + connector := getPolicyConnector(m) + client := global_infra.NewSitesClient(connector) + + obj := getSiteFromSchema(d) + revision := int64(d.Get("revision").(int)) + obj.Revision = &revision + + _, err := client.Update(id, obj) + if err != nil { + return handleUpdateError("Site", id, err) + } + + return nil +} + +func resourceNsxtPolicySiteDelete(d *schema.ResourceData, m interface{}) error { + id := d.Id() + if id == "" { + return fmt.Errorf("error obtaining Site ID") + } + connector := getPolicyConnector(m) + client := global_infra.NewSitesClient(connector) + err := client.Delete(id, nil) + if err != nil { + return handleDeleteError("Site", id, err) + } + + return nil +} diff --git a/nsxt/utils.go b/nsxt/utils.go index 714afef6b..18ea22b6e 100644 --- a/nsxt/utils.go +++ b/nsxt/utils.go @@ -16,6 +16,7 @@ import ( "github.com/vmware/go-vmware-nsxt/common" "github.com/vmware/go-vmware-nsxt/manager" "github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client" + gm_model "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-gm/model" mp_model "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/node" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/search" @@ -780,3 +781,41 @@ func getMPTagsFromSchema(d *schema.ResourceData) []mp_model.Tag { func setMPTagsInSchema(d *schema.ResourceData, tags []mp_model.Tag) { setCustomizedMPTagsInSchema(d, tags, "tag") } + +func getCustomizedGMTagsFromSchema(d *schema.ResourceData, schemaName string) []gm_model.Tag { + tags := d.Get(schemaName).(*schema.Set).List() + tagList := make([]gm_model.Tag, 0) + for _, tag := range tags { + data := tag.(map[string]interface{}) + scope := data["scope"].(string) + tag := data["tag"].(string) + elem := gm_model.Tag{ + Scope: &scope, + Tag: &tag} + + tagList = append(tagList, elem) + } + return tagList +} + +func setCustomizedGMTagsInSchema(d *schema.ResourceData, tags []gm_model.Tag, schemaName string) { + var tagList []map[string]string + for _, tag := range tags { + elem := make(map[string]string) + elem["scope"] = *tag.Scope + elem["tag"] = *tag.Tag + tagList = append(tagList, elem) + } + err := d.Set(schemaName, tagList) + if err != nil { + log.Printf("[WARNING] Failed to set tag in schema: %v", err) + } +} + +func getGMTagsFromSchema(d *schema.ResourceData) []gm_model.Tag { + return getCustomizedGMTagsFromSchema(d, "tag") +} + +func setGMTagsInSchema(d *schema.ResourceData, tags []gm_model.Tag) { + setCustomizedGMTagsInSchema(d, tags, "tag") +} diff --git a/website/docs/r/policy_site.html.markdown b/website/docs/r/policy_site.html.markdown new file mode 100644 index 000000000..6d49d8390 --- /dev/null +++ b/website/docs/r/policy_site.html.markdown @@ -0,0 +1,65 @@ +--- +subcategory: "Beta" +layout: "nsxt" +page_title: "NSXT: nsxt_policy_site" +description: A resource to configure Policy Site. +--- + +# nsxt_policy_site + +This resource provides a method for the management of Policy Site. + +This resource is applicable to NSX Global Manager. + +## Example Usage + +```hcl +resource "nsxt_policy_site" "test" { + display_name = "test" + description = "Terraform provisioned Site" + site_connection_info { + fqdn = "192.168.230.230" + username = "admin" + password = "somepasswd" + thumbprint = "207d65dcb6f17aa5a1ef2365ee6ae0b396867baa92464e5f8a46f6853708b9ef" + } + site_type = "ONPREM_LM" +} +``` +## Argument Reference + +The following arguments are supported: + +* `display_name` - (Required) Display name of the resource. +* `description` - (Optional) Description of the resource. +* `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 resource. +* `fail_if_rtep_misconfigured` - (Optional) Fail onboarding if RTEPs misconfigured. Default is true. +* `fail_if_rtt_exceeded` - (Optional) Fail onboarding if maximum RTT exceeded. Default is true. +* `maximum_rtt` - (Optional) Maximum acceptable packet round trip time (RTT). Default is 250. +* `site_connection_info` - (Optional) Connection information. + * `fqdn` - (Optional) Fully Qualified Domain Name of the Management Node. + * `password` - (Optional) Password. + * `site_uuid` - (Optional) ID of Site. + * `thumbprint` - (Optional) Thumbprint of Enforcement Point. + * `username` - (Optional) Username. +* `site_type` - (Required) Persistent Site Type. Allowed values are `ONPREM_LM`, `SDDC_LM`. + +## 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 object can be [imported][docs-import] into this resource, via the following command: + +[docs-import]: https://www.terraform.io/cli/import + +``` +terraform import nsxt_policy_site.test POLICY_PATH +``` +The above command imports Site named `test` with policy path `POLICY_PATH`.