Skip to content

Commit 5fc2174

Browse files
swordqiuQiu Jian
andauthored
feature: support create disk backup from file (#25175)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
1 parent 72888ba commit 5fc2174

20 files changed

Lines changed: 371 additions & 178 deletions

File tree

pkg/apis/compute/backup.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ type DiskBackupCreateInput struct {
136136
apis.VirtualResourceCreateInput
137137
apis.EncryptedResourceCreateInput
138138

139+
// swagger:ignore
140+
SizeMb int `json:"size_mb"`
141+
// path to find backup file, in case of create backup from a backup file
142+
BackupFilePath string `json:"backup_file_path"`
139143
// description: disk id
140144
DiskId string `json:"disk_id"`
141145
// swagger:ignore
@@ -165,6 +169,9 @@ type DiskBackupPackMetadata struct {
165169
// 操作系统类型
166170
OsType string `json:"os_type"`
167171
DiskConfig *SBackupDiskConfig `json:"disk_config"`
172+
173+
// 备份文件路径
174+
BackupFilePath string `json:"backup_file_path"`
168175
}
169176

170177
type DiskBackupExportInfo struct {

pkg/apis/compute/disk.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,11 +358,12 @@ type DiskAllocateInput struct {
358358
}
359359

360360
type DiskAllocateFromBackupInput struct {
361-
BackupId string `json:"backup_id"`
362-
BackupStorageId string `json:"backup_storage_id"`
363-
BackupStorageAccessInfo *jsonutils.JSONDict `json:"backup_storage_access_info"`
364-
DiskConfig *DiskConfig `json:"disk_config"`
365-
BackupAsTar *DiskBackupAsTarInput `json:"backup_as_tar"`
361+
BackupId string `json:"backup_id"`
362+
BackupStorageId string `json:"backup_storage_id"`
363+
BackupStorageAccessInfo *SBackupStorageAccessInfo `json:"backup_storage_access_info"`
364+
DiskConfig *DiskConfig `json:"disk_config"`
365+
BackupAsTar *DiskBackupAsTarInput `json:"backup_as_tar"`
366+
BackupFilePath string `json:"backup_file_path"`
366367
}
367368

368369
type DiskDeleteInput struct {

pkg/apis/compute/instance_backup.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ type SSimpleBackup struct {
9595
EncryptKeyId string `json:"encrypt_key_id"`
9696
// 创建时间
9797
CreatedAt time.Time `json:"created_at"`
98+
// 备份文件路径
99+
BackupFilePath string `json:"backup_file_path"`
98100
}
99101

100102
type InstanceBackupRecoveryInput struct {
@@ -112,3 +114,12 @@ type InstanceBackupManagerCreateFromPackageInput struct {
112114
BackupStorageId string `json:"backup_storage_id"`
113115
PackageName string `json:"package_name"`
114116
}
117+
118+
type SStoragePackInstanceBackup struct {
119+
PackageName string `json:"package_name"`
120+
BackupStorageId string `json:"backup_storage_id"`
121+
BackupStorageAccessInfo *SBackupStorageAccessInfo `json:"backup_storage_access_info"`
122+
BackupIds []string `json:"backup_ids"`
123+
Metadata *InstanceBackupPackMetadata `json:"metadata"`
124+
DiskBackups []SSimpleBackup `json:"disk_backups"`
125+
}

pkg/compute/models/backup_storage.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,22 @@ func (bs *SBackupStorage) GetIBackupStorage() (backupstorage.IBackupStorage, err
396396
return nil, errors.Wrap(err, "GetAccessInfo")
397397
}
398398
log.Infof("GetIBackupStorage %s %s", bs.Id, accessInfo.String())
399-
ibs, err := backupstorage.GetBackupStorage(bs.Id, jsonutils.Marshal(accessInfo).(*jsonutils.JSONDict))
399+
ibs, err := backupstorage.GetBackupStorage(bs.Id, accessInfo)
400400
if err != nil {
401401
return nil, errors.Wrap(err, "GetBackupStorage")
402402
}
403403
return ibs, nil
404404
}
405+
406+
func (manager *SBackupStorageManager) fetchItems(filterFunc func(q *sqlchemy.SQuery) *sqlchemy.SQuery) ([]SBackupStorage, error) {
407+
q := manager.Query()
408+
if filterFunc != nil {
409+
q = filterFunc(q)
410+
}
411+
ret := make([]SBackupStorage, 0)
412+
err := db.FetchModelObjects(manager, q, &ret)
413+
if err != nil {
414+
return nil, errors.Wrap(err, "FetchModelObjects")
415+
}
416+
return ret, nil
417+
}

pkg/compute/models/diskbackups.go

Lines changed: 127 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type SDiskBackup struct {
6060

6161
db.SEncryptedResource
6262

63-
DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
63+
DiskId string `width:"36" charset:"ascii" nullable:"true" create:"optional" list:"user" index:"true"`
6464
BackupStorageId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
6565
StorageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
6666

@@ -71,6 +71,8 @@ type SDiskBackup struct {
7171
// 操作系统类型
7272
OsType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
7373
DiskConfig *SBackupDiskConfig
74+
75+
BackupFilePath string `width:"256" charset:"utf8" nullable:"true" list:"user" create:"optional"`
7476
}
7577

7678
var DiskBackupManager *SDiskBackupManager
@@ -229,13 +231,87 @@ func (db *SDiskBackup) GetRegionDriver() (IRegionDriver, error) {
229231
return cloudRegion.GetDriver(), nil
230232
}
231233

234+
func (dm *SDiskBackupManager) validateCreateDataFromBackupFile(
235+
ctx context.Context,
236+
userCred mcclient.TokenCredential,
237+
ownerId mcclient.IIdentityProvider,
238+
query jsonutils.JSONObject,
239+
input api.DiskBackupCreateInput,
240+
) (api.DiskBackupCreateInput, error) {
241+
var backupStorages []*SBackupStorage
242+
if len(input.DiskId) > 0 {
243+
return input, errors.Wrap(httperrors.ErrInputParameter, "disk_id is not allowed")
244+
}
245+
if len(input.BackupStorageId) > 0 {
246+
bsObj, err := BackupStorageManager.FetchByIdOrName(ctx, userCred, input.BackupStorageId)
247+
if err != nil {
248+
if errors.Cause(err) == sql.ErrNoRows {
249+
return input, httperrors.NewResourceNotFoundError2(BackupStorageManager.Keyword(), input.BackupStorageId)
250+
}
251+
if errors.Cause(err) == sqlchemy.ErrDuplicateEntry {
252+
return input, httperrors.NewDuplicateResourceError(BackupStorageManager.Keyword(), input.BackupStorageId)
253+
}
254+
return input, httperrors.NewGeneralError(err)
255+
}
256+
backupStorages = append(backupStorages, bsObj.(*SBackupStorage))
257+
} else {
258+
bsItems, err := BackupStorageManager.fetchItems(func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
259+
q = q.Equals("status", api.BACKUPSTORAGE_STATUS_ONLINE)
260+
q = q.Equals("enabled", 1)
261+
return q
262+
})
263+
if err != nil {
264+
return input, httperrors.NewGeneralError(err)
265+
}
266+
for i := range bsItems {
267+
backupStorages = append(backupStorages, &bsItems[i])
268+
}
269+
}
270+
if len(backupStorages) == 0 {
271+
return input, errors.Wrap(httperrors.ErrInputParameter, "no backup storage found")
272+
}
273+
var errs []error
274+
input.SizeMb = -1
275+
for i := range backupStorages {
276+
ibs, err := backupStorages[i].GetIBackupStorage()
277+
if err != nil {
278+
return input, errors.Wrap(err, "GetIBackupStorage")
279+
}
280+
exists, sizeBytes, offlineReason, err := ibs.IsBackupExists("", input.BackupFilePath)
281+
if !exists {
282+
if err != nil {
283+
errs = append(errs, errors.Wrapf(err, "Backup storage error %s(%s)", backupStorages[i].GetName(), backupStorages[i].GetId()))
284+
}
285+
if len(offlineReason) > 0 {
286+
errs = append(errs, errors.Wrapf(errors.ErrInvalidStatus, "Backup storage %s(%s) is offline: %s", backupStorages[i].GetName(), backupStorages[i].GetId(), offlineReason))
287+
}
288+
errs = append(errs, errors.Wrapf(errors.ErrNotFound, "Backup file %s not found in backup storage %s(%s)", input.BackupFilePath, backupStorages[i].GetName(), backupStorages[i].GetId()))
289+
} else {
290+
input.BackupStorageId = backupStorages[i].GetId()
291+
input.SizeMb = int(sizeBytes / 1024 / 1024)
292+
break
293+
}
294+
}
295+
if input.SizeMb == -1 {
296+
if len(errs) > 0 {
297+
return input, errors.NewAggregate(errs)
298+
}
299+
return input, errors.Wrap(httperrors.ErrInputParameter, "no backup storage found")
300+
}
301+
return input, nil
302+
}
303+
232304
func (dm *SDiskBackupManager) ValidateCreateData(
233305
ctx context.Context,
234306
userCred mcclient.TokenCredential,
235307
ownerId mcclient.IIdentityProvider,
236308
query jsonutils.JSONObject,
237309
input api.DiskBackupCreateInput,
238310
) (api.DiskBackupCreateInput, error) {
311+
if len(input.BackupFilePath) > 0 {
312+
// create disk backup from a backup file
313+
return dm.validateCreateDataFromBackupFile(ctx, userCred, ownerId, query, input)
314+
}
239315
if input.NeedEncrypt() {
240316
return input, errors.Wrap(httperrors.ErrInputParameter, "encryption should not be specified")
241317
}
@@ -272,9 +348,7 @@ func (dm *SDiskBackupManager) ValidateCreateData(
272348
}
273349
return input, httperrors.NewGeneralError(err)
274350
}
275-
if err != nil {
276-
return input, err
277-
}
351+
278352
bs := ibs.(*SBackupStorage)
279353
if bs.Status != api.BACKUPSTORAGE_STATUS_ONLINE {
280354
return input, httperrors.NewForbiddenError("can't backup guest to backup storage with status %s", bs.Status)
@@ -329,36 +403,42 @@ func (db *SDiskBackup) CustomizeCreate(ctx context.Context, userCred mcclient.To
329403
if err != nil {
330404
return err
331405
}
332-
diskObj, err := DiskManager.FetchById(db.DiskId)
333-
if err != nil {
334-
return errors.Wrap(err, "DiskManager.FetchById")
335-
}
336-
disk := diskObj.(*SDisk)
337-
db.DiskConfig = &SBackupDiskConfig{
338-
DiskConfig: *disk.ToDiskConfig(),
339-
Name: disk.GetName(),
340-
BackupAsTar: input.BackupAsTar,
406+
if len(db.DiskId) > 0 {
407+
diskObj, err := DiskManager.FetchById(db.DiskId)
408+
if err != nil {
409+
return errors.Wrap(err, "DiskManager.FetchById")
410+
}
411+
disk := diskObj.(*SDisk)
412+
db.DiskConfig = &SBackupDiskConfig{
413+
DiskConfig: *disk.ToDiskConfig(),
414+
Name: disk.GetName(),
415+
BackupAsTar: input.BackupAsTar,
416+
}
417+
db.DiskType = disk.DiskType
418+
db.DiskSizeMb = disk.DiskSize
419+
db.OsArch = disk.OsArch
420+
db.StorageId = disk.StorageId
421+
db.DomainId = disk.DomainId
422+
db.ProjectId = disk.ProjectId
341423
}
342-
db.DiskType = disk.DiskType
343-
db.DiskSizeMb = disk.DiskSize
344-
db.OsArch = disk.OsArch
345-
db.StorageId = disk.StorageId
346-
db.DomainId = disk.DomainId
347-
db.ProjectId = disk.ProjectId
348424
return nil
349425
}
350426

351427
func (db *SDiskBackup) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
352428
db.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
353-
disk, err := db.GetDisk()
354-
if err != nil {
355-
log.Errorf("unable to GetDisk: %s", err.Error())
356-
}
357-
err = disk.InheritTo(ctx, userCred, db)
358-
if err != nil {
359-
log.Errorf("unable to inherit from disk %s to backup %s: %s", disk.GetId(), db.GetId(), err.Error())
429+
if len(db.DiskId) > 0 {
430+
disk, err := db.GetDisk()
431+
if err != nil {
432+
log.Errorf("unable to GetDisk: %s", err.Error())
433+
}
434+
err = disk.InheritTo(ctx, userCred, db)
435+
if err != nil {
436+
log.Errorf("unable to inherit from disk %s to backup %s: %s", disk.GetId(), db.GetId(), err.Error())
437+
}
438+
db.StartBackupCreateTask(ctx, userCred, nil, "")
439+
} else {
440+
db.SetStatus(ctx, userCred, api.BACKUP_STATUS_READY, "import from backup file")
360441
}
361-
db.StartBackupCreateTask(ctx, userCred, nil, "")
362442
}
363443

364444
func (db *SDiskBackup) StartBackupCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
@@ -568,6 +648,8 @@ func (diskBackup *SDiskBackup) PackMetadata() *api.DiskBackupPackMetadata {
568648
DiskConfig: diskBackup.DiskConfig.DiskConfig,
569649
Name: diskBackup.DiskConfig.Name,
570650
},
651+
// 备份文件路径
652+
BackupFilePath: diskBackup.BackupFilePath,
571653
}
572654
}
573655

@@ -591,6 +673,7 @@ func (manager *SDiskBackupManager) CreateFromPackMetadata(ctx context.Context, o
591673
backup.Name = name
592674
backup.Id = id
593675
backup.Status = api.BACKUP_STATUS_READY
676+
backup.BackupFilePath = metadata.BackupFilePath
594677
err := DiskBackupManager.TableSpec().Insert(ctx, backup)
595678
if err != nil {
596679
return nil, err
@@ -631,7 +714,7 @@ func (diskBackup *SDiskBackup) GetDetailsExportInfo(ctx context.Context, userCre
631714
return nil, errors.Wrap(err, "GetIBackupStorage")
632715
}
633716

634-
exportInfo.AccessUrl, err = ibs.GetExternalAccessUrl(diskBackup.Id)
717+
exportInfo.AccessUrl, err = ibs.GetExternalAccessUrl(diskBackup.Id, diskBackup.BackupFilePath)
635718
if err != nil {
636719
log.Errorf("SDiskBackup %s(%s) GetExternalAccessUrl fail: %v", diskBackup.GetName(), diskBackup.GetId(), err)
637720
}
@@ -744,10 +827,25 @@ func (diskBackup *SDiskBackup) DoImport(ctx context.Context, userCred mcclient.T
744827
}
745828
defer resp.Body.Close()
746829

747-
err = ibs.SaveBackupFrom(ctx, resp.Body, resp.ContentLength, diskBackup.Id)
830+
err = ibs.SaveBackupFrom(ctx, resp.Body, resp.ContentLength, diskBackup.Id, "")
748831
if err != nil {
749832
return errors.Wrap(err, "SaveBackupFrom")
750833
}
751834

752835
return nil
753836
}
837+
838+
func (diskBackup SDiskBackup) ToSimpleBackup() api.SSimpleBackup {
839+
return api.SSimpleBackup{
840+
Id: diskBackup.Id,
841+
Name: diskBackup.Name,
842+
SizeMb: diskBackup.SizeMb,
843+
DiskSizeMb: diskBackup.DiskSizeMb,
844+
DiskType: diskBackup.DiskType,
845+
Status: diskBackup.Status,
846+
EncryptKeyId: diskBackup.EncryptKeyId,
847+
CreatedAt: diskBackup.CreatedAt,
848+
849+
BackupFilePath: diskBackup.BackupFilePath,
850+
}
851+
}

pkg/compute/models/disks.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,9 +920,10 @@ func (self *SDisk) getDiskAllocateFromBackupInput(ctx context.Context, backupId
920920
return &api.DiskAllocateFromBackupInput{
921921
BackupId: backupId,
922922
BackupStorageId: bs.GetId(),
923-
BackupStorageAccessInfo: jsonutils.Marshal(accessInfo).(*jsonutils.JSONDict),
923+
BackupStorageAccessInfo: accessInfo,
924924
DiskConfig: &backup.DiskConfig.DiskConfig,
925925
BackupAsTar: backup.DiskConfig.BackupAsTar,
926+
BackupFilePath: backup.BackupFilePath,
926927
}, nil
927928
}
928929

pkg/compute/models/instance_backup.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,7 @@ func (self *SInstanceBackup) getMoreDetails(userCred mcclient.TokenCredential, o
225225
backups, _ := self.GetBackups()
226226
out.DiskBackups = []api.SSimpleBackup{}
227227
for i := 0; i < len(backups); i++ {
228-
out.DiskBackups = append(out.DiskBackups, api.SSimpleBackup{
229-
Id: backups[i].Id,
230-
Name: backups[i].Name,
231-
SizeMb: backups[i].SizeMb,
232-
DiskSizeMb: backups[i].DiskSizeMb,
233-
DiskType: backups[i].DiskType,
234-
Status: backups[i].Status,
235-
EncryptKeyId: backups[i].EncryptKeyId,
236-
CreatedAt: backups[i].CreatedAt,
237-
})
228+
out.DiskBackups = append(out.DiskBackups, backups[i].ToSimpleBackup())
238229
}
239230
out.Size = self.SizeMb * 1024 * 1024
240231
return out

0 commit comments

Comments
 (0)