Skip to content

Commit

Permalink
Merge pull request #988 from ksamoray/edge_ha_profile
Browse files Browse the repository at this point in the history
Implement edge high availability profile resource
  • Loading branch information
ksamoray authored Oct 5, 2023
2 parents 82bbd0b + fd709be commit d341d2e
Show file tree
Hide file tree
Showing 4 changed files with 411 additions and 0 deletions.
1 change: 1 addition & 0 deletions nsxt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ func Provider() *schema.Provider {
"nsxt_cluster_virtual_ip": resourceNsxtClusterVirualIP(),
"nsxt_policy_host_transport_node_profile": resourceNsxtPolicyHostTransportNodeProfile(),
"nsxt_policy_host_transport_node": resourceNsxtPolicyHostTransportNode(),
"nsxt_edge_high_availability_profile": resourceNsxtEdgeHighAvailabilityProfile(),
},

ConfigureFunc: providerConfigure,
Expand Down
195 changes: 195 additions & 0 deletions nsxt/resource_nsxt_edge_high_availability_profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/* Copyright © 2023 VMware, 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/bindings"
"github.com/vmware/vsphere-automation-sdk-go/runtime/data"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model"
)

func resourceNsxtEdgeHighAvailabilityProfile() *schema.Resource {
return &schema.Resource{
Create: resourceNsxtEdgeHighAvailabilityProfileCreate,
Read: resourceNsxtEdgeHighAvailabilityProfileRead,
Update: resourceNsxtEdgeHighAvailabilityProfileUpdate,
Delete: resourceNsxtEdgeHighAvailabilityProfileDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"revision": getRevisionSchema(),
"description": getDescriptionSchema(),
"display_name": getDisplayNameSchema(),
"tag": getTagsSchema(),
"bfd_allowed_hops": {
Type: schema.TypeInt,
Optional: true,
Default: 255,
Description: "BFD allowed hops",
ValidateFunc: validation.IntBetween(1, 255),
},
"bfd_declare_dead_multiple": {
Type: schema.TypeInt,
Optional: true,
Default: 3,
Description: "Number of times a packet is missed before BFD declares the neighbor down",
ValidateFunc: validation.IntBetween(2, 16),
},
"bfd_probe_interval": {
Type: schema.TypeInt,
Optional: true,
Default: 500,
Description: "the time interval (in millisecond) between probe packets for heartbeat purpose",
ValidateFunc: validation.IntBetween(50, 60000),
},
"standby_relocation_threshold": {
Type: schema.TypeInt,
Optional: true,
Default: 30,
Description: "Standby service context relocation wait time",
ValidateFunc: validation.IntBetween(10, 20000),
},
},
}
}

func resourceNsxtEdgeHighAvailabilityProfileCreate(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)
client := nsx.NewClusterProfilesClient(connector)
description := d.Get("description").(string)
displayName := d.Get("display_name").(string)
tags := getMPTagsFromSchema(d)
bfdAllowedHops := int64(d.Get("bfd_allowed_hops").(int))
bfdDeclareDeadMultiple := int64(d.Get("bfd_declare_dead_multiple").(int))
bfdProbeInterval := int64(d.Get("bfd_probe_interval").(int))
standbyRelocationThreshold := int64(d.Get("standby_relocation_threshold").(int))

obj := model.EdgeHighAvailabilityProfile{
Description: &description,
DisplayName: &displayName,
Tags: tags,
BfdAllowedHops: &bfdAllowedHops,
BfdDeclareDeadMultiple: &bfdDeclareDeadMultiple,
BfdProbeInterval: &bfdProbeInterval,
StandbyRelocationConfig: &model.StandbyRelocationConfig{
StandbyRelocationThreshold: &standbyRelocationThreshold,
},
ResourceType: model.EdgeHighAvailabilityProfile__TYPE_IDENTIFIER,
}
converter := bindings.NewTypeConverter()
dataValue, errs := converter.ConvertToVapi(obj, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}

structValue, err := client.Create(dataValue.(*data.StructValue))
if err != nil {
return handleCreateError("Edge High Availability Profile", displayName, err)
}
o, errs := converter.ConvertToGolang(structValue, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}
obj = o.(model.EdgeHighAvailabilityProfile)

d.SetId(*obj.Id)
return resourceNsxtEdgeHighAvailabilityProfileRead(d, m)
}

func resourceNsxtEdgeHighAvailabilityProfileRead(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)
id := d.Id()
if id == "" {
return fmt.Errorf("error obtaining logical object id")
}
client := nsx.NewClusterProfilesClient(connector)
structValue, err := client.Get(id)
if err != nil {
return handleReadError(d, "Edge High Availability Profile", id, err)
}
converter := bindings.NewTypeConverter()
o, errs := converter.ConvertToGolang(structValue, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}
obj := o.(model.EdgeHighAvailabilityProfile)
d.Set("revision", obj.Revision)
d.Set("description", obj.Description)
d.Set("display_name", obj.DisplayName)
setMPTagsInSchema(d, obj.Tags)
d.Set("bfd_allowed_hops", obj.BfdAllowedHops)
d.Set("bfd_declare_dead_multiple", obj.BfdDeclareDeadMultiple)
d.Set("bfd_probe_interval", obj.BfdProbeInterval)
if obj.StandbyRelocationConfig != nil {
d.Set("standby_relocation_threshold", obj.StandbyRelocationConfig.StandbyRelocationThreshold)
}

return nil
}

func resourceNsxtEdgeHighAvailabilityProfileUpdate(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)
id := d.Id()
if id == "" {
return fmt.Errorf("error obtaining logical object id")
}
client := nsx.NewClusterProfilesClient(connector)
revision := int64(d.Get("revision").(int))
description := d.Get("description").(string)
displayName := d.Get("display_name").(string)
tags := getMPTagsFromSchema(d)
bfdAllowedHops := int64(d.Get("bfd_allowed_hops").(int))
bfdDeclareDeadMultiple := int64(d.Get("bfd_declare_dead_multiple").(int))
bfdProbeInterval := int64(d.Get("bfd_probe_interval").(int))
standbyRelocationThreshold := int64(d.Get("standby_relocation_threshold").(int))

obj := model.EdgeHighAvailabilityProfile{
Revision: &revision,
Description: &description,
DisplayName: &displayName,
Tags: tags,
BfdAllowedHops: &bfdAllowedHops,
BfdDeclareDeadMultiple: &bfdDeclareDeadMultiple,
BfdProbeInterval: &bfdProbeInterval,
StandbyRelocationConfig: &model.StandbyRelocationConfig{
StandbyRelocationThreshold: &standbyRelocationThreshold,
},
ResourceType: model.EdgeHighAvailabilityProfile__TYPE_IDENTIFIER,
}
converter := bindings.NewTypeConverter()
dataValue, errs := converter.ConvertToVapi(obj, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}

_, err := client.Update(id, dataValue.(*data.StructValue))
if err != nil {
return handleUpdateError("Edge High Availability Profile", id, err)
}

return resourceNsxtEdgeHighAvailabilityProfileRead(d, m)
}

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

id := d.Id()
if id == "" {
return fmt.Errorf("error obtaining logical object id")
}

client := nsx.NewClusterProfilesClient(connector)

err := client.Delete(id)
if err != nil {
return handleDeleteError("Edge High Availability Profile", id, err)
}
return nil
}
158 changes: 158 additions & 0 deletions nsxt/resource_nsxt_edge_high_availability_profile_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* Copyright © 2023 VMware, Inc. All Rights Reserved.
SPDX-License-Identifier: MPL-2.0 */

