From a1182cd9217ad083214da88f8c35ab58bc51f5bb Mon Sep 17 00:00:00 2001 From: Anna Khmelnitsky Date: Wed, 31 Aug 2022 19:33:34 +0000 Subject: [PATCH] Support bridge confguration on segment Add policy_bridge_profile data source and bridge_config clause on segments. Signed-off-by: Anna Khmelnitsky --- .../data_source_nsxt_policy_bridge_profile.go | 32 +++++++ ..._source_nsxt_policy_bridge_profile_test.go | 95 ++++++++++++++++++ nsxt/provider.go | 1 + nsxt/resource_nsxt_policy_segment_test.go | 96 +++++++++++++++++++ nsxt/segment_common.go | 82 ++++++++++++++++ nsxt/utils_test.go | 1 + .../d/policy_bridge_profile.html.markdown | 33 +++++++ website/docs/r/policy_segment.html.markdown | 5 + .../docs/r/policy_vlan_segment.html.markdown | 5 + 9 files changed, 350 insertions(+) create mode 100644 nsxt/data_source_nsxt_policy_bridge_profile.go create mode 100644 nsxt/data_source_nsxt_policy_bridge_profile_test.go create mode 100644 website/docs/d/policy_bridge_profile.html.markdown diff --git a/nsxt/data_source_nsxt_policy_bridge_profile.go b/nsxt/data_source_nsxt_policy_bridge_profile.go new file mode 100644 index 000000000..c88a166c1 --- /dev/null +++ b/nsxt/data_source_nsxt_policy_bridge_profile.go @@ -0,0 +1,32 @@ +/* Copyright © 2022 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceNsxtPolicyBridgeProfile() *schema.Resource { + return &schema.Resource{ + Read: dataSourceNsxtPolicyBridgeProfileRead, + + Schema: map[string]*schema.Schema{ + "id": getDataSourceIDSchema(), + "display_name": getDataSourceExtendedDisplayNameSchema(), + "description": getDataSourceDescriptionSchema(), + "path": getPathSchema(), + }, + } +} + +func dataSourceNsxtPolicyBridgeProfileRead(d *schema.ResourceData, m interface{}) error { + connector := getPolicyConnector(m) + + _, err := policyDataSourceResourceRead(d, connector, isPolicyGlobalManager(m), "L2BridgeEndpointProfile", nil) + if err != nil { + return err + } + + return nil +} diff --git a/nsxt/data_source_nsxt_policy_bridge_profile_test.go b/nsxt/data_source_nsxt_policy_bridge_profile_test.go new file mode 100644 index 000000000..db26f4233 --- /dev/null +++ b/nsxt/data_source_nsxt_policy_bridge_profile_test.go @@ -0,0 +1,95 @@ +/* Copyright © 2022 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: MPL-2.0 */ + +package nsxt + +import ( + "fmt" + "testing" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + ep "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" +) + +func TestAccDataSourceNsxtPolicyBridgeProfile_basic(t *testing.T) { + name := getAccTestDataSourceName() + testResourceName := "data.nsxt_policy_bridge_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccNSXVersion(t, "3.0.0") }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + return testAccDataSourceNsxtPolicyBridgeProfileDeleteByName(name) + }, + Steps: []resource.TestStep{ + { + PreConfig: func() { + if err := testAccDataSourceNsxtPolicyBridgeProfileCreate(name); err != nil { + panic(err) + } + }, + Config: testAccNsxtPolicyBridgeProfileReadTemplate(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "description", name), + resource.TestCheckResourceAttrSet(testResourceName, "path"), + ), + }, + }, + }) +} + +func testAccDataSourceNsxtPolicyBridgeProfileCreate(name string) error { + connector, err := testAccGetPolicyConnector() + if err != nil { + return fmt.Errorf("Error during test client initialization: %v", err) + } + + displayName := name + description := name + obj := model.L2BridgeEndpointProfile{ + Description: &description, + DisplayName: &displayName, + } + + // Generate a random ID for the resource + uuid, _ := uuid.NewRandom() + id := uuid.String() + + client := ep.NewEdgeBridgeProfilesClient(connector) + err = client.Patch(defaultSite, defaultEnforcementPoint, id, obj) + + if err != nil { + return fmt.Errorf("Error during Bridge Profile creation: %v", err) + } + return nil +} + +func testAccDataSourceNsxtPolicyBridgeProfileDeleteByName(name string) error { + connector, err := testAccGetPolicyConnector() + if err != nil { + return fmt.Errorf("Error during test client initialization: %v", err) + } + + // Find the object by name + objID, err := testGetObjIDByName(name, "L2BridgeEndpointProfile") + if err != nil { + return nil + } + client := ep.NewEdgeBridgeProfilesClient(connector) + err = client.Delete(defaultSite, defaultEnforcementPoint, objID) + if err != nil { + return fmt.Errorf("Error during Bridge Profile deletion: %v", err) + } + return nil +} + +func testAccNsxtPolicyBridgeProfileReadTemplate(name string) string { + return fmt.Sprintf(` +data "nsxt_policy_bridge_profile" "test" { + display_name = "%s" +}`, name) +} diff --git a/nsxt/provider.go b/nsxt/provider.go index 4573874b2..8061be2b6 100644 --- a/nsxt/provider.go +++ b/nsxt/provider.go @@ -254,6 +254,7 @@ func Provider() *schema.Provider { "nsxt_policy_bfd_profile": dataSourceNsxtPolicyBfdProfile(), "nsxt_policy_intrusion_service_profile": dataSourceNsxtPolicyIntrusionServiceProfile(), "nsxt_policy_lb_service": dataSourceNsxtPolicyLbService(), + "nsxt_policy_bridge_profile": dataSourceNsxtPolicyBridgeProfile(), }, ResourcesMap: map[string]*schema.Resource{ diff --git a/nsxt/resource_nsxt_policy_segment_test.go b/nsxt/resource_nsxt_policy_segment_test.go index 9688c94fd..81e5cdcfd 100644 --- a/nsxt/resource_nsxt_policy_segment_test.go +++ b/nsxt/resource_nsxt_policy_segment_test.go @@ -290,6 +290,69 @@ func TestAccResourceNsxtPolicySegment_withProfiles(t *testing.T) { }) } +var testAccSegmentBridgeProfileName = getAccTestResourceName() + +func TestAccResourceNsxtPolicySegment_withBridge(t *testing.T) { + name := getAccTestResourceName() + testResourceName := "nsxt_policy_segment.test" + tzName := getOverlayTransportZoneName() + vlanTzName := getVlanTransportZoneName() + vlan := "12" + vlanUpdated := "20" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + err := testAccNsxtPolicySegmentCheckDestroy(state, name) + if err != nil { + return err + } + + return testAccDataSourceNsxtPolicyBridgeProfileDeleteByName(testAccSegmentBridgeProfileName) + }, + Steps: []resource.TestStep{ + { + PreConfig: func() { + if err := testAccDataSourceNsxtPolicyBridgeProfileCreate(testAccSegmentBridgeProfileName); err != nil { + panic(err) + } + }, + Config: testAccNsxtPolicySegmentWithBridgeTemplate(tzName, vlanTzName, name, vlan), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicySegmentExists(testResourceName), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.#", "1"), + resource.TestCheckResourceAttrSet(testResourceName, "bridge_config.0.profile_path"), + resource.TestCheckResourceAttrSet(testResourceName, "bridge_config.0.transport_zone_path"), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.0.vlan_ids.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.0.vlan_ids.0", vlan), + ), + }, + { + Config: testAccNsxtPolicySegmentWithBridgeTemplate(tzName, vlanTzName, name, vlanUpdated), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicySegmentExists(testResourceName), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.#", "1"), + resource.TestCheckResourceAttrSet(testResourceName, "bridge_config.0.profile_path"), + resource.TestCheckResourceAttrSet(testResourceName, "bridge_config.0.transport_zone_path"), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.0.vlan_ids.#", "1"), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.0.vlan_ids.0", vlanUpdated), + ), + }, + { + Config: testAccNsxtPolicySegmentWithBridgeRemoveAll(tzName, name), + Check: resource.ComposeTestCheckFunc( + testAccNsxtPolicySegmentExists(testResourceName), + resource.TestCheckResourceAttr(testResourceName, "display_name", name), + resource.TestCheckResourceAttr(testResourceName, "bridge_config.#", "0"), + ), + }, + }, + }) +} + func TestAccResourceNsxtPolicySegment_withDhcp(t *testing.T) { name := getAccTestResourceName() testResourceName := "nsxt_policy_segment.test" @@ -569,6 +632,39 @@ resource "nsxt_policy_segment" "test" { `, name) } +func testAccNsxtPolicySegmentWithBridgeTemplate(tzName string, bridgeTzName string, name string, vlan string) string { + return testAccNsxtPolicySegmentDeps(tzName) + fmt.Sprintf(` +data "nsxt_policy_bridge_profile" "test" { + display_name = "%s" +} + +data "nsxt_policy_transport_zone" "bridge" { + display_name = "%s" +} + +resource "nsxt_policy_segment" "test" { + display_name = "%s" + transport_zone_path = data.nsxt_policy_transport_zone.test.path + + bridge_config { + profile_path = data.nsxt_policy_bridge_profile.test.path + transport_zone_path = data.nsxt_policy_transport_zone.bridge.path + vlan_ids = ["%s"] + } + +} +`, testAccSegmentBridgeProfileName, bridgeTzName, name, vlan) +} + +func testAccNsxtPolicySegmentWithBridgeRemoveAll(tzName string, name string) string { + return testAccNsxtPolicySegmentDeps(tzName) + fmt.Sprintf(` +resource "nsxt_policy_segment" "test" { + display_name = "%s" + transport_zone_path = data.nsxt_policy_transport_zone.test.path +} +`, name) +} + func testAccNsxtPolicySegmentBasicAdvConfigTemplate(tzName string, name string) string { return testAccNsxtPolicySegmentDeps(tzName) + fmt.Sprintf(` diff --git a/nsxt/segment_common.go b/nsxt/segment_common.go index 2790be759..c1d4cdf08 100644 --- a/nsxt/segment_common.go +++ b/nsxt/segment_common.go @@ -138,6 +138,28 @@ func getPolicySegmentSubnetSchema() *schema.Resource { } } +func getPolicySegmentBridgeConfigSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "profile_path": getPolicyPathSchema(true, false, "profile path"), + "uplink_teaming_policy": { + Type: schema.TypeString, + Optional: true, + }, + "vlan_ids": { + Type: schema.TypeList, + Description: "VLAN specification for bridge endpoint", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateVLANIdOrRange, + }, + Required: true, + }, + "transport_zone_path": getPolicyPathSchema(true, false, "vlan transport zone path"), + }, + } +} + func getPolicySegmentL2ExtensionConfigurationSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -328,6 +350,12 @@ func getPolicyCommonSegmentSchema(vlanRequired bool, isFixed bool) map[string]*s Default: model.Segment_REPLICATION_MODE_MTEP, ValidateFunc: validation.StringInSlice(replicationModeValues, false), }, + "bridge_config": { + Type: schema.TypeList, + Description: "Beidge configuration", + Elem: getPolicySegmentBridgeConfigSchema(), + Optional: true, + }, } if isFixed { @@ -808,6 +836,8 @@ func policySegmentResourceToInfraStruct(id string, d *schema.ResourceData, isVla } } + setBridgeConfigInStruct(d, &obj) + childSegment := model.ChildSegment{ Segment: &obj, ResourceType: "ChildSegment", @@ -863,6 +893,40 @@ func resourceNsxtPolicySegmentExists(gwPath string, isFixed bool) func(id string } } +func setBridgeConfigInStruct(d *schema.ResourceData, segment *model.Segment) { + + configs := d.Get("bridge_config").([]interface{}) + if len(configs) == 0 { + return + } + + for _, config := range configs { + bridgeConfig := config.(map[string]interface{}) + profilePath := bridgeConfig["profile_path"].(string) + policyName := bridgeConfig["uplink_teaming_policy"].(string) + tzPath := bridgeConfig["transport_zone_path"].(string) + vlanIds := interfaceListToStringList(bridgeConfig["vlan_ids"].([]interface{})) + + profile := model.BridgeProfileConfig{ + BridgeProfilePath: &profilePath, + } + + if len(policyName) > 0 { + profile.UplinkTeamingPolicyName = &policyName + } + if len(tzPath) > 0 { + profile.VlanTransportZonePath = &tzPath + } + + if len(vlanIds) > 0 { + profile.VlanIds = vlanIds + } + + segment.BridgeProfiles = append(segment.BridgeProfiles, profile) + + } +} + func nsxtPolicySegmentProfilesSetInStruct(d *schema.ResourceData, segment *model.Segment) error { var children []*data.StructValue @@ -1230,6 +1294,22 @@ func nsxtPolicySegmentProfilesRead(d *schema.ResourceData, m interface{}) error return nil } +func setSegmentBridgeConfigInSchema(d *schema.ResourceData, obj *model.Segment) { + var configs []map[string]interface{} + + for _, profile := range obj.BridgeProfiles { + config := make(map[string]interface{}) + config["profile_path"] = profile.BridgeProfilePath + config["uplink_teaming_policy"] = profile.UplinkTeamingPolicyName + config["vlan_ids"] = profile.VlanIds + config["transport_zone_path"] = profile.VlanTransportZonePath + + configs = append(configs, config) + } + + d.Set("bridge_config", configs) +} + func nsxtPolicyLocalManagerGetSegment(connector *client.RestConnector, id string, gwPath string, isFixed bool) (model.Segment, error) { if !isFixed { return infra.NewSegmentsClient(connector).Get(id) @@ -1386,6 +1466,8 @@ func nsxtPolicySegmentRead(d *schema.ResourceData, m interface{}, isVlan bool, i } } + setSegmentBridgeConfigInSchema(d, &obj) + return nil } diff --git a/nsxt/utils_test.go b/nsxt/utils_test.go index aa878ce44..ebec61fa1 100644 --- a/nsxt/utils_test.go +++ b/nsxt/utils_test.go @@ -27,6 +27,7 @@ const edgeClusterDefaultName string = "edgecluster1" const vlanTransportZoneName string = "transportzone2" const overlayTransportZoneNamePrefix string = "1-transportzone" const macPoolDefaultName string = "DefaultMacPool" +const defaultEnforcementPoint string = "default" const realizationResourceName string = "data.nsxt_policy_realization_info.realization_info" const defaultTestResourceName string = "terraform-acctest" diff --git a/website/docs/d/policy_bridge_profile.html.markdown b/website/docs/d/policy_bridge_profile.html.markdown new file mode 100644 index 000000000..639b2d7fd --- /dev/null +++ b/website/docs/d/policy_bridge_profile.html.markdown @@ -0,0 +1,33 @@ +--- +subcategory: "Policy - Gateways and Routing" +layout: "nsxt" +page_title: "NSXT: policy_bridge_profile" +description: Policy Bridge Profile data source. +--- + +# nsxt_policy_bridge_profile + +This data source provides information about Edge Bridge Profile configured on NSX. +This data source is applicable to NSX Policy Manager. + +## Example Usage + +```hcl +data "nsxt_policy_bridge_profile" "test" { + display_name = "profile1" +} +``` + +## Argument Reference + +* `id` - (Optional) The ID of Profile to retrieve. If ID is specified, no additional argument should be configured. + +* `display_name` - (Optional) The Display Name prefix of the Profile to retrieve. + +## 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. diff --git a/website/docs/r/policy_segment.html.markdown b/website/docs/r/policy_segment.html.markdown index 3a98b6aba..e1ef1ea8e 100644 --- a/website/docs/r/policy_segment.html.markdown +++ b/website/docs/r/policy_segment.html.markdown @@ -105,6 +105,11 @@ The following arguments are supported: * `security_profile_path` - (Optional) Path for segment security profile to be associated with the segment. * `qos_profile` - (Optional) QoS profile specification for the segment. * `qos_profile_path` - (Optional) Path for qos profile to be associated with the segment. +* `bridge_config` - (Optional) List of edge bridge configuration for the segment. + * `profile_path` - (Required) Path for edge bridge profile to be associated with the segment. + * `transport_zone_path` - (Required) Path for vlan transport zone for the bridge. + * `vlan_ids` - (Required) List of VLAN IDs or ranges. + * `uplink_teaming_policy` - (Optional) The name of the switching uplink teaming policy for the bridge endpoint. ## Attributes Reference diff --git a/website/docs/r/policy_vlan_segment.html.markdown b/website/docs/r/policy_vlan_segment.html.markdown index e4230d9fe..58f88ab37 100644 --- a/website/docs/r/policy_vlan_segment.html.markdown +++ b/website/docs/r/policy_vlan_segment.html.markdown @@ -108,6 +108,11 @@ The following arguments are supported: * `security_profile_path` - (Optional) Path for segment security profile to be associated with the segment. * `qos_profile` - (Optional) QoS profile specification for the segment. * `qos_profile_path` - (Optional) Path for qos profile to be associated with the segment. +* `bridge_config` - (Optional) List of edge bridge configuration for the segment. + * `profile_path` - (Required) Path for edge bridge profile to be associated with the segment. + * `transport_zone_path` - (Required) Path for vlan transport zone for the bridge. + * `vlan_ids` - (Required) List of VLAN IDs or ranges. + * `uplink_teaming_policy` - (Optional) The name of the switching uplink teaming policy for the bridge endpoint. ## Attributes Reference