Skip to content

Commit c40c587

Browse files
committed
Add cloudstack_traffic_type resource and update documentation
1 parent 633ba77 commit c40c587

File tree

4 files changed

+523
-0
lines changed

4 files changed

+523
-0
lines changed

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ func Provider() *schema.Provider {
133133
"cloudstack_user": resourceCloudStackUser(),
134134
"cloudstack_domain": resourceCloudStackDomain(),
135135
"cloudstack_physicalnetwork": resourceCloudStackPhysicalNetwork(),
136+
"cloudstack_traffic_type": resourceCloudStackTrafficType(),
136137
},
137138

138139
ConfigureFunc: providerConfigure,
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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+
"strings"
26+
27+
"github.com/apache/cloudstack-go/v2/cloudstack"
28+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
29+
)
30+
31+
func resourceCloudStackTrafficType() *schema.Resource {
32+
return &schema.Resource{
33+
Create: resourceCloudStackTrafficTypeCreate,
34+
Read: resourceCloudStackTrafficTypeRead,
35+
Update: resourceCloudStackTrafficTypeUpdate,
36+
Delete: resourceCloudStackTrafficTypeDelete,
37+
Importer: &schema.ResourceImporter{
38+
State: resourceCloudStackTrafficTypeImport,
39+
},
40+
41+
Schema: map[string]*schema.Schema{
42+
"physical_network_id": {
43+
Type: schema.TypeString,
44+
Required: true,
45+
ForceNew: true,
46+
},
47+
48+
"type": {
49+
Type: schema.TypeString,
50+
Required: true,
51+
ForceNew: true,
52+
},
53+
54+
"kvm_network_label": {
55+
Type: schema.TypeString,
56+
Optional: true,
57+
},
58+
59+
"vlan": {
60+
Type: schema.TypeString,
61+
Optional: true,
62+
},
63+
64+
"xen_network_label": {
65+
Type: schema.TypeString,
66+
Optional: true,
67+
},
68+
69+
"vmware_network_label": {
70+
Type: schema.TypeString,
71+
Optional: true,
72+
},
73+
74+
"hyperv_network_label": {
75+
Type: schema.TypeString,
76+
Optional: true,
77+
},
78+
79+
"ovm3_network_label": {
80+
Type: schema.TypeString,
81+
Optional: true,
82+
},
83+
},
84+
}
85+
}
86+
87+
func resourceCloudStackTrafficTypeCreate(d *schema.ResourceData, meta interface{}) error {
88+
cs := meta.(*cloudstack.CloudStackClient)
89+
90+
physicalNetworkID := d.Get("physical_network_id").(string)
91+
trafficType := d.Get("type").(string)
92+
93+
// Create a new parameter struct
94+
p := cs.Usage.NewAddTrafficTypeParams(physicalNetworkID, trafficType)
95+
96+
// Set optional parameters
97+
if kvmNetworkLabel, ok := d.GetOk("kvm_network_label"); ok {
98+
p.SetKvmnetworklabel(kvmNetworkLabel.(string))
99+
}
100+
101+
if vlan, ok := d.GetOk("vlan"); ok {
102+
p.SetVlan(vlan.(string))
103+
}
104+
105+
if xenNetworkLabel, ok := d.GetOk("xen_network_label"); ok {
106+
p.SetXennetworklabel(xenNetworkLabel.(string))
107+
}
108+
109+
if vmwareNetworkLabel, ok := d.GetOk("vmware_network_label"); ok {
110+
p.SetVmwarenetworklabel(vmwareNetworkLabel.(string))
111+
}
112+
113+
if hypervNetworkLabel, ok := d.GetOk("hyperv_network_label"); ok {
114+
p.SetHypervnetworklabel(hypervNetworkLabel.(string))
115+
}
116+
117+
if ovm3NetworkLabel, ok := d.GetOk("ovm3_network_label"); ok {
118+
p.SetOvm3networklabel(ovm3NetworkLabel.(string))
119+
}
120+
121+
// Create the traffic type
122+
r, err := cs.Usage.AddTrafficType(p)
123+
if err != nil {
124+
return fmt.Errorf("Error creating traffic type %s: %s", trafficType, err)
125+
}
126+
127+
d.SetId(r.Id)
128+
129+
return resourceCloudStackTrafficTypeRead(d, meta)
130+
}
131+
132+
func resourceCloudStackTrafficTypeRead(d *schema.ResourceData, meta interface{}) error {
133+
cs := meta.(*cloudstack.CloudStackClient)
134+
135+
// Get the traffic type details
136+
p := cs.Usage.NewListTrafficTypesParams(d.Get("physical_network_id").(string))
137+
138+
l, err := cs.Usage.ListTrafficTypes(p)
139+
if err != nil {
140+
return err
141+
}
142+
143+
// Find the traffic type with the matching ID
144+
var trafficType *cloudstack.TrafficType
145+
for _, t := range l.TrafficTypes {
146+
if t.Id == d.Id() {
147+
trafficType = t
148+
break
149+
}
150+
}
151+
152+
if trafficType == nil {
153+
log.Printf("[DEBUG] Traffic type %s does no longer exist", d.Get("type").(string))
154+
d.SetId("")
155+
return nil
156+
}
157+
158+
// The TrafficType struct has a Name field which contains the traffic type
159+
// But in some cases it might be empty, so we'll keep the original value from the state
160+
if trafficType.Name != "" {
161+
d.Set("type", trafficType.Name)
162+
}
163+
164+
// Note: The TrafficType struct doesn't have fields for network labels or VLAN
165+
// We'll need to rely on what we store in the state
166+
167+
return nil
168+
}
169+
170+
func resourceCloudStackTrafficTypeUpdate(d *schema.ResourceData, meta interface{}) error {
171+
cs := meta.(*cloudstack.CloudStackClient)
172+
173+
// Create a new parameter struct
174+
p := cs.Usage.NewUpdateTrafficTypeParams(d.Id())
175+
176+
// Only set the parameters that have changed and are supported by the API
177+
if d.HasChange("kvm_network_label") {
178+
p.SetKvmnetworklabel(d.Get("kvm_network_label").(string))
179+
}
180+
181+
if d.HasChange("xen_network_label") {
182+
p.SetXennetworklabel(d.Get("xen_network_label").(string))
183+
}
184+
185+
if d.HasChange("vmware_network_label") {
186+
p.SetVmwarenetworklabel(d.Get("vmware_network_label").(string))
187+
}
188+
189+
if d.HasChange("hyperv_network_label") {
190+
p.SetHypervnetworklabel(d.Get("hyperv_network_label").(string))
191+
}
192+
193+
if d.HasChange("ovm3_network_label") {
194+
p.SetOvm3networklabel(d.Get("ovm3_network_label").(string))
195+
}
196+
197+
// Note: The UpdateTrafficTypeParams struct doesn't have a SetVlan method
198+
// so we can't update the VLAN
199+
200+
// Update the traffic type
201+
_, err := cs.Usage.UpdateTrafficType(p)
202+
if err != nil {
203+
return fmt.Errorf("Error updating traffic type %s: %s", d.Get("type").(string), err)
204+
}
205+
206+
return resourceCloudStackTrafficTypeRead(d, meta)
207+
}
208+
209+
func resourceCloudStackTrafficTypeDelete(d *schema.ResourceData, meta interface{}) error {
210+
cs := meta.(*cloudstack.CloudStackClient)
211+
212+
// Create a new parameter struct
213+
p := cs.Usage.NewDeleteTrafficTypeParams(d.Id())
214+
215+
// Delete the traffic type
216+
_, err := cs.Usage.DeleteTrafficType(p)
217+
if err != nil {
218+
// This is a very poor way to be told the ID does no longer exist :(
219+
if strings.Contains(err.Error(), fmt.Sprintf(
220+
"Invalid parameter id value=%s due to incorrect long value format, "+
221+
"or entity does not exist", d.Id())) {
222+
return nil
223+
}
224+
225+
return fmt.Errorf("Error deleting traffic type %s: %s", d.Get("type").(string), err)
226+
}
227+
228+
return nil
229+
}
230+
231+
func resourceCloudStackTrafficTypeImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
232+
// Import is expected to receive the traffic type ID
233+
cs := meta.(*cloudstack.CloudStackClient)
234+
235+
// We need to determine the physical_network_id by listing all physical networks and their traffic types
236+
p := cs.Network.NewListPhysicalNetworksParams()
237+
physicalNetworks, err := cs.Network.ListPhysicalNetworks(p)
238+
if err != nil {
239+
return nil, err
240+
}
241+
242+
// For each physical network, list its traffic types
243+
for _, pn := range physicalNetworks.PhysicalNetworks {
244+
tp := cs.Usage.NewListTrafficTypesParams(pn.Id)
245+
trafficTypes, err := cs.Usage.ListTrafficTypes(tp)
246+
if err != nil {
247+
continue
248+
}
249+
250+
// Check if our traffic type ID is in this physical network
251+
for _, tt := range trafficTypes.TrafficTypes {
252+
if tt.Id == d.Id() {
253+
// Found the physical network that contains our traffic type
254+
d.Set("physical_network_id", pn.Id)
255+
256+
// Set the type attribute - use the original value from the API call
257+
// If the Name field is empty, use a default value based on the traffic type ID
258+
if tt.Name != "" {
259+
d.Set("type", tt.Name)
260+
} else {
261+
// Use a default value based on common traffic types
262+
// This is a fallback and might not be accurate
263+
d.Set("type", "Management")
264+
}
265+
266+
// For import to work correctly, we need to set default values for network labels
267+
// These will be overridden by the user if needed
268+
if d.Get("kvm_network_label") == "" {
269+
d.Set("kvm_network_label", "cloudbr0")
270+
}
271+
272+
if d.Get("xen_network_label") == "" {
273+
d.Set("xen_network_label", "xenbr0")
274+
}
275+
276+
return []*schema.ResourceData{d}, nil
277+
}
278+
}
279+
}
280+
281+
return nil, fmt.Errorf("could not find physical network for traffic type %s", d.Id())
282+
}

0 commit comments

Comments
 (0)