package nsxt

import (
"fmt"
"testing"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/vmware/vsphere-automation-sdk-go/runtime/bindings"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx"
"github.com/vmware/vsphere-automation-sdk-go/services/nsxt-mp/nsx/model"
)

func TestAccResourceNsxtEdgeHighAvailabilityProfile_basic(t *testing.T) {
profileName := getAccTestResourceName()
updateProfileName := "updated-" + profileName
testResourceName := "nsxt_edge_high_availability_profile.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccOnlyLocalManager(t); testAccTestFabric(t); testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: func(state *terraform.State) error {
return testAccNSXEdgeHighAvailabilityProfileCheckDestroy(state, updateProfileName)
},

Steps: []resource.TestStep{
{
Config: testAccNSXEdgeHighAvailabilityProfileCreateTemplate(profileName),
Check: resource.ComposeTestCheckFunc(
testAccNSXEdgeHighAvailabilityProfileExists(profileName, testResourceName),
resource.TestCheckResourceAttr(testResourceName, "display_name", profileName),
resource.TestCheckResourceAttr(testResourceName, "description", "Terraform test edge high availability profile"),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "1"),
),
},
{
Config: testAccNSXEdgeHighAvailabilityProfileCreateTemplate(updateProfileName),
Check: resource.ComposeTestCheckFunc(
testAccNSXEdgeHighAvailabilityProfileExists(updateProfileName, testResourceName),
resource.TestCheckResourceAttr(testResourceName, "display_name", updateProfileName),
resource.TestCheckResourceAttr(testResourceName, "description", "Terraform test edge high availability profile"),
resource.TestCheckResourceAttr(testResourceName, "tag.#", "1"),
),
},
},
})
}

