Skip to content

Commit 5105978

Browse files
authored
Add support for snapshot policies (#223)
* Add support for snapshot policies * fix test * update doc * fix test * fix test * update test * update test to use cardinals as opposed to frequency of interval type * add forceNew if the policy is modified to recreate the resource + fix test * update test * update test * test * handle tags
1 parent 96f287a commit 5105978

File tree

4 files changed

+853
-0
lines changed

4 files changed

+853
-0
lines changed

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ func Provider() *schema.Provider {
149149
"cloudstack_network_service_provider": resourceCloudStackNetworkServiceProvider(),
150150
"cloudstack_role": resourceCloudStackRole(),
151151
"cloudstack_limits": resourceCloudStackLimits(),
152+
"cloudstack_snapshot_policy": resourceCloudStackSnapshotPolicy(),
152153
},
153154

154155
ConfigureFunc: providerConfigure,
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
28+
)
29+
30+
func intervalTypeToString(intervalType int) string {
31+
switch intervalType {
32+
case 0:
33+
return "HOURLY"
34+
case 1:
35+
return "DAILY"
36+
case 2:
37+
return "WEEKLY"
38+
case 3:
39+
return "MONTHLY"
40+
default:
41+
return fmt.Sprintf("%d", intervalType)
42+
}
43+
}
44+
45+
func resourceCloudStackSnapshotPolicy() *schema.Resource {
46+
return &schema.Resource{
47+
Create: resourceCloudstackSnapshotPolicyCreate,
48+
Read: resourceCloudstackSnapshotPolicyRead,
49+
Update: resourceCloudstackSnapshotPolicyUpdate,
50+
Delete: resourceCloudstackSnapshotPolicyDelete,
51+
52+
Schema: map[string]*schema.Schema{
53+
"volume_id": {
54+
Type: schema.TypeString,
55+
Required: true,
56+
ForceNew: true,
57+
},
58+
"interval_type": {
59+
Type: schema.TypeString,
60+
Required: true,
61+
ForceNew: true,
62+
},
63+
"max_snaps": {
64+
Type: schema.TypeInt,
65+
Required: true,
66+
ForceNew: true,
67+
},
68+
"schedule": {
69+
Type: schema.TypeString,
70+
Required: true,
71+
ForceNew: true,
72+
},
73+
"timezone": {
74+
Type: schema.TypeString,
75+
Required: true,
76+
ForceNew: true,
77+
},
78+
"zone_ids": {
79+
Type: schema.TypeList,
80+
Optional: true,
81+
ForceNew: true,
82+
Elem: &schema.Schema{
83+
Type: schema.TypeString,
84+
},
85+
},
86+
"custom_id": {
87+
Type: schema.TypeString,
88+
Optional: true,
89+
ForceNew: true,
90+
},
91+
"tags": tagsSchema(),
92+
},
93+
}
94+
}
95+
96+
func resourceCloudstackSnapshotPolicyCreate(d *schema.ResourceData, meta interface{}) error {
97+
cs := meta.(*cloudstack.CloudStackClient)
98+
99+
p := cs.Snapshot.NewCreateSnapshotPolicyParams(
100+
d.Get("interval_type").(string),
101+
d.Get("max_snaps").(int),
102+
d.Get("schedule").(string),
103+
d.Get("timezone").(string),
104+
d.Get("volume_id").(string),
105+
)
106+
107+
if v, ok := d.GetOk("zone_ids"); ok && v != nil {
108+
zoneIDs := []string{}
109+
for _, id := range v.([]interface{}) {
110+
zoneIDs = append(zoneIDs, id.(string))
111+
}
112+
p.SetZoneids(zoneIDs)
113+
}
114+
115+
snapshotPolicy, err := cs.Snapshot.CreateSnapshotPolicy(p)
116+
if err != nil {
117+
return fmt.Errorf("Error creating snapshot policy: %s", err)
118+
}
119+
120+
log.Printf("[DEBUG] CreateSnapshotPolicy response: %+v", snapshotPolicy)
121+
122+
if snapshotPolicy.Id == "" {
123+
log.Printf("[DEBUG] CloudStack returned empty ID, trying to find created policy by volume ID")
124+
125+
listParams := cs.Snapshot.NewListSnapshotPoliciesParams()
126+
listParams.SetVolumeid(d.Get("volume_id").(string))
127+
128+
resp, listErr := cs.Snapshot.ListSnapshotPolicies(listParams)
129+
if listErr != nil {
130+
return fmt.Errorf("Error listing snapshot policies to find created policy: %s", listErr)
131+
}
132+
133+
if resp.Count == 0 {
134+
return fmt.Errorf("No snapshot policies found for volume after creation")
135+
}
136+
137+
foundPolicy := resp.SnapshotPolicies[resp.Count-1]
138+
log.Printf("[DEBUG] Found policy with ID: %s", foundPolicy.Id)
139+
d.SetId(foundPolicy.Id)
140+
} else {
141+
d.SetId(snapshotPolicy.Id)
142+
}
143+
144+
log.Printf("[DEBUG] Snapshot policy created with ID: %s", d.Id())
145+
146+
// Set tags if provided
147+
if err := setTags(cs, d, "SnapshotPolicy"); err != nil {
148+
return fmt.Errorf("Error setting tags on snapshot policy %s: %s", d.Id(), err)
149+
}
150+
151+
return resourceCloudstackSnapshotPolicyRead(d, meta)
152+
}
153+
154+
func resourceCloudstackSnapshotPolicyRead(d *schema.ResourceData, meta interface{}) error {
155+
cs := meta.(*cloudstack.CloudStackClient)
156+
157+
if d.Id() == "" {
158+
log.Printf("[DEBUG] Snapshot policy ID is empty")
159+
return fmt.Errorf("Snapshot policy ID is empty")
160+
}
161+
162+
p := cs.Snapshot.NewListSnapshotPoliciesParams()
163+
p.SetId(d.Id())
164+
165+
resp, err := cs.Snapshot.ListSnapshotPolicies(p)
166+
if err != nil {
167+
return fmt.Errorf("Failed to list snapshot policies: %s", err)
168+
}
169+
170+
if resp.Count == 0 {
171+
log.Printf("[DEBUG] Snapshot policy %s not found, removing from state", d.Id())
172+
d.SetId("")
173+
return nil
174+
}
175+
176+
snapshotPolicy := resp.SnapshotPolicies[0]
177+
178+
d.Set("volume_id", snapshotPolicy.Volumeid)
179+
d.Set("interval_type", intervalTypeToString(snapshotPolicy.Intervaltype))
180+
d.Set("max_snaps", snapshotPolicy.Maxsnaps)
181+
d.Set("schedule", snapshotPolicy.Schedule)
182+
d.Set("timezone", snapshotPolicy.Timezone)
183+
184+
if snapshotPolicy.Zone != nil {
185+
zoneIDs := []string{}
186+
for _, zone := range snapshotPolicy.Zone {
187+
if zoneMap, ok := zone.(map[string]interface{}); ok {
188+
if id, ok := zoneMap["id"].(string); ok {
189+
zoneIDs = append(zoneIDs, id)
190+
}
191+
}
192+
}
193+
d.Set("zone_ids", zoneIDs)
194+
} else {
195+
d.Set("zone_ids", nil)
196+
}
197+
198+
// Handle tags
199+
tags := make(map[string]interface{})
200+
for _, tag := range snapshotPolicy.Tags {
201+
tags[tag.Key] = tag.Value
202+
}
203+
d.Set("tags", tags)
204+
205+
return nil
206+
}
207+
208+
func resourceCloudstackSnapshotPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
209+
cs := meta.(*cloudstack.CloudStackClient)
210+
211+
p := cs.Snapshot.NewUpdateSnapshotPolicyParams()
212+
p.SetId(d.Id())
213+
if v, ok := d.GetOk("custom_id"); ok {
214+
p.SetCustomid(v.(string))
215+
}
216+
217+
_, err := cs.Snapshot.UpdateSnapshotPolicy(p)
218+
if err != nil {
219+
return err
220+
}
221+
222+
// Handle tags
223+
if d.HasChange("tags") {
224+
if err := updateTags(cs, d, "SnapshotPolicy"); err != nil {
225+
return fmt.Errorf("Error updating tags on snapshot policy %s: %s", d.Id(), err)
226+
}
227+
}
228+
229+
return resourceCloudstackSnapshotPolicyRead(d, meta)
230+
}
231+
232+
func resourceCloudstackSnapshotPolicyDelete(d *schema.ResourceData, meta interface{}) error {
233+
cs := meta.(*cloudstack.CloudStackClient)
234+
235+
p := cs.Snapshot.NewDeleteSnapshotPoliciesParams()
236+
p.SetId(d.Id())
237+
238+
_, err := cs.Snapshot.DeleteSnapshotPolicies(p)
239+
if err != nil {
240+
return fmt.Errorf("Failed to delete snapshot policy %s: %s", d.Id(), err)
241+
}
242+
243+
d.SetId("")
244+
245+
return nil
246+
}

0 commit comments

Comments
 (0)