diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd3e2f52..978d1055b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ # +## 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: diff --git a/vsphere/resource_vsphere_compute_cluster.go b/vsphere/resource_vsphere_compute_cluster.go index 77b822ad8..a843ffa43 100644 --- a/vsphere/resource_vsphere_compute_cluster.go +++ b/vsphere/resource_vsphere_compute_cluster.go @@ -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, @@ -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.", @@ -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) @@ -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) } @@ -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{ @@ -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) @@ -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 diff --git a/vsphere/resource_vsphere_compute_cluster_test.go b/vsphere/resource_vsphere_compute_cluster_test.go index ff3efc95f..2919d0fea 100644 --- a/vsphere/resource_vsphere_compute_cluster_test.go +++ b/vsphere/resource_vsphere_compute_cluster_test.go @@ -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() { @@ -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) @@ -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 diff --git a/website/docs/r/compute_cluster.html.markdown b/website/docs/r/compute_cluster.html.markdown index a027e6e3b..71764a54f 100644 --- a/website/docs/r/compute_cluster.html.markdown +++ b/website/docs/r/compute_cluster.html.markdown @@ -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. @@ -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 @@ -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 @@ -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)