func TestAccResourceNsxtEdgeHighAvailabilityProfile_importBasic(t *testing.T) {
name := getAccTestResourceName()
profileName := getAccTestResourceName()
testResourceName := "nsxt_edge_high_availability_profile.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccOnlyLocalManager(t); testAccTestFabric(t); testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: func(state *terraform.State) error {
return testAccNSXEdgeHighAvailabilityProfileCheckDestroy(state, name)
},
Steps: []resource.TestStep{
{
Config: testAccNSXEdgeHighAvailabilityProfileCreateTemplate(profileName),
},
{
ResourceName: testResourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccNSXEdgeHighAvailabilityProfileExists(displayName string, resourceName string) resource.TestCheckFunc {
return func(state *terraform.State) error {

connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))
rs, ok := state.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("NSX Edge High Availability Profile resource %s not found in resources", resourceName)
}

resourceID := rs.Primary.ID
if resourceID == "" {
return fmt.Errorf("NSX Edge High Availability Profile resource ID not set in resources ")
}

client := nsx.NewClusterProfilesClient(connector)
structValue, err := client.Get(resourceID)
if err != nil {
return fmt.Errorf("error while retrieving Edge High Availability Profile ID %s. Error: %v", resourceID, err)
}
converter := bindings.NewTypeConverter()
o, errs := converter.ConvertToGolang(structValue, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}
obj := o.(model.EdgeHighAvailabilityProfile)

if displayName == *obj.DisplayName {
return nil
}
return fmt.Errorf("NSX Edge High Availability Profile %s wasn't found", displayName)
}
}

func testAccNSXEdgeHighAvailabilityProfileCheckDestroy(state *terraform.State, displayName string) error {
connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))

// This addresses the fact that object is retrieved even though it had been deleted
time.Sleep(1 * time.Second)

for _, rs := range state.RootModule().Resources {
if rs.Type != "nsxt_edge_high_availability_profile" {
continue
}

resourceID := rs.Primary.Attributes["id"]
client := nsx.NewClusterProfilesClient(connector)
structValue, err := client.Get(resourceID)

if isNotFoundError(err) {
return nil
}

if err != nil {
return fmt.Errorf("error while retrieving Edge High Availability Profile ID %s. Error: %v", resourceID, err)
}
converter := bindings.NewTypeConverter()
o, errs := converter.ConvertToGolang(structValue, model.EdgeHighAvailabilityProfileBindingType())
if errs != nil {
return errs[0]
}
obj := o.(model.EdgeHighAvailabilityProfile)

if obj.DisplayName != nil && displayName == *obj.DisplayName {
return fmt.Errorf("NSX Edge High Availability Profile %s still exists", displayName)
}
}

return nil
}

func testAccNSXEdgeHighAvailabilityProfileCreateTemplate(displayName string) string {
return fmt.Sprintf(`
resource "nsxt_edge_high_availability_profile" "test" {
description = "Terraform test edge high availability profile"
display_name = "%s"
tag {
scope = "scope1"
tag = "tag1"
}
}
`, displayName)
}
Loading

0 comments on commit d341d2e

Please sign in to comment.