1 package client
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8
9 "github.com/google/go-querystring/query"
10
11 "github.com/LINBIT/golinstor/devicelayerkind"
12 )
13
14 type Backup struct {
15 Id string `json:"id"`
16 StartTime string `json:"start_time,omitempty"`
17 StartTimestamp *TimeStampMs `json:"start_timestamp,omitempty"`
18 FinishedTime string `json:"finished_time,omitempty"`
19 FinishedTimestamp *TimeStampMs `json:"finished_timestamp,omitempty"`
20 OriginRsc string `json:"origin_rsc"`
21 OriginSnap string `json:"origin_snap"`
22 OriginNode string `json:"origin_node,omitempty"`
23 FailMessages string `json:"fail_messages,omitempty"`
24 Vlms []BackupVolumes `json:"vlms"`
25 Success bool `json:"success,omitempty"`
26 Shipping bool `json:"shipping,omitempty"`
27 Restorable bool `json:"restorable,omitempty"`
28 S3 BackupS3 `json:"s3,omitempty"`
29 BasedOnId string `json:"based_on_id,omitempty"`
30 }
31
32 type BackupInfo struct {
33 Rsc string `json:"rsc"`
34 Snap string `json:"snap"`
35 Full string `json:"full"`
36 Latest string `json:"latest"`
37 Count int32 `json:"count,omitempty"`
38 DlSizeKib int64 `json:"dl_size_kib"`
39 AllocSizeKib int64 `json:"alloc_size_kib"`
40 Storpools []BackupInfoStorPool `json:"storpools"`
41 }
42
43 type BackupInfoRequest struct {
44 SrcRscName string `json:"src_rsc_name,omitempty"`
45 SrcSnapName string `json:"src_snap_name,omitempty"`
46 LastBackup string `json:"last_backup,omitempty"`
47 StorPoolMap map[string]string `json:"stor_pool_map,omitempty"`
48 NodeName string `json:"node_name,omitempty"`
49 }
50
51 type BackupInfoStorPool struct {
52 Name string `json:"name"`
53 ProviderKind ProviderKind `json:"provider_kind,omitempty"`
54 TargetName string `json:"target_name,omitempty"`
55 RemainingSpaceKib int64 `json:"remaining_space_kib,omitempty"`
56 Vlms []BackupInfoVolume `json:"vlms"`
57 }
58
59 type BackupInfoVolume struct {
60 Name string `json:"name,omitempty"`
61 LayerType devicelayerkind.DeviceLayerKind `json:"layer_type"`
62 DlSizeKib int64 `json:"dl_size_kib,omitempty"`
63 AllocSizeKib int64 `json:"alloc_size_kib"`
64 UsableSizeKib int64 `json:"usable_size_kib,omitempty"`
65 }
66
67 type BackupList struct {
68
69 Linstor map[string]Backup `json:"linstor,omitempty"`
70
71 Other BackupOther `json:"other,omitempty"`
72 }
73
74 type BackupOther struct {
75 Files *[]string `json:"files,omitempty"`
76 }
77
78 type BackupRestoreRequest struct {
79 SrcRscName string `json:"src_rsc_name,omitempty"`
80 SrcSnapName string `json:"src_snap_name,omitempty"`
81 LastBackup string `json:"last_backup,omitempty"`
82 StorPoolMap map[string]string `json:"stor_pool_map,omitempty"`
83 TargetRscName string `json:"target_rsc_name"`
84 Passphrase string `json:"passphrase,omitempty"`
85 NodeName string `json:"node_name"`
86 DownloadOnly bool `json:"download_only,omitempty"`
87 }
88
89 type BackupS3 struct {
90 MetaName string `json:"meta_name,omitempty"`
91 }
92
93 type BackupAbortRequest struct {
94 RscName string `json:"rsc_name"`
95 Restore *bool `json:"restore,omitempty"`
96 Create *bool `json:"create,omitempty"`
97 }
98
99 type BackupCreate struct {
100 RscName string `json:"rsc_name"`
101 SnapName string `json:"snap_name,omitempty"`
102 NodeName string `json:"node_name,omitempty"`
103 Incremental bool `json:"incremental,omitempty"`
104 }
105
106 type BackupShipRequest struct {
107 SrcNodeName string `json:"src_node_name,omitempty"`
108 SrcRscName string `json:"src_rsc_name"`
109 DstRscName string `json:"dst_rsc_name"`
110 DstNodeName string `json:"dst_node_name,omitempty"`
111 DstNetIfName string `json:"dst_net_if_name,omitempty"`
112 DstStorPool string `json:"dst_stor_pool,omitempty"`
113 StorPoolRename map[string]string `json:"stor_pool_rename,omitempty"`
114 DownloadOnly *bool `json:"download_only,omitempty"`
115 }
116
117 type BackupVolumes struct {
118 VlmNr int64 `json:"vlm_nr"`
119 FinishedTime *string `json:"finished_time,omitempty"`
120 FinishedTimestamp *TimeStampMs `json:"finished_timestamp,omitempty"`
121 S3 *BackupVolumesS3 `json:"s3,omitempty"`
122 }
123
124 type BackupVolumesS3 struct {
125 Key *string `json:"key,omitempty"`
126 }
127
128 type BackupDeleteOpts struct {
129 ID string `url:"id,omitempty"`
130 IDPrefix string `url:"id_prefix,omitempty"`
131 Cascading bool `url:"cascading,omitempty"`
132 Timestamp *TimeStampMs `url:"timestamp,omitempty"`
133 ResourceName string `url:"resource_name,omitempty"`
134 NodeName string `url:"node_name,omitempty"`
135 AllLocalCluster bool `url:"all_local_cluster,omitempty"`
136 All bool `url:"all,omitempty"`
137 S3Key string `url:"s3key,omitempty"`
138 S3KeyForce string `url:"s3key_force,omitempty"`
139 DryRun bool `url:"dryrun,omitempty"`
140 }
141
142 type BackupProvider interface {
143
144
145 GetAll(ctx context.Context, remoteName string, rscName string, snapName string) (*BackupList, error)
146
147 DeleteAll(ctx context.Context, remoteName string, filter BackupDeleteOpts) error
148
149 Create(ctx context.Context, remoteName string, request BackupCreate) (string, error)
150
151 Info(ctx context.Context, remoteName string, request BackupInfoRequest) (*BackupInfo, error)
152
153 Abort(ctx context.Context, remoteName string, request BackupAbortRequest) error
154
155 Ship(ctx context.Context, remoteName string, request BackupShipRequest) (string, error)
156
157 Restore(ctx context.Context, remoteName string, request BackupRestoreRequest) error
158 }
159
160 var _ BackupProvider = &BackupService{}
161
162 type BackupService struct {
163 client *Client
164 }
165
166 func (b *BackupService) GetAll(ctx context.Context, remoteName string, rscName string, snapName string) (*BackupList, error) {
167 vals, err := query.Values(struct {
168 ResourceName string `url:"rsc_name,omitempty"`
169 SnapshotName string `url:"snap_name,omitempty"`
170 }{ResourceName: rscName, SnapshotName: snapName})
171 if err != nil {
172 return nil, fmt.Errorf("failed to encode resource names: %w", err)
173 }
174
175 var list BackupList
176 _, err = b.client.doGET(ctx, "/v1/remotes/"+remoteName+"/backups?"+vals.Encode(), &list)
177 if err != nil {
178 return nil, err
179 }
180 return &list, err
181 }
182
183 func (b *BackupService) DeleteAll(ctx context.Context, remoteName string, filter BackupDeleteOpts) error {
184 vals, err := query.Values(filter)
185 if err != nil {
186 return fmt.Errorf("failed to encode filter options: %w", err)
187 }
188
189 _, err = b.client.doDELETE(ctx, "/v1/remotes/"+remoteName+"/backups?"+vals.Encode(), nil)
190 return err
191 }
192
193 func (b *BackupService) Create(ctx context.Context, remoteName string, request BackupCreate) (string, error) {
194 req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups", request)
195 if err != nil {
196 return "", err
197 }
198
199 var resp []ApiCallRc
200 _, err = b.client.do(ctx, req, &resp)
201 if err != nil {
202 return "", err
203 }
204
205 for _, rc := range resp {
206 if s, ok := rc.ObjRefs["Snapshot"]; ok {
207 return s, nil
208 }
209 }
210
211 return "", errors.New("missing snapshot reference")
212 }
213
214 func (b *BackupService) Info(ctx context.Context, remoteName string, request BackupInfoRequest) (*BackupInfo, error) {
215 req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups/info", request)
216 if err != nil {
217 return nil, err
218 }
219
220 var resp BackupInfo
221 _, err = b.client.do(ctx, req, &resp)
222 if err != nil {
223 return nil, err
224 }
225
226 return &resp, nil
227 }
228
229 func (b *BackupService) Abort(ctx context.Context, remoteName string, request BackupAbortRequest) error {
230 _, err := b.client.doPOST(ctx, "/v1/remotes/"+remoteName+"/backups/abort", request)
231 return err
232 }
233
234 func (b *BackupService) Ship(ctx context.Context, remoteName string, request BackupShipRequest) (string, error) {
235 req, err := b.client.newRequest(http.MethodPost, "/v1/remotes/"+remoteName+"/backups/ship", request)
236 if err != nil {
237 return "", err
238 }
239
240 var resp []ApiCallRc
241 _, err = b.client.do(ctx, req, &resp)
242 if err != nil {
243 return "", err
244 }
245
246 for _, rc := range resp {
247
248
249 if s, ok := rc.ObjRefs["Snapshot"]; ok {
250 return s, nil
251 }
252 }
253
254 return "", errors.New("missing snapshot reference")
255 }
256
257 func (b *BackupService) Restore(ctx context.Context, remoteName string, request BackupRestoreRequest) error {
258 _, err := b.client.doPOST(ctx, "/v1/remotes/"+remoteName+"/backups/restore", request)
259 return err
260 }
261
View as plain text