Skip to content

feat: add disk stats for datastore #1896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -152,6 +152,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.