Skip to content

Commit

Permalink
Support vpc nat resource and its friends
Browse files Browse the repository at this point in the history
This PR offers support for:
1. vpc nat rule resource
2. vpc ip address allocation resource
   only addresses allocated from vpc subnet can be used
   as dest in nat rule
3. vpc nat data source
   this is helpful for specifying parent_path for nat rule

Signed-off-by: Anna Khmelnitsky <[email protected]>
  • Loading branch information
annakhm committed Jun 28, 2024
1 parent 9115874 commit a93eeaf
Show file tree
Hide file tree
Showing 7 changed files with 909 additions and 11 deletions.
53 changes: 53 additions & 0 deletions nsxt/data_source_nsxt_vpc_nat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
SPDX-License-Identifier: MPL-2.0 */

package nsxt

import (
"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/services/nsxt/model"
)

var vpcNatTypes = []string{
model.PolicyNat_NAT_TYPE_INTERNAL,
model.PolicyNat_NAT_TYPE_USER,
model.PolicyNat_NAT_TYPE_DEFAULT,
model.PolicyNat_NAT_TYPE_NAT64,
}

func dataSourceNsxtVpcNat() *schema.Resource {
return &schema.Resource{
Read: dataSourceNsxtVpcNatRead,

Schema: map[string]*schema.Schema{
"id": getDataSourceIDSchema(),
"nat_type": {
Type: schema.TypeString,
Description: "Nat Type",
Required: true,
ValidateFunc: validation.StringInSlice(vpcNatTypes, false),
},
"display_name": getDataSourceExtendedDisplayNameSchema(),
"description": getDataSourceDescriptionSchema(),
"path": getPathSchema(),
"context": getContextSchema(false, false, true),
},
}
}

func dataSourceNsxtVpcNatRead(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)

natType := d.Get("nat_type").(string)
query := make(map[string]string)
query["nat_type"] = natType

_, err := policyDataSourceResourceReadWithValidation(d, connector, getSessionContext(d, m), "PolicyNat", query, false)
if err != nil {
return err
}

return nil
}
26 changes: 15 additions & 11 deletions nsxt/policy_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ func listPolicyResourcesByNameAndType(connector client.Connector, context utl.Se
return searchLMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Global:
return searchGMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy:
return searchMultitenancyPolicyResources(connector, utl.DefaultOrgID, context.ProjectID, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy, utl.VPC:
return searchMultitenancyResources(connector, context, *buildPolicyResourcesQuery(&query, additionalQuery))
}

return nil, errors.New("invalid ClientType %d")
return nil, errors.New("invalid ClientType")
}

