-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement policy site resource #1104
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No default here? What happens if this is unspecified? Should the value be Computed in this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spec doesn't specify a default here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this depends on env, and perhaps default depends on env as well. If NSX auto-assigns this value, we can define it as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turns out this this is required...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe no need to set a default for now. |
||
}, | ||
}, | ||
} | ||
} | ||
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add here: |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not clear to me the behaviour that NSX will have if the update site_connection_info. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't try to set it (I need an extra LM to test this). But connectivity info is updatable in UI. |
||
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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The NSX API guide does not mention it, but I think we should specify the unit of measure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IDK which are the units as I couldn't find that attribute in the UI |
||
* `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`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think
site_number
might be useful as a computed attribute?Looking at the API spec, do you think we should include FederationConfig, remote_path, relative_path as computed too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't found use for those yet, usually we add read-only attrs only if they're used as reference anywhere else or just seem very useful. As I haven't complete yet the federation object implementation, I might find their use later on maybe. Anyway it's always easier to add attributes than removing them.