Skip to content

Commit

Permalink
feat: add vsan esa support (#1874)
Browse files Browse the repository at this point in the history
* feat: add vSAN ESA support

* nit

* add version checking

* fix bug of enabling vSAN ESA for the first time

* refactor version check of vsan esa.

* nit

* address comments

* add unmap read back due to delete by mistake

* skip vsan esa acceptance test due to version imcompatible

* docs: update `CHANGELOG.md`

Updated `CHANGELOG.md`.

Signed-off-by: Ryan Johnson <[email protected]>

---------

Signed-off-by: Ryan Johnson <[email protected]>
Co-authored-by: Ryan Johnson <[email protected]>
  • Loading branch information
zxinyu08 and Ryan Johnson authored Oct 18, 2023
1 parent f48cce8 commit 7c5fae0
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 40 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# <!-- markdownlint-disable first-line-h1 no-inline-html -->

## 2.6.0 (Unreleased)

FEATURES:

* `resource/compute_cluster`: Adds support for vSAN Express Storage Architecture in vSphere 8.0. ([#1874](https://github.com/terraform-providers/terraform-provider-vsphere/pull/1874))

## 2.5.1 (October 12, 2023)

BUG FIXES:

* `resource/virtual_machine`: Fixed cloning regression on datastore cluster. Restored behavior not to send relocate specs for the virtual disks when it is cloned on datastore cluster with exception when `datastore_id`` is explicitly specified for the virtual disk. ([#2037](https://github.com/hashicorp/terraform-provider-vsphere/pull/2037))
* `resource/virtual_machine`: Fixed cloning regression on datastore cluster. Restored behavior not to send relocate specs for the virtual disks when it is cloned on datastore cluster with exception when `datastore_id` is explicitly specified for the virtual disk. ([#2037](https://github.com/hashicorp/terraform-provider-vsphere/pull/2037))
* `resource/vsphere_virtual_disk`: Fixed improper disk type handling forcing disks to be recreated. ([#2033](https://github.com/hashicorp/terraform-provider-vsphere/pull/2033))

IMPROVEMENTS:
Expand Down
98 changes: 61 additions & 37 deletions vsphere/resource_vsphere_compute_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,17 +498,26 @@ func resourceVSphereComputeCluster() *schema.Resource {
Default: false,
Description: "Whether the vSAN service is enabled for the cluster.",
},
"vsan_esa_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether the vSAN ESA service is enabled for the cluster.",
ConflictsWith: []string{"vsan_dedup_enabled", "vsan_compression_enabled"},
},
"vsan_dedup_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether the vSAN deduplication service is enabled for the cluster.",
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether the vSAN deduplication service is enabled for the cluster.",
ConflictsWith: []string{"vsan_esa_enabled"},
},
"vsan_compression_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether the vSAN compression service is enabled for the cluster.",
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether the vSAN compression service is enabled for the cluster.",
ConflictsWith: []string{"vsan_esa_enabled"},
},
"vsan_performance_enabled": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -561,13 +570,11 @@ func resourceVSphereComputeCluster() *schema.Resource {
Description: "A list of disk UUIDs to add to the vSAN cluster.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// use 4gb disk from ova in the future for acctests
"cache": {
Type: schema.TypeString,
Description: "Cache disk.",
Optional: true,
},
// use 8gb disk from ova in the future for acctests
"storage": {
Type: schema.TypeSet,
Description: "List of storage disks.",
Expand Down Expand Up @@ -1352,6 +1359,7 @@ func resourceVSphereComputeClusterFlattenData(
}

d.Set("vsan_enabled", structure.BoolNilFalse(vsanConfig.Enabled))
d.Set("vsan_esa_enabled", structure.BoolNilFalse(vsanConfig.VsanEsaEnabled))

if vsanConfig.DataEfficiencyConfig != nil {
d.Set("vsan_dedup_enabled", vsanConfig.DataEfficiencyConfig.DedupEnabled)
Expand Down Expand Up @@ -1385,17 +1393,15 @@ func resourceVSphereComputeClusterFlattenData(
d.Set("vsan_dit_rekey_interval", 0)
}

if version.AtLeast(viapi.VSphereVersion{Product: version.Product, Major: 7, Minor: 0, Patch: 1}) {
var dsIDs []string
if vsanConfig.DatastoreConfig != nil {
for _, ds := range vsanConfig.DatastoreConfig.(*vsantypes.VsanAdvancedDatastoreConfig).RemoteDatastores {
dsIDs = append(dsIDs, ds.Value)
}
}
if err := d.Set("vsan_remote_datastore_ids", schema.NewSet(schema.HashString, structure.SliceStringsToInterfaces(dsIDs))); err != nil {
return err
var dsIDs []string
if vsanConfig.DatastoreConfig != nil {
for _, ds := range vsanConfig.DatastoreConfig.(*vsantypes.VsanAdvancedDatastoreConfig).RemoteDatastores {
dsIDs = append(dsIDs, ds.Value)
}
}
if err := d.Set("vsan_remote_datastore_ids", schema.NewSet(schema.HashString, structure.SliceStringsToInterfaces(dsIDs))); err != nil {
return err
}

return flattenClusterConfigSpecEx(d, props.ConfigurationEx.(*types.ClusterConfigInfoEx), version)
}
Expand Down Expand Up @@ -1463,6 +1469,19 @@ func resourceVSphereComputeClusterApplyVsanConfig(d *schema.ResourceData, meta i
return err
}
version := viapi.ParseVersionFromClient(client)

if version.AtLeast(viapi.VSphereVersion{Product: version.Product, Major: 8, Minor: 0}) {
if !d.Get("vsan_enabled").(bool) && d.Get("vsan_esa_enabled").(bool) {
return fmt.Errorf("vSAN ESA service cannot be enabled on cluster due to vSAN is disabled: %s", d.Get("name").(string))
}
if !d.HasChange("vsan_enabled") && d.HasChange("vsan_esa_enabled") {
return fmt.Errorf("vSAN ESA service must be configured along with vSAN service: %s", d.Get("name").(string))
}
if d.Get("vsan_esa_enabled").(bool) && !d.Get("vsan_unmap_enabled").(bool) {
return fmt.Errorf("vSAN unmap service should be explicitly enabled when vSAN ESA is enabled: %s", d.Get("name").(string))
}
}

conf := vsantypes.VimVsanReconfigSpec{
Modify: true,
VsanClusterConfig: &vsantypes.VsanClusterConfigInfo{
Expand All @@ -1478,15 +1497,22 @@ func resourceVSphereComputeClusterApplyVsanConfig(d *schema.ResourceData, meta i
},
}

dedupEnabled := d.Get("vsan_dedup_enabled").(bool)
compressionEnabled := d.Get("vsan_compression_enabled").(bool)
if dedupEnabled && !compressionEnabled {
return fmt.Errorf("vsan compression must be enabled if vsan dedup is enabled")
if version.AtLeast(viapi.VSphereVersion{Product: version.Product, Major: 8, Minor: 0}) {
vsanEsaEnabled := d.Get("vsan_esa_enabled").(bool)
conf.VsanClusterConfig.(*vsantypes.VsanClusterConfigInfo).VsanEsaEnabled = &vsanEsaEnabled
}

conf.DataEfficiencyConfig = &vsantypes.VsanDataEfficiencyConfig{
DedupEnabled: dedupEnabled,
CompressionEnabled: &compressionEnabled,
if d.Get("vsan_enabled").(bool) && !d.Get("vsan_esa_enabled").(bool) {
dedupEnabled := d.Get("vsan_dedup_enabled").(bool)
compressionEnabled := d.Get("vsan_compression_enabled").(bool)
if dedupEnabled && !compressionEnabled {
return fmt.Errorf("vsan compression must be enabled if vsan dedup is enabled")
}

conf.DataEfficiencyConfig = &vsantypes.VsanDataEfficiencyConfig{
DedupEnabled: dedupEnabled,
CompressionEnabled: &compressionEnabled,
}
}

perfConfig, err := expandVsanPerfConfig(d)
Expand All @@ -1505,17 +1531,15 @@ func resourceVSphereComputeClusterApplyVsanConfig(d *schema.ResourceData, meta i
}

// handle remote datastore/HCI Mesh in a separate call
if version.AtLeast(viapi.VSphereVersion{Product: version.Product, Major: 7, Minor: 0, Patch: 1}) {
datastoreConfig, err := expandVsanDatastoreConfig(d, meta)
if err != nil {
return err
}
if err := vsanclient.Reconfigure(meta.(*Client).vsanClient, cluster.Reference(), vsantypes.VimVsanReconfigSpec{
Modify: true,
DatastoreConfig: datastoreConfig,
}); err != nil {
return fmt.Errorf("cannot apply vsan remote datastores on cluster '%s': %s", d.Get("name").(string), err)
}
datastoreConfig, err := expandVsanDatastoreConfig(d, meta)
if err != nil {
return err
}
if err := vsanclient.Reconfigure(meta.(*Client).vsanClient, cluster.Reference(), vsantypes.VimVsanReconfigSpec{
Modify: true,
DatastoreConfig: datastoreConfig,
}); err != nil {
return fmt.Errorf("cannot apply vsan remote datastores on cluster '%s': %s", d.Get("name").(string), err)
}

return nil
Expand Down
61 changes: 61 additions & 0 deletions vsphere/resource_vsphere_compute_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,29 @@ func TestAccResourceVSphereComputeCluster_vsanDITEncryption(t *testing.T) {
})
}

func TestAccResourceVSphereComputeCluster_vsanEsaEnabled(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
testAccResourceVSphereComputeClusterPreCheck(t)
testAccResourceVSphereComputeClusterVSANEsaPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccResourceVSphereComputeClusterCheckExists(false),
Steps: []resource.TestStep{
{
Config: testAccResourceVSphereComputeClusterConfigVSANEsaEnabled(),
Check: resource.ComposeTestCheckFunc(
testAccResourceVSphereComputeClusterCheckExists(true),
resource.TestCheckResourceAttr("vsphere_compute_cluster.compute_cluster", "vsan_enabled", "true"),
resource.TestCheckResourceAttr("vsphere_compute_cluster.compute_cluster", "vsan_esa_enabled", "true"),
),
},
},
})
}

func TestAccResourceVSphereComputeCluster_explicitFailoverHost(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand Down Expand Up @@ -563,6 +586,20 @@ func testAccResourceVSphereComputeClusterPreCheck(t *testing.T) {
}
}

func testAccResourceVSphereComputeClusterVSANEsaPreCheck(t *testing.T) {
meta, err := testAccProviderMeta(t)
if err != nil {
t.Skip("can not get meta")
}
client, err := resourceVSphereComputeClusterClient(meta)
if err != nil {
t.Skip("can not get client")
}
if version := viapi.ParseVersionFromClient(client); !version.AtLeast(viapi.VSphereVersion{Product: version.Product, Major: 8, Minor: 0}) {
t.Skip("vSAN ESA acceptance test should be run on vSphere 8.0 or higher")
}
}

func testAccResourceVSphereComputeClusterCheckExists(expected bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, err := testGetComputeCluster(s, "compute_cluster", resourceVSphereComputeClusterName)
Expand Down Expand Up @@ -982,6 +1019,30 @@ resource "vsphere_compute_cluster" "compute_cluster" {
)
}

func testAccResourceVSphereComputeClusterConfigVSANEsaEnabled() string {
return fmt.Sprintf(`
%s
resource "vsphere_compute_cluster" "compute_cluster" {
name = "testacc-compute-cluster"
datacenter_id = data.vsphere_datacenter.rootdc1.id
host_system_ids = [data.vsphere_host.roothost3.id, data.vsphere_host.roothost4.id]
vsan_enabled = true
vsan_esa_enabled = true
vsan_unmap_enabled = true
force_evacuate_on_destroy = true
}
`,
testhelper.CombineConfigs(
testhelper.ConfigDataRootDC1(),
testhelper.ConfigDataRootHost3(),
testhelper.ConfigDataRootHost4(),
),
)
}

func testAccResourceVSphereComputeClusterConfigBasic() string {
return fmt.Sprintf(`
%s
Expand Down
13 changes: 11 additions & 2 deletions website/docs/r/compute_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,11 @@ details, see the referenced link in the above paragraph.
### vSAN Settings

* `vsan_enabled` - (Optional) Enables vSAN on the cluster.
* `vsan_esa_enabled` - (Optional) Enables vSAN ESA on the cluster.
* `vsan_unmap_enabled` - (Optional) Enables vSAN unmap on the cluster.
You must explicitly enable vSAN unmap when you enable vSAN ESA on the cluster.
* `vsan_dedup_enabled` - (Optional) Enables vSAN deduplication on the cluster.
Cannot be independently set to true. When vSAN deduplication is enabled, vSAN
Cannot be independently set to `true`. When vSAN deduplication is enabled, vSAN
compression must also be enabled.
* `vsan_compression_enabled` - (Optional) Enables vSAN compression on the
cluster.
Expand All @@ -473,7 +476,6 @@ details, see the referenced link in the above paragraph.
performance service on the cluster.
* `vsan_network_diagnostic_mode_enabled` - (Optional) Enables network
diagnostic mode for vSAN performance service on the cluster.
* `vsan_unmap_enabled` - (Optional) Enables vSAN unmap on the cluster.
* `vsan_remote_datastore_ids` - (Optional) The remote vSAN datastore IDs to be
mounted to this cluster. Conflicts with `vsan_dit_encryption_enabled` and
`vsan_dit_rekey_interval`, i.e., vSAN HCI Mesh feature cannot be enabled with
Expand Down Expand Up @@ -505,6 +507,7 @@ resource "vsphere_compute_cluster" "compute_cluster" {
ha_enabled = false
vsan_enabled = true
vsan_esa_enabled = true
vsan_dedup_enabled = true
vsan_compression_enabled = true
vsan_performance_enabled = true
Expand Down Expand Up @@ -558,3 +561,9 @@ specific version of vSphere.
These settings require vSphere 7.0 or higher:

* [`drs_scale_descendants_shares`](#drs_scale_descendants_shares)

### Settings that Require vSphere 8.0 or higher

These settings require vSphere 8.0 or higher:

* [`vsan_esa_enabled`](#vsan_esa_enabled)

0 comments on commit 7c5fae0

Please sign in to comment.