@@ -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
7678var 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+
232304func (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
351427func (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
364444func (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+ }
0 commit comments