Skip to content

Commit c7f0776

Browse files
Add Hosts resource and Pod data source (#69)
1 parent f08f5a5 commit c7f0776

File tree

5 files changed

+792
-0
lines changed

5 files changed

+792
-0
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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/helper/schema"
31+
)
32+
33+
func dataSourceCloudstackPod() *schema.Resource {
34+
return &schema.Resource{
35+
Read: datasourceCloudStackPodRead,
36+
Schema: map[string]*schema.Schema{
37+
"filter": dataSourceFiltersSchema(),
38+
39+
//Computed values
40+
"pod_id": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
},
44+
"name": {
45+
Type: schema.TypeString,
46+
Computed: true,
47+
},
48+
"zone_id": {
49+
Type: schema.TypeString,
50+
Computed: true,
51+
},
52+
"end_ip": {
53+
Type: schema.TypeString,
54+
Computed: true,
55+
},
56+
"gateway": {
57+
Type: schema.TypeString,
58+
Computed: true,
59+
},
60+
"netmask": {
61+
Type: schema.TypeString,
62+
Computed: true,
63+
},
64+
"start_ip": {
65+
Type: schema.TypeString,
66+
Computed: true,
67+
},
68+
"allocation_state": {
69+
Type: schema.TypeString,
70+
Computed: true,
71+
},
72+
"zone_name": {
73+
Type: schema.TypeString,
74+
Computed: true,
75+
},
76+
"vlan_id": {
77+
Type: schema.TypeString,
78+
Computed: true,
79+
},
80+
"capacity": {
81+
Type: schema.TypeList,
82+
Computed: true,
83+
Elem: &schema.Resource{
84+
Schema: map[string]*schema.Schema{
85+
"capacity_allocated": {
86+
Type: schema.TypeInt,
87+
Computed: true,
88+
},
89+
"capacity_total": {
90+
Type: schema.TypeInt,
91+
Computed: true,
92+
},
93+
"capacity_used": {
94+
Type: schema.TypeInt,
95+
Computed: true,
96+
},
97+
"cluster_id": {
98+
Type: schema.TypeString,
99+
Computed: true,
100+
},
101+
"cluster_name": {
102+
Type: schema.TypeString,
103+
Computed: true,
104+
},
105+
"name": {
106+
Type: schema.TypeString,
107+
Computed: true,
108+
},
109+
"percent_used": {
110+
Type: schema.TypeInt,
111+
Computed: true,
112+
},
113+
"pod_id": {
114+
Type: schema.TypeString,
115+
Computed: true,
116+
},
117+
"pod_name": {
118+
Type: schema.TypeString,
119+
Computed: true,
120+
},
121+
"type": {
122+
Type: schema.TypeString,
123+
Computed: true,
124+
},
125+
"zone_id": {
126+
Type: schema.TypeString,
127+
Computed: true,
128+
},
129+
"zone_name": {
130+
Type: schema.TypeString,
131+
Computed: true,
132+
},
133+
},
134+
},
135+
},
136+
"ip_ranges": {
137+
Type: schema.TypeList,
138+
Computed: true,
139+
Elem: &schema.Resource{
140+
Schema: map[string]*schema.Schema{
141+
"end_ip": {
142+
Type: schema.TypeString,
143+
Computed: true,
144+
},
145+
"gateway": {
146+
Type: schema.TypeString,
147+
Computed: true,
148+
},
149+
"for_system_vms": {
150+
Type: schema.TypeString,
151+
Computed: true,
152+
},
153+
"start_ip": {
154+
Type: schema.TypeString,
155+
Computed: true,
156+
},
157+
"vlan_id": {
158+
Type: schema.TypeString,
159+
Computed: true,
160+
},
161+
},
162+
},
163+
},
164+
},
165+
}
166+
}
167+
168+
func dsFlattenPodCapacity(capacity []cloudstack.PodCapacity) []map[string]interface{} {
169+
cap := make([]map[string]interface{}, len(capacity))
170+
for i, c := range capacity {
171+
cap[i] = map[string]interface{}{
172+
"capacity_allocated": c.Capacityallocated,
173+
"capacity_total": c.Capacitytotal,
174+
"capacity_used": c.Capacityused,
175+
"cluster_id": c.Clusterid,
176+
"cluster_name": c.Clustername,
177+
"name": c.Name,
178+
"percent_used": c.Percentused,
179+
"pod_id": c.Podid,
180+
"pod_name": c.Podname,
181+
"type": c.Type,
182+
"zone_id": c.Zoneid,
183+
"zone_name": c.Zonename,
184+
}
185+
}
186+
return cap
187+
}
188+
189+
func dsFlattenPodIpRanges(ip_ranges []cloudstack.PodIpranges) []map[string]interface{} {
190+
ranges := make([]map[string]interface{}, len(ip_ranges))
191+
for i, ip_range := range ip_ranges {
192+
ranges[i] = map[string]interface{}{
193+
"end_ip": ip_range.Endip,
194+
"for_system_vms": ip_range.Forsystemvms,
195+
"start_ip": ip_range.Startip,
196+
"vlan_id": ip_range.Vlanid,
197+
}
198+
}
199+
return ranges
200+
}
201+
202+
func datasourceCloudStackPodRead(d *schema.ResourceData, meta interface{}) error {
203+
cs := meta.(*cloudstack.CloudStackClient)
204+
p := cs.Pod.NewListPodsParams()
205+
206+
csPods, err := cs.Pod.ListPods(p)
207+
if err != nil {
208+
return fmt.Errorf("failed to list pods: %s", err)
209+
}
210+
211+
filters := d.Get("filter")
212+
213+
for _, pod := range csPods.Pods {
214+
match, err := applyPodFilters(pod, filters.(*schema.Set))
215+
if err != nil {
216+
return err
217+
}
218+
if match {
219+
return podDescriptionAttributes(d, pod)
220+
}
221+
}
222+
223+
return fmt.Errorf("no pods found")
224+
}
225+
226+
func podDescriptionAttributes(d *schema.ResourceData, pod *cloudstack.Pod) error {
227+
d.SetId(pod.Id)
228+
var end_ip string
229+
if len(pod.Endip) > 0 {
230+
end_ip = pod.Endip[0]
231+
}
232+
233+
fields := map[string]interface{}{
234+
"pod_id": pod.Id,
235+
"name": pod.Name,
236+
"allocation_state": pod.Allocationstate,
237+
"gateway": pod.Gateway,
238+
"netmask": pod.Netmask,
239+
"start_ip": pod.Startip[0],
240+
"vlan_id": pod.Vlanid[0],
241+
"zone_id": pod.Zoneid,
242+
"zone_name": pod.Zonename,
243+
"end_ip": end_ip,
244+
"ip_ranges": dsFlattenPodIpRanges(pod.Ipranges),
245+
"capacity": dsFlattenPodCapacity(pod.Capacity),
246+
}
247+
248+
for k, v := range fields {
249+
if err := d.Set(k, v); err != nil {
250+
log.Printf("[WARN] Error setting %s: %s", k, err)
251+
}
252+
}
253+
254+
return nil
255+
}
256+
257+
func applyPodFilters(pod *cloudstack.Pod, filters *schema.Set) (bool, error) {
258+
val := reflect.ValueOf(pod).Elem()
259+
260+
for _, f := range filters.List() {
261+
filter := f.(map[string]interface{})
262+
r, err := regexp.Compile(filter["value"].(string))
263+
if err != nil {
264+
return false, fmt.Errorf("invalid regex: %s", err)
265+
}
266+
updatedName := strings.ReplaceAll(filter["name"].(string), "_", "")
267+
podField := val.FieldByNameFunc(func(fieldName string) bool {
268+
if strings.EqualFold(fieldName, updatedName) {
269+
updatedName = fieldName
270+
return true
271+
}
272+
return false
273+
}).String()
274+
275+
if r.MatchString(podField) {
276+
return true, nil
277+
}
278+
}
279+
280+
return false, nil
281+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
"testing"
24+
25+
"github.com/hashicorp/terraform/helper/resource"
26+
)
27+
28+
func TestAccPodDataSource_basic(t *testing.T) {
29+
resource.Test(t, resource.TestCase{
30+
PreCheck: func() { testAccPreCheck(t) },
31+
Providers: testAccProviders,
32+
Steps: []resource.TestStep{
33+
{
34+
Config: testPodDataSourceConfig_basic,
35+
Check: resource.ComposeTestCheckFunc(
36+
resource.TestCheckResourceAttr("data.cloudstack_pod.test", "name", "POD0"),
37+
),
38+
},
39+
},
40+
})
41+
}
42+
43+
const testPodDataSourceConfig_basic = `
44+
data "cloudstack_pod" "test" {
45+
filter {
46+
name = "name"
47+
value = "POD0"
48+
}
49+
}
50+
`

cloudstack/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func Provider() terraform.ResourceProvider {
8989
"cloudstack_ipaddress": dataSourceCloudstackIPAddress(),
9090
"cloudstack_user": dataSourceCloudstackUser(),
9191
"cloudstack_vpn_connection": dataSourceCloudstackVPNConnection(),
92+
"cloudstack_pod": dataSourceCloudstackPod(),
9293
},
9394

9495
ResourcesMap: map[string]*schema.Resource{
@@ -98,6 +99,7 @@ func Provider() terraform.ResourceProvider {
9899
"cloudstack_disk": resourceCloudStackDisk(),
99100
"cloudstack_egress_firewall": resourceCloudStackEgressFirewall(),
100101
"cloudstack_firewall": resourceCloudStackFirewall(),
102+
"cloudstack_host": resourceCloudStackHost(),
101103
"cloudstack_instance": resourceCloudStackInstance(),
102104
"cloudstack_ipaddress": resourceCloudStackIPAddress(),
103105
"cloudstack_kubernetes_cluster": resourceCloudStackKubernetesCluster(),

0 commit comments

Comments
 (0)