func listInventoryResourcesByNameAndType(connector client.Connector, context utl.SessionContext, displayName string, resourceType string, additionalQuery *string) ([]*data.StructValue, error) {
Expand Down Expand Up @@ -158,11 +158,11 @@ func listPolicyResourcesByID(connector client.Connector, context utl.SessionCont
return searchLMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Global:
return searchGMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy:
return searchMultitenancyPolicyResources(connector, utl.DefaultOrgID, context.ProjectID, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy, utl.VPC:
return searchMultitenancyResources(connector, context, *buildPolicyResourcesQuery(&query, additionalQuery))
}

return nil, errors.New("invalid ClientType %d")
return nil, errors.New("invalid ClientType")
}

func listPolicyResourcesByNsxID(connector client.Connector, context utl.SessionContext, resourceID *string, additionalQuery *string) ([]*data.StructValue, error) {
Expand All @@ -172,10 +172,10 @@ func listPolicyResourcesByNsxID(connector client.Connector, context utl.SessionC
return searchLMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Global:
return searchGMPolicyResources(connector, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy:
return searchMultitenancyPolicyResources(connector, utl.DefaultOrgID, context.ProjectID, *buildPolicyResourcesQuery(&query, additionalQuery))
case utl.Multitenancy, utl.VPC:
return searchMultitenancyResources(connector, context, *buildPolicyResourcesQuery(&query, additionalQuery))
}
return nil, errors.New("invalid ClientType %d")
return nil, errors.New("invalid ClientType")
}

func buildPolicyResourcesQuery(query *string, additionalQuery *string) *string {
Expand Down Expand Up @@ -241,7 +241,11 @@ func searchLMPolicyResources(connector client.Connector, query string) ([]*data.
return searchLM(connector, query)
}

func searchMultitenancyPolicyResources(connector client.Connector, org string, project string, query string) ([]*data.StructValue, error) {
query = query + fmt.Sprintf(" AND path:\\/orgs\\/%s\\/projects\\/%s*", org, project)
func searchMultitenancyResources(connector client.Connector, context utl.SessionContext, query string) ([]*data.StructValue, error) {
if len(context.VPCID) > 0 {
query = query + fmt.Sprintf(" AND path:\\/orgs\\/%s\\/projects\\/%s\\/vpcs\\/%s*", utl.DefaultOrgID, context.ProjectID, context.VPCID)
} else {
query = query + fmt.Sprintf(" AND path:\\/orgs\\/%s\\/projects\\/%s*", utl.DefaultOrgID, context.ProjectID)
}
return searchLM(connector, query)
}
14 changes: 14 additions & 0 deletions nsxt/policy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,20 @@ func nsxtPolicyPathResourceImporterHelper(d *schema.ResourceData, m interface{})
return []*schema.ResourceData{d}, ErrNotAPolicyPath
}

func nsxtParentPathResourceImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
importID := d.Id()
if isPolicyPath(importID) {
pathSegs := strings.Split(importID, "/")
segCount := len(pathSegs)
d.SetId(pathSegs[segCount-1])
// to get parent path size, remove last two tokens (id and separator) plus two slashes
truncateSize := len(pathSegs[segCount-1]) + len(pathSegs[segCount-2]) + 2
d.Set("parent_path", importID[:len(importID)-truncateSize])
return []*schema.ResourceData{d}, nil
}
return []*schema.ResourceData{d}, ErrNotAPolicyPath
}

func isPolicyPath(policyPath string) bool {
pathSegs := strings.Split(policyPath, "/")
if len(pathSegs) < 4 {
Expand Down
3 changes: 3 additions & 0 deletions nsxt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ func Provider() *schema.Provider {
"nsxt_policy_gateway_flood_protection_profile": dataSourceNsxtPolicyGatewayFloodProtectionProfile(),
"nsxt_manager_info": dataSourceNsxtManagerInfo(),
"nsxt_policy_vpc": dataSourceNsxtPolicyVPC(),
"nsxt_vpc_nat": dataSourceNsxtVpcNat(),
},

ResourcesMap: map[string]*schema.Resource{
Expand Down Expand Up @@ -499,6 +500,8 @@ func Provider() *schema.Provider {
"nsxt_vpc_security_policy": resourceNsxtVPCSecurityPolicy(),
"nsxt_vpc_group": resourceNsxtVPCGroup(),
"nsxt_vpc_gateway_policy": resourceNsxtVPCGatewayPolicy(),
"nsxt_vpc_nat_rule": resourceNsxtPolicyVpcNatRule(),
"nsxt_vpc_ip_address_allocation": resourceNsxtVpcIPAddressAllocation(),
},

ConfigureFunc: providerConfigure,
Expand Down
222 changes: 222 additions & 0 deletions nsxt/resource_nsxt_vpc_ip_address_allocation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
SPDX-License-Identifier: MPL-2.0 */

package nsxt

import (
"fmt"
"log"
"reflect"

"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/model"
clientLayer "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects/vpcs"

utl "github.com/vmware/terraform-provider-nsxt/api/utl"
"github.com/vmware/terraform-provider-nsxt/nsxt/metadata"
)

var vpcIPAddressAllocationIPAddressTypeValues = []string{
model.VpcIpAddressAllocation_IP_ADDRESS_TYPE_IPV4,
model.VpcIpAddressAllocation_IP_ADDRESS_TYPE_IPV6,
}

var vpcIPAddressAllocationIPAddressBlockVisibilityValues = []string{
model.VpcIpAddressAllocation_IP_ADDRESS_BLOCK_VISIBILITY_EXTERNAL,
model.VpcIpAddressAllocation_IP_ADDRESS_BLOCK_VISIBILITY_PRIVATE,
}

var vpcIPAddressAllocationSchema = map[string]*metadata.ExtendedSchema{
"nsx_id": metadata.GetExtendedSchema(getNsxIDSchema()),
"path": metadata.GetExtendedSchema(getPathSchema()),
"display_name": metadata.GetExtendedSchema(getDisplayNameSchema()),
"description": metadata.GetExtendedSchema(getDescriptionSchema()),
"revision": metadata.GetExtendedSchema(getRevisionSchema()),
"tag": metadata.GetExtendedSchema(getTagsSchema()),
"context": metadata.GetExtendedSchema(getContextSchema(false, false, true)),
"ip_address_type": {
Schema: schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice(vpcIPAddressAllocationIPAddressTypeValues, false),
Optional: true,
Default: model.VpcIpAddressAllocation_IP_ADDRESS_TYPE_IPV4,
},
Metadata: metadata.Metadata{
SchemaType: "string",
SdkFieldName: "IpAddressType",
},
},
"allocation_ip": {
Schema: schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
Metadata: metadata.Metadata{
SchemaType: "string",
SdkFieldName: "AllocationIp",
OmitIfEmpty: true,
},
},
"ip_address_block_visibility": {
Schema: schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice(vpcIPAddressAllocationIPAddressBlockVisibilityValues, false),
Optional: true,
Default: model.VpcIpAddressAllocation_IP_ADDRESS_BLOCK_VISIBILITY_EXTERNAL,
},
Metadata: metadata.Metadata{
SchemaType: "string",
SdkFieldName: "IpAddressBlockVisibility",
},
},
}

func resourceNsxtVpcIPAddressAllocation() *schema.Resource {
return &schema.Resource{
Create: resourceNsxtVpcIPAddressAllocationCreate,
Read: resourceNsxtVpcIPAddressAllocationRead,
Update: resourceNsxtVpcIPAddressAllocationUpdate,
Delete: resourceNsxtVpcIPAddressAllocationDelete,
Importer: &schema.ResourceImporter{
State: nsxtPolicyPathResourceImporter,
},
Schema: metadata.GetSchemaFromExtendedSchema(vpcIPAddressAllocationSchema),
}
}

func resourceNsxtVpcIPAddressAllocationExists(sessionContext utl.SessionContext, parentPath string, id string, connector client.Connector) (bool, error) {
var err error
parents := getVpcParentsFromContext(sessionContext)
client := clientLayer.NewIpAddressAllocationsClient(connector)
_, err = client.Get(parents[0], parents[1], parents[2], id)
if err == nil {
return true, nil
}

if isNotFoundError(err) {
return false, nil
}

return false, logAPIError("Error retrieving resource", err)
}

func resourceNsxtVpcIPAddressAllocationCreate(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)

id, err := getOrGenerateIDWithParent(d, m, resourceNsxtVpcIPAddressAllocationExists)
if err != nil {
return err
}

parents := getVpcParentsFromContext(getSessionContext(d, m))
displayName := d.Get("display_name").(string)
description := d.Get("description").(string)
tags := getPolicyTagsFromSchema(d)

obj := model.VpcIpAddressAllocation{
DisplayName: &displayName,
Description: &description,
Tags: tags,
}

elem := reflect.ValueOf(&obj).Elem()
if err := metadata.SchemaToStruct(elem, d, vpcIPAddressAllocationSchema, "", nil); err != nil {
return err
}

log.Printf("[INFO] Creating VpcIpAddressAllocation with ID %s", id)

client := clientLayer.NewIpAddressAllocationsClient(connector)
err = client.Patch(parents[0], parents[1], parents[2], id, obj)
if err != nil {
return handleCreateError("VpcIpAddressAllocation", id, err)
}
d.SetId(id)
d.Set("nsx_id", id)

return resourceNsxtVpcIPAddressAllocationRead(d, m)
}

func resourceNsxtVpcIPAddressAllocationRead(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)

id := d.Id()
if id == "" {
return fmt.Errorf("Error obtaining VpcIpAddressAllocation ID")
}

client := clientLayer.NewIpAddressAllocationsClient(connector)
parents := getVpcParentsFromContext(getSessionContext(d, m))
obj, err := client.Get(parents[0], parents[1], parents[2], id)
if err != nil {
return handleReadError(d, "VpcIpAddressAllocation", id, err)
}

setPolicyTagsInSchema(d, obj.Tags)
d.Set("nsx_id", id)
d.Set("display_name", obj.DisplayName)
d.Set("description", obj.Description)
d.Set("revision", obj.Revision)
d.Set("path", obj.Path)

elem := reflect.ValueOf(&obj).Elem()
return metadata.StructToSchema(elem, d, vpcIPAddressAllocationSchema, "", nil)
}

func resourceNsxtVpcIPAddressAllocationUpdate(d *schema.ResourceData, m interface{}) error {

connector := getPolicyConnector(m)

id := d.Id()
if id == "" {
return fmt.Errorf("Error obtaining VpcIpAddressAllocation ID")
}

parents := getVpcParentsFromContext(getSessionContext(d, m))
description := d.Get("description").(string)
displayName := d.Get("display_name").(string)
tags := getPolicyTagsFromSchema(d)

revision := int64(d.Get("revision").(int))

obj := model.VpcIpAddressAllocation{
DisplayName: &displayName,
Description: &description,
Tags: tags,
Revision: &revision,
}

elem := reflect.ValueOf(&obj).Elem()
if err := metadata.SchemaToStruct(elem, d, vpcIPAddressAllocationSchema, "", nil); err != nil {
return err
}
client := clientLayer.NewIpAddressAllocationsClient(connector)
_, err := client.Update(parents[0], parents[1], parents[2], id, obj)
if err != nil {
return handleUpdateError("VpcIpAddressAllocation", id, err)
}

return resourceNsxtVpcIPAddressAllocationRead(d, m)
}

func resourceNsxtVpcIPAddressAllocationDelete(d *schema.ResourceData, m interface{}) error {
id := d.Id()
if id == "" {
return fmt.Errorf("Error obtaining VpcIpAddressAllocation ID")
}

connector := getPolicyConnector(m)
parents := getVpcParentsFromContext(getSessionContext(d, m))

client := clientLayer.NewIpAddressAllocationsClient(connector)
err := client.Delete(parents[0], parents[1], parents[2], id)

if err != nil {
return handleDeleteError("VpcIpAddressAllocation", id, err)
}

return nil
}
Loading

0 comments on commit a93eeaf

Please sign in to comment.