mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-02 10:14:20 +08:00
432 lines
15 KiB
Go
432 lines
15 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package models
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/gotypes"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
|
)
|
|
|
|
type SDiskBackupManager struct {
|
|
db.SVirtualResourceBaseManager
|
|
SDiskResourceBaseManager
|
|
SManagedResourceBaseManager
|
|
SCloudregionResourceBaseManager
|
|
db.SMultiArchResourceBaseManager
|
|
}
|
|
|
|
type SDiskBackup struct {
|
|
db.SVirtualResourceBase
|
|
|
|
SManagedResourceBase
|
|
SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
|
db.SMultiArchResourceBase
|
|
|
|
DiskId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
|
|
BackupStorageId string `width:"36" charset:"ascii" nullable:"true" create:"required" list:"user" index:"true"`
|
|
StorageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
|
|
|
|
// 备份大小
|
|
SizeMb int `nullable:"false" list:"user" create:"optional"`
|
|
DiskSizeMb int `nullable:"false" list:"user" create:"optional"`
|
|
DiskType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
|
// 操作系统类型
|
|
OsType string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional"`
|
|
DiskConfig *SBackupDiskConfig
|
|
}
|
|
|
|
var DiskBackupManager *SDiskBackupManager
|
|
|
|
func init() {
|
|
gotypes.RegisterSerializable(reflect.TypeOf(&SBackupDiskConfig{}), func() gotypes.ISerializable {
|
|
return &SBackupDiskConfig{}
|
|
})
|
|
DiskBackupManager = &SDiskBackupManager{
|
|
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
|
SDiskBackup{},
|
|
"diskbackups_tbl",
|
|
"diskbackup",
|
|
"diskbackups",
|
|
),
|
|
}
|
|
DiskBackupManager.SetVirtualObject(DiskBackupManager)
|
|
}
|
|
|
|
type SBackupDiskConfig struct {
|
|
api.DiskConfig
|
|
Name string
|
|
}
|
|
|
|
func (dc *SBackupDiskConfig) String() string {
|
|
return jsonutils.Marshal(dc).String()
|
|
}
|
|
|
|
func (dc *SBackupDiskConfig) IsZero() bool {
|
|
return dc == nil
|
|
}
|
|
|
|
func (dm *SDiskBackupManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.DiskBackupListInput) (*sqlchemy.SQuery, error) {
|
|
var err error
|
|
q, err = dm.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VirtualResourceListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
|
|
}
|
|
q, err = dm.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.ManagedResourceListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
|
|
}
|
|
q, err = dm.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, input.RegionalFilterListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
|
|
}
|
|
q, err = dm.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, input.MultiArchResourceBaseListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
|
|
}
|
|
if input.DiskId != "" {
|
|
q = q.Equals("disk_id", input.DiskId)
|
|
}
|
|
if input.BackupStorageId != "" {
|
|
q = q.Equals("backup_storage_id", input.BackupStorageId)
|
|
}
|
|
if input.IsInstanceBackup != nil {
|
|
insjsq := InstanceBackupJointManager.Query().SubQuery()
|
|
if !*input.IsInstanceBackup {
|
|
q = q.LeftJoin(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("disk_backup_id"))).
|
|
Filter(sqlchemy.IsNull(insjsq.Field("disk_backup_id")))
|
|
} else {
|
|
q = q.Join(insjsq, sqlchemy.Equals(q.Field("id"), insjsq.Field("disk_backup_id")))
|
|
}
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
func (self *SDiskBackup) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
|
if self.Status == api.BACKUP_STATUS_DELETING {
|
|
return httperrors.NewBadRequestError("Cannot delete disk backup in status %s", self.Status)
|
|
}
|
|
is, err := InstanceBackupJointManager.IsSubBackup(self.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if is {
|
|
return httperrors.NewBadRequestError("disk backup referenced by instance backup")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (dm *SDiskBackupManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.DiskBackupDetails {
|
|
rows := make([]api.DiskBackupDetails, len(objs))
|
|
virtRows := dm.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
|
manRows := dm.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
|
regionRows := dm.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
|
|
|
for i := range rows {
|
|
rows[i].VirtualResourceDetails = virtRows[i]
|
|
rows[i].ManagedResourceInfo = manRows[i]
|
|
rows[i].CloudregionResourceInfo = regionRows[i]
|
|
rows[i] = objs[i].(*SDiskBackup).getMoreDetails(rows[i])
|
|
}
|
|
return rows
|
|
}
|
|
|
|
func (db *SDiskBackup) getMoreDetails(out api.DiskBackupDetails) api.DiskBackupDetails {
|
|
disk, _ := db.GetDisk()
|
|
if disk != nil {
|
|
out.DiskName = disk.Name
|
|
}
|
|
backupStorage, _ := db.GetBackupStorage()
|
|
if backupStorage != nil {
|
|
out.BackupStorageName = backupStorage.GetName()
|
|
}
|
|
if t, _ := InstanceBackupJointManager.IsSubBackup(db.Id); t {
|
|
out.IsSubBackup = true
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (db *SDiskBackup) GetDisk() (*SDisk, error) {
|
|
iDisk, err := DiskManager.FetchById(db.DiskId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disk := iDisk.(*SDisk)
|
|
return disk, nil
|
|
}
|
|
|
|
func (db *SDiskBackup) GetStorage() (*SStorage, error) {
|
|
iStorage, err := StorageManager.FetchById(db.StorageId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return iStorage.(*SStorage), nil
|
|
}
|
|
|
|
func (db *SDiskBackup) GetBackupStorage() (*SBackupStorage, error) {
|
|
ibs, err := BackupStorageManager.FetchById(db.BackupStorageId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ibs.(*SBackupStorage), nil
|
|
}
|
|
|
|
func (db *SDiskBackup) GetRegionDriver() (IRegionDriver, error) {
|
|
cloudRegion, err := db.GetRegion()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "db.GetRegion")
|
|
}
|
|
return cloudRegion.GetDriver(), nil
|
|
}
|
|
|
|
func (dm *SDiskBackupManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DiskBackupCreateInput) (api.DiskBackupCreateInput, error) {
|
|
if len(input.DiskId) == 0 {
|
|
return input, httperrors.NewMissingParameterError("disk_id")
|
|
}
|
|
if len(input.BackupStorageId) == 0 {
|
|
return input, httperrors.NewMissingParameterError("backup_storage_id")
|
|
}
|
|
// check disk
|
|
_disk, err := validators.ValidateModel(userCred, DiskManager, &input.DiskId)
|
|
if err != nil {
|
|
return input, err
|
|
}
|
|
disk := _disk.(*SDisk)
|
|
if disk.Status != api.DISK_READY {
|
|
return input, httperrors.NewInvalidStatusError("disk %s status is not %s", disk.Name, api.DISK_READY)
|
|
}
|
|
_, err = validators.ValidateModel(userCred, BackupStorageManager, &input.BackupStorageId)
|
|
if err != nil {
|
|
return input, err
|
|
}
|
|
storage, err := disk.GetStorage()
|
|
if err != nil {
|
|
return input, errors.Wrapf(err, "unable to get storage of disk %s", disk.GetId())
|
|
}
|
|
input.ManagerId = storage.ManagerId
|
|
region, err := storage.GetRegion()
|
|
if err != nil {
|
|
return input, err
|
|
}
|
|
input.CloudregionId = region.Id
|
|
|
|
return input, nil
|
|
}
|
|
|
|
func (db *SDiskBackup) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
|
err := db.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
diskObj, err := DiskManager.FetchById(db.DiskId)
|
|
if err != nil {
|
|
return errors.Wrap(err, "DiskManager.FetchById")
|
|
}
|
|
disk := diskObj.(*SDisk)
|
|
db.DiskConfig = &SBackupDiskConfig{
|
|
DiskConfig: *disk.ToDiskConfig(),
|
|
Name: disk.GetName(),
|
|
}
|
|
db.DiskType = disk.DiskType
|
|
db.DiskSizeMb = disk.DiskSize
|
|
db.OsArch = disk.OsArch
|
|
db.StorageId = disk.StorageId
|
|
return nil
|
|
}
|
|
|
|
func (db *SDiskBackup) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
|
db.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
|
db.StartBackupCreateTask(ctx, userCred, nil, "")
|
|
}
|
|
|
|
func (db *SDiskBackup) StartBackupCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
|
|
task, err := taskman.TaskManager.NewTask(ctx, "DiskBackupCreateTask", db, userCred, params, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
task.ScheduleRun(nil)
|
|
return nil
|
|
}
|
|
|
|
func (manager *SDiskBackupManager) ListItemExportKeys(ctx context.Context,
|
|
q *sqlchemy.SQuery,
|
|
userCred mcclient.TokenCredential,
|
|
keys stringutils2.SSortedStrings,
|
|
) (*sqlchemy.SQuery, error) {
|
|
var err error
|
|
q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if keys.Contains("disk") {
|
|
q, err = manager.SDiskResourceBaseManager.ListItemExportKeys(ctx, q, userCred, stringutils2.NewSortedStrings([]string{"disk"}))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SDiskResourceBaseManager.ListItemExportKeys")
|
|
}
|
|
}
|
|
if keys.ContainsAny(manager.SStorageResourceBaseManager.GetExportKeys()...) {
|
|
q, err = manager.SStorageResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SStorageResourceBaseManager.ListItemExportKeys")
|
|
}
|
|
}
|
|
|
|
return q, nil
|
|
}
|
|
|
|
func (manager *SDiskBackupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
|
var err error
|
|
|
|
q, err = manager.SVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
|
|
if err == nil {
|
|
return q, nil
|
|
}
|
|
|
|
q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
|
|
if err == nil {
|
|
return q, nil
|
|
}
|
|
|
|
q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
|
|
if err == nil {
|
|
return q, nil
|
|
}
|
|
|
|
return q, httperrors.ErrNotFound
|
|
}
|
|
|
|
func (manager *SDiskBackupManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.DiskBackupListInput) (*sqlchemy.SQuery, error) {
|
|
var err error
|
|
q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
|
|
}
|
|
q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
|
|
}
|
|
|
|
q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
func (self *SDiskBackup) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
|
return nil
|
|
}
|
|
|
|
func (self *SDiskBackup) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
|
return db.DeleteModel(ctx, userCred, self)
|
|
}
|
|
|
|
func (self *SDiskBackup) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
|
return self.StartBackupDeleteTask(ctx, userCred, "")
|
|
}
|
|
|
|
func (self *SDiskBackup) StartBackupDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
|
self.SetStatus(userCred, api.BACKUP_STATUS_DELETING, "")
|
|
log.Infof("start to delete diskbackup %s and set deleting", self.GetId())
|
|
task, err := taskman.TaskManager.NewTask(ctx, "DiskBackupDeleteTask", self, userCred, nil, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
task.ScheduleRun(nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *SDiskBackup) PerformRecovery(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskBackupRecoveryInput) (jsonutils.JSONObject, error) {
|
|
return nil, self.StartRecoveryTask(ctx, userCred, "", input.Name)
|
|
}
|
|
|
|
func (self *SDiskBackup) StartRecoveryTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, diskName string) error {
|
|
self.SetStatus(userCred, api.BACKUP_STATUS_RECOVERY, "")
|
|
var params *jsonutils.JSONDict
|
|
if diskName != "" {
|
|
params = jsonutils.NewDict()
|
|
params.Set("disk_name", jsonutils.NewString(diskName))
|
|
}
|
|
task, err := taskman.TaskManager.NewTask(ctx, "DiskBackupRecoveryTask", self, userCred, params, parentTaskId, "", nil)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
task.ScheduleRun(nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (manager *SDiskBackupManager) CreateBackup(ctx context.Context, owner mcclient.IIdentityProvider, diskId, backupStorageId, name string) (*SDiskBackup, error) {
|
|
iDisk, err := DiskManager.FetchById(diskId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disk := iDisk.(*SDisk)
|
|
storage, _ := disk.GetStorage()
|
|
backup := &SDiskBackup{}
|
|
backup.SetModelManager(manager, backup)
|
|
backup.ProjectId = owner.GetProjectId()
|
|
backup.DomainId = owner.GetProjectDomainId()
|
|
backup.DiskId = disk.Id
|
|
backup.DiskConfig = &SBackupDiskConfig{
|
|
DiskConfig: *disk.ToDiskConfig(),
|
|
Name: disk.GetName(),
|
|
}
|
|
backup.DiskType = disk.DiskType
|
|
backup.DiskSizeMb = disk.DiskSize
|
|
backup.OsArch = disk.OsArch
|
|
backup.StorageId = disk.StorageId
|
|
backup.ManagerId = storage.ManagerId
|
|
if cloudregion, _ := storage.GetRegion(); cloudregion != nil {
|
|
backup.CloudregionId = cloudregion.GetId()
|
|
}
|
|
backup.BackupStorageId = backupStorageId
|
|
backup.Name = name
|
|
backup.Status = api.BACKUP_STATUS_CREATING
|
|
err = DiskBackupManager.TableSpec().Insert(ctx, backup)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return backup, nil
|
|
}
|
|
|
|
func (self *SDiskBackup) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskBackupSyncstatusInput) (jsonutils.JSONObject, error) {
|
|
var openTask = true
|
|
count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if count > 0 {
|
|
return nil, httperrors.NewBadRequestError("Snapshot has %d task active, can't sync status", count)
|
|
}
|
|
|
|
return nil, StartResourceSyncStatusTask(ctx, userCred, self, "DiskBackupSyncstatusTask", "")
|
|
}
|