Skip to content

Commit 2d1d47f

Browse files
committed
Added LoadBalancer support for Cloudstack Provider
PR is complete with docs and tests
1 parent 53499a9 commit 2d1d47f

5 files changed

Lines changed: 542 additions & 0 deletions

File tree

builtin/providers/cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func Provider() terraform.ResourceProvider {
4646
"cloudstack_firewall": resourceCloudStackFirewall(),
4747
"cloudstack_instance": resourceCloudStackInstance(),
4848
"cloudstack_ipaddress": resourceCloudStackIPAddress(),
49+
"cloudstack_loadbalancer_rule": resourceCloudStackLoadBalancerRule(),
4950
"cloudstack_network": resourceCloudStackNetwork(),
5051
"cloudstack_network_acl": resourceCloudStackNetworkACL(),
5152
"cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(),
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package cloudstack
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strings"
7+
8+
"github.com/hashicorp/terraform/helper/schema"
9+
"github.com/xanzy/go-cloudstack/cloudstack"
10+
)
11+
12+
func resourceCloudStackLoadBalancerRule() *schema.Resource {
13+
return &schema.Resource{
14+
Create: resourceCloudStackLoadBalancerRuleCreate,
15+
Read: resourceCloudStackLoadBalancerRuleRead,
16+
Update: resourceCloudStackLoadBalancerRuleUpdate,
17+
Delete: resourceCloudStackLoadBalancerRuleDelete,
18+
19+
Schema: map[string]*schema.Schema{
20+
"name": &schema.Schema{
21+
Type: schema.TypeString,
22+
Required: true,
23+
},
24+
25+
"description": &schema.Schema{
26+
Type: schema.TypeString,
27+
Optional: true,
28+
Computed: true,
29+
},
30+
31+
"ipaddress": &schema.Schema{
32+
Type: schema.TypeString,
33+
Required: true,
34+
ForceNew: true,
35+
},
36+
37+
"network": &schema.Schema{
38+
Type: schema.TypeString,
39+
Optional: true,
40+
ForceNew: true,
41+
},
42+
43+
"algorithm": &schema.Schema{
44+
Type: schema.TypeString,
45+
Required: true,
46+
},
47+
48+
"private_port": &schema.Schema{
49+
Type: schema.TypeInt,
50+
Required: true,
51+
ForceNew: true,
52+
},
53+
54+
"public_port": &schema.Schema{
55+
Type: schema.TypeInt,
56+
Required: true,
57+
ForceNew: true,
58+
},
59+
60+
"members": &schema.Schema{
61+
Type: schema.TypeList,
62+
Required: true,
63+
ForceNew: true,
64+
Elem: &schema.Schema{Type: schema.TypeString},
65+
},
66+
},
67+
}
68+
}
69+
70+
func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta interface{}) error {
71+
cs := meta.(*cloudstack.CloudStackClient)
72+
d.Partial(true)
73+
74+
// Create a new parameter struct
75+
p := cs.LoadBalancer.NewCreateLoadBalancerRuleParams(
76+
d.Get("algorithm").(string),
77+
d.Get("name").(string),
78+
d.Get("private_port").(int),
79+
d.Get("public_port").(int),
80+
)
81+
82+
// Set the description
83+
if description, ok := d.GetOk("description"); ok {
84+
p.SetDescription(description.(string))
85+
} else {
86+
p.SetDescription(d.Get("name").(string))
87+
}
88+
89+
// Retrieve the network and the UUID
90+
if network, ok := d.GetOk("network"); ok {
91+
networkid, e := retrieveUUID(cs, "network", network.(string))
92+
if e != nil {
93+
return e.Error()
94+
}
95+
96+
// Set the default network ID
97+
p.SetNetworkid(networkid)
98+
}
99+
100+
// Retrieve the ipaddress UUID
101+
ipaddressid, e := retrieveUUID(cs, "ipaddress", d.Get("ipaddress").(string))
102+
if e != nil {
103+
return e.Error()
104+
}
105+
p.SetPublicipid(ipaddressid)
106+
107+
// Create the load balancer rule
108+
r, err := cs.LoadBalancer.CreateLoadBalancerRule(p)
109+
if err != nil {
110+
return err
111+
}
112+
113+
// Set the load balancer rule UUID and set partials
114+
d.SetId(r.Id)
115+
d.SetPartial("name")
116+
d.SetPartial("description")
117+
d.SetPartial("ipaddress")
118+
d.SetPartial("network")
119+
d.SetPartial("algorithm")
120+
d.SetPartial("private_port")
121+
d.SetPartial("public_port")
122+
123+
// Create a new parameter struct
124+
ap := cs.LoadBalancer.NewAssignToLoadBalancerRuleParams(r.Id)
125+
126+
var mbs []string
127+
for _, id := range d.Get("members").([]interface{}) {
128+
mbs = append(mbs, id.(string))
129+
}
130+
131+
ap.SetVirtualmachineids(mbs)
132+
133+
_, err = cs.LoadBalancer.AssignToLoadBalancerRule(ap)
134+
if err != nil {
135+
return err
136+
}
137+
138+
d.SetPartial("members")
139+
140+
d.Partial(false)
141+
return resourceCloudStackLoadBalancerRuleRead(d, meta)
142+
}
143+
144+
func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) error {
145+
cs := meta.(*cloudstack.CloudStackClient)
146+
147+
// Get the load balancer details
148+
lb, count, err := cs.LoadBalancer.GetLoadBalancerRuleByID(d.Id())
149+
if err != nil {
150+
if count == 0 {
151+
log.Printf("[DEBUG] Load balancer rule %s does no longer exist", d.Get("name").(string))
152+
d.SetId("")
153+
return nil
154+
}
155+
156+
return err
157+
}
158+
159+
d.Set("algorithm", lb.Algorithm)
160+
d.Set("public_port", lb.Publicport)
161+
d.Set("private_port", lb.Privateport)
162+
163+
// Get the network details
164+
network, _, err := cs.Network.GetNetworkByID(lb.Networkid)
165+
if err != nil {
166+
return err
167+
}
168+
169+
setValueOrUUID(d, "ipaddress", lb.Publicip, lb.Publicipid)
170+
setValueOrUUID(d, "network", network.Name, lb.Networkid)
171+
172+
return nil
173+
}
174+
175+
func resourceCloudStackLoadBalancerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
176+
cs := meta.(*cloudstack.CloudStackClient)
177+
178+
if d.HasChange("name") || d.HasChange("description") || d.HasChange("algorithm") {
179+
name := d.Get("name").(string)
180+
181+
// Create new parameter struct
182+
p := cs.LoadBalancer.NewUpdateLoadBalancerRuleParams(d.Id())
183+
184+
if d.HasChange("name") {
185+
log.Printf("[DEBUG] Name has changed for load balancer rule %s, starting update", name)
186+
187+
p.SetName(name)
188+
}
189+
190+
if d.HasChange("description") {
191+
log.Printf(
192+
"[DEBUG] Description has changed for load balancer rule %s, starting update", name)
193+
194+
p.SetDescription(d.Get("description").(string))
195+
}
196+
197+
if d.HasChange("algorithm") {
198+
algorithm := d.Get("algorithm").(string)
199+
200+
log.Printf(
201+
"[DEBUG] Algorithm has changed to %s for load balancer rule %s, starting update",
202+
algorithm,
203+
name,
204+
)
205+
206+
// Set the new Algorithm
207+
p.SetAlgorithm(algorithm)
208+
}
209+
210+
_, err := cs.LoadBalancer.UpdateLoadBalancerRule(p)
211+
if err != nil {
212+
return fmt.Errorf(
213+
"Error updating load balancer rule %s", name)
214+
}
215+
}
216+
return resourceCloudStackLoadBalancerRuleRead(d, meta)
217+
}
218+
219+
func resourceCloudStackLoadBalancerRuleDelete(d *schema.ResourceData, meta interface{}) error {
220+
cs := meta.(*cloudstack.CloudStackClient)
221+
222+
// Create a new parameter struct
223+
p := cs.LoadBalancer.NewDeleteLoadBalancerRuleParams(d.Id())
224+
225+
log.Printf("[INFO] Deleting load balancer rule: %s", d.Get("name").(string))
226+
if _, err := cs.LoadBalancer.DeleteLoadBalancerRule(p); err != nil {
227+
// This is a very poor way to be told the UUID does no longer exist :(
228+
if !strings.Contains(err.Error(), fmt.Sprintf(
229+
"Invalid parameter id value=%s due to incorrect long value format, "+
230+
"or entity does not exist", d.Id())) {
231+
return err
232+
}
233+
}
234+
235+
return nil
236+
}

0 commit comments

Comments
 (0)