diff --git a/nsxt/data_source_nsxt_policy_ip_block_test.go b/nsxt/data_source_nsxt_policy_ip_block_test.go index 782bf5e0f..46c4e8a6b 100644 --- a/nsxt/data_source_nsxt_policy_ip_block_test.go +++ b/nsxt/data_source_nsxt_policy_ip_block_test.go @@ -42,7 +42,7 @@ func testAccDataSourceNsxtPolicyIPBlockBasic(t *testing.T, withContext bool, pre Steps: []resource.TestStep{ { PreConfig: func() { - if err := testAccDataSourceNsxtPolicyIPBlockCreate(name); err != nil { + if err := testAccDataSourceNsxtPolicyIPBlockCreate(name, newUUID(), "4001::/64", false); err != nil { t.Error(err) } }, @@ -57,7 +57,7 @@ func testAccDataSourceNsxtPolicyIPBlockBasic(t *testing.T, withContext bool, pre }) } -func testAccDataSourceNsxtPolicyIPBlockCreate(name string) error { +func testAccDataSourceNsxtPolicyIPBlockCreate(name, id, cidr string, isPrivate bool) error { connector, err := testAccGetPolicyConnector() if err != nil { return fmt.Errorf("Error during test client initialization: %v", err) @@ -66,15 +66,15 @@ func testAccDataSourceNsxtPolicyIPBlockCreate(name string) error { displayName := name description := name - cidr := "4001::/64" obj := model.IpAddressBlock{ Description: &description, DisplayName: &displayName, Cidr: &cidr, } - - // Generate a random ID for the resource - id := newUUID() + if isPrivate { + visibility := model.IpAddressBlock_VISIBILITY_PRIVATE + obj.Visibility = &visibility + } err = client.Patch(id, obj) if err != nil { diff --git a/nsxt/data_source_nsxt_policy_project.go b/nsxt/data_source_nsxt_policy_project.go index 90c1e3ead..2e2a08d36 100644 --- a/nsxt/data_source_nsxt_policy_project.go +++ b/nsxt/data_source_nsxt_policy_project.go @@ -27,25 +27,7 @@ func dataSourceNsxtPolicyProject() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "site_info": { - Type: schema.TypeList, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "edge_cluster_paths": { - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Optional: true, - }, - "site_path": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - Optional: true, - }, + "site_info": getSiteInfoSchema(), "tier0_gateway_paths": { Type: schema.TypeList, Elem: &schema.Schema{ @@ -57,6 +39,27 @@ func dataSourceNsxtPolicyProject() *schema.Resource { } } +func getSiteInfoSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "edge_cluster_paths": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + }, + "site_path": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Optional: true, + } +} func dataSourceNsxtPolicyProjectRead(d *schema.ResourceData, m interface{}) error { connector := getPolicyConnector(m) client := infra.NewProjectsClient(connector) diff --git a/nsxt/data_source_nsxt_policy_vpc.go b/nsxt/data_source_nsxt_policy_vpc.go new file mode 100644 index 000000000..b575cda24 --- /dev/null +++ b/nsxt/data_source_nsxt_policy_vpc.go @@ -0,0 +1,102 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects" +) + +func dataSourceNsxtPolicyVPC() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNsxtPolicyVPCRead, + Schema: map[string]*schema.Schema{ + "id": getDataSourceIDSchema(), + "display_name": getDataSourceDisplayNameSchema(), + "description": getDataSourceDescriptionSchema(), + "path": getPathSchema(), + "context": getContextSchema(), + "site_info": getSiteInfoSchema(), + "default_gateway_path": getPathSchema(), + "short_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func dataSourceNsxtPolicyVPCRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + client := projects.NewVpcsClient(connector) + + objID := d.Get("id").(string) + objName := d.Get("display_name").(string) + ctx := getSessionContext(d, m) + + var obj model.Vpc + + if objID != "" { + // Get by id + objGet, err := client.Get(defaultOrgID, ctx.ProjectID, objID) + if err != nil { + return handleDataSourceReadError(d, "VPC", objID, err) + } + obj = objGet + } else if objName == "" { + return fmt.Errorf("error obtaining VPC ID or name during read") + } else { + // Get by full name/prefix + objList, err := client.List(defaultOrgID, ctx.ProjectID, nil, nil, nil, nil, nil, nil) + if err != nil { + return handleListError("VPC", err) + } + // go over the list to find the correct one (prefer a perfect match. If not - prefix match) + var perfectMatch []model.Vpc + var prefixMatch []model.Vpc + for _, objInList := range objList.Results { + if strings.HasPrefix(*objInList.DisplayName, objName) { + prefixMatch = append(prefixMatch, objInList) + } + if *objInList.DisplayName == objName { + perfectMatch = append(perfectMatch, objInList) + } + } + if len(perfectMatch) > 0 { + if len(perfectMatch) > 1 { + return fmt.Errorf("found multiple VPCs with name '%s'", objName) + } + obj = perfectMatch[0] + } else if len(prefixMatch) > 0 { + if len(prefixMatch) > 1 { + return fmt.Errorf("found multiple VPCs with name starting with '%s'", objName) + } + obj = prefixMatch[0] + } else { + return fmt.Errorf("VPC with name '%s' was not found", objName) + } + } + + d.SetId(*obj.Id) + d.Set("display_name", obj.DisplayName) + d.Set("description", obj.Description) + d.Set("path", obj.Path) + d.Set("default_gateway_path", obj.DefaultGatewayPath) + d.Set("short_id", obj.ShortId) + + var siteInfosList []map[string]interface{} + for _, item := range obj.SiteInfos { + data := make(map[string]interface{}) + data["edge_cluster_paths"] = item.EdgeClusterPaths + data["site_path"] = item.SitePath + siteInfosList = append(siteInfosList, data) + } + d.Set("site_info", siteInfosList) + + return nil +} diff --git a/nsxt/data_source_policy_vpc_test.go b/nsxt/data_source_policy_vpc_test.go new file mode 100644 index 000000000..f96390952 --- /dev/null +++ b/nsxt/data_source_policy_vpc_test.go @@ -0,0 +1,128 @@ +/* Copyright © 2024 Broadcom, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/orgs/projects" +) + +func TestAccDataSourceNsxtPolicyVPC_basic(t *testing.T) { + name := getAccTestDataSourceName() + testResourceName := "data.nsxt_policy_vpc.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccOnlyMultitenancy(t) + testAccPreCheck(t) + testAccNSXVersion(t, "4.1.2") + }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccDataSourceNsxtPolicyVPCDeleteByName(name) + }, + Steps: []resource.TestStep{ + { + PreConfig: func() { + if err := testAccDataSourceNsxtPolicyVPCCreate(name); err != nil { + t.Error(err) + } + }, + Config: testAccNsxtPolicyVPCReadTemplate(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "description", name), + resource.TestCheckResourceAttrSet(testResourceName, "path"), + //resource.TestCheckResourceAttrSet(testResourceName, "default_gateway_path"), + resource.TestCheckResourceAttrSet(testResourceName, "short_id"), + ), + }, + }, + }) +} + +func testAccDataSourceNsxtPolicyVPCCreate(name string) error { + + connector, err := testAccGetPolicyConnector() + if err != nil { + return fmt.Errorf("error during test client initialization: %v", err) + } + + ipBlockID := newUUID() + err = testAccDataSourceNsxtPolicyIPBlockCreate(name, ipBlockID, "192.168.240.0/24", true) + if err != nil { + return err + } + + client := projects.NewVpcsClient(connector) + projID := os.Getenv("NSXT_PROJECT_ID") + + displayName := name + description := name + addrType := model.Vpc_IP_ADDRESS_TYPE_IPV4 + enableDhcp := false + disableGateway := true + obj := model.Vpc{ + Description: &description, + DisplayName: &displayName, + IpAddressType: &addrType, + DhcpConfig: &model.DhcpConfig{EnableDhcp: &enableDhcp}, + ServiceGateway: &model.ServiceGateway{Disable: &disableGateway}, + PrivateIpv4Blocks: []string{fmt.Sprintf("/orgs/default/projects/%s/infra/ip-blocks/%s", projID, ipBlockID)}, + } + + // Generate a random ID for the resource + id := newUUID() + + err = client.Patch(defaultOrgID, projID, id, obj) + if err != nil { + return handleCreateError("VPC", id, err) + } + return nil +} + +func testAccDataSourceNsxtPolicyVPCDeleteByName(name string) error { + connector, err := testAccGetPolicyConnector() + if err != nil { + return fmt.Errorf("error during test client initialization: %v", err) + } + + client := projects.NewVpcsClient(connector) + projID := os.Getenv("NSXT_PROJECT_ID") + + // Find the object by name + objList, err := client.List(defaultOrgID, projID, nil, nil, nil, nil, nil, nil) + if err != nil { + return handleListError("VPC", err) + } + for _, objInList := range objList.Results { + if *objInList.DisplayName == name { + err := client.Delete(defaultOrgID, projID, *objInList.Id) + if err != nil { + return handleDeleteError("VPC", *objInList.Id, err) + } + return testAccDataSourceNsxtPolicyIPBlockDeleteByName(name) + } + } + return fmt.Errorf("error while deleting VPC '%s': resource not found", name) +} + +func testAccNsxtPolicyVPCReadTemplate(name string) string { + context := testAccNsxtPolicyMultitenancyContext() + return fmt.Sprintf(` +data "nsxt_policy_ip_block" "test" { +%s + display_name = "%s" +} +data "nsxt_policy_vpc" "test" { +%s + display_name = "%s" +}`, context, name, context, name) +} diff --git a/nsxt/provider.go b/nsxt/provider.go index 8beb5962a..0eef507a4 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -325,6 +325,7 @@ func Provider() *schema.Provider { "nsxt_policy_distributed_flood_protection_profile": dataSourceNsxtPolicyDistributedFloodProtectionProfile(), "nsxt_policy_gateway_flood_protection_profile": dataSourceNsxtPolicyGatewayFloodProtectionProfile(), "nsxt_manager_info": dataSourceNsxtManagerInfo(), + "nsxt_policy_vpc": dataSourceNsxtPolicyVPC(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/website/docs/d/policy_vpc.html.markdown b/website/docs/d/policy_vpc.html.markdown new file mode 100644 index 000000000..9c265e456 --- /dev/null +++ b/website/docs/d/policy_vpc.html.markdown @@ -0,0 +1,45 @@ +--- +subcategory: "Multitenancy" +layout: "nsxt" +page_title: "NSXT: policy_vpc" +description: Policy VPC data source. +--- + +# nsxt_policy_vpc + +This data source provides information about policy VPC configured on NSX. +This data source is applicable to NSX Policy Manager. + +## Example Usage + +```hcl +data "nsxt_policy_project" "demoproj" { + display_name = "demoproj" +} + +data "nsxt_policy_vpc" "test" { + context { + project_id = data.nsxt_policy_project.demoproj.id + } + display_name = "vpc1" +} +``` + +## Argument Reference + +* `id` - (Optional) The ID of VPC to retrieve. If ID is specified, no additional argument should be configured. +* `display_name` - (Optional) The Display Name prefix of the VPC to retrieve. +* `context` - (Optional) The context which the object belongs to + * `project_id` - (Required) The ID of the project which the object belongs to + +## Attributes Reference + +In addition to arguments listed above, the following attributes are exported: + +* `description` - The description of the resource. +* `path` - The NSX path of the policy resource. +* `short_id` - Defaults to id if id is less than equal to 8 characters or defaults to random generated id if not set. +* `site_info` - Information related to sites applicable for given Project. + * `edge_cluster_paths` - The edge cluster on which the networking elements for the Org will be created. + * `site_path` - This represents the path of the site which is managed by Global Manager. For the local manager, if set, this needs to point to 'default'. +* `default_gateway_path` - The tier 0 has to be pre-created before Project is created.