Skip to content

Commit

Permalink
feat: add disk stats for datastore (#1896)
Browse files Browse the repository at this point in the history
- `data/vsphere_datastore_stats` Add datastore stats to report total capacity and free space of datastores.
- `data/vsphere_datastore`  Add stats for a single datastore.

Signed-off-by: nikfot <[email protected]>
  • Loading branch information
nikfot authored Feb 12, 2024
1 parent 5a22e9a commit 8370be8
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 5 deletions.
10 changes: 10 additions & 0 deletions vsphere/data_source_vsphere_datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func dataSourceVSphereDatastore() *schema.Resource {
Description: "The managed object ID of the datacenter the datastore is in. This is not required when using ESXi directly, or if there is only one datacenter in your infrastructure.",
Optional: true,
},
"stats": {
Type: schema.TypeMap,
Description: "The usage stats of the datastore, include total capacity and free space in bytes.",
Optional: true,
},
},
}
}
Expand All @@ -48,5 +53,10 @@ func dataSourceVSphereDatastoreRead(d *schema.ResourceData, meta interface{}) er
}

d.SetId(ds.Reference().Value)
props, err := datastore.Properties(ds)
if err != nil {
return fmt.Errorf("error getting properties for datastore ID %q: %s", ds.Reference().Value, err)
}
d.Set("stats", map[string]string{"capacity": fmt.Sprintf("%v", props.Summary.Capacity), "free": fmt.Sprintf("%v", props.Summary.FreeSpace)})
return nil
}
71 changes: 71 additions & 0 deletions vsphere/data_source_vsphere_datastore_stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vsphere

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/datastore"
"github.com/vmware/govmomi/object"
)

func dataSourceVSphereDatastoreStats() *schema.Resource {
return &schema.Resource{
Read: dataSourceVSphereDatastoreStatsRead,

Schema: map[string]*schema.Schema{
"datacenter_id": {
Type: schema.TypeString,
Description: "The managed object ID of the datacenter to get datastores from.",
Required: true,
},
"free_space": {
Type: schema.TypeMap,
Description: "The free space of the datastores.",
Optional: true,
},
"capacity": {
Type: schema.TypeMap,
Description: "The capacity of the datastores.",
Optional: true,
},
},
}
}

func dataSourceVSphereDatastoreStatsRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client).vimClient
var dc *object.Datacenter
if dcID, ok := d.GetOk("datacenter_id"); ok {
var err error
dc, err = datacenterFromID(client, dcID.(string))
if err != nil {
return fmt.Errorf("cannot locate datacenter: %s", err)
}
}
dss, err := datastore.List(client)
if err != nil {
return fmt.Errorf("error listing datastores: %s", err)
}
for i := range dss {
ds, err := datastore.FromPath(client, dss[i].Name(), dc)
if err != nil {
return fmt.Errorf("error fetching datastore: %s", err)
}
props, err := datastore.Properties(ds)
if err != nil {
return fmt.Errorf("error getting properties for datastore ID %q: %s", ds.Reference().Value, err)
}
cap := d.Get("capacity").(map[string]interface{})
cap[dss[i].Name()] = fmt.Sprintf("%v", props.Summary.Capacity)
d.Set("capacity", cap)
fr := d.Get("free_space").(map[string]interface{})
fr[dss[i].Name()] = fmt.Sprintf("%v", props.Summary.FreeSpace)
d.Set("free_space", fr)
}
d.SetId(fmt.Sprintf("%s_stats", dc.Reference().Value))

return nil
}
86 changes: 86 additions & 0 deletions vsphere/data_source_vsphere_datastore_stats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package vsphere

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceVSphereDatastoreStats_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
testAccDataSourceVSphereDatastoreStatsPreCheck(t)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceVSphereDatastoreStatsConfig(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.vsphere_datastore_stats.datastore_stats", "datacenter_id", os.Getenv("VSPHERE_DATACENTER"),
),
resource.TestCheckResourceAttr(
"data.vsphere_datastore_stats.datastore_stats", "id", fmt.Sprintf("%s_stats", os.Getenv("VSPHERE_DATACENTER")),
),
testCheckOutputBool("found_free_space", "true"),
testCheckOutputBool("found_capacity", "true"),
testCheckOutputBool("free_values_exist", "true"),
testCheckOutputBool("capacity_values_exist", "true"),
),
},
},
})
}

