-
Notifications
You must be signed in to change notification settings - Fork 5
/
snapshot.go
156 lines (138 loc) · 5.42 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
package main
import (
"fmt"
"strings"
"time"
"github.com/golang/glog"
"ecfs/log"
"github.com/elastifile/emanage-go/src/emanage-client"
"github.com/elastifile/errors"
)
const maxSnapshotNameLen = 36
func createSnapshot(emsClient *emanageClient, name string, volumeId volumeHandleType, params map[string]string) (snapshot *emanage.Snapshot, err error) {
glog.V(log.HIGH_LEVEL_INFO).Infof("ecfs: Creating snapshot %v for volume %v", name, volumeId)
glog.V(log.DEBUG).Infof("ecfs: Creating snapshot %v - parameters: %v", name, params)
dc, err := emsClient.GetClient().GetDcByName(string(volumeId))
if err != nil {
err = errors.WrapPrefix(err, fmt.Sprintf("Failed to get Data Container by name: %v", volumeId), 0)
return
}
snap := &emanage.Snapshot{Name: name, DataContainerID: dc.Id}
snapshot, err = emsClient.Snapshots.Create(snap)
if err != nil {
if isErrorAlreadyExists(err) {
glog.V(log.DEBUG).Infof("ecfs: Snapshot %v for volume %v already exists - assuming duplicate request", name, volumeId)
snapshot, err = emsClient.GetSnapshotByName(name)
if err != nil {
err = errors.WrapPrefix(err, fmt.Sprintf("Failed to get snapshot by name %v", name), 0)
return
}
}
return nil, errors.WrapPrefix(err,
fmt.Sprintf("Failed to create snapshot %v on Data Container %v", name, volumeId), 0)
}
return
}
func waitForSnapshotToBeDeleted(emsClient *emanageClient, snapshotId int, timeout time.Duration) (err error) {
timeoutExpired := time.After(timeout)
tick := time.Tick(10 * time.Second)
var snapshot *emanage.Snapshot
for {
select {
case <-tick:
snapshot, err = emsClient.GetClient().Snapshots.GetById(snapshotId)
if err != nil {
if isErrorDoesNotExist(err) {
glog.V(log.DEBUG).Infof("ecfs: Snapshot id %v not found - assuming already deleted", snapshotId)
return nil
}
return errors.WrapPrefix(err, fmt.Sprintf("Failed to get snapshot by id %v", snapshot.ID), 0)
}
if snapshot.Status == ecfsSnapshotStatus_REMOVED {
glog.V(log.DETAILED_INFO).Infof("ecfs: Snapshot delete operation reported completed by EMS - "+
"snapshot %v, dcId: %v, status: %v", snapshot.Name, snapshot.DataContainerID, snapshot.Status)
return err
}
case <-timeoutExpired:
return errors.Errorf("Timed out waiting for snapshot %v to be deleted after %v", snapshotId, timeout)
}
}
}
func deleteSnapshot(emsClient *emanageClient, name string) error {
glog.V(log.INFO).Infof("ecfs: Deleting snapshot %v", name)
snapshot, err := emsClient.GetSnapshotByName(name)
if err != nil {
if isErrorDoesNotExist(err) { // This operation has to be idempotent
glog.V(log.DEBUG).Infof("ecfs: Snapshot %v not found - assuming already deleted", name)
return nil
}
if isWorkaround("EL-13618 - Failed read-dir") {
const EL13618 = "Failed read-dir"
if strings.Contains(err.Error(), EL13618) {
glog.Warningf("ecfs: Snapshot delete failed due to EL-13618 - returning success to cleanup the pv. Actual error: %v", err)
return nil
}
}
return errors.WrapPrefix(err, fmt.Sprintf("Failed to get snapshot by name %v", name), 0)
}
// Handle subsequent requests to remove snapshot
if snapshot.Status == ecfsSnapshotStatus_REMOVING { // Operation MUST be idempotent
glog.V(log.DEBUG).Infof("ecfs: Requested to delete snapshot that's already being removed - snapshot %v, dcId: %v, status: %v",
snapshot.Name, snapshot.DataContainerID, snapshot.Status)
err = waitForSnapshotToBeDeleted(emsClient, snapshot.ID, 3*time.Minute)
if err != nil {
return errors.Wrap(err, 0)
}
}
glog.V(log.VERBOSE_DEBUG).Infof("ecfs: Deleting export from snapshot %v (%v)", snapshot.ID, snapshot.Name)
err = deleteExportFromSnapshot(emsClient.GetClient(), snapshot.ID)
if err != nil {
if !isErrorDoesNotExist(err) {
glog.Warningf("Failed to delete export from snapshot %v (%v)", snapshot.ID, snapshot.Name)
}
}
glog.V(log.DEBUG).Infof("ecfs: Calling emanage snapshot.Delete - snapshot %v, dcId: %v, status: %v",
snapshot.Name, snapshot.DataContainerID, snapshot.Status)
tasks := snapshot.Delete()
if tasks.Error() != nil {
return tasks.Error()
}
glog.V(log.DEBUG).Infof("ecfs: Waiting for snapshot %v to be deleted by the backend", name)
return tasks.Wait()
}
func listSnapshots(emsClient *emanageClient, snapshotId, volumeId string, maxEntries int32, startToken string) (snapshots emanage.SnapshotList, nextToken string, err error) {
// TODO: List pagination is not supported in eManage client (page, per_page) - see TESLA-3310
glog.V(log.DETAILED_INFO).Info("Listing snapshots",
"snapshotId", snapshotId, "volumeId", volumeId, "maxEntries", maxEntries, "startToken", startToken)
if snapshotId != "" {
glog.V(log.DEBUG).Infof("ecfs: Listing snapshots by snapshotId %v", snapshotId)
var snapshot *emanage.Snapshot
snapshot, err = emsClient.GetSnapshotByName(snapshotId)
if err != nil {
err = errors.Wrap(err, 0)
return
}
snapshots = append(snapshots, snapshot)
} else if volumeId != "" {
glog.V(log.DEBUG).Infof("ecfs: Listing snapshots by volumeId %v", volumeId)
var dc *emanage.DataContainer
dc, err = emsClient.GetDcByName(volumeId)
if err != nil {
err = errors.Wrap(err, 0)
return
}
snapshots, err = emsClient.Snapshots.GetByDataContainerId(dc.Id)
if err != nil {
err = errors.Wrap(err, 0)
return
}
} else {
glog.V(log.DEBUG).Infof("ecfs: Listing all snapshots")
snapshots, err = emsClient.Snapshots.Get()
if err != nil {
err = errors.Wrap(err, 0)
return
}
}
return
}