From fd4b544fbc72141356c553c2450788e636c34be4 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Thu, 19 Nov 2020 10:16:43 +0800 Subject: [PATCH] feat: rds recovery new instance from backup supported --- pkg/apis/compute/dbinstance.go | 6 ++ pkg/cloudprovider/dbinstance.go | 11 +++ pkg/cloudprovider/resources.go | 3 + pkg/compute/models/cloudsync.go | 24 ++++++ pkg/compute/models/dbinstance_backups.go | 92 ++++++++++++++++++++- pkg/compute/models/dbinstances.go | 57 +++++++++---- pkg/compute/models/regiondrivers.go | 1 + pkg/compute/regiondrivers/aliyun.go | 35 ++++---- pkg/compute/regiondrivers/base.go | 4 + pkg/compute/regiondrivers/managedvirtual.go | 88 ++++++++++++++++++-- pkg/compute/tasks/dbinstance_create_task.go | 13 ++- pkg/mcclient/options/dbinstances.go | 35 ++++---- pkg/multicloud/aliyun/dbinstance_backup.go | 33 +++++++- pkg/multicloud/dbinstance_backup_base.go | 8 ++ pkg/multicloud/google/dbinstance_backup.go | 2 + 15 files changed, 343 insertions(+), 69 deletions(-) diff --git a/pkg/apis/compute/dbinstance.go b/pkg/apis/compute/dbinstance.go index 2df0a39de7..da3bb85b48 100644 --- a/pkg/apis/compute/dbinstance.go +++ b/pkg/apis/compute/dbinstance.go @@ -135,6 +135,9 @@ type DBInstanceCreateInput struct { // required: true DiskSizeGB int `json:"disk_size_gb"` + // 指定连接端口 + Port int `json:"port"` + // rds初始化密码 // 阿里云不需要此参数 // 华为云会默认创建一个用户,若不传此参数, 则为随机密码 @@ -154,6 +157,9 @@ type DBInstanceCreateInput struct { // rds实例内存大小 // 若指定实例套餐,此参数将根据套餐设置 VmemSizeMb int `json:"vmem_size_mb"` + + // 从备份中创建新实例 + DBInstancebackupId string `json:"dbinstancebackup_id"` } type SDBInstanceChangeConfigInput struct { diff --git a/pkg/cloudprovider/dbinstance.go b/pkg/cloudprovider/dbinstance.go index 894e7e35a5..9f5d1f4154 100644 --- a/pkg/cloudprovider/dbinstance.go +++ b/pkg/cloudprovider/dbinstance.go @@ -16,6 +16,14 @@ package cloudprovider import "yunion.io/x/onecloud/pkg/util/billing" +type TBackupMethod string + +const ( + BackupMethodLogical = TBackupMethod("Logical") + BackupMethodPhysical = TBackupMethod("Physical") + BackupMethodUnknown = TBackupMethod("") +) + type SDBInstanceNetwork struct { IP string NetworkId string @@ -61,6 +69,9 @@ type SManagedDBInstanceCreateConfig struct { BillingCycle *billing.SBillingCycle Tags map[string]string + + // 仅从备份恢复到新实例用到 + RdsId string } type SManagedDBInstanceChangeConfig struct { diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 25cf6eb0b1..6e16742f95 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -849,6 +849,9 @@ type ICloudDBInstanceBackup interface { GetBackupSizeMb() int GetDBNames() string GetBackupMode() string + GetBackupMethod() TBackupMethod + + CreateICloudDBInstance(opts *SManagedDBInstanceCreateConfig) (ICloudDBInstance, error) Delete() error } diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 9857e0a086..cd38d11279 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -866,6 +866,10 @@ func syncDBInstanceResource(ctx context.Context, userCred mcclient.TokenCredenti if err != nil { log.Errorf("syncDBInstanceAccounts: %v", err) } + err = syncDBInstanceBackups(ctx, userCred, syncResults, localInstance, remoteInstance) + if err != nil { + log.Errorf("syncDBInstanceBackups: %v", err) + } } func syncDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) error { @@ -901,6 +905,26 @@ func syncDBInstanceSecgroups(ctx context.Context, userCred mcclient.TokenCredent return nil } +func syncDBInstanceBackups(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) error { + backups, err := remoteInstance.GetIDBInstanceBackups() + if err != nil { + return errors.Wrapf(err, "GetIDBInstanceBackups") + } + + region := localInstance.GetRegion() + provider := localInstance.GetCloudprovider() + + result := DBInstanceBackupManager.SyncDBInstanceBackups(ctx, userCred, provider, localInstance, region, backups) + syncResults.Add(DBInstanceBackupManager, result) + + msg := result.Result() + log.Infof("SyncDBInstanceBackups for dbinstance %s result: %s", localInstance.Name, msg) + if result.IsError() { + return result.AllError() + } + return nil +} + func syncDBInstanceParameters(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) error { parameters, err := remoteInstance.GetIDBInstanceParameters() if err != nil { diff --git a/pkg/compute/models/dbinstance_backups.go b/pkg/compute/models/dbinstance_backups.go index 6c323791e1..b4928c4c18 100644 --- a/pkg/compute/models/dbinstance_backups.go +++ b/pkg/compute/models/dbinstance_backups.go @@ -85,9 +85,8 @@ type SDBInstanceBackup struct { // example: 32 BackupSizeMb int `nullable:"false" list:"user" json:"backup_size_mb"` - // RDS实例Id - // example: 239b9663-6d06-4ef4-8cfc-320a7fb6660d - // DBInstanceId string `width:"36" charset:"ascii" name:"dbinstance_id" nullable:"false" list:"user" create:"required" index:"true"` + // 备份方式 Logical|Physical + BackupMethod string `width:"32" charset:"ascii" nullable:"true" list:"user" create:"optional" json:"backup_method"` } func (manager *SDBInstanceBackupManager) GetContextManagers() [][]db.IModelManager { @@ -381,6 +380,22 @@ func (backup *SDBInstanceBackup) GetIDBInstanceBackup() (cloudprovider.ICloudDBI if err != nil { return nil, errors.Wrapf(err, "GetIDBInstance") } + err = cloudprovider.Wait(time.Second*3, time.Second*15, func() (bool, error) { + backups, err := iRds.GetIDBInstanceBackups() + if err != nil { + return false, errors.Wrapf(err, "GetIDBInstanceBackups") + } + for i := range backups { + if backups[i].GetGlobalId() == backup.ExternalId { + return true, nil + } + } + log.Warningf("failed to found backup %s", backup.ExternalId) + return false, nil + }) + if err != nil { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "timeout for search backup %s", backup.ExternalId) + } backups, err := iRds.GetIDBInstanceBackups() if err != nil { return nil, errors.Wrapf(err, "GetIDBInstanceBackups") @@ -390,7 +405,7 @@ func (backup *SDBInstanceBackup) GetIDBInstanceBackup() (cloudprovider.ICloudDBI return backups[i], nil } } - return nil, errors.Wrapf(cloudprovider.ErrNotFound, "externalId: %s", backup.ExternalId) + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "search backup %s", backup.ExternalId) } iRegion, err := backup.GetIRegion() @@ -464,6 +479,7 @@ func (self *SDBInstanceBackup) SyncWithCloudDBInstanceBackup( self.Engine = extBackup.GetEngine() self.EngineVersion = extBackup.GetEngineVersion() self.DBNames = extBackup.GetDBNames() + self.BackupMethod = string(extBackup.GetBackupMethod()) if dbinstanceId := extBackup.GetDBInstanceId(); len(dbinstanceId) > 0 { //有可能云上删除了实例,未删除备份 @@ -522,6 +538,7 @@ func (manager *SDBInstanceBackupManager) newFromCloudDBInstanceBackup( backup.BackupSizeMb = extBackup.GetBackupSizeMb() backup.DBNames = extBackup.GetDBNames() backup.BackupMode = extBackup.GetBackupMode() + backup.BackupMethod = string(extBackup.GetBackupMethod()) backup.ExternalId = extBackup.GetGlobalId() if dbinstanceId := extBackup.GetDBInstanceId(); len(dbinstanceId) > 0 { @@ -616,3 +633,70 @@ func (manager *SDBInstanceBackupManager) ListItemExportKeys(ctx context.Context, func (self *SDBInstanceBackup) GetChangeOwnerCandidateDomainIds() []string { return self.SManagedResourceBase.GetChangeOwnerCandidateDomainIds() } + +func (self *SDBInstanceBackup) fillRdsConfig(output *api.DBInstanceCreateInput) error { + if self.Status != api.DBINSTANCE_BACKUP_READY { + return fmt.Errorf("backup %s status is %s require %s", self.Name, self.Status, api.DBINSTANCE_BACKUP_READY) + } + if len(self.DBInstanceId) == 0 { + if len(self.Engine) == 0 { + return fmt.Errorf("backup engine %s is unknown", self.Name) + } + output.Engine = self.Engine + if len(self.EngineVersion) == 0 { + return fmt.Errorf("backup engine version %s is unknown", self.Name) + } + output.EngineVersion = self.EngineVersion + return nil + } + rds, err := self.GetDBInstance() + if err != nil { + return errors.Wrapf(err, "backup.GetDBInstance") + } + if len(output.NetworkId) == 0 { + networks, err := rds.GetDBNetworks() + if err != nil { + return errors.Wrapf(err, "GetDBNetworks") + } + if len(networks) > 0 { + output.NetworkId = networks[0].NetworkId + } + } + + if output.VcpuCount == 0 { + output.VcpuCount = rds.VcpuCount + } + if output.VmemSizeMb == 0 { + output.VmemSizeMb = rds.VmemSizeMb + } + if output.DiskSizeGB == 0 { + output.DiskSizeGB = rds.DiskSizeGB + } + if output.Port == 0 { + output.Port = rds.Port + } + if len(output.Category) == 0 { + output.Category = rds.Category + } + if len(output.StorageType) == 0 { + output.StorageType = rds.StorageType + } + output.Engine = rds.Engine + output.EngineVersion = rds.EngineVersion + if len(output.InstanceType) == 0 { + output.InstanceType = rds.InstanceType + } + if len(output.VpcId) == 0 { + output.VpcId = rds.VpcId + } + if len(output.Zone1) == 0 { + output.Zone1 = rds.Zone1 + } + if len(output.Zone2) == 0 { + output.Zone2 = rds.Zone2 + } + if len(output.Zone3) == 0 { + output.Zone3 = rds.Zone3 + } + return nil +} diff --git a/pkg/compute/models/dbinstances.go b/pkg/compute/models/dbinstances.go index e63655a75f..0037e8cc0d 100644 --- a/pkg/compute/models/dbinstances.go +++ b/pkg/compute/models/dbinstances.go @@ -132,6 +132,9 @@ type SDBInstance struct { Zone2 string `width:"36" charset:"ascii" nullable:"false" create:"optional" list:"user"` // 可用区3 Zone3 string `width:"36" charset:"ascii" nullable:"false" create:"optional" list:"user"` + + // 从备份创建新实例 + DBInstancebackupId string `width:"36" name:"dbinstancebackup_id" charset:"ascii" nullable:"false" create:"optional"` } func (manager *SDBInstanceManager) GetContextManagers() [][]db.IModelManager { @@ -284,6 +287,17 @@ func (manager *SDBInstanceManager) BatchCreateValidateCreateData(ctx context.Con } func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DBInstanceCreateInput) (api.DBInstanceCreateInput, error) { + if len(input.DBInstancebackupId) > 0 { + _backup, err := validators.ValidateModel(userCred, DBInstanceBackupManager, &input.DBInstancebackupId) + if err != nil { + return input, err + } + backup := _backup.(*SDBInstanceBackup) + err = backup.fillRdsConfig(&input) + if err != nil { + return input, err + } + } for _, v := range map[string]*string{"zone1": &input.Zone1, "zone2": &input.Zone2, "zone3": &input.Zone3} { if len(*v) > 0 { _, err := validators.ValidateModel(userCred, ZoneManager, v) @@ -298,28 +312,35 @@ func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred return input, httperrors.NewWeakPasswordError() } } - if len(input.NetworkId) == 0 { - return input, httperrors.NewMissingParameterError("network_id") - } - _network, err := validators.ValidateModel(userCred, NetworkManager, &input.NetworkId) - if err != nil { - return input, err - } - - network := _network.(*SNetwork) - - if len(input.Address) > 0 { - ip := net.ParseIP(input.Address).To4() - if ip == nil { - return input, httperrors.NewInputParameterError("invalid address: %s", input.Address) + var vpc *SVpc + var network *SNetwork + if len(input.NetworkId) > 0 { + _network, err := validators.ValidateModel(userCred, NetworkManager, &input.NetworkId) + if err != nil { + return input, err } - addr, _ := netutils.NewIPV4Addr(input.Address) - if !network.IsAddressInRange(addr) { - return input, httperrors.NewInputParameterError("Ip %s not in network %s(%s) range", input.Address, network.Name, network.Id) + network = _network.(*SNetwork) + if len(input.Address) > 0 { + ip := net.ParseIP(input.Address).To4() + if ip == nil { + return input, httperrors.NewInputParameterError("invalid address: %s", input.Address) + } + addr, _ := netutils.NewIPV4Addr(input.Address) + if !network.IsAddressInRange(addr) { + return input, httperrors.NewInputParameterError("Ip %s not in network %s(%s) range", input.Address, network.Name, network.Id) + } } + vpc = network.GetVpc() + } else if len(input.VpcId) > 0 { + _vpc, err := validators.ValidateModel(userCred, VpcManager, &input.VpcId) + if err != nil { + return input, err + } + vpc = _vpc.(*SVpc) + } else { + return input, httperrors.NewMissingParameterError("vpc_id") } - vpc := network.GetVpc() input.VpcId = vpc.Id input.ManagerId = vpc.ManagerId cloudprovider := vpc.GetCloudprovider() diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index 6c3b931372..87d1ae696c 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -161,6 +161,7 @@ type IDBInstanceDriver interface { ValidateResetDBInstancePassword(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, account string) error RequestCreateDBInstance(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, task taskman.ITask) error + RequestCreateDBInstanceFromBackup(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, task taskman.ITask) error RequestCreateDBInstanceBackup(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, backup *SDBInstanceBackup, task taskman.ITask) error RequestChangeDBInstanceConfig(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, task taskman.ITask) error diff --git a/pkg/compute/regiondrivers/aliyun.go b/pkg/compute/regiondrivers/aliyun.go index 37019f0547..faa0641ded 100644 --- a/pkg/compute/regiondrivers/aliyun.go +++ b/pkg/compute/regiondrivers/aliyun.go @@ -1000,25 +1000,26 @@ func (self *SAliyunRegionDriver) ValidateCreateDBInstanceData(ctx context.Contex return input, httperrors.NewInputParameterError("slave dbinstance not support prepaid billing type") } - wire := network.GetWire() - if wire == nil { - return input, httperrors.NewGeneralError(fmt.Errorf("failed to found wire for network %s(%s)", network.Name, network.Id)) - } - zone := wire.GetZone() - if zone == nil { - return input, httperrors.NewGeneralError(fmt.Errorf("failed to found zone for wire %s(%s)", wire.Name, wire.Id)) - } - - match := false - for _, sku := range skus { - if utils.IsInStringArray(zone.Id, []string{sku.Zone1, sku.Zone2, sku.Zone3}) { - match = true - break + if network != nil { + wire := network.GetWire() + if wire == nil { + return input, httperrors.NewGeneralError(fmt.Errorf("failed to found wire for network %s(%s)", network.Name, network.Id)) + } + zone := wire.GetZone() + if zone == nil { + return input, httperrors.NewGeneralError(fmt.Errorf("failed to found zone for wire %s(%s)", wire.Name, wire.Id)) } - } - if !match { - return input, httperrors.NewInputParameterError("failed to match any skus in the network %s(%s) zone %s(%s)", network.Name, network.Id, zone.Name, zone.Id) + match := false + for _, sku := range skus { + if utils.IsInStringArray(zone.Id, []string{sku.Zone1, sku.Zone2, sku.Zone3}) { + match = true + break + } + } + if !match { + return input, httperrors.NewInputParameterError("failed to match any skus in the network %s(%s) zone %s(%s)", network.Name, network.Id, zone.Name, zone.Id) + } } var master *models.SDBInstance diff --git a/pkg/compute/regiondrivers/base.go b/pkg/compute/regiondrivers/base.go index eafdfc5f90..18f952e588 100644 --- a/pkg/compute/regiondrivers/base.go +++ b/pkg/compute/regiondrivers/base.go @@ -295,6 +295,10 @@ func (self *SBaseRegionDriver) RequestCreateDBInstance(ctx context.Context, user return fmt.Errorf("Not Implement RequestCreateDBInstance") } +func (self *SBaseRegionDriver) RequestCreateDBInstanceFromBackup(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *models.SDBInstance, task taskman.ITask) error { + return fmt.Errorf("Not Implement RequestCreateDBInstanceFromBackup") +} + func (self *SBaseRegionDriver) RequestCreateDBInstanceBackup(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *models.SDBInstance, backup *models.SDBInstanceBackup, task taskman.ITask) error { return fmt.Errorf("Not Implement RequestCreateDBInstanceBackup") } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 8cdf288196..66b5ca9545 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -1801,6 +1801,85 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont return nil } +func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstanceFromBackup(ctx context.Context, userCred mcclient.TokenCredential, rds *models.SDBInstance, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + _backup, err := models.DBInstanceBackupManager.FetchById(rds.DBInstancebackupId) + if err != nil { + return nil, errors.Wrapf(err, "DBInstanceBackupManager.FetchById(%s)", rds.DBInstancebackupId) + } + backup := _backup.(*models.SDBInstanceBackup) + iBackup, err := backup.GetIDBInstanceBackup() + if err != nil { + return nil, errors.Wrapf(err, "backup.GetIDBInstanceBackup") + } + vpc, err := rds.GetVpc() + if err != nil { + return nil, errors.Wrap(err, "rds.GetVpc()") + } + desc := cloudprovider.SManagedDBInstanceCreateConfig{ + Name: rds.Name, + Description: rds.Description, + StorageType: rds.StorageType, + DiskSizeGB: rds.DiskSizeGB, + VcpuCount: rds.VcpuCount, + VmemSizeMb: rds.VmemSizeMb, + VpcId: vpc.ExternalId, + Engine: rds.Engine, + EngineVersion: rds.EngineVersion, + Category: rds.Category, + Port: rds.Port, + } + if len(backup.DBInstanceId) > 0 { + parentRds, err := backup.GetDBInstance() + if err != nil { + return nil, errors.Wrapf(err, "backup.GetDBInstance") + } + desc.RdsId = parentRds.ExternalId + } + + log.Debugf("create from backup params: %s", jsonutils.Marshal(desc).String()) + + networks, err := rds.GetDBNetworks() + if err != nil { + return nil, errors.Wrapf(err, "dbinstance.GetDBNetworks") + } + + if len(networks) > 0 { + net, err := networks[0].GetNetwork() + if err != nil { + return nil, errors.Wrapf(err, "GetNetwork") + } + desc.NetworkId, desc.Address = net.ExternalId, networks[0].IpAddr + } + if rds.BillingType == billing_api.BILLING_TYPE_PREPAID { + bc, err := billing.ParseBillingCycle(rds.BillingCycle) + if err != nil { + log.Errorf("failed to parse billing cycle %s: %v", rds.BillingCycle, err) + } else if bc.IsValid() { + desc.BillingCycle = &bc + desc.BillingCycle.AutoRenew = rds.AutoRenew + } + } + + iRds, err := iBackup.CreateICloudDBInstance(&desc) + if err != nil { + return nil, errors.Wrapf(err, "iBackup.CreateICloudDBInstance") + } + + err = db.SetExternalId(rds, userCred, iRds.GetGlobalId()) + if err != nil { + return nil, errors.Wrapf(err, "db.SetExternalId") + } + + err = cloudprovider.WaitStatus(iRds, api.DBINSTANCE_RUNNING, time.Second*5, time.Hour*1) + if err != nil { + return nil, errors.Wrapf(err, "cloudprovider.WaitStatus runing") + } + return nil, nil + }) + return nil +} + func (self *SManagedVirtualizationRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error { task.ScheduleRun(nil) return nil @@ -2433,14 +2512,9 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstanceBackup(ct return nil, errors.Wrapf(err, "backup.GetIDBInstanceBackup") } - _, err = db.Update(backup, func() error { - backup.StartTime = iBackup.GetStartTime() - backup.EndTime = iBackup.GetEndTime() - backup.BackupSizeMb = iBackup.GetBackupSizeMb() - return nil - }) + err = backup.SyncWithCloudDBInstanceBackup(ctx, userCred, iBackup, instance.GetCloudprovider()) if err != nil { - return nil, errors.Wrap(err, "db.Update") + log.Warningf("sync backup info error: %v", err) } instance.SetStatus(userCred, api.DBINSTANCE_RUNNING, "") diff --git a/pkg/compute/tasks/dbinstance_create_task.go b/pkg/compute/tasks/dbinstance_create_task.go index 021e32e44d..9eb6ff426d 100644 --- a/pkg/compute/tasks/dbinstance_create_task.go +++ b/pkg/compute/tasks/dbinstance_create_task.go @@ -48,12 +48,17 @@ func (self *DBInstanceCreateTask) OnInit(ctx context.Context, obj db.IStandalone self.CreateDBInstance(ctx, dbinstance) } -func (self *DBInstanceCreateTask) CreateDBInstance(ctx context.Context, dbinstance *models.SDBInstance) { - region := dbinstance.GetRegion() +func (self *DBInstanceCreateTask) CreateDBInstance(ctx context.Context, rds *models.SDBInstance) { + region := rds.GetRegion() self.SetStage("OnCreateDBInstanceComplete", nil) - err := region.GetDriver().RequestCreateDBInstance(ctx, self.UserCred, dbinstance, self) + var err error + if len(rds.DBInstancebackupId) > 0 { + err = region.GetDriver().RequestCreateDBInstanceFromBackup(ctx, self.UserCred, rds, self) + } else { + err = region.GetDriver().RequestCreateDBInstance(ctx, self.UserCred, rds, self) + } if err != nil { - self.taskFailed(ctx, dbinstance, err) + self.taskFailed(ctx, rds, err) return } } diff --git a/pkg/mcclient/options/dbinstances.go b/pkg/mcclient/options/dbinstances.go index 9f8e9eb913..f901883b3c 100644 --- a/pkg/mcclient/options/dbinstances.go +++ b/pkg/mcclient/options/dbinstances.go @@ -22,23 +22,24 @@ import ( ) type DBInstanceCreateOptions struct { - NAME string `help:"DBInstance Name"` - InstanceType string `help:"InstanceType for DBInstance"` - VcpuCount int `help:"Core of cpu for DBInstance"` - VmemSizeMb int `help:"Memory size of DBInstance"` - Port int `help:"Port of DBInstance"` - Category string `help:"Category of DBInstance"` - Network string `help:"Network of DBInstance"` - Address string `help:"Address of DBInstance"` - Engine string `help:"Engine of DBInstance"` - EngineVersion string `help:"EngineVersion of DBInstance Engine"` - StorageType string `help:"StorageTyep of DBInstance"` - Secgroup string `help:"Secgroup name or Id for DBInstance"` - Zone string `help:"ZoneId or name for DBInstance"` - DiskSizeGB int `help:"Storage size for DBInstance"` - Duration string `help:"Duration for DBInstance"` - AllowDelete *bool `help:"not lock dbinstance" ` - Tags []string `help:"Tags info,prefix with 'user:', eg: user:project=default" json:"-"` + NAME string `help:"DBInstance Name"` + InstanceType string `help:"InstanceType for DBInstance"` + VcpuCount int `help:"Core of cpu for DBInstance"` + VmemSizeMb int `help:"Memory size of DBInstance"` + Port int `help:"Port of DBInstance"` + Category string `help:"Category of DBInstance"` + Network string `help:"Network of DBInstance"` + Address string `help:"Address of DBInstance"` + Engine string `help:"Engine of DBInstance"` + EngineVersion string `help:"EngineVersion of DBInstance Engine"` + StorageType string `help:"StorageTyep of DBInstance"` + Secgroup string `help:"Secgroup name or Id for DBInstance"` + Zone string `help:"ZoneId or name for DBInstance"` + DiskSizeGB int `help:"Storage size for DBInstance"` + Duration string `help:"Duration for DBInstance"` + AllowDelete *bool `help:"not lock dbinstance" ` + Tags []string `help:"Tags info,prefix with 'user:', eg: user:project=default" json:"-"` + DBInstancebackupId string `help:"create dbinstance from backup" json:"dbinstancebackup_id"` } func (opts *DBInstanceCreateOptions) Params() (*jsonutils.JSONDict, error) { diff --git a/pkg/multicloud/aliyun/dbinstance_backup.go b/pkg/multicloud/aliyun/dbinstance_backup.go index b548cb4785..8abc1d8394 100644 --- a/pkg/multicloud/aliyun/dbinstance_backup.go +++ b/pkg/multicloud/aliyun/dbinstance_backup.go @@ -19,8 +19,7 @@ import ( "strings" "time" - "github.com/coredns/coredns/plugin/pkg/log" - + "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/utils" @@ -325,3 +324,33 @@ func (region *SRegion) waitBackupCreateComplete(instanceId, jobId string) (strin } return "", fmt.Errorf("failed to found backup job %s backupid", jobId) } + +func (self *SDBInstanceBackup) GetBackupMethod() cloudprovider.TBackupMethod { + return cloudprovider.TBackupMethod(self.BackupMethod) +} + +func (self *SDBInstanceBackup) CreateICloudDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) { + rdsId, err := self.region.CreateDBInstanceByBackup(self.BackupId, opts) + if err != nil { + return nil, errors.Wrapf(err, "CreateDBInstanceByBackup") + } + return self.region.GetDBInstanceDetail(rdsId) +} + +func (self *SRegion) CreateDBInstanceByBackup(backupId string, opts *cloudprovider.SManagedDBInstanceCreateConfig) (string, error) { + params := map[string]string{ + "DBInstanceId": opts.RdsId, + "DBInstanceStorageType": opts.StorageType, + "PayType": "Postpaid", + "BackupId": backupId, + } + resp, err := self.rdsRequest("CloneDBInstance", params) + if err != nil { + return "", errors.Wrapf(err, "rdsRequest") + } + rdsId, err := resp.GetString("DBInstanceId") + if err != nil { + return "", fmt.Errorf("missing DBInstanceId after CloneDBInstance") + } + return rdsId, nil +} diff --git a/pkg/multicloud/dbinstance_backup_base.go b/pkg/multicloud/dbinstance_backup_base.go index 94a0bc057f..93b942048c 100644 --- a/pkg/multicloud/dbinstance_backup_base.go +++ b/pkg/multicloud/dbinstance_backup_base.go @@ -36,3 +36,11 @@ func (backup *SDBInstanceBackupBase) Delete() error { func (backup *SDBInstanceBackupBase) GetProjectId() string { return "" } + +func (backup *SDBInstanceBackupBase) CreateICloudDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) { + return nil, errors.Wrap(cloudprovider.ErrNotImplemented, "CreateICloudDBInstance") +} + +func (backup *SDBInstanceBackupBase) GetBackupMethod() cloudprovider.TBackupMethod { + return cloudprovider.BackupMethodUnknown +} diff --git a/pkg/multicloud/google/dbinstance_backup.go b/pkg/multicloud/google/dbinstance_backup.go index e24b68fbb9..9af5c191dc 100644 --- a/pkg/multicloud/google/dbinstance_backup.go +++ b/pkg/multicloud/google/dbinstance_backup.go @@ -23,6 +23,7 @@ import ( "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/multicloud" ) type OperationError struct { @@ -32,6 +33,7 @@ type OperationError struct { } type SDBInstanceBackup struct { + multicloud.SDBInstanceBackupBase rds *SDBInstance Kind string