Skip to content

Commit 049d7b6

Browse files
committed
feat: Add cloudstack_network data source and associated tests
1 parent e53f30c commit 049d7b6

4 files changed

Lines changed: 632 additions & 0 deletions

File tree

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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+
"encoding/json"
24+
"fmt"
25+
"log"
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 dataSourceCloudstackNetwork() *schema.Resource {
34+
return &schema.Resource{
35+
Read: dataSourceCloudstackNetworkRead,
36+
Schema: map[string]*schema.Schema{
37+
"filter": dataSourceFiltersSchema(),
38+
39+
"zone_id": {
40+
Type: schema.TypeString,
41+
Optional: true,
42+
},
43+
44+
"project_id": {
45+
Type: schema.TypeString,
46+
Optional: true,
47+
},
48+
49+
"vpc_id": {
50+
Type: schema.TypeString,
51+
Optional: true,
52+
},
53+
54+
// Computed values
55+
"name": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
},
59+
60+
"display_text": {
61+
Type: schema.TypeString,
62+
Computed: true,
63+
},
64+
65+
"cidr": {
66+
Type: schema.TypeString,
67+
Computed: true,
68+
},
69+
70+
"gateway": {
71+
Type: schema.TypeString,
72+
Computed: true,
73+
},
74+
75+
"netmask": {
76+
Type: schema.TypeString,
77+
Computed: true,
78+
},
79+
80+
"vlan": {
81+
Type: schema.TypeString,
82+
Computed: true,
83+
},
84+
85+
"network_offering_id": {
86+
Type: schema.TypeString,
87+
Computed: true,
88+
},
89+
90+
"network_offering_name": {
91+
Type: schema.TypeString,
92+
Computed: true,
93+
},
94+
95+
"network_domain": {
96+
Type: schema.TypeString,
97+
Computed: true,
98+
},
99+
100+
"acl_id": {
101+
Type: schema.TypeString,
102+
Computed: true,
103+
},
104+
105+
"acl_name": {
106+
Type: schema.TypeString,
107+
Computed: true,
108+
},
109+
110+
"zone_name": {
111+
Type: schema.TypeString,
112+
Computed: true,
113+
},
114+
115+
"project": {
116+
Type: schema.TypeString,
117+
Computed: true,
118+
},
119+
120+
"vpc_name": {
121+
Type: schema.TypeString,
122+
Computed: true,
123+
},
124+
125+
"state": {
126+
Type: schema.TypeString,
127+
Computed: true,
128+
},
129+
130+
"type": {
131+
Type: schema.TypeString,
132+
Computed: true,
133+
},
134+
135+
"traffic_type": {
136+
Type: schema.TypeString,
137+
Computed: true,
138+
},
139+
140+
"is_default": {
141+
Type: schema.TypeBool,
142+
Computed: true,
143+
},
144+
145+
"is_persistent": {
146+
Type: schema.TypeBool,
147+
Computed: true,
148+
},
149+
150+
"tags": tagsSchema(),
151+
},
152+
}
153+
}
154+
155+
func dataSourceCloudstackNetworkRead(d *schema.ResourceData, meta interface{}) error {
156+
cs := meta.(*cloudstack.CloudStackClient)
157+
p := cs.Network.NewListNetworksParams()
158+
159+
// Set optional filters
160+
if zoneID, ok := d.GetOk("zone_id"); ok {
161+
p.SetZoneid(zoneID.(string))
162+
}
163+
164+
if projectID, ok := d.GetOk("project_id"); ok {
165+
p.SetProjectid(projectID.(string))
166+
}
167+
168+
if vpcID, ok := d.GetOk("vpc_id"); ok {
169+
p.SetVpcid(vpcID.(string))
170+
}
171+
172+
// List all networks
173+
csNetworks, err := cs.Network.ListNetworks(p)
174+
if err != nil {
175+
return fmt.Errorf("Failed to list networks: %s", err)
176+
}
177+
178+
filters := d.Get("filter")
179+
var networks []*cloudstack.Network
180+
181+
for _, n := range csNetworks.Networks {
182+
match, err := applyNetworkFilters(n, filters.(*schema.Set))
183+
if err != nil {
184+
return err
185+
}
186+
if match {
187+
networks = append(networks, n)
188+
}
189+
}
190+
191+
if len(networks) == 0 {
192+
return fmt.Errorf("No network matching the specified filters")
193+
}
194+
195+
if len(networks) > 1 {
196+
return fmt.Errorf("More than one network found matching the specified filters. Please refine your filters to match a single network")
197+
}
198+
199+
network := networks[0]
200+
log.Printf("[DEBUG] Selected network: %s\n", network.Name)
201+
202+
return networkDescriptionAttributes(d, network)
203+
}
204+
205+
func networkDescriptionAttributes(d *schema.ResourceData, network *cloudstack.Network) error {
206+
d.SetId(network.Id)
207+
d.Set("name", network.Name)
208+
d.Set("display_text", network.Displaytext)
209+
d.Set("cidr", network.Cidr)
210+
d.Set("gateway", network.Gateway)
211+
d.Set("netmask", network.Netmask)
212+
d.Set("vlan", network.Vlan)
213+
d.Set("network_offering_id", network.Networkofferingid)
214+
d.Set("network_offering_name", network.Networkofferingname)
215+
d.Set("network_domain", network.Networkdomain)
216+
d.Set("acl_id", network.Aclid)
217+
d.Set("acl_name", network.Aclname)
218+
d.Set("zone_id", network.Zoneid)
219+
d.Set("zone_name", network.Zonename)
220+
d.Set("project", network.Project)
221+
d.Set("project_id", network.Projectid)
222+
d.Set("vpc_id", network.Vpcid)
223+
d.Set("vpc_name", network.Vpcname)
224+
d.Set("state", network.State)
225+
d.Set("type", network.Type)
226+
d.Set("traffic_type", network.Traffictype)
227+
d.Set("is_default", network.Isdefault)
228+
d.Set("is_persistent", network.Ispersistent)
229+
d.Set("tags", tagsToMap(network.Tags))
230+
231+
return nil
232+
}
233+
234+
func applyNetworkFilters(network *cloudstack.Network, filters *schema.Set) (bool, error) {
235+
var networkJSON map[string]interface{}
236+
k, _ := json.Marshal(network)
237+
err := json.Unmarshal(k, &networkJSON)
238+
if err != nil {
239+
return false, err
240+
}
241+
242+
for _, f := range filters.List() {
243+
m := f.(map[string]interface{})
244+
log.Printf("[DEBUG] Applying filter: %v", m)
245+
246+
r, err := regexp.Compile(m["value"].(string))
247+
if err != nil {
248+
return false, fmt.Errorf("Invalid regex: %s", err)
249+
}
250+
251+
updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
252+
253+
// Check if the field exists in the JSON
254+
networkField, ok := networkJSON[updatedName]
255+
if !ok {
256+
log.Printf("[DEBUG] Field %s not found in network", updatedName)
257+
return false, nil
258+
}
259+
260+
// Handle different field types
261+
var fieldValue string
262+
switch v := networkField.(type) {
263+
case string:
264+
fieldValue = v
265+
case bool:
266+
fieldValue = fmt.Sprintf("%t", v)
267+
case float64:
268+
fieldValue = fmt.Sprintf("%v", v)
269+
default:
270+
log.Printf("[DEBUG] Unsupported field type for %s", updatedName)
271+
return false, nil
272+
}
273+
274+
if !r.MatchString(fieldValue) {
275+
return false, nil
276+
}
277+
}
278+
279+
return true, nil
280+
}

0 commit comments

Comments
 (0)