github.com/LINBIT/golinstor@v0.52.0/client/backup.go (about) 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 // Linstor is a map of all entries found that could be parsed as LINSTOR backups. 69 Linstor map[string]Backup `json:"linstor,omitempty"` 70 // Other are files that could not be parsed as LINSTOR backups. 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 // GetAll fetches information on all backups stored at the given remote. Optionally limited to the given 144 // resource names. 145 GetAll(ctx context.Context, remoteName string, rscName string, snapName string) (*BackupList, error) 146 // DeleteAll backups that fit the given criteria. 147 DeleteAll(ctx context.Context, remoteName string, filter BackupDeleteOpts) error 148 // Create a new backup operation. 149 Create(ctx context.Context, remoteName string, request BackupCreate) (string, error) 150 // Info retrieves information about a specific backup instance. 151 Info(ctx context.Context, remoteName string, request BackupInfoRequest) (*BackupInfo, error) 152 // Abort all running backup operations of a resource. 153 Abort(ctx context.Context, remoteName string, request BackupAbortRequest) error 154 // Ship ships a backup from one LINSTOR cluster to another. 155 Ship(ctx context.Context, remoteName string, request BackupShipRequest) (string, error) 156 // Restore starts to restore a resource from a backup. 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 // LINSTOR will report the name of the created (local) snapshot in one of the messages. 248 // There may be multiple such references, but the name of the snapshot stays the same. 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 }