File: backup.go

package info (click to toggle)
golang-github-linbit-golinstor 0.57.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 472 kB
  • sloc: makefile: 11
file content (260 lines) | stat: -rw-r--r-- 9,662 bytes parent folder | download | duplicates (2)
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package client

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/google/go-querystring/query"

	"github.com/LINBIT/golinstor/devicelayerkind"
)

type Backup struct {
	Id                string          `json:"id"`
	StartTime         string          `json:"start_time,omitempty"`
	StartTimestamp    *TimeStampMs    `json:"start_timestamp,omitempty"`
	FinishedTime      string          `json:"finished_time,omitempty"`
	FinishedTimestamp *TimeStampMs    `json:"finished_timestamp,omitempty"`
	OriginRsc         string          `json:"origin_rsc"`
	OriginSnap        string          `json:"origin_snap"`
	OriginNode        string          `json:"origin_node,omitempty"`
	FailMessages      string          `json:"fail_messages,omitempty"`
	Vlms              []BackupVolumes `json:"vlms"`
	Success           bool            `json:"success,omitempty"`
	Shipping          bool            `json:"shipping,omitempty"`
	Restorable        bool            `json:"restorable,omitempty"`
	S3                BackupS3        `json:"s3,omitempty"`
	BasedOnId         string          `json:"based_on_id,omitempty"`
}

type BackupInfo struct {
	Rsc          string               `json:"rsc"`
	Snap         string               `json:"snap"`
	Full         string               `json:"full"`
	Latest       string               `json:"latest"`
	Count        int32                `json:"count,omitempty"`
	DlSizeKib    int64                `json:"dl_size_kib"`
	AllocSizeKib int64                `json:"alloc_size_kib"`
	Storpools    []BackupInfoStorPool `json:"storpools"`
}

type BackupInfoRequest struct {
	SrcRscName  string            `json:"src_rsc_name,omitempty"`
	SrcSnapName string            `json:"src_snap_name,omitempty"`
	LastBackup  string            `json:"last_backup,omitempty"`
	StorPoolMap map[string]string `json:"stor_pool_map,omitempty"`
	NodeName    string            `json:"node_name,omitempty"`
}

type BackupInfoStorPool struct {
	Name              string             `json:"name"`
	ProviderKind      ProviderKind       `json:"provider_kind,omitempty"`
	TargetName        string             `json:"target_name,omitempty"`
	RemainingSpaceKib int64              `json:"remaining_space_kib,omitempty"`
	Vlms              []BackupInfoVolume `json:"vlms"`
}

type BackupInfoVolume struct {
	Name          string                          `json:"name,omitempty"`
	LayerType     devicelayerkind.DeviceLayerKind `json:"layer_type"`
	DlSizeKib     int64                           `json:"dl_size_kib,omitempty"`
	AllocSizeKib  int64                           `json:"alloc_size_kib"`
	UsableSizeKib int64                           `json:"usable_size_kib,omitempty"`
}

type BackupList struct {
	// Linstor is a map of all entries found that could be parsed as LINSTOR backups.
	Linstor map[string]Backup `json:"linstor,omitempty"`
	// Other are files that could not be parsed as LINSTOR backups.
	Other BackupOther `json:"other,omitempty"`
}

type BackupOther struct {
	Files *[]string `json:"files,omitempty"`
}

type BackupRestoreRequest struct {
	SrcRscName    string            `json:"src_rsc_name,omitempty"`
	SrcSnapName   string            `json:"src_snap_name,omitempty"`
	LastBackup    string            `json:"last_backup,omitempty"`
	StorPoolMap   map[string]string `json:"stor_pool_map,omitempty"`
	TargetRscName string            `json:"target_rsc_name"`
	Passphrase    string            `json:"passphrase,omitempty"`
	NodeName      string            `json:"node_name"`
	DownloadOnly  bool              `json:"download_only,omitempty"`
}

type BackupS3 struct {
	MetaName string `json:"meta_name,omitempty"`
}

type BackupAbortRequest struct {
	RscName string `json:"rsc_name"`
	Restore *bool  `json:"restore,omitempty"`
	Create  *bool  `json:"create,omitempty"`
}

type BackupCreate struct {
	RscName     string `json:"rsc_name"`
	SnapName    string `json:"snap_name,omitempty"`
	NodeName    string `json:"node_name,omitempty"`
	Incremental bool   `json:"incremental,omitempty"`
}

type BackupShipRequest struct {
	SrcNodeName    string            `json:"src_node_name,omitempty"`
	SrcRscName     string            `json:"src_rsc_name"`
	DstRscName     string            `json:"dst_rsc_name"`
	DstNodeName    string            `json:"dst_node_name,omitempty"`
	DstNetIfName   string            `json:"dst_net_if_name,omitempty"`
	DstStorPool    string            `json:"dst_stor_pool,omitempty"`
	StorPoolRename map[string]string `json:"stor_pool_rename,omitempty"`
	DownloadOnly   *bool             `json:"download_only,omitempty"`
}

