forked from Telmate/proxmox-api-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnapshot.go
214 lines (190 loc) · 7.33 KB
/
snapshot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package proxmox
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"unicode"
)
type ConfigSnapshot struct {
Name SnapshotName `json:"name,omitempty"`
Description string `json:"description,omitempty"`
VmState bool `json:"ram,omitempty"`
}
// TODO write tests for this
func (config ConfigSnapshot) mapToApiValues() map[string]interface{} {
return map[string]interface{}{
"snapname": config.Name,
"description": config.Description,
"vmstate": config.VmState,
}
}
// Creates a snapshot and validates the input
func (config ConfigSnapshot) Create(c *Client, vmr *VmRef) (err error) {
if err = c.CheckVmRef(vmr); err != nil {
return
}
if err = config.Validate(); err != nil {
return
}
return config.Create_Unsafe(c, vmr)
}
// Create a snapshot without validating the input, use ConfigSnapshot.Create() to validate the input.
func (config ConfigSnapshot) Create_Unsafe(c *Client, vmr *VmRef) error {
params := config.mapToApiValues()
_, err := c.PostWithTask(params, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/")
if err != nil {
params, _ := json.Marshal(¶ms)
return fmt.Errorf("error creating Snapshot: %v, (params: %v)", err, string(params))
}
return nil
}
// deprecated use ConfigSnapshot.Create() instead
func (config ConfigSnapshot) CreateSnapshot(c *Client, vmr *VmRef) error {
return config.Create(c, vmr)
}
func (config ConfigSnapshot) Validate() error {
return config.Name.Validate()
}
type rawSnapshots []interface{}
func ListSnapshots(c *Client, vmr *VmRef) (rawSnapshots, error) {
if err := c.CheckVmRef(vmr); err != nil {
return nil, err
}
return c.GetItemConfigInterfaceArray("/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/", "Guest", "SNAPSHOTS")
}
// Updates the description of the specified snapshot, same as SnapshotName.UpdateDescription()
func UpdateSnapshotDescription(c *Client, vmr *VmRef, snapshot SnapshotName, description string) (err error) {
return snapshot.UpdateDescription(c, vmr, description)
}
// Deletes a snapshot, same as SnapshotName.Delete()
func DeleteSnapshot(c *Client, vmr *VmRef, snapshot SnapshotName) (exitStatus string, err error) {
return snapshot.Delete(c, vmr)
}
// Rollback to a snapshot, same as SnapshotName.Rollback()
func RollbackSnapshot(c *Client, vmr *VmRef, snapshot SnapshotName) (exitStatus string, err error) {
return snapshot.Rollback(c, vmr)
}
// Used for formatting the output when retrieving snapshots
type Snapshot struct {
Name SnapshotName `json:"name"`
SnapTime uint `json:"time,omitempty"`
Description string `json:"description,omitempty"`
VmState bool `json:"ram,omitempty"`
Children []*Snapshot `json:"children,omitempty"`
Parent SnapshotName `json:"parent,omitempty"`
}
// Formats the taskResponse as a list of snapshots
func (raw rawSnapshots) FormatSnapshotsList() (list []*Snapshot) {
list = make([]*Snapshot, len(raw))
for i, e := range raw {
list[i] = &Snapshot{}
if _, isSet := e.(map[string]interface{})["description"]; isSet {
list[i].Description = e.(map[string]interface{})["description"].(string)
}
if _, isSet := e.(map[string]interface{})["name"]; isSet {
list[i].Name = SnapshotName(e.(map[string]interface{})["name"].(string))
}
if _, isSet := e.(map[string]interface{})["parent"]; isSet {
list[i].Parent = SnapshotName(e.(map[string]interface{})["parent"].(string))
}
if _, isSet := e.(map[string]interface{})["snaptime"]; isSet {
list[i].SnapTime = uint(e.(map[string]interface{})["snaptime"].(float64))
}
if _, isSet := e.(map[string]interface{})["vmstate"]; isSet {
list[i].VmState = Itob(int(e.(map[string]interface{})["vmstate"].(float64)))
}
}
return
}
// Formats a list of snapshots as a tree of snapshots
func (raw rawSnapshots) FormatSnapshotsTree() (tree []*Snapshot) {
list := raw.FormatSnapshotsList()
for _, e := range list {
for _, ee := range list {
if e.Parent == ee.Name {
ee.Children = append(ee.Children, e)
break
}
}
}
for _, e := range list {
if e.Parent == "" {
tree = append(tree, e)
}
e.Parent = ""
}
return
}
// Minimum length of 3 characters
// Maximum length of 40 characters
// First character must be a letter
// Must only contain the following characters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
type SnapshotName string
const (
SnapshotName_Error_IllegalCharacters string = "SnapshotName must only contain the following characters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
SnapshotName_Error_MaxLength string = "SnapshotName must be at most 40 characters long"
SnapshotName_Error_MinLength string = "SnapshotName must be at least 3 characters long"
SnapshotName_Error_StartNoLetter string = "SnapshotName must start with a letter"
)
// Deletes the specified snapshot, validates the input
func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err error) {
if err = c.CheckVmRef(vmr); err != nil {
return
}
if err = snap.Validate(); err != nil {
return
}
// TODO check if snapshot exists
return snap.Delete_Unsafe(c, vmr)
}
// Deletes the specified snapshot without validating the input, use SnapshotName.Delete() to validate the input.
func (snap SnapshotName) Delete_Unsafe(c *Client, vmr *VmRef) (exitStatus string, err error) {
return c.DeleteWithTask("/nodes/" + vmr.node + "/" + vmr.vmType + "/" + strconv.Itoa(vmr.vmId) + "/snapshot/" + string(snap))
}
// Rollback to the specified snapshot, validates the input
func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err error) {
if err = c.CheckVmRef(vmr); err != nil {
return
}
if err = snap.Validate(); err != nil {
return
}
// TODO check if snapshot exists
return snap.Rollback_Unsafe(c, vmr)
}
// Rollback to the specified snapshot without validating the input, use SnapshotName.Rollback() to validate the input.
func (snap SnapshotName) Rollback_Unsafe(c *Client, vmr *VmRef) (exitStatus string, err error) {
return c.PostWithTask(nil, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.FormatInt(int64(vmr.vmId), 10)+"/snapshot/"+string(snap)+"/rollback")
}
// Updates the description of the specified snapshot, validates the input
func (snap SnapshotName) UpdateDescription(c *Client, vmr *VmRef, description string) (err error) {
if err = c.CheckVmRef(vmr); err != nil {
return
}
if err = snap.Validate(); err != nil {
return
}
// TODO check if snapshot exists
return snap.UpdateDescription_Unsafe(c, vmr, description)
}
// Updates the description of the specified snapshot without validating the input, use SnapshotName.UpdateDescription() to validate the input.
func (snap SnapshotName) UpdateDescription_Unsafe(c *Client, vmr *VmRef, description string) error {
return c.Put(map[string]interface{}{"description": description}, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/"+string(snap)+"/config")
}
func (name SnapshotName) Validate() error {
regex, _ := regexp.Compile(`^([a-zA-Z])([a-z]|[A-Z]|[0-9]|_|-){2,39}$`)
if !regex.Match([]byte(name)) {
if len(name) < 3 {
return fmt.Errorf(SnapshotName_Error_MinLength)
}
if len(name) > 40 {
return fmt.Errorf(SnapshotName_Error_MaxLength)
}
if !unicode.IsLetter(rune(name[0])) {
return fmt.Errorf(SnapshotName_Error_StartNoLetter)
}
return fmt.Errorf(SnapshotName_Error_IllegalCharacters)
}
return nil
}