diff --git a/cmd/climc/shell/compute/dbinstances.go b/cmd/climc/shell/compute/dbinstances.go index 15f52932ac..79f141f4c0 100644 --- a/cmd/climc/shell/compute/dbinstances.go +++ b/cmd/climc/shell/compute/dbinstances.go @@ -15,262 +15,34 @@ package compute import ( - "fmt" - "strings" - - "yunion.io/x/jsonutils" - - computeapi "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/cmd/climc/shell" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modulebase" "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/mcclient/options" + "yunion.io/x/onecloud/pkg/mcclient/options/compute" ) func init() { - type DBInstanceListOptions struct { - options.BaseListOptions - BillingType string `help:"billing type" choices:"postpaid|prepaid"` - } - R(&DBInstanceListOptions{}, "dbinstance-list", "List DB instance", func(s *mcclient.ClientSession, opts *DBInstanceListOptions) error { - params, err := options.ListStructToParams(opts) - if err != nil { - return err - } - - result, err := modules.DBInstance.List(s, params) - if err != nil { - return err - } - - if len(opts.ExportFile) > 0 { - exportList(result, opts.ExportFile, opts.ExportKeys, opts.ExportTexts, modules.DBInstance.GetColumns(s)) - } else { - printList(result, modules.DBInstance.GetColumns(s)) - } - return nil - }) - - R(&options.DBInstanceCreateOptions{}, "dbinstance-create", "Create DB instance", func(s *mcclient.ClientSession, opts *options.DBInstanceCreateOptions) error { - params, err := opts.Params() - if err != nil { - return err - } - if opts.AllowDelete != nil && *opts.AllowDelete { - params.Add(jsonutils.JSONFalse, "disable_delete") - } - result, err := modules.DBInstance.Create(s, params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceRenewOptions struct { - ID string `help:"ID or name of server to renew"` - DURATION string `help:"Duration of renew, ADMIN only command"` - } - R(&DBInstanceRenewOptions{}, "dbinstance-renew", "Renew a dbinstance", func(s *mcclient.ClientSession, args *DBInstanceRenewOptions) error { - params := jsonutils.NewDict() - params.Add(jsonutils.NewString(args.DURATION), "duration") - result, err := modules.DBInstance.PerformAction(s, args.ID, "renew", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceUpdateOptions struct { - ID string `help:"ID or name of server to renew"` - Name string - Description string - Delete string `help:"Lock or not lock dbinstance" choices:"enable|disable"` - } - R(&DBInstanceUpdateOptions{}, "dbinstance-update", "Update a dbinstance", func(s *mcclient.ClientSession, args *DBInstanceUpdateOptions) error { - params, err := options.StructToParams(args) - if err != nil { - return err - } - if len(args.Delete) > 0 { - if args.Delete == "disable" { - params.Add(jsonutils.JSONTrue, "disable_delete") - } else { - params.Add(jsonutils.JSONFalse, "disable_delete") - } - } - result, err := modules.DBInstance.Update(s, args.ID, params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceChangeConfigOptions struct { - ID string `help:"ID or name of server to renew"` - DiskSizeGb int64 `help:"Change DBInstance storage size"` - VcpuCount int64 `help:"Change DBInstance vcpu count"` - VmemSizeMb int64 `help:"Change DBInstance vmem size mb"` - InstanceType string `help:"Change DBInstance instanceType"` - Category string `help:"Change DBInstance category"` - } - R(&DBInstanceChangeConfigOptions{}, "dbinstance-change-config", "ChangeConfig a dbinstance", func(s *mcclient.ClientSession, args *DBInstanceChangeConfigOptions) error { - params := jsonutils.NewDict() - if len(args.Category) > 0 { - params.Add(jsonutils.NewString(args.Category), "category") - } - if len(args.InstanceType) > 0 { - params.Add(jsonutils.NewString(args.InstanceType), "instance_type") - } - if args.DiskSizeGb > 0 { - params.Add(jsonutils.NewInt(args.DiskSizeGb), "disk_size_gb") - } - if args.VcpuCount > 0 { - params.Add(jsonutils.NewInt(args.VcpuCount), "vcpu_count") - } - if args.VmemSizeMb > 0 { - params.Add(jsonutils.NewInt(args.VmemSizeMb), "vmeme_size_mb") - } - result, err := modules.DBInstance.PerformAction(s, args.ID, "change-config", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceIdOptions struct { - ID string `help:"ID of dbinstance"` - } - - R(&DBInstanceIdOptions{}, "dbinstance-open-public-connection", "Open DB instance public connection", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - params := jsonutils.NewDict() - params.Add(jsonutils.JSONTrue, "open") - result, err := modules.DBInstance.PerformAction(s, opts.ID, "public-connection", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-close-public-connection", "Close DB instance public connection", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - params := jsonutils.NewDict() - params.Add(jsonutils.JSONFalse, "open") - result, err := modules.DBInstance.PerformAction(s, opts.ID, "public-connection", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceRecoveryOptions struct { - ID string - BACKUP string - Databases []string - } - - R(&DBInstanceRecoveryOptions{}, "dbinstance-recovery", "Recovery DB instance database from backup", func(s *mcclient.ClientSession, opts *DBInstanceRecoveryOptions) error { - params := jsonutils.NewDict() - params.Set("dbinstancebackup", jsonutils.NewString(opts.BACKUP)) - dbs := jsonutils.NewDict() - for _, database := range opts.Databases { - if len(database) > 0 { - dbInfo := strings.Split(database, ":") - if len(dbInfo) == 1 { - dbs.Add(jsonutils.NewString(dbInfo[0]), dbInfo[0]) - } else if len(dbInfo) == 2 { - dbs.Add(jsonutils.NewString(dbInfo[1]), dbInfo[0]) - } else { - return fmt.Errorf("Invalid dbinfo: %s", database) - } - } - } - if dbs.Length() > 0 { - params.Add(dbs, "databases") - } - result, err := modules.DBInstance.PerformAction(s, opts.ID, "recovery", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-show", "Show DB instance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.Get(s, opts.ID, nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-reboot", "Reboot DB instance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.PerformAction(s, opts.ID, "reboot", nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceDeleteOptions struct { - ID string `help:"DBInstance Id or name"` - KeepBackup bool `help:"Keep dbinstance manual backup after delete dbinstance"` - } - - R(&DBInstanceDeleteOptions{}, "dbinstance-delete", "Delete DB instance", func(s *mcclient.ClientSession, opts *DBInstanceDeleteOptions) error { - params := jsonutils.NewDict() - if opts.KeepBackup { - params.Add(jsonutils.JSONTrue, "keep_backup") - } - result, err := modules.DBInstance.Delete(s, opts.ID, params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-purge", "Purge DB instance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.PerformAction(s, opts.ID, "purge", nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-syncstatus", "Sync status for DB instance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.PerformAction(s, opts.ID, "syncstatus", nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-sync", "Sync conf from cloud dbinstance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.PerformAction(s, opts.ID, "sync", nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&DBInstanceIdOptions{}, "dbinstance-sync-status", "Sync status for DB instance", func(s *mcclient.ClientSession, opts *DBInstanceIdOptions) error { - result, err := modules.DBInstance.PerformAction(s, opts.ID, "sync-status", nil) - if err != nil { - return err - } - printObject(result) - return nil - }) + cmd := shell.NewResourceCmd(&modules.DBInstance) //.WithContextManager(&modules.Networks) + cmd.List(&compute.DBInstanceListOptions{}) + cmd.Create(&compute.DBInstanceCreateOptions{}) + cmd.Update(&compute.DBInstanceUpdateOptions{}) + cmd.Show(&compute.DBInstanceIdOptions{}) + cmd.Delete(&compute.DBInstanceDeleteOptions{}) + cmd.Perform("renew", &compute.DBInstanceRenewOptions{}) + cmd.Perform("change-config", &compute.DBInstanceChangeConfigOptions{}) + cmd.Perform("public-connection", &compute.DBInstancePublicConnectionOptions{}) + cmd.Perform("recovery", &compute.DBInstanceRecoveryOptions{}) + cmd.Perform("reboot", &compute.DBInstanceIdOptions{}) + cmd.Perform("purge", &compute.DBInstanceIdOptions{}) + cmd.Perform("syncstatus", &compute.DBInstanceIdOptions{}) + cmd.Perform("sync", &compute.DBInstanceIdOptions{}) + cmd.Perform("change-owner", &compute.DBInstanceChangeOwnerOptions{}) + cmd.PerformWithKeyword("add-tag", "user-metadata", &options.ResourceMetadataOptions{}) + cmd.PerformWithKeyword("set-tag", "set-user-metadata", &options.ResourceMetadataOptions{}) + cmd.Perform("remote-update", &compute.DBInstanceRemoteUpdateOptions{}) + cmd.Perform("set-secgroup", &compute.DBInstanceSetSecgroupOptions{}) type DBInstanceNetworkListOptions struct { options.BaseListOptions @@ -336,64 +108,4 @@ func init() { return nil }) - type DBInstanceChangeOwnerOptions struct { - ID string `help:"DBInstance to change owner" json:"-"` - PROJECT string `help:"Project ID or change" json:"tenant"` - } - R(&DBInstanceChangeOwnerOptions{}, "dbinstance-change-owner", "Change owner porject of a dbinstance", func(s *mcclient.ClientSession, opts *DBInstanceChangeOwnerOptions) error { - params, err := options.StructToParams(opts) - if err != nil { - return err - } - result, err := modules.DBInstance.PerformAction(s, opts.ID, "change-owner", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&options.ResourceMetadataOptions{}, "dbinstance-add-tag", "Set tag of a dbinstance", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error { - params, err := opts.Params() - if err != nil { - return err - } - result, err := modules.DBInstance.PerformAction(s, opts.ID, "user-metadata", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&options.ResourceMetadataOptions{}, "dbinstance-set-tag", "Set tag of a dbinstance", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error { - params, err := opts.Params() - if err != nil { - return err - } - result, err := modules.DBInstance.PerformAction(s, opts.ID, "set-user-metadata", params) - if err != nil { - return err - } - printObject(result) - return nil - }) - - type DBInstanceRemoteUpdateOptions struct { - ID string `json:"-"` - computeapi.DBInstanceRemoteUpdateInput - } - - R(&DBInstanceRemoteUpdateOptions{}, "dbinstance-remote-update", "Change owner porject of a dbinstance", func(s *mcclient.ClientSession, opts *DBInstanceRemoteUpdateOptions) error { - params, err := options.StructToParams(opts) - if err != nil { - return err - } - result, err := modules.DBInstance.PerformAction(s, opts.ID, "remote-update", params) - if err != nil { - return err - } - printObject(result) - return nil - }) } diff --git a/pkg/apis/compute/dbinstance.go b/pkg/apis/compute/dbinstance.go index da3bb85b48..3b3ff169d4 100644 --- a/pkg/apis/compute/dbinstance.go +++ b/pkg/apis/compute/dbinstance.go @@ -368,3 +368,7 @@ type DBInstanceAutoRenewInput struct { // 是否自动续费 AutoRenew bool `json:"auto_renew"` } + +type DBInstanceSetSecgroupInput struct { + SecgroupIds []string `json:"secgroup_ids"` +} diff --git a/pkg/apis/compute/dbinstance_const.go b/pkg/apis/compute/dbinstance_const.go index 8145ed14c5..5d45f9cd19 100644 --- a/pkg/apis/compute/dbinstance_const.go +++ b/pkg/apis/compute/dbinstance_const.go @@ -36,6 +36,7 @@ const ( DBINSTANCE_SET_AUTO_RENEW = "set_auto_renew" //设置自动续费中 DBINSTANCE_SET_AUTO_RENEW_FAILED = "set_auto_renew_failed" //设置自动续费失败 DBINSTANCE_UNKNOWN = "unknown" + DBINSTANCE_SYNC_SECGROUP_FAILED = "sync_secgroup_failed" // 同步安全组失败 DBINSTANCE_CHANGE_CONFIG = "change_config" //调整配置 DBINSTANCE_CHANGE_CONFIG_FAILED = "change_config_failed" //调整配置失败 diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index f5de668010..fe2db2bae8 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -836,6 +836,7 @@ type ICloudDBInstance interface { GetMasterInstanceId() string GetSecurityGroupIds() ([]string, error) + SetSecurityGroups(ids []string) error GetPort() int GetEngine() string GetEngineVersion() string diff --git a/pkg/compute/models/dbinstances.go b/pkg/compute/models/dbinstances.go index 68208d82f2..25a2db2fb7 100644 --- a/pkg/compute/models/dbinstances.go +++ b/pkg/compute/models/dbinstances.go @@ -2017,3 +2017,65 @@ func (self *SDBInstance) OnMetadataUpdated(ctx context.Context, userCred mcclien log.Errorf("StartRemoteUpdateTask fail: %s", err) } } + +func (self *SDBInstance) AllowPerformSetSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "set-secgroup") +} + +func (self *SDBInstance) PerformSetSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DBInstanceSetSecgroupInput) (jsonutils.JSONObject, error) { + if self.Status != api.DBINSTANCE_RUNNING { + return nil, httperrors.NewInvalidStatusError("this operation requires rds state to be %s", api.DBINSTANCE_RUNNING) + } + if len(input.SecgroupIds) == 0 { + return nil, httperrors.NewMissingParameterError("secgroup_ids") + } + for i := range input.SecgroupIds { + _, err := validators.ValidateModel(userCred, SecurityGroupManager, &input.SecgroupIds[i]) + if err != nil { + return nil, err + } + } + driver, err := self.GetRegionDriver() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetRegionDriver")) + } + max := driver.GetRdsSupportSecgroupCount() + if len(input.SecgroupIds) > max { + return nil, httperrors.NewUnsupportOperationError("%s supported secgroup count is %d", driver.GetProvider(), max) + } + + secgroups, err := self.GetSecgroups() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetSecgroups")) + } + secMaps := map[string]bool{} + for i := range secgroups { + if !utils.IsInStringArray(secgroups[i].Id, input.SecgroupIds) { + err := self.RevokeSecgroup(ctx, userCred, secgroups[i].Id) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "RevokeSecgroup(%s)", secgroups[i].Id)) + } + } + secMaps[secgroups[i].Id] = true + } + for _, id := range input.SecgroupIds { + if _, ok := secMaps[id]; !ok { + err = self.AssignSecgroup(ctx, userCred, id) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "AssignSecgroup(%s)", id)) + } + } + } + + return nil, self.StartSyncSecgroupsTask(ctx, userCred, "") +} + +func (self *SDBInstance) StartSyncSecgroupsTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + task, err := taskman.TaskManager.NewTask(ctx, "DBInstanceSyncSecgroupsTask", self, userCred, nil, parentTaskId, "", nil) + if err != nil { + return errors.Wrap(err, "NewTask") + } + self.SetStatus(userCred, api.DBINSTANCE_DEPLOYING, "sync secgroups") + task.ScheduleRun(nil) + return nil +} diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index 096dac8a70..1cccfd3415 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -178,6 +178,7 @@ type IDBInstanceDriver interface { ValidateDBInstanceRecovery(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, backup *SDBInstanceBackup, input api.SDBInstanceRecoveryConfigInput) error RequestRemoteUpdateDBInstance(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, replaceTags bool, task taskman.ITask) error + RequestSyncRdsSecurityGroups(ctx context.Context, userCred mcclient.TokenCredential, rds *SDBInstance, task taskman.ITask) error } type IElasticcacheDriver interface { diff --git a/pkg/compute/regiondrivers/aliyun.go b/pkg/compute/regiondrivers/aliyun.go index 37e935bcc5..4d9c48d884 100644 --- a/pkg/compute/regiondrivers/aliyun.go +++ b/pkg/compute/regiondrivers/aliyun.go @@ -1545,6 +1545,10 @@ func (self *SAliyunRegionDriver) IsSupportedDBInstance() bool { return true } +func (self *SAliyunRegionDriver) GetRdsSupportSecgroupCount() int { + return 3 +} + func (self *SAliyunRegionDriver) IsSupportedDBInstanceAutoRenew() bool { return true } diff --git a/pkg/compute/regiondrivers/base.go b/pkg/compute/regiondrivers/base.go index d0f024e920..20ea561585 100644 --- a/pkg/compute/regiondrivers/base.go +++ b/pkg/compute/regiondrivers/base.go @@ -424,3 +424,7 @@ func (self *SBaseRegionDriver) IsSupportedElasticcacheAutoRenew() bool { func (self *SBaseRegionDriver) RequestElasticcacheSetAutoRenew(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, autoRenew bool, task taskman.ITask) error { return fmt.Errorf("Not Implement RequestElasticcacheSetAutoRenew") } + +func (self *SBaseRegionDriver) RequestSyncRdsSecurityGroups(ctx context.Context, userCred mcclient.TokenCredential, rds *models.SDBInstance, task taskman.ITask) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "RequestSyncRdsSecurityGroups") +} diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 26da140bf3..8e62e1b89c 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -39,6 +39,7 @@ import ( "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/billing" + "yunion.io/x/onecloud/pkg/util/logclient" "yunion.io/x/onecloud/pkg/util/rand" "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/seclib2" @@ -1858,6 +1859,14 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont if err != nil { return nil, errors.Wrapf(err, "cloudprovider.WaitStatus runing") } + + secgroupIds, err := iRds.GetSecurityGroupIds() + if err == nil && len(secgroupIds) != len(desc.SecgroupIds) { + err = iRds.SetSecurityGroups(desc.SecgroupIds) + if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented { + logclient.AddSimpleActionLog(dbinstance, logclient.ACT_SYNC_CONF, map[string][]string{"secgroup_ids": desc.SecgroupIds}, userCred, false) + } + } return nil, nil }) @@ -3216,3 +3225,34 @@ func IsInPrivateIpRange(ar netutils.IPV4AddrRange) error { return nil } + +func (self *SManagedVirtualizationRegionDriver) RequestSyncRdsSecurityGroups(ctx context.Context, userCred mcclient.TokenCredential, rds *models.SDBInstance, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + vpc, err := rds.GetVpc() + if err != nil { + return nil, errors.Wrapf(err, "rds.GetVpc") + } + secgroups, err := rds.GetSecgroups() + if err != nil { + return nil, errors.Wrapf(err, "GetSecgroups") + } + iRds, err := rds.GetIDBInstance() + if err != nil { + return nil, errors.Wrapf(err, "GetIDBInstance") + } + secgroupIds := []string{} + for i := range secgroups { + secgroupId, err := self.RequestSyncSecurityGroup(ctx, userCred, vpc.ExternalId, vpc, &secgroups[i], iRds.GetProjectId(), "rds") + if err != nil { + return nil, errors.Wrapf(err, "RequestSyncSecurityGroup") + } + secgroupIds = append(secgroupIds, secgroupId) + } + err = iRds.SetSecurityGroups(secgroupIds) + if err != nil { + return nil, errors.Wrapf(err, "SetSecurityGroups") + } + return nil, nil + }) + return nil +} diff --git a/pkg/compute/tasks/dbinstance_sync_secgroups_task.go b/pkg/compute/tasks/dbinstance_sync_secgroups_task.go new file mode 100644 index 0000000000..975dc57dc4 --- /dev/null +++ b/pkg/compute/tasks/dbinstance_sync_secgroups_task.go @@ -0,0 +1,77 @@ +// 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 tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + 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/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type DBInstanceSyncSecgroupsTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(DBInstanceSyncSecgroupsTask{}) +} + +func (self *DBInstanceSyncSecgroupsTask) taskFailed(ctx context.Context, rds *models.SDBInstance, err error) { + rds.SetStatus(self.UserCred, api.DBINSTANCE_SYNC_SECGROUP_FAILED, err.Error()) + db.OpsLog.LogEvent(rds, db.ACT_SYNC_CONF, err, self.GetUserCred()) + logclient.AddActionLogWithStartable(self, rds, logclient.ACT_SYNC_CONF, err, self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *DBInstanceSyncSecgroupsTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + rds := obj.(*models.SDBInstance) + + driver, err := rds.GetRegionDriver() + if err != nil { + self.taskFailed(ctx, rds, errors.Wrapf(err, "GetRegionDriver")) + return + } + + self.SetStage("OnSyncSecurityGroupsComplete", nil) + err = driver.RequestSyncRdsSecurityGroups(ctx, self.GetUserCred(), rds, self) + if err != nil { + self.taskFailed(ctx, rds, errors.Wrapf(err, "RequestSyncRdsSecurityGroups")) + return + } +} + +func (self *DBInstanceSyncSecgroupsTask) OnSyncSecurityGroupsCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) +} + +func (self *DBInstanceSyncSecgroupsTask) OnSyncSecurityGroupsComplete(ctx context.Context, rds *models.SDBInstance, data jsonutils.JSONObject) { + self.SetStage("OnSyncComplete", nil) + rds.StartDBInstanceSyncTask(ctx, self.GetUserCred(), nil, self.GetTaskId()) +} + +func (self *DBInstanceSyncSecgroupsTask) OnSyncCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) +} + +func (self *DBInstanceSyncSecgroupsTask) OnSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/mcclient/options/compute/dbinstance_network.go b/pkg/mcclient/options/compute/dbinstance_network.go new file mode 100644 index 0000000000..c88cdf13bd --- /dev/null +++ b/pkg/mcclient/options/compute/dbinstance_network.go @@ -0,0 +1,35 @@ +// 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 compute + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type DBInstanceNetworkListOptions struct { + options.BaseListOptions + DBInstance string `help:"ID or Name of DBInstance" json:"dbinstance"` + Network string `help:"Network ID or name"` +} + +func (opts *DBInstanceNetworkListOptions) GetContextId() string { + return opts.DBInstance +} + +func (opts *DBInstanceNetworkListOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(opts) +} diff --git a/pkg/mcclient/options/compute/dbinstances.go b/pkg/mcclient/options/compute/dbinstances.go new file mode 100644 index 0000000000..f403ac9ce1 --- /dev/null +++ b/pkg/mcclient/options/compute/dbinstances.go @@ -0,0 +1,232 @@ +// 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 compute + +import ( + "fmt" + "strings" + + "yunion.io/x/jsonutils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +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:"-"` + DBInstancebackupId string `help:"create dbinstance from backup" json:"dbinstancebackup_id"` +} + +func (opts *DBInstanceCreateOptions) Params() (jsonutils.JSONObject, error) { + params, err := options.StructToParams(opts) + if err != nil { + return nil, err + } + if opts.AllowDelete != nil && *opts.AllowDelete { + params.Add(jsonutils.JSONFalse, "disable_delete") + } + Tagparams := jsonutils.NewDict() + for _, tag := range opts.Tags { + info := strings.Split(tag, "=") + if len(info) == 2 { + if len(info[0]) == 0 { + return nil, fmt.Errorf("invalidate tag info %s", tag) + } + Tagparams.Add(jsonutils.NewString(info[1]), info[0]) + } else if len(info) == 1 { + Tagparams.Add(jsonutils.NewString(info[0]), info[0]) + } else { + return nil, fmt.Errorf("invalidate tag info %s", tag) + } + } + params.Add(Tagparams, "__meta__") + return params, nil +} + +type DBInstanceListOptions struct { + options.BaseListOptions + BillingType string `help:"billing type" choices:"postpaid|prepaid"` +} + +func (opts *DBInstanceListOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(opts) +} + +type DBInstanceIdOptions struct { + ID string `help:"DBInstance Id"` +} + +func (opts *DBInstanceIdOptions) GetId() string { + return opts.ID +} + +func (opts *DBInstanceIdOptions) Params() (jsonutils.JSONObject, error) { + return nil, nil +} + +type DBInstanceRenewOptions struct { + DBInstanceIdOptions + DURATION string `help:"Duration of renew, ADMIN only command"` +} + +func (opts *DBInstanceRenewOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(opts.DURATION), "duration") + return params, nil +} + +type DBInstanceUpdateOptions struct { + DBInstanceIdOptions + Name string + Description string + Delete string `help:"Lock or not lock dbinstance" choices:"enable|disable"` +} + +func (opts *DBInstanceUpdateOptions) Params() (jsonutils.JSONObject, error) { + params, err := options.StructToParams(opts) + if err != nil { + return nil, err + } + if len(opts.Delete) > 0 { + if opts.Delete == "disable" { + params.Add(jsonutils.JSONTrue, "disable_delete") + } else { + params.Add(jsonutils.JSONFalse, "disable_delete") + } + } + return params, nil +} + +type DBInstanceChangeConfigOptions struct { + DBInstanceIdOptions + DiskSizeGb int64 `help:"Change DBInstance storage size"` + VcpuCount int64 `help:"Change DBInstance vcpu count"` + VmemSizeMb int64 `help:"Change DBInstance vmem size mb"` + InstanceType string `help:"Change DBInstance instanceType"` + Category string `help:"Change DBInstance category"` +} + +func (opts *DBInstanceChangeConfigOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + if len(opts.Category) > 0 { + params.Add(jsonutils.NewString(opts.Category), "category") + } + if len(opts.InstanceType) > 0 { + params.Add(jsonutils.NewString(opts.InstanceType), "instance_type") + } + if opts.DiskSizeGb > 0 { + params.Add(jsonutils.NewInt(opts.DiskSizeGb), "disk_size_gb") + } + if opts.VcpuCount > 0 { + params.Add(jsonutils.NewInt(opts.VcpuCount), "vcpu_count") + } + if opts.VmemSizeMb > 0 { + params.Add(jsonutils.NewInt(opts.VmemSizeMb), "vmeme_size_mb") + } + return params, nil +} + +type DBInstancePublicConnectionOptions struct { + DBInstanceIdOptions + IS_OPEN string `help:"Open Or Close public connection" choices:"true|false"` +} + +func (opts *DBInstancePublicConnectionOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(map[string]bool{"open": opts.IS_OPEN == "true"}), nil +} + +type DBInstanceRecoveryOptions struct { + DBInstanceIdOptions + BACKUP string + Databases []string +} + +func (opts *DBInstanceRecoveryOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + params.Set("dbinstancebackup", jsonutils.NewString(opts.BACKUP)) + dbs := jsonutils.NewDict() + for _, database := range opts.Databases { + if len(database) > 0 { + dbInfo := strings.Split(database, ":") + if len(dbInfo) == 1 { + dbs.Add(jsonutils.NewString(dbInfo[0]), dbInfo[0]) + } else if len(dbInfo) == 2 { + dbs.Add(jsonutils.NewString(dbInfo[1]), dbInfo[0]) + } else { + return nil, fmt.Errorf("Invalid dbinfo: %s", database) + } + } + } + if dbs.Length() > 0 { + params.Add(dbs, "databases") + } + return params, nil +} + +type DBInstanceDeleteOptions struct { + DBInstanceIdOptions + KeepBackup bool `help:"Keep dbinstance manual backup after delete dbinstance"` +} + +func (opts *DBInstanceDeleteOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.NewDict() + if opts.KeepBackup { + params.Add(jsonutils.JSONTrue, "keep_backup") + } + return params, nil +} + +type DBInstanceChangeOwnerOptions struct { + DBInstanceIdOptions + PROJECT string `help:"Project ID or change" json:"tenant"` +} + +func (opts *DBInstanceChangeOwnerOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(opts) +} + +type DBInstanceRemoteUpdateOptions struct { + DBInstanceIdOptions + api.DBInstanceRemoteUpdateInput +} + +func (opts *DBInstanceRemoteUpdateOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(opts) +} + +type DBInstanceSetSecgroupOptions struct { + DBInstanceIdOptions + SECGROUP_IDS []string `help:"Security Group Ids"` +} + +func (opts *DBInstanceSetSecgroupOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(map[string][]string{"secgroup_ids": opts.SECGROUP_IDS}), nil +} diff --git a/pkg/mcclient/options/compute/doc.go b/pkg/mcclient/options/compute/doc.go new file mode 100644 index 0000000000..9a168f4974 --- /dev/null +++ b/pkg/mcclient/options/compute/doc.go @@ -0,0 +1 @@ +package compute // import "yunion.io/x/onecloud/pkg/mcclient/options/compute" diff --git a/pkg/mcclient/options/dbinstances.go b/pkg/mcclient/options/dbinstances.go deleted file mode 100644 index f901883b3c..0000000000 --- a/pkg/mcclient/options/dbinstances.go +++ /dev/null @@ -1,66 +0,0 @@ -// 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 options - -import ( - "fmt" - "strings" - - "yunion.io/x/jsonutils" -) - -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:"-"` - DBInstancebackupId string `help:"create dbinstance from backup" json:"dbinstancebackup_id"` -} - -func (opts *DBInstanceCreateOptions) Params() (*jsonutils.JSONDict, error) { - params, err := StructToParams(opts) - if err != nil { - return nil, err - } - Tagparams := jsonutils.NewDict() - for _, tag := range opts.Tags { - info := strings.Split(tag, "=") - if len(info) == 2 { - if len(info[0]) == 0 { - return nil, fmt.Errorf("invalidate tag info %s", tag) - } - Tagparams.Add(jsonutils.NewString(info[1]), info[0]) - } else if len(info) == 1 { - Tagparams.Add(jsonutils.NewString(info[0]), info[0]) - } else { - return nil, fmt.Errorf("invalidate tag info %s", tag) - } - } - params.Add(Tagparams, "__meta__") - return params, nil -} diff --git a/pkg/multicloud/aliyun/dbinstance.go b/pkg/multicloud/aliyun/dbinstance.go index 3df4b2b570..029cfd4ab4 100644 --- a/pkg/multicloud/aliyun/dbinstance.go +++ b/pkg/multicloud/aliyun/dbinstance.go @@ -509,6 +509,50 @@ func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInst return iparameters, nil } +func (self *SDBInstance) GetSecurityGroupIds() ([]string, error) { + return self.region.GetRdsSecgroupIds(self.DBInstanceId) +} + +func (self *SDBInstance) SetSecurityGroups(ids []string) error { + return self.region.SetRdsSecgroups(self.DBInstanceId, ids) +} + +func (self *SRegion) SetRdsSecgroups(rdsId string, secIds []string) error { + params := map[string]string{ + "DBInstanceId": rdsId, + "SecurityGroupId": strings.Join(secIds, ","), + } + _, err := self.rdsRequest("ModifySecurityGroupConfiguration", params) + if err != nil { + return errors.Wrapf(err, "ModifySecurityGroupConfiguration") + } + return nil +} + +func (self *SRegion) GetRdsSecgroupIds(rdsId string) ([]string, error) { + params := map[string]string{ + "DBInstanceId": rdsId, + } + resp, err := self.rdsRequest("DescribeSecurityGroupConfiguration", params) + if err != nil { + return nil, errors.Wrapf(err, "DescribeSecurityGroupConfiguration") + } + items := []struct { + NetworkType string + SecurityGroupId string + RegionId string + }{} + err = resp.Unmarshal(&items, "Items", "EcsSecurityGroupRelation") + if err != nil { + return nil, errors.Wrapf(err, "resp.Unmarshal") + } + ids := []string{} + for _, item := range items { + ids = append(ids, item.SecurityGroupId) + } + return ids, nil +} + func (region *SRegion) GetIDBInstanceBackupById(backupId string) (cloudprovider.ICloudDBInstanceBackup, error) { backups, err := region.GetIDBInstanceBackups() if err != nil { diff --git a/pkg/multicloud/dbinstance_base.go b/pkg/multicloud/dbinstance_base.go index 8ed1d6faa6..b398dbd883 100644 --- a/pkg/multicloud/dbinstance_base.go +++ b/pkg/multicloud/dbinstance_base.go @@ -80,6 +80,10 @@ func (instance *SDBInstanceBase) GetSecurityGroupIds() ([]string, error) { return []string{}, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetSecurityGroupIds") } +func (self *SDBInstanceBase) SetSecurityGroups(ids []string) error { + return errors.Wrapf(cloudprovider.ErrNotImplemented, "SetSecurityGroups") +} + func (instance *SDBInstanceBase) Renew(bc billing.SBillingCycle) error { return errors.Wrapf(cloudprovider.ErrNotImplemented, "Renew") } diff --git a/pkg/multicloud/qcloud/rds_mysql.go b/pkg/multicloud/qcloud/rds_mysql.go index 38e66d8317..3138ea827d 100644 --- a/pkg/multicloud/qcloud/rds_mysql.go +++ b/pkg/multicloud/qcloud/rds_mysql.go @@ -257,6 +257,21 @@ func (self *SMySQLInstance) GetSecurityGroupIds() ([]string, error) { return ids, nil } +func (self *SMySQLInstance) SetSecurityGroups(ids []string) error { + return self.region.ModifyMySQLInstanceSecurityGroups(self.InstanceId, ids) +} + +func (self *SRegion) ModifyMySQLInstanceSecurityGroups(rdsId string, secIds []string) error { + params := map[string]string{ + "InstanceId": rdsId, + } + for idx, id := range secIds { + params[fmt.Sprintf("SecurityGroupIds.%d", idx)] = id + } + _, err := self.cdbRequest("ModifyDBInstanceSecurityGroups", params) + return err +} + func (self *SMySQLInstance) Renew(bc billing.SBillingCycle) error { month := bc.GetMonths() return self.region.RenewMySQLDBInstance(self.InstanceId, month)