type BackupVolumes struct {
	VlmNr             int64            `json:"vlm_nr"`
	FinishedTime      *string          `json:"finished_time,omitempty"`
	FinishedTimestamp *TimeStampMs     `json:"finished_timestamp,omitempty"`
	S3                *BackupVolumesS3 `json:"s3,omitempty"`
}

type BackupVolumesS3 struct {
	Key *string `json:"key,omitempty"`
}

type BackupDeleteOpts struct {
	ID              string       `url:"id,omitempty"`
	IDPrefix        string       `url:"id_prefix,omitempty"`
	Cascading       bool         `url:"cascading,omitempty"`
	Timestamp       *TimeStampMs `url:"timestamp,omitempty"`
	ResourceName    string       `url:"resource_name,omitempty"`
	NodeName        string       `url:"node_name,omitempty"`
	AllLocalCluster bool         `url:"all_local_cluster,omitempty"`
	All             bool         `url:"all,omitempty"`
	S3Key           string       `url:"s3key,omitempty"`
	S3KeyForce      string       `url:"s3key_force,omitempty"`
	DryRun          bool         `url:"dryrun,omitempty"`
}

type BackupProvider interface {
	// GetAll fetches information on all backups stored at the given remote. Optionally limited to the given
	// resource names.
	GetAll(ctx context.Context, remoteName string, rscName string, snapName string) (*BackupList, error)
	// DeleteAll backups that fit the given criteria.
	DeleteAll(ctx context.Context, remoteName string, filter BackupDeleteOpts) error
	// Create a new backup operation.
	Create(ctx context.Context, remoteName string, request BackupCreate) (string, error)
	// Info retrieves information about a specific backup instance.
	Info(ctx context.Context, remoteName string, request BackupInfoRequest) (*BackupInfo, error)
	// Abort all running backup operations of a resource.
	Abort(ctx context.Context, remoteName string, request BackupAbortRequest) error
	// Ship ships a backup from one LINSTOR cluster to another.
	Ship(ctx context.Context, remoteName string, request BackupShipRequest) (string, error)
	// Restore starts to restore a resource from a backup.
	Restore(ctx context.Context, remoteName string, request BackupRestoreRequest) error
}

var _ BackupProvider = &BackupService{}

type BackupService struct {
	client *Client
}

func (b *BackupService) GetAll(ctx context.Context, remoteName string, rscName string, snapName string) (*BackupList, error) {
	vals, err := query.Values(struct {
		ResourceName string `url:"rsc_name,omitempty"`
		SnapshotName string `url:"snap_name,omitempty"`
	}{ResourceName: rscName, SnapshotName: snapName})
	if err != nil {
		return nil, fmt.Errorf("failed to encode resource names: %w", err)
	}

	var list BackupList
	_, err = b.client.doGET(ctx, "/v1/remotes/"+remoteName+"/backups?"+vals.Encode(), &list)
	if err != nil {
		return nil, err
	}
	return &list, err
}

func (b *BackupService) DeleteAll(ctx context.Context, remoteName string, filter BackupDeleteOpts) error {
	vals, err := query.Values(filter)
	if err != nil {
		return fmt.Errorf("failed to encode filter options: %w", err)
	}

	_, err = b.client.doDELETE(ctx, "/v1/remotes/"+remoteName+"/backups?"+vals.Encode(), nil)
	return err
}

func (b *BackupService) Create(ctx context.Context, remoteName string, request BackupCreate) (string, error) {
	req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups", request)
	if err != nil {
		return "", err
	}

	var resp []ApiCallRc
	_, err = b.client.doJSON(ctx, req, &resp)
	if err != nil {
		return "", err
	}

	for _, rc := range resp {
		if s, ok := rc.ObjRefs["Snapshot"]; ok {
			return s, nil
		}
	}

	return "", errors.New("missing snapshot reference")
}

func (b *BackupService) Info(ctx context.Context, remoteName string, request BackupInfoRequest) (*BackupInfo, error) {
	req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups/info", request)
	if err != nil {
		return nil, err
	}

	var resp BackupInfo
	_, err = b.client.doJSON(ctx, req, &resp)
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

func (b *BackupService) Abort(ctx context.Context, remoteName string, request BackupAbortRequest) error {
	_, err := b.client.doPOST(ctx, "/v1/remotes/"+remoteName+"/backups/abort", request)
	return err
}

func (b *BackupService) Ship(ctx context.Context, remoteName string, request BackupShipRequest) (string, error) {
	req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups/ship", request)
	if err != nil {
		return "", err
	}

	var resp []ApiCallRc
	_, err = b.client.doJSON(ctx, req, &resp)
	if err != nil {
		return "", err
	}

	for _, rc := range resp {
		// LINSTOR will report the name of the created (local) snapshot in one of the messages.
		// There may be multiple such references, but the name of the snapshot stays the same.
		if s, ok := rc.ObjRefs["Snapshot"]; ok {
			return s, nil
		}
	}

	return "", errors.New("missing snapshot reference")
}

func (b *BackupService) Restore(ctx context.Context, remoteName string, request BackupRestoreRequest) error {
	_, err := b.client.doPOST(ctx, "/v1/remotes/"+remoteName+"/backups/restore", request)
	return err
}