This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
The following commit(s) were added to refs/heads/main by this push:
new 5191290 add cloudstack_cluster data source and documentation (#222)
5191290 is described below
commit 5191290128f0b785eb43bd78bd3010d01c6ae284
Author: Ian <[email protected]>
AuthorDate: Tue Sep 16 05:47:22 2025 -0700
add cloudstack_cluster data source and documentation (#222)
---
cloudstack/data_source_cloudstack_cluster.go | 357 ++++++++++++++++++++++
cloudstack/data_source_cloudstack_cluster_test.go | 75 +++++
cloudstack/provider.go | 1 +
website/docs/d/cluster.html.markdown | 88 ++++++
4 files changed, 521 insertions(+)
diff --git a/cloudstack/data_source_cloudstack_cluster.go
b/cloudstack/data_source_cloudstack_cluster.go
new file mode 100644
index 0000000..35bdfe6
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_cluster.go
@@ -0,0 +1,357 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package cloudstack
+
+import (
+ "fmt"
+ "log"
+ "reflect"
+ "regexp"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudstackCluster() *schema.Resource {
+ return &schema.Resource{
+ Read: datasourceCloudStackClusterRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "allocation_state": {
+ Description: "Allocation state of this cluster
for allocation of new resources",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_name": {
+ Description: "the cluster name",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_type": {
+ Description: "Type of the cluster:
CloudManaged, ExternalManaged",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "guest_vswitch_name": {
+ Description: "Name of virtual switch used for
guest traffic in the cluster. This would override zone wide traffic label
setting.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "guest_vswitch_type": {
+ Description: "Type of virtual switch used for
guest traffic in the cluster. Allowed values are, vmwaresvs (for VMware
standard vSwitch) and vmwaredvs (for VMware distributed vSwitch)",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "hypervisor": {
+ Description: "hypervisor type of the cluster:
XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ovm3_cluster": {
+ Description: "Ovm3 native OCFS2 clustering
enabled for cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ovm3_pool": {
+ Description: "Ovm3 native pooling enabled for
cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ovm3_vip": {
+ Description: "Ovm3 vip to use for pool (and
cluster)",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ovm3vip": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "password": {
+ Description: "the password for the host",
+ Type: schema.TypeString,
+ Computed: true,
+ Sensitive: true,
+ },
+ "public_vswitch_name": {
+ Description: "Name of virtual switch used for
public traffic in the cluster. This would override zone wide traffic label
setting.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "public_vswitch_type": {
+ Description: "Type of virtual switch used for
public traffic in the cluster. Allowed values are, vmwaresvs (for VMware
standard vSwitch) and vmwaredvs (for VMware distributed vSwitch)",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "pod_id": {
+ Description: "The Pod ID for the cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "pod_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "url": {
+ Description: "the URL",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "username": {
+ Description: "the username for the cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vsm_ip_address": {
+ Description: "the ipaddress of the VSM
associated with this cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vsm_password": {
+ Description: "the password for the VSM
associated with this cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ Sensitive: true,
+ },
+ "vsm_username": {
+ Description: "the username for the VSM
associated with this cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "zone_id": {
+ Description: "the Zone ID for the cluster",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "zone_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "managed_state": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cpu_overcommit_ratio": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "memory_overcommit_ratio": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "arch": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "capacity": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "capacity_allocated": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ },
+ "capacity_total": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ },
+ "capacity_used": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ },
+ "cluster_id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "cluster_name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "percent_used": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ },
+ "pod_id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "pod_name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "type": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "zone_id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ "zone_name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dsFlattenClusterCapacity(capacity []cloudstack.ClusterCapacity)
[]map[string]interface{} {
+ cap := make([]map[string]interface{}, len(capacity))
+ for i, c := range capacity {
+ cap[i] = map[string]interface{}{
+ "capacity_allocated": c.Capacityallocated,
+ "capacity_total": c.Capacitytotal,
+ "capacity_used": c.Capacityused,
+ "cluster_id": c.Clusterid,
+ "cluster_name": c.Clustername,
+ "name": c.Name,
+ "percent_used": c.Percentused,
+ "pod_id": c.Podid,
+ "pod_name": c.Podname,
+ "type": c.Type,
+ "zone_id": c.Zoneid,
+ "zone_name": c.Zonename,
+ }
+ }
+ return cap
+}
+
+func datasourceCloudStackClusterRead(d *schema.ResourceData, meta interface{})
error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.Cluster.NewListClustersParams()
+
+ csClusters, err := cs.Cluster.ListClusters(p)
+ if err != nil {
+ return fmt.Errorf("failed to list clusters: %s", err)
+ }
+
+ filters := d.Get("filter")
+
+ for _, cluster := range csClusters.Clusters {
+ match, err := applyClusterFilters(cluster,
filters.(*schema.Set))
+ if err != nil {
+ return err
+ }
+ if match {
+ return clusterDescriptionAttributes(d, cluster)
+ }
+ }
+
+ return fmt.Errorf("no clusters found")
+}
+
+func clusterDescriptionAttributes(d *schema.ResourceData, cluster
*cloudstack.Cluster) error {
+ d.SetId(cluster.Id)
+
+ fields := map[string]interface{}{
+ "id": cluster.Id,
+ "allocation_state": cluster.Allocationstate,
+ "cluster_name": cluster.Name,
+ "name": cluster.Name,
+ "cluster_type": cluster.Clustertype,
+ "hypervisor": cluster.Hypervisortype,
+ "ovm3_vip": cluster.Ovm3vip,
+ "ovm3vip": cluster.Ovm3vip,
+ "pod_id": cluster.Podid,
+ "pod_name": cluster.Podname,
+ "zone_id": cluster.Zoneid,
+ "zone_name": cluster.Zonename,
+ "managed_state": cluster.Managedstate,
+ "cpu_overcommit_ratio": cluster.Cpuovercommitratio,
+ "memory_overcommit_ratio": cluster.Memoryovercommitratio,
+ "arch": cluster.Arch,
+ "capacity":
dsFlattenClusterCapacity(cluster.Capacity),
+ }
+
+ // Set fields that may not be available in all cluster responses to
empty strings
+ // These are typically only available during cluster
creation/configuration
+ emptyStringFields := []string{
+ "guest_vswitch_name",
+ "guest_vswitch_type",
+ "ovm3_cluster",
+ "ovm3_pool",
+ "password",
+ "public_vswitch_name",
+ "public_vswitch_type",
+ "url",
+ "username",
+ "vsm_ip_address",
+ "vsm_password",
+ "vsm_username",
+ }
+
+ for k, v := range fields {
+ if err := d.Set(k, v); err != nil {
+ log.Printf("[WARN] Error setting %s: %s", k, err)
+ }
+ }
+
+ for _, field := range emptyStringFields {
+ if err := d.Set(field, ""); err != nil {
+ log.Printf("[WARN] Error setting %s: %s", field, err)
+ }
+ }
+
+ return nil
+}
+
+func applyClusterFilters(cluster *cloudstack.Cluster, filters *schema.Set)
(bool, error) {
+ val := reflect.ValueOf(cluster).Elem()
+
+ for _, f := range filters.List() {
+ filter := f.(map[string]interface{})
+ r, err := regexp.Compile(filter["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("invalid regex: %s", err)
+ }
+ updatedName := strings.ReplaceAll(filter["name"].(string), "_",
"")
+ clusterField := val.FieldByNameFunc(func(fieldName string) bool
{
+ if strings.EqualFold(fieldName, updatedName) {
+ updatedName = fieldName
+ return true
+ }
+ return false
+ }).String()
+
+ if r.MatchString(clusterField) {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
diff --git a/cloudstack/data_source_cloudstack_cluster_test.go
b/cloudstack/data_source_cloudstack_cluster_test.go
new file mode 100644
index 0000000..96fbfd9
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_cluster_test.go
@@ -0,0 +1,75 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package cloudstack
+
+import (
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccClusterDataSource_basic(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testClusterDataSourceConfig_basic,
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttr("data.cloudstack_cluster.test", "name",
"terraform-test-cluster"),
+ ),
+ },
+ },
+ })
+}
+
+const testClusterDataSourceConfig_basic = `
+data "cloudstack_zone" "zone" {
+ filter {
+ name = "name"
+ value = "Sandbox-simulator"
+ }
+}
+
+data "cloudstack_pod" "pod" {
+ filter {
+ name = "name"
+ value = "POD0"
+ }
+}
+
+# Create a cluster first
+resource "cloudstack_cluster" "test_cluster" {
+ cluster_name = "terraform-test-cluster"
+ cluster_type = "CloudManaged"
+ hypervisor = "KVM"
+ pod_id = data.cloudstack_pod.pod.id
+ zone_id = data.cloudstack_zone.zone.id
+}
+
+# Then query it with the data source
+data "cloudstack_cluster" "test" {
+ filter {
+ name = "name"
+ value = "terraform-test-cluster"
+ }
+ depends_on = [cloudstack_cluster.test_cluster]
+}
+`
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index b52b3d3..f4eedc1 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -93,6 +93,7 @@ func Provider() *schema.Provider {
"cloudstack_domain":
dataSourceCloudstackDomain(),
"cloudstack_physical_network":
dataSourceCloudStackPhysicalNetwork(),
"cloudstack_role":
dataSourceCloudstackRole(),
+ "cloudstack_cluster":
dataSourceCloudstackCluster(),
},
ResourcesMap: map[string]*schema.Resource{
diff --git a/website/docs/d/cluster.html.markdown
b/website/docs/d/cluster.html.markdown
new file mode 100644
index 0000000..901a3ca
--- /dev/null
+++ b/website/docs/d/cluster.html.markdown
@@ -0,0 +1,88 @@
+---
+subcategory: "Cluster"
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_cluster"
+description: |-
+ Gets information about a cluster.
+---
+
+# cloudstack_cluster
+
+Use this data source to get information about a cluster for use in other
resources.
+
+## Example Usage
+
+```hcl
+data "cloudstack_cluster" "cluster" {
+ filter {
+ name = "name"
+ value = "cluster-1"
+ }
+}
+
+output "cluster_id" {
+ value = data.cloudstack_cluster.cluster.id
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `filter` - (Required) One or more name/value pairs to filter off of. See
detailed documentation below.
+
+### Filter Arguments
+
+* `name` - (Required) The name of the field to filter on. This can be any of
the fields returned by the CloudStack API.
+* `value` - (Required) The value to filter on. This should be a regular
expression.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the cluster.
+* `allocation_state` - Allocation state of this cluster for allocation of new
resources.
+* `cluster_name` - The cluster name.
+* `name` - The name of the cluster.
+* `cluster_type` - Type of the cluster: CloudManaged, ExternalManaged.
+* `guest_vswitch_name` - Name of virtual switch used for guest traffic in the
cluster. This would override zone wide traffic label setting.
+* `guest_vswitch_type` - Type of virtual switch used for guest traffic in the
cluster. Allowed values are, vmwaresvs (for VMware standard vSwitch) and
vmwaredvs (for VMware distributed vSwitch).
+* `hypervisor` - Hypervisor type of the cluster: XenServer, KVM, VMware,
Hyperv, BareMetal, Simulator, Ovm3.
+* `ovm3_cluster` - Ovm3 native OCFS2 clustering enabled for cluster.
+* `ovm3_pool` - Ovm3 native pooling enabled for cluster.
+* `ovm3_vip` - Ovm3 vip to use for pool (and cluster).
+* `ovm3vip` - Ovm3 vip used for pool (and cluster).
+* `password` - The password for the host.
+* `public_vswitch_name` - Name of virtual switch used for public traffic in
the cluster. This would override zone wide traffic label setting.
+* `public_vswitch_type` - Type of virtual switch used for public traffic in
the cluster. Allowed values are, vmwaresvs (for VMware standard vSwitch) and
vmwaredvs (for VMware distributed vSwitch).
+* `pod_id` - The Pod ID for the cluster.
+* `pod_name` - The name of the pod where the cluster is created.
+* `url` - The URL.
+* `username` - The username for the cluster.
+* `vsm_ip_address` - The ipaddress of the VSM associated with this cluster.
+* `vsm_password` - The password for the VSM associated with this cluster.
+* `vsm_username` - The username for the VSM associated with this cluster.
+* `zone_id` - The Zone ID for the cluster.
+* `zone_name` - The name of the zone where the cluster is created.
+* `managed_state` - The managed state of the cluster.
+* `cpu_overcommit_ratio` - The CPU overcommit ratio of the cluster.
+* `memory_overcommit_ratio` - The memory overcommit ratio of the cluster.
+* `arch` - The CPU arch of the cluster.
+* `capacity` - The capacity information of the cluster. See Capacity below for
more details.
+
+### Capacity
+
+The `capacity` attribute supports the following:
+
+* `capacity_allocated` - The capacity allocated.
+* `capacity_total` - The total capacity.
+* `capacity_used` - The capacity used.
+* `cluster_id` - The ID of the cluster.
+* `cluster_name` - The name of the cluster.
+* `name` - The name of the capacity.
+* `percent_used` - The percentage of capacity used.
+* `pod_id` - The ID of the pod.
+* `pod_name` - The name of the pod.
+* `type` - The type of the capacity.
+* `zone_id` - The ID of the zone.
+* `zone_name` - The name of the zone.