Skip to content

Commit 5191290

Browse files
authored
add cloudstack_cluster data source and documentation (#222)
1 parent 63f866b commit 5191290

File tree

4 files changed

+521
-0
lines changed

4 files changed

+521
-0
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"fmt"
24+
"log"
25+
"reflect"
26+
"regexp"
27+
"strings"
28+
29+
"github.com/apache/cloudstack-go/v2/cloudstack"
30+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
31+
)
32+
33+
func dataSourceCloudstackCluster() *schema.Resource {
34+
return &schema.Resource{
35+
Read: datasourceCloudStackClusterRead,
36+
Schema: map[string]*schema.Schema{
37+
"filter": dataSourceFiltersSchema(),
38+
39+
//Computed values
40+
"id": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
},
44+
"allocation_state": {
45+
Description: "Allocation state of this cluster for allocation of new resources",
46+
Type: schema.TypeString,
47+
Computed: true,
48+
},
49+
"cluster_name": {
50+
Description: "the cluster name",
51+
Type: schema.TypeString,
52+
Computed: true,
53+
},
54+
"name": {
55+
Type: schema.TypeString,
56+
Computed: true,
57+
},
58+
"cluster_type": {
59+
Description: "Type of the cluster: CloudManaged, ExternalManaged",
60+
Type: schema.TypeString,
61+
Computed: true,
62+
},
63+
"guest_vswitch_name": {
64+
Description: "Name of virtual switch used for guest traffic in the cluster. This would override zone wide traffic label setting.",
65+
Type: schema.TypeString,
66+
Computed: true,
67+
},
68+
"guest_vswitch_type": {
69+
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)",
70+
Type: schema.TypeString,
71+
Computed: true,
72+
},
73+
"hypervisor": {
74+
Description: "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3",
75+
Type: schema.TypeString,
76+
Computed: true,
77+
},
78+
"ovm3_cluster": {
79+
Description: "Ovm3 native OCFS2 clustering enabled for cluster",
80+
Type: schema.TypeString,
81+
Computed: true,
82+
},
83+
"ovm3_pool": {
84+
Description: "Ovm3 native pooling enabled for cluster",
85+
Type: schema.TypeString,
86+
Computed: true,
87+
},
88+
"ovm3_vip": {
89+
Description: "Ovm3 vip to use for pool (and cluster)",
90+
Type: schema.TypeString,
91+
Computed: true,
92+
},
93+
"ovm3vip": {
94+
Type: schema.TypeString,
95+
Computed: true,
96+
},
97+
"password": {
98+
Description: "the password for the host",
99+
Type: schema.TypeString,
100+
Computed: true,
101+
Sensitive: true,
102+
},
103+
"public_vswitch_name": {
104+
Description: "Name of virtual switch used for public traffic in the cluster. This would override zone wide traffic label setting.",
105+
Type: schema.TypeString,
106+
Computed: true,
107+
},
108+
"public_vswitch_type": {
109+
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)",
110+
Type: schema.TypeString,
111+
Computed: true,
112+
},
113+
"pod_id": {
114+
Description: "The Pod ID for the cluster",
115+
Type: schema.TypeString,
116+
Computed: true,
117+
},
118+
"pod_name": {
119+
Type: schema.TypeString,
120+
Computed: true,
121+
},
122+
"url": {
123+
Description: "the URL",
124+
Type: schema.TypeString,
125+
Computed: true,
126+
},
127+
"username": {
128+
Description: "the username for the cluster",
129+
Type: schema.TypeString,
130+
Computed: true,
131+
},
132+
"vsm_ip_address": {
133+
Description: "the ipaddress of the VSM associated with this cluster",
134+
Type: schema.TypeString,
135+
Computed: true,
136+
},
137+
"vsm_password": {
138+
Description: "the password for the VSM associated with this cluster",
139+
Type: schema.TypeString,
140+
Computed: true,
141+
Sensitive: true,
142+
},
143+
"vsm_username": {
144+
Description: "the username for the VSM associated with this cluster",
145+
Type: schema.TypeString,
146+
Computed: true,
147+
},
148+
"zone_id": {
149+
Description: "the Zone ID for the cluster",
150+
Type: schema.TypeString,
151+
Computed: true,
152+
},
153+
"zone_name": {
154+
Type: schema.TypeString,
155+
Computed: true,
156+
},
157+
"managed_state": {
158+
Type: schema.TypeString,
159+
Computed: true,
160+
},
161+
"cpu_overcommit_ratio": {
162+
Type: schema.TypeString,
163+
Computed: true,
164+
},
165+
"memory_overcommit_ratio": {
166+
Type: schema.TypeString,
167+
Computed: true,
168+
},
169+
"arch": {
170+
Type: schema.TypeString,
171+
Computed: true,
172+
},
173+
"capacity": {
174+
Type: schema.TypeList,
175+
Computed: true,
176+
Elem: &schema.Resource{
177+
Schema: map[string]*schema.Schema{
178+
"capacity_allocated": {
179+
Type: schema.TypeInt,
180+
Computed: true,
181+
},
182+
"capacity_total": {
183+
Type: schema.TypeInt,
184+
Computed: true,
185+
},
186+
"capacity_used": {
187+
Type: schema.TypeInt,
188+
Computed: true,
189+
},
190+
"cluster_id": {
191+
Type: schema.TypeString,
192+
Computed: true,
193+
},
194+
"cluster_name": {
195+
Type: schema.TypeString,
196+
Computed: true,
197+
},
198+
"name": {
199+
Type: schema.TypeString,
200+
Computed: true,
201+
},
202+
"percent_used": {
203+
Type: schema.TypeInt,
204+
Computed: true,
205+
},
206+
"pod_id": {
207+
Type: schema.TypeString,
208+
Computed: true,
209+
},
210+
"pod_name": {
211+
Type: schema.TypeString,
212+
Computed: true,
213+
},
214+
"type": {
215+
Type: schema.TypeString,
216+
Computed: true,
217+
},
218+
"zone_id": {
219+
Type: schema.TypeString,
220+
Computed: true,
221+
},
222+
"zone_name": {
223+
Type: schema.TypeString,
224+
Computed: true,
225+
},
226+
},
227+
},
228+
},
229+
},
230+
}
231+
}
232+
233+
func dsFlattenClusterCapacity(capacity []cloudstack.ClusterCapacity) []map[string]interface{} {
234+
cap := make([]map[string]interface{}, len(capacity))
235+
for i, c := range capacity {
236+
cap[i] = map[string]interface{}{
237+
"capacity_allocated": c.Capacityallocated,
238+
"capacity_total": c.Capacitytotal,
239+
"capacity_used": c.Capacityused,
240+
"cluster_id": c.Clusterid,
241+
"cluster_name": c.Clustername,
242+
"name": c.Name,
243+
"percent_used": c.Percentused,
244+
"pod_id": c.Podid,
245+
"pod_name": c.Podname,
246+
"type": c.Type,
247+
"zone_id": c.Zoneid,
248+
"zone_name": c.Zonename,
249+
}
250+
}
251+
return cap
252+
}
253+
254+
func datasourceCloudStackClusterRead(d *schema.ResourceData, meta interface{}) error {
255+
cs := meta.(*cloudstack.CloudStackClient)
256+
p := cs.Cluster.NewListClustersParams()
257+
258+
csClusters, err := cs.Cluster.ListClusters(p)
259+
if err != nil {
260+
return fmt.Errorf("failed to list clusters: %s", err)
261+
}
262+
263+
filters := d.Get("filter")
264+
265+
for _, cluster := range csClusters.Clusters {
266+
match, err := applyClusterFilters(cluster, filters.(*schema.Set))
267+
if err != nil {
268+
return err
269+
}
270+
if match {
271+
return clusterDescriptionAttributes(d, cluster)
272+
}
273+
}
274+
275+
return fmt.Errorf("no clusters found")
276+
}
277+
278+
func clusterDescriptionAttributes(d *schema.ResourceData, cluster *cloudstack.Cluster) error {
279+
d.SetId(cluster.Id)
280+
281+
fields := map[string]interface{}{
282+
"id": cluster.Id,
283+
"allocation_state": cluster.Allocationstate,
284+
"cluster_name": cluster.Name,
285+
"name": cluster.Name,
286+
"cluster_type": cluster.Clustertype,
287+
"hypervisor": cluster.Hypervisortype,
288+
"ovm3_vip": cluster.Ovm3vip,
289+
"ovm3vip": cluster.Ovm3vip,
290+
"pod_id": cluster.Podid,
291+
"pod_name": cluster.Podname,
292+
"zone_id": cluster.Zoneid,
293+
"zone_name": cluster.Zonename,
294+
"managed_state": cluster.Managedstate,
295+
"cpu_overcommit_ratio": cluster.Cpuovercommitratio,
296+
"memory_overcommit_ratio": cluster.Memoryovercommitratio,
297+
"arch": cluster.Arch,
298+
"capacity": dsFlattenClusterCapacity(cluster.Capacity),
299+
}
300+
301+
// Set fields that may not be available in all cluster responses to empty strings
302+
// These are typically only available during cluster creation/configuration
303+
emptyStringFields := []string{
304+
"guest_vswitch_name",
305+
"guest_vswitch_type",
306+
"ovm3_cluster",
307+
"ovm3_pool",
308+
"password",
309+
"public_vswitch_name",
310+
"public_vswitch_type",
311+
"url",
312+
"username",
313+
"vsm_ip_address",
314+
"vsm_password",
315+
"vsm_username",
316+
}
317+
318+
for k, v := range fields {
319+
if err := d.Set(k, v); err != nil {
320+
log.Printf("[WARN] Error setting %s: %s", k, err)
321+
}
322+
}
323+
324+
for _, field := range emptyStringFields {
325+
if err := d.Set(field, ""); err != nil {
326+
log.Printf("[WARN] Error setting %s: %s", field, err)
327+
}
328+
}
329+
330+
return nil
331+
}
332+
333+
func applyClusterFilters(cluster *cloudstack.Cluster, filters *schema.Set) (bool, error) {
334+
val := reflect.ValueOf(cluster).Elem()
335+
336+
for _, f := range filters.List() {
337+
filter := f.(map[string]interface{})
338+
r, err := regexp.Compile(filter["value"].(string))
339+
if err != nil {
340+
return false, fmt.Errorf("invalid regex: %s", err)
341+
}
342+
updatedName := strings.ReplaceAll(filter["name"].(string), "_", "")
343+
clusterField := val.FieldByNameFunc(func(fieldName string) bool {
344+
if strings.EqualFold(fieldName, updatedName) {
345+
updatedName = fieldName
346+
return true
347+
}
348+
return false
349+
}).String()
350+
351+
if r.MatchString(clusterField) {
352+
return true, nil
353+
}
354+
}
355+
356+
return false, nil
357+
}

0 commit comments

Comments
 (0)