func testAccDataSourceVSphereDatastoreStatsPreCheck(t *testing.T) {
if os.Getenv("VSPHERE_DATACENTER") == "" {
t.Skip("set TF_VAR_VSPHERE_DATACENTER to run vsphere_datastore_stats acceptance tests")
}
if os.Getenv("VSPHERE_USER") == "" {
t.Skip("set TF_VAR_VSPHERE_DATACENTER to run vsphere_datastore_stats acceptance tests")
}
if os.Getenv("VSPHERE_PASSWORD") == "" {
t.Skip("set TF_VAR_VSPHERE_PASSWORD to run vsphere_datastore_stats acceptance tests")
}
}

func testAccDataSourceVSphereDatastoreStatsConfig() string {
return fmt.Sprintf(`
variable "datacenter_id" {
default = "%s"
}
data "vsphere_datastore_stats" "datastore_stats" {
datacenter_id = "${var.datacenter_id}"
}
output "found_free_space" {
value = "${length(data.vsphere_datastore_stats.datastore_stats.free_space) >= 1 ? "true" : "false" }"
}
output "found_capacity" {
value = "${length(data.vsphere_datastore_stats.datastore_stats.capacity) >= 1 ? "true" : "false" }"
}
output "free_values_exist" {
value = alltrue([
for free in values(data.vsphere_datastore_stats.datastore_stats.free_space):
free >= 1
])
}
output "capacity_values_exist" {
value = alltrue([
for free in values(data.vsphere_datastore_stats.datastore_stats.capacity):
free >= 1
])
}
`, os.Getenv("VSPHERE_DATACENTER"))
}
42 changes: 42 additions & 0 deletions vsphere/data_source_vsphere_datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,28 @@ func TestAccDataSourceVSphereDatastore_noDatacenterAndAbsolutePath(t *testing.T)
})
}

func TestAccDataSourceVSphereDatastore_getStats(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
RunSweepers()
testAccPreCheck(t)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceVSphereDatastoreConfigGetStats(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.vsphere_datastore.datastore_data", "name", os.Getenv("VSPHERE_DATASTORE_NAME"),
),

testCheckOutputBool("found_stats", "true"),
),
},
},
})
}

func testAccDataSourceVSphereDatastorePreCheck(t *testing.T) {
if os.Getenv("TF_VAR_VSPHERE_NFS_DS_NAME") == "" {
t.Skip("set TF_VAR_VSPHERE_NFS_DS_NAME to run vsphere_nas_datastore acceptance tests")
Expand Down Expand Up @@ -90,3 +112,23 @@ data "vsphere_datastore" "datastore_data" {
testhelper.CombineConfigs(testhelper.ConfigDataRootDC1(), testhelper.ConfigDataRootHost1(), testhelper.ConfigDataRootHost2(), testhelper.ConfigResDS1(), testhelper.ConfigDataRootComputeCluster1(), testhelper.ConfigResResourcePool1(), testhelper.ConfigDataRootPortGroup1()),
)
}

func testAccDataSourceVSphereDatastoreConfigGetStats() string {
return fmt.Sprintf(`
variable "datastore_name" {
default = "%s"
}
variable "datacenter_id" {
default = "%s"
}
data "vsphere_datastore" "datastore_data" {
name = "${var.datastore_name}"
datacenter_id = "${var.datacenter_id}"
}
output "found_stats" {
value = "${length(data.vsphere_datastore.datastore_data.stats) >= 1 ? "true" : "false" }"
}
`, os.Getenv("VSPHERE_DATASTORE_NAME"), os.Getenv("VSPHERE_DATACENTER"),
)
}
1 change: 1 addition & 0 deletions vsphere/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func Provider() *schema.Provider {
"vsphere_datacenter": dataSourceVSphereDatacenter(),
"vsphere_datastore": dataSourceVSphereDatastore(),
"vsphere_datastore_cluster": dataSourceVSphereDatastoreCluster(),
"vsphere_datastore_stats": dataSourceVSphereDatastoreStats(),
"vsphere_distributed_virtual_switch": dataSourceVSphereDistributedVirtualSwitch(),
"vsphere_dynamic": dataSourceVSphereDynamic(),
"vsphere_folder": dataSourceVSphereFolder(),
Expand Down
14 changes: 9 additions & 5 deletions website/docs/d/datastore.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: |-
Provides a data source to return the ID of a vSphere datastore object.
---

# vsphere\_datastore
# vsphere_datastore

The `vsphere_datastore` data source can be used to discover the ID of a
vSphere datastore object. This can then be used with resources or data sources
Expand All @@ -33,8 +33,8 @@ data "vsphere_datastore" "datastore" {

The following arguments are supported:

* `name` - (Required) The name of the datastore. This can be a name or path.
* `datacenter_id` - (Optional) The [managed object reference ID][docs-about-morefs]
- `name` - (Required) The name of the datastore. This can be a name or path.
- `datacenter_id` - (Optional) The [managed object reference ID][docs-about-morefs]
of the datacenter the datastore is located in. This can be omitted if the
search path used in `name` is an absolute path. For default datacenters, use
the `id` attribute from an empty `vsphere_datacenter` data source.
Expand All @@ -43,5 +43,9 @@ The following arguments are supported:

## Attribute Reference

The only exported attribute from this data source is `id`, which represents the
ID of the datastore.
The following attributes are exported:

- `id` - The ID of the datastore.
- `stats` - The disk space usage statistics for the specific datastore. The total
datastore capacity is represented as `capacity` and the free remaining disk is
represented as `free`.
80 changes: 80 additions & 0 deletions website/docs/d/datastore_stats.html .markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
subcategory: "Storage"
layout: "vsphere"
page_title: "VMware vSphere: vsphere_datastore_stats"
sidebar_current: "docs-vsphere-data-source-datastore-stats"
description: |-
Provides a data source to return the usage stats for all vSphere datastore objects
in a datacenter.
---

# vsphere_datastore_stats

The `vsphere_datastore_stats` data source can be used to retrieve the usage stats
of all vSphere datastore objects in a datacenter. This can then be used as a
standalone datasource to get information required as input to other data sources.

## Example Usage

```hcl
data "vsphere_datacenter" "datacenter" {
name = "dc-01"
}
data "vsphere_datastore_stats" "datastore_stats" {
datacenter_id = data.vsphere_datacenter.datacenter.id
}
```

A usefull example of this datasource would be to determine the
datastore with the most free space. For example, in addition to
the above:

Create an `outputs.tf` like that:

```hcl
output "max_free_space_name" {
value = local.max_free_space_name
}
output "max_free_space" {
value = local.max_free_space
}
```

and a `locals.tf` like that:

```hcl
locals {
free_space_values = { for k, v in data.vsphere_datastore_stats.datastore_stats.free_space : k => tonumber(v) }
filtered_values = { for k, v in local.free_space_values : k => tonumber(v) if v != null }
numeric_values = [for v in values(local.filtered_values) : tonumber(v)]
max_free_space = max(local.numeric_values...)
max_free_space_name = [for k, v in local.filtered_values : k if v == local.max_free_space][0]
}
```

This way you can get the storage object with the most
space available.

## Argument Reference

The following arguments are supported:

- `datacenter_id` - (Required) The [managed object reference ID][docs-about-morefs]
of the datacenter the datastores are located in. For default datacenters, use
the `id` attribute from an empty `vsphere_datacenter` data source.

[docs-about-morefs]: /docs/providers/vsphere/index.html#use-of-managed-object-references-by-the-vsphere-provider

## Attribute Reference

The following attributes are exported:

- `datacenter_id` - The [managed object reference ID][docs-about-morefs]
of the datacenter the datastores are located in.
- `free_space` - A mapping of the free space for each datastore in the
datacenter, where the name of the datastore is used as key and the free
space as value.
- `capacity` - A mapping of the capacity for all datastore in the datacenter
, where the name of the datastore is used as key and the capacity as value.

0 comments on commit 8370be8

Please sign in to comment.