From 5cd26b297e0e04de99798e99b9996d60791372f3 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Wed, 21 Oct 2020 17:08:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E5=A4=9A=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/apis/compute/dbinstance.go | 36 +- pkg/apis/compute/dbinstance_secgroup.go | 32 ++ pkg/apis/compute/secgroup.go | 2 + pkg/cloudcommon/validators/validators.go | 13 + pkg/cloudprovider/dbinstance.go | 2 +- pkg/cloudprovider/resources.go | 4 +- pkg/compute/models/cloudsync.go | 30 +- pkg/compute/models/dbinstance_secgroups.go | 227 ++++++++++ pkg/compute/models/dbinstancejoints.go | 43 +- pkg/compute/models/dbinstancenetworks.go | 189 +++----- pkg/compute/models/dbinstances.go | 407 +++++++++--------- pkg/compute/models/purge.go | 40 +- pkg/compute/models/regiondrivers.go | 2 +- pkg/compute/models/secgroups.go | 9 + pkg/compute/regiondrivers/base.go | 4 + pkg/compute/regiondrivers/huawei.go | 8 +- pkg/compute/regiondrivers/managedvirtual.go | 61 +-- pkg/compute/regiondrivers/qcloud.go | 11 +- pkg/compute/service/handlers.go | 1 + .../modules/mod_dbinstancenetworks.go | 2 +- pkg/mcclient/options/secgroup.go | 11 +- pkg/multicloud/aliyun/dbinstance.go | 11 +- pkg/multicloud/aws/dbinstance.go | 4 +- pkg/multicloud/dbinstance_base.go | 8 +- pkg/multicloud/huawei/dbinstance.go | 20 +- pkg/multicloud/qcloud/rds_mysql.go | 46 +- 26 files changed, 793 insertions(+), 430 deletions(-) create mode 100644 pkg/apis/compute/dbinstance_secgroup.go create mode 100644 pkg/compute/models/dbinstance_secgroups.go diff --git a/pkg/apis/compute/dbinstance.go b/pkg/apis/compute/dbinstance.go index e51d487ef3..881335fc5c 100644 --- a/pkg/apis/compute/dbinstance.go +++ b/pkg/apis/compute/dbinstance.go @@ -27,9 +27,9 @@ type DBInstanceCreateInput struct { // Ip子网名称或Id,建议使用Id // 谷歌云并不实际使用Ip子网,仅仅通过Ip子网确定Vpc // required: true - Network string `json:"network"` + NetworkId string `json:"network_id"` // swagger:ignore - NetworkId string + Network string `json:"network" yunion-deprecated-by:"network_id"` // Ip子网内的地址,不填则按照ip子网的地址分配策略分配一个ip // required: false @@ -41,11 +41,17 @@ type DBInstanceCreateInput struct { // swagger:ignore MasterInstanceId string - // 安全组名称或Id - // default: default - Secgroup string `json:"secgroup"` + // 安全组Id列表 + // + // + // | 云平台 | 最大支出安全组数量 | + // |-------------|----------| + // | 腾讯云 | 5 | + // | 华为云 | 1 | + // | 阿里云 | 不支持| + SecgroupIds []string `json:"secgroup_ids"` // swagger:ignore - SecgroupId string + Secgroup string `json:"secgroup" yunion-deprecated-by:"secgroup_ids"` // 主可用区名称或Id, 此参数从指定的套餐所在的可用区获取 Zone1 string `json:"zone1"` @@ -59,10 +65,6 @@ type DBInstanceCreateInput struct { // swagger:ignore ZoneId string - // 区域名称或Id,建议使用Id - // swagger:ignore - Cloudregion string `json:"cloudregion"` - // swagger:ignore CloudregionId string @@ -72,9 +74,6 @@ type DBInstanceCreateInput struct { // swagger:ignore ManagerId string - // swagger:ignore - NetworkExternalId string - // 包年包月时间周期 Duration string `json:"duration"` @@ -155,9 +154,6 @@ type DBInstanceCreateInput struct { // rds实例内存大小 // 若指定实例套餐,此参数将根据套餐设置 VmemSizeMb int `json:"vmem_size_mb"` - - // swagger:ignore - Provider string } type SDBInstanceChangeConfigInput struct { @@ -346,7 +342,7 @@ type DBInstanceFilterListInput struct { VpcFilterListInput } -type DBInstanceNetworkListInput struct { +type DBInstanceJoinListInput struct { apis.VirtualJointResourceBaseListInput DBInstanceFilterListInput } @@ -355,3 +351,9 @@ type DBInstanceRemoteUpdateInput struct { // 是否覆盖替换所有标签 ReplaceTags *bool `json:"replace_tags" help:"replace all remote tags"` } + +type DBInstanceNetworkListInput struct { + DBInstanceJoinListInput + + NetworkFilterListInput +} diff --git a/pkg/apis/compute/dbinstance_secgroup.go b/pkg/apis/compute/dbinstance_secgroup.go new file mode 100644 index 0000000000..9013624e61 --- /dev/null +++ b/pkg/apis/compute/dbinstance_secgroup.go @@ -0,0 +1,32 @@ +// 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/onecloud/pkg/apis" + +type DBInstanceSecgroupListInput struct { + DBInstanceJoinListInput + + SecgroupFilterListInput +} + +type DBInstanceSecgroupDetails struct { + apis.VirtualJointResourceBaseDetails + + // RDS名称 + DBInstance string `json:"dbinstance"` + // 安全组名称 + Secgroup string `json:"secgroup"` +} diff --git a/pkg/apis/compute/secgroup.go b/pkg/apis/compute/secgroup.go index a2e65eedfd..aa35498d7f 100644 --- a/pkg/apis/compute/secgroup.go +++ b/pkg/apis/compute/secgroup.go @@ -134,6 +134,8 @@ type SecgroupListInput struct { ServerFilterListInput + DBInstanceResourceInput + // equals Equals string diff --git a/pkg/cloudcommon/validators/validators.go b/pkg/cloudcommon/validators/validators.go index f82f830ae2..de862d5c8b 100644 --- a/pkg/cloudcommon/validators/validators.go +++ b/pkg/cloudcommon/validators/validators.go @@ -30,6 +30,7 @@ import ( "strings" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/util/netutils" "yunion.io/x/pkg/util/regutils" @@ -821,3 +822,15 @@ func NewIPv4AddrValidator(key string) *ValidatorIPv4Addr { v.SetParent(v) return v } + +var ValidateModel = func(userCred mcclient.TokenCredential, manager db.IStandaloneModelManager, id *string) (db.IModel, error) { + model, err := manager.FetchByIdOrName(userCred, *id) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2(manager.Keyword(), *id) + } + return nil, httperrors.NewGeneralError(err) + } + *id = model.GetId() + return model, nil +} diff --git a/pkg/cloudprovider/dbinstance.go b/pkg/cloudprovider/dbinstance.go index 9e5f32ac01..894e7e35a5 100644 --- a/pkg/cloudprovider/dbinstance.go +++ b/pkg/cloudprovider/dbinstance.go @@ -47,7 +47,7 @@ type SManagedDBInstanceCreateConfig struct { VcpuCount int VmemSizeMb int VpcId string - SecgroupId string + SecgroupIds []string NetworkId string Address string Engine string diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index d547846d8c..bf5b6f2ef8 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -762,7 +762,7 @@ type ICloudDBInstance interface { Reboot() error GetMasterInstanceId() string - GetSecurityGroupId() string + GetSecurityGroupIds() ([]string, error) GetPort() int GetEngine() string GetEngineVersion() string @@ -785,7 +785,7 @@ type ICloudDBInstance interface { GetZone3Id() string GetIVpcId() string - GetDBNetwork() (*SDBInstanceNetwork, error) + GetDBNetworks() ([]SDBInstanceNetwork, error) GetIDBInstanceParameters() ([]ICloudDBInstanceParameter, error) GetIDBInstanceDatabases() ([]ICloudDBInstanceDatabase, error) GetIDBInstanceAccounts() ([]ICloudDBInstanceAccount, error) diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 6c546b0c55..3f982328d7 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -823,6 +823,10 @@ func syncDBInstanceResource(ctx context.Context, userCred mcclient.TokenCredenti if err != nil { log.Errorf("syncDBInstanceNetwork error: %v", err) } + err = syncDBInstanceSecgroups(ctx, userCred, syncResults, localInstance, remoteInstance) + if err != nil { + log.Errorf("syncDBInstanceSecgroups error: %v", err) + } err = syncDBInstanceParameters(ctx, userCred, syncResults, localInstance, remoteInstance) if err != nil { log.Errorf("syncDBInstanceParameters error: %v", err) @@ -838,16 +842,12 @@ func syncDBInstanceResource(ctx context.Context, userCred mcclient.TokenCredenti } func syncDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) error { - network, err := remoteInstance.GetDBNetwork() + networks, err := remoteInstance.GetDBNetworks() if err != nil { - return errors.Wrapf(err, "GetDBNetwork") + return errors.Wrapf(err, "GetDBNetworks") } - if network == nil { - return nil - } - - result := DBInstanceNetworkManager.SyncDBInstanceNetwork(ctx, userCred, localInstance, network) + result := DBInstanceNetworkManager.SyncDBInstanceNetwork(ctx, userCred, localInstance, networks) syncResults.Add(DBInstanceNetworkManager, result) msg := result.Result() @@ -858,6 +858,22 @@ func syncDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredentia return nil } +func syncDBInstanceSecgroups(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) error { + secIds, err := remoteInstance.GetSecurityGroupIds() + if err != nil { + return errors.Wrapf(err, "GetSecurityGroupIds") + } + result := DBInstanceSecgroupManager.SyncDBInstanceSecgroups(ctx, userCred, localInstance, secIds) + syncResults.Add(DBInstanceSecgroupManager, result) + + msg := result.Result() + log.Infof("SyncDBInstanceSecgroups 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_secgroups.go b/pkg/compute/models/dbinstance_secgroups.go new file mode 100644 index 0000000000..b540b767b0 --- /dev/null +++ b/pkg/compute/models/dbinstance_secgroups.go @@ -0,0 +1,227 @@ +// 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" + + "gopkg.in/fatih/set.v0" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/sqlchemy" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SDBInstanceSecgroupManager struct { + SDBInstanceJointsManager + SSecurityGroupResourceBaseManager +} + +var DBInstanceSecgroupManager *SDBInstanceSecgroupManager + +func init() { + db.InitManager(func() { + DBInstanceSecgroupManager = &SDBInstanceSecgroupManager{ + SDBInstanceJointsManager: NewDBInstanceJointsManager( + SDBInstanceSecgroup{}, + "dbinstance_secgroups_tbl", + "dbinstance_secgroup", + "dbinstance_secgroups", + SecurityGroupManager, + ), + } + DBInstanceSecgroupManager.SetVirtualObject(DBInstanceSecgroupManager) + }) +} + +type SDBInstanceSecgroup struct { + SDBInstanceJointsBase + + SSecurityGroupResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` +} + +func (manager *SDBInstanceSecgroupManager) GetSlaveFieldName() string { + return "secgroup_id" +} + +func (self *SDBInstanceSecgroup) Detach(ctx context.Context, userCred mcclient.TokenCredential) error { + return db.DeleteModel(ctx, userCred, self) +} + +func (manager *SDBInstanceSecgroupManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.DBInstanceSecgroupListInput, +) (*sqlchemy.SQuery, error) { + var err error + q, err = manager.SDBInstanceJointsManager.ListItemFilter(ctx, q, userCred, query.DBInstanceJoinListInput) + if err != nil { + return nil, errors.Wrap(err, "SDBInstanceJointsManager.ListItemFilter") + } + q, err = manager.SSecurityGroupResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SecgroupFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.ListItemFilter") + } + + return q, nil +} + +func (manager *SDBInstanceSecgroupManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.DBInstanceSecgroupListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SDBInstanceJointsManager.OrderByExtraFields(ctx, q, userCred, query.DBInstanceJoinListInput) + if err != nil { + return nil, errors.Wrap(err, "SDBInstanceJointsManager.OrderByExtraFields") + } + q, err = manager.SSecurityGroupResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.SecgroupFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.OrderByExtraFields") + } + + return q, nil +} + +func (manager *SDBInstanceSecgroupManager) ListItemExportKeys(ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + keys stringutils2.SSortedStrings, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SVirtualJointResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SVirtualJointResourceBaseManager.ListItemExportKeys") + } + if keys.ContainsAny(manager.SSecurityGroupResourceBaseManager.GetExportKeys()...) { + q, err = manager.SSecurityGroupResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.ListItemExportKeys") + } + } + + return q, nil +} + +func (self *SDBInstanceSecgroup) GetExtraDetails( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + isList bool, +) (api.DBInstanceSecgroupDetails, error) { + return api.DBInstanceSecgroupDetails{}, nil +} + +func (manager *SDBInstanceSecgroupManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []api.DBInstanceSecgroupDetails { + rows := make([]api.DBInstanceSecgroupDetails, len(objs)) + + vjRows := manager.SVirtualJointResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + secgrpIds := make([]string, len(rows)) + rdsIds := make([]string, len(rows)) + for i := range rows { + rows[i].VirtualJointResourceBaseDetails = vjRows[i] + rdsSec := objs[i].(*SDBInstanceSecgroup) + secgrpIds[i], rdsIds[i] = rdsSec.SecgroupId, rdsSec.DBInstanceId + } + + secMaps, err := db.FetchIdNameMap2(SecurityGroupManager, secgrpIds) + if err != nil { + log.Errorf("FetchIdNameMap2 for SecurityGroupManager fail %s", err) + return rows + } + rdsMaps, err := db.FetchIdNameMap2(DBInstanceManager, rdsIds) + if err != nil { + log.Errorf("FetchIdNameMap2 for DBInstanceManager fail %s", err) + return rows + } + + for i := range rows { + rows[i].Secgroup, _ = secMaps[secgrpIds[i]] + rows[i].DBInstance, _ = rdsMaps[rdsIds[i]] + } + + return rows +} + +func (manager *SDBInstanceSecgroupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + var err error + q, err = manager.SVirtualJointResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SDBInstanceSecgroupManager) SyncDBInstanceSecgroups(ctx context.Context, userCred mcclient.TokenCredential, rds *SDBInstance, extIds []string) compare.SyncResult { + result := compare.SyncResult{} + + secgroups, err := rds.GetSecgroups() + if err != nil { + result.Error(err) + return result + } + + extSecgroups, err := rds.getSecgroupsByExternalIds(extIds) + if err != nil { + result.Error(err) + return result + } + + localSet := set.New(set.ThreadSafe) + for i := range secgroups { + localSet.Add(secgroups[i].Id) + } + remoteSet := set.New(set.ThreadSafe) + for i := range extSecgroups { + remoteSet.Add(extSecgroups[i].Id) + } + for _, del := range set.Difference(localSet, remoteSet).List() { + err = rds.RevokeSecgroup(ctx, userCred, del.(string)) + if err != nil { + result.DeleteError(err) + continue + } + result.Delete() + } + for _, add := range set.Difference(remoteSet, localSet).List() { + err = rds.AssignSecgroup(ctx, userCred, add.(string)) + if err != nil { + result.AddError(err) + continue + } + result.Add() + } + return result +} diff --git a/pkg/compute/models/dbinstancejoints.go b/pkg/compute/models/dbinstancejoints.go index d45f258665..2d6a682648 100644 --- a/pkg/compute/models/dbinstancejoints.go +++ b/pkg/compute/models/dbinstancejoints.go @@ -15,9 +15,14 @@ package models import ( - "yunion.io/x/pkg/errors" + "context" + "yunion.io/x/pkg/errors" + "yunion.io/x/sqlchemy" + + api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/mcclient" ) type SDBInstanceJointsManager struct { @@ -56,3 +61,39 @@ func (self *SDBInstanceJointsBase) getDBInstance() (*SDBInstance, error) { func (manager *SDBInstanceJointsManager) GetMasterFieldName() string { return "dbinstance_id" } + +func (manager *SDBInstanceJointsManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.DBInstanceJoinListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SVirtualJointResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualJointResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SVirtualJointResourceBase.ListItemFilter") + } + q, err = manager.SDBInstanceResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DBInstanceFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SDBInstanceResourceBaseManager.ListItemFilter") + } + return q, nil +} + +func (manager *SDBInstanceJointsManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.DBInstanceJoinListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SVirtualJointResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualJointResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SVirtualJointResourceBaseManager.OrderByExtraFields") + } + q, err = manager.SDBInstanceResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DBInstanceFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SDBInstanceResourceBaseManager.OrderByExtraFields") + } + return q, nil +} diff --git a/pkg/compute/models/dbinstancenetworks.go b/pkg/compute/models/dbinstancenetworks.go index ef99d0f692..3d5b0c2466 100644 --- a/pkg/compute/models/dbinstancenetworks.go +++ b/pkg/compute/models/dbinstancenetworks.go @@ -16,7 +16,6 @@ package models import ( "context" - "database/sql" "fmt" "yunion.io/x/jsonutils" @@ -28,13 +27,13 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" - "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/mcclient" ) type SDBInstanceNetworkManager struct { SDBInstanceJointsManager + SNetworkResourceBaseManager } var DBInstanceNetworkManager *SDBInstanceNetworkManager @@ -81,13 +80,14 @@ func (manager *SDBInstanceNetworkManager) ListItemFilter( userCred mcclient.TokenCredential, query api.DBInstanceNetworkListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SVirtualJointResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualJointResourceBaseListInput) + var err error + q, err = manager.SDBInstanceJointsManager.ListItemFilter(ctx, q, userCred, query.DBInstanceJoinListInput) if err != nil { - return nil, errors.Wrap(err, "SVirtualJointResourceBase.ListItemFilter") + return nil, errors.Wrap(err, "SDBInstanceJointsManager.ListItemFilter") } - q, err = manager.SDBInstanceResourceBaseManager.ListItemFilter(ctx, q, userCred, query.DBInstanceFilterListInput) + q, err = manager.SNetworkResourceBaseManager.ListItemFilter(ctx, q, userCred, query.NetworkFilterListInput) if err != nil { - return nil, errors.Wrap(err, "SDBInstanceResourceBaseManager.ListItemFilter") + return nil, errors.Wrap(err, "SNetworkResourceBaseManager.ListItemFilter") } return q, nil } @@ -100,81 +100,32 @@ func (self *SDBInstanceNetwork) GetNetwork() (*SNetwork, error) { return network.(*SNetwork), nil } -type SDBInstanceNetworkRequestData struct { - DBInstance *SDBInstance - NetworkId string - reserved bool // allocate from reserved - Address string // the address user intends to use - strategy api.IPAllocationDirection // allocate bottom up, top down, randomly +func (manager *SDBInstanceNetworkManager) newNetwork(ctx context.Context, userCred mcclient.TokenCredential, rdsId, networkId, ipAddr string) error { + ds := &SDBInstanceNetwork{} + ds.SetModelManager(DBInstanceNetworkManager, ds) + ds.DBInstanceId = rdsId + ds.NetworkId = networkId + ds.IpAddr = ipAddr + return manager.TableSpec().Insert(ctx, ds) } -func (m *SDBInstanceNetworkManager) NewDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredential, req *SDBInstanceNetworkRequestData) (*SDBInstanceNetwork, error) { - networkMan := db.GetModelManager("network").(*SNetworkManager) - if networkMan == nil { - return nil, fmt.Errorf("failed getting network manager") - } - im, err := networkMan.FetchById(req.NetworkId) - if err != nil { - return nil, err - } - network := im.(*SNetwork) - in := &SDBInstanceNetwork{ - NetworkId: network.Id, - } - in.DBInstanceId = req.DBInstance.Id - in.SetModelManager(m, in) - - lockman.LockObject(ctx, network) - defer lockman.ReleaseObject(ctx, network) - usedMap := network.GetUsedAddresses() - recentReclaimed := map[string]bool{} - ipAddr, err := network.GetFreeIP(ctx, userCred, - usedMap, recentReclaimed, req.Address, req.strategy, req.reserved) - if err != nil { - return nil, err - } - in.IpAddr = ipAddr - err = m.TableSpec().Insert(ctx, in) - if err != nil { - // NOTE no need to free ipAddr as GetFreeIP has no side effect - return nil, err - } - return in, nil -} - -func (manager *SDBInstanceNetworkManager) SyncDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, network *cloudprovider.SDBInstanceNetwork) compare.SyncResult { +func (manager *SDBInstanceNetworkManager) SyncDBInstanceNetwork(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, exts []cloudprovider.SDBInstanceNetwork) compare.SyncResult { result := compare.SyncResult{} - if network == nil { - return result - } - dbNetwork, err := dbinstance.GetDBNetwork() - if err != nil && err != sql.ErrNoRows { + networks, err := dbinstance.GetDBNetworks() + if err != nil { result.Error(err) return result } - if dbNetwork == nil { - err = manager.newFromCloudDBNetwork(ctx, userCred, dbinstance, network) - if err != nil { - result.AddError(err) - } else { - result.Add() - } - } else { - err = dbNetwork.syncWithCloudDBNetwork(ctx, userCred, dbinstance, network) - if err != nil { - result.UpdateError(err) - } else { - result.Update() - } + localMap := map[string]SDBInstanceNetwork{} + for i := range networks { + localMap[networks[i].NetworkId+networks[i].IpAddr] = networks[i] } - return result -} + remoteMap := map[string]bool{} -func (self *SDBInstanceNetwork) syncWithCloudDBNetwork(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, network *cloudprovider.SDBInstanceNetwork) error { - _, err := db.UpdateWithLock(ctx, self, func() error { - _localnetwork, err := db.FetchByExternalIdAndManagerId(NetworkManager, network.NetworkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { + for i := range exts { + _network, err := db.FetchByExternalIdAndManagerId(NetworkManager, exts[i].NetworkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { wire := WireManager.Query().SubQuery() vpc := VpcManager.Query().SubQuery() return q.Join(wire, sqlchemy.Equals(wire.Field("id"), q.Field("wire_id"))). @@ -182,63 +133,65 @@ func (self *SDBInstanceNetwork) syncWithCloudDBNetwork(ctx context.Context, user Filter(sqlchemy.Equals(vpc.Field("manager_id"), dbinstance.ManagerId)) }) if err != nil { - return errors.Wrapf(err, "FetchByExternalIdAndManagerId") + result.Error(err) + continue } - localnetwork := _localnetwork.(*SNetwork) - self.NetworkId = localnetwork.Id + network := _network.(*SNetwork) + exts[i].NetworkId = network.GetId() + remoteMap[exts[i].NetworkId+exts[i].IP] = true + _, ok := localMap[exts[i].NetworkId+exts[i].IP] + if !ok { + ipAddr, err := netutils.NewIPV4Addr(exts[i].IP) + if err != nil { + result.AddError(errors.Wrapf(err, "invalid ip")) + } - ipAdd, err := netutils.NewIPV4Addr(network.IP) - if err != nil { - return errors.Wrapf(err, "NewIPV4Addr") - } - if !localnetwork.IsAddressInRange(ipAdd) { - return fmt.Errorf("IP %s not in network %s(%s) address range", network.IP, localnetwork.Name, localnetwork.Id) - } - self.IpAddr = network.IP + if !network.IsAddressInRange(ipAddr) { + result.AddError(fmt.Errorf("IP %s not in network %s(%s) address range", exts[i].IP, network.Name, network.Id)) + continue + } - return nil - }) - if err != nil { - return errors.Wrapf(err, "syncWithCloudDBNetwork.UpdateWithLock") + err = manager.newNetwork(ctx, userCred, dbinstance.Id, exts[i].NetworkId, exts[i].IP) + if err != nil { + result.AddError(err) + continue + } + result.Add() + } } - return nil + for i := range networks { + _, ok := remoteMap[networks[i].NetworkId] + if ok { + continue + } + err = networks[i].Detach(ctx, userCred) + if err != nil { + result.DeleteError(err) + continue + } + result.Delete() + } + return result } -func (manager *SDBInstanceNetworkManager) newFromCloudDBNetwork(ctx context.Context, userCred mcclient.TokenCredential, dbinstance *SDBInstance, network *cloudprovider.SDBInstanceNetwork) error { - lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred)) - defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred)) +func (manager *SDBInstanceNetworkManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.DBInstanceNetworkListInput, +) (*sqlchemy.SQuery, error) { + var err error - dbNetwork := SDBInstanceNetwork{} - dbNetwork.SetModelManager(manager, &dbNetwork) - - dbNetwork.DBInstanceId = dbinstance.Id - _localnetwork, err := db.FetchByExternalIdAndManagerId(NetworkManager, network.NetworkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { - wire := WireManager.Query().SubQuery() - vpc := VpcManager.Query().SubQuery() - return q.Join(wire, sqlchemy.Equals(wire.Field("id"), q.Field("wire_id"))). - Join(vpc, sqlchemy.Equals(vpc.Field("id"), wire.Field("vpc_id"))). - Filter(sqlchemy.Equals(vpc.Field("manager_id"), dbinstance.ManagerId)) - }) + q, err = manager.SDBInstanceJointsManager.OrderByExtraFields(ctx, q, userCred, query.DBInstanceJoinListInput) if err != nil { - return errors.Wrapf(err, "newFromCloudDBNetwork.FetchByExternalIdAndManagerId") + return nil, errors.Wrap(err, "SDBInstanceJointsManager.OrderByExtraFields") + } + q, err = manager.SNetworkResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.NetworkFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SNetworkResourceBaseManager.OrderByExtraFields") } - localnetwork := _localnetwork.(*SNetwork) - ipAdd, err := netutils.NewIPV4Addr(network.IP) - if err != nil { - return errors.Wrapf(err, "newFromCloudDBNetwork.NewIPV4Addr") - } - if !localnetwork.IsAddressInRange(ipAdd) { - return fmt.Errorf("IP %s not in network %s(%s) address range", network.IP, localnetwork.Name, localnetwork.Id) - } - dbNetwork.NetworkId = localnetwork.Id - dbNetwork.IpAddr = network.IP - - err = manager.TableSpec().Insert(ctx, &dbNetwork) - if err != nil { - return errors.Wrapf(err, "newFromCloudDBNetwork.Insert") - } - return nil + return q, nil } func (manager *SDBInstanceNetworkManager) InitializeData() error { diff --git a/pkg/compute/models/dbinstances.go b/pkg/compute/models/dbinstances.go index d92f370d27..6ad5811a97 100644 --- a/pkg/compute/models/dbinstances.go +++ b/pkg/compute/models/dbinstances.go @@ -18,6 +18,7 @@ import ( "context" "database/sql" "fmt" + "net" "strings" "time" @@ -116,10 +117,6 @@ type SDBInstance struct { // 维护时间 MaintainTime string `width:"64" charset:"ascii" nullable:"true" list:"user" create:"optional"` - // 安全组Id - // example: default - SecgroupId string `width:"128" charset:"ascii" list:"user" default:"default" create:"optional"` - // 虚拟私有网络Id // example: ed20d84e-3158-41b1-870c-1725e412e8b6 VpcId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional"` @@ -279,90 +276,69 @@ func (manager *SDBInstanceManager) BatchCreateValidateCreateData(ctx context.Con if err != nil { return nil, errors.Wrapf(err, "data.Unmarshal") } - return manager.ValidateCreateData(ctx, userCred, ownerId, query, input) + input, err = manager.ValidateCreateData(ctx, userCred, ownerId, query, input) + if err != nil { + return nil, errors.Wrapf(err, "ValidateCreateData") + } + return input.JSON(input), nil } -func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DBInstanceCreateInput) (*jsonutils.JSONDict, error) { - data := input.JSON(input) - networkV := validators.NewModelIdOrNameValidator("network", "network", ownerId) - addressV := validators.NewIPv4AddrValidator("address") - secgroupV := validators.NewModelIdOrNameValidator("secgroup", "secgroup", ownerId) - masterV := validators.NewModelIdOrNameValidator("master_instance", "dbinstance", ownerId) - zone1V := validators.NewModelIdOrNameValidator("zone1", "zone", ownerId) - zone2V := validators.NewModelIdOrNameValidator("zone2", "zone", ownerId) - zone3V := validators.NewModelIdOrNameValidator("zone3", "zone", ownerId) - keyV := map[string]validators.IValidator{ - "network": networkV, - "address": addressV.Optional(true), - "master": masterV.ModelIdKey("master_instance_id").Optional(true), - "secgroup": secgroupV.Optional(true), - "zone1": zone1V.ModelIdKey("zone1").Optional(true), - "zone2": zone2V.ModelIdKey("zone2").Optional(true), - "zone3": zone3V.ModelIdKey("zone3").Optional(true), - } - for _, v := range keyV { - err := v.Validate(data) - if err != nil { - return nil, err +func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.DBInstanceCreateInput) (api.DBInstanceCreateInput, error) { + 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) + if err != nil { + return input, err + } } } - err := data.Unmarshal(&input) - if err != nil { - return nil, errors.Wrapf(err, "Unmarshal input failed: %v", err) - } - - if len(input.Password) == 0 { - input.Password = seclib2.RandomPassword2(12) - } - - // reset_password == flase 则置密码为空 - if input.ResetPassword != nil && !*input.ResetPassword { - input.Password = "" - } - if len(input.Password) > 0 { if !seclib2.MeetComplxity(input.Password) { - return nil, httperrors.NewWeakPasswordError() + 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 := networkV.Model.(*SNetwork) - input.NetworkExternalId = network.ExternalId + 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() input.VpcId = vpc.Id input.ManagerId = vpc.ManagerId cloudprovider := vpc.GetCloudprovider() if cloudprovider == nil { - return nil, httperrors.NewGeneralError(fmt.Errorf("failed to get vpc %s(%s) cloudprovider", vpc.Name, vpc.Id)) + return input, httperrors.NewGeneralError(fmt.Errorf("failed to get vpc %s(%s) cloudprovider", vpc.Name, vpc.Id)) } - if !cloudprovider.GetEnabled() { - return nil, httperrors.NewInputParameterError("cloudprovider %s(%s) disabled", cloudprovider.Name, cloudprovider.Id) + if !cloudprovider.IsAvailable() { + return input, httperrors.NewInputParameterError("cloudprovider %s(%s) is not available", cloudprovider.Name, cloudprovider.Id) } - region, err := vpc.GetRegion() if err != nil { - return nil, err + return input, err } input.CloudregionId = region.Id - input.Cloudregion = region.Name - input.Provider = region.Provider - - if addressV.IP != nil { - ip, err := netutils.NewIPV4Addr(addressV.IP.String()) - if err != nil { - return nil, err - } - if !network.IsAddressInRange(ip) { - return nil, httperrors.NewInputParameterError("Ip %s not in network %s(%s) range", addressV.IP.String(), network.Name, network.Id) - } - } if len(input.Duration) > 0 { billingCycle, err := billing.ParseBillingCycle(input.Duration) if err != nil { - return nil, httperrors.NewInputParameterError("invalid duration %s", input.Duration) + return input, httperrors.NewInputParameterError("invalid duration %s", input.Duration) } if !utils.IsInStringArray(input.BillingType, []string{billing_api.BILLING_TYPE_PREPAID, billing_api.BILLING_TYPE_POSTPAID}) { @@ -371,7 +347,7 @@ func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred if input.BillingType == billing_api.BILLING_TYPE_PREPAID { if !region.GetDriver().IsSupportedBillingCycle(billingCycle, man.KeywordPlural()) { - return nil, httperrors.NewInputParameterError("unsupported duration %s", input.Duration) + return input, httperrors.NewInputParameterError("unsupported duration %s", input.Duration) } } @@ -380,71 +356,54 @@ func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred input.ExpiredAt = billingCycle.EndAt(tm) } + for k, v := range map[string]string{ + "engine": input.Engine, + "engine_version": input.EngineVersion, + "category": input.Category, + "storage_type": input.StorageType, + } { + if len(v) == 0 { + return input, httperrors.NewMissingParameterError(k) + } + } + + info := getDBInstanceInfo(region, nil) + if info == nil { + return input, httperrors.NewNotSupportedError("cloudregion %s not support create rds", region.Name) + } + + versionsInfo, ok := info[input.Engine] + if !ok { + return input, httperrors.NewNotSupportedError("cloudregion %s not support create %s rds", region.Name, input.Engine) + } + + categoryInfo, ok := versionsInfo[input.EngineVersion] + if !ok { + return input, httperrors.NewNotSupportedError("cloudregion %s not support create %s rds", region.Name, input.EngineVersion) + } + + storageInfo, ok := categoryInfo[input.Category] + if !ok { + return input, httperrors.NewNotSupportedError("cloudregion %s not support create %s rds", region.Name, input.Category) + } + + if !utils.IsInStringArray(input.StorageType, storageInfo) { + return input, httperrors.NewNotSupportedError("cloudregion %s not support create %s rds", region.Name, input.StorageType) + } + if len(input.InstanceType) == 0 && (input.VcpuCount == 0 || input.VmemSizeMb == 0) { - return nil, httperrors.NewMissingParameterError("Missing instance_type or vcpu_count, vmem_size_mb parameters") - } - - engines, err := DBInstanceSkuManager.GetEngines(input.Provider, input.CloudregionId) - if err != nil { - return nil, httperrors.NewGeneralError(err) - } - - if len(input.Engine) == 0 { - return nil, httperrors.NewMissingParameterError("engine") - } - - if !utils.IsInStringArray(input.Engine, engines) { - return nil, httperrors.NewInputParameterError("%s(%s) not support engine %s, only support %s", input.Provider, input.Cloudregion, input.Engine, engines) - } - - if len(input.EngineVersion) == 0 { - return nil, httperrors.NewMissingParameterError("engine_version") - } - - versions, err := DBInstanceSkuManager.GetEngineVersions(input.Provider, input.CloudregionId, input.Engine) - if err != nil { - return nil, httperrors.NewGeneralError(err) - } - - if !utils.IsInStringArray(input.EngineVersion, versions) { - return nil, httperrors.NewInputParameterError("%s(%s) engine %s not support version %s, only support %s", input.Provider, input.Cloudregion, input.Engine, input.EngineVersion, versions) - } - - if len(input.Category) == 0 { - return nil, httperrors.NewMissingParameterError("category") - } - - categories, err := DBInstanceSkuManager.GetCategories(input.Provider, input.CloudregionId, input.Engine, input.EngineVersion) - if err != nil { - return nil, httperrors.NewGeneralError(err) - } - - if !utils.IsInStringArray(input.Category, categories) { - return nil, httperrors.NewInputParameterError("%s(%s) engine %s(%s) not support category %s, only support %s", input.Provider, input.Cloudregion, input.Engine, input.EngineVersion, input.Category, categories) - } - - if len(input.StorageType) == 0 { - return nil, httperrors.NewMissingParameterError("storage_type") - } - - storageTypes, err := DBInstanceSkuManager.GetStorageTypes(input.Provider, input.CloudregionId, input.Engine, input.EngineVersion, input.Category) - if err != nil { - return nil, httperrors.NewGeneralError(err) - } - - if !utils.IsInStringArray(input.StorageType, storageTypes) { - return nil, httperrors.NewInputParameterError("%s(%s) engine %s(%s) %s not support storage %s, only support %s", input.Provider, input.Cloudregion, input.Engine, input.EngineVersion, input.Category, input.StorageType, storageTypes) + return input, httperrors.NewMissingParameterError("Missing instance_type or vcpu_count, vmem_size_mb parameters") } instance := SDBInstance{} jsonutils.Update(&instance, input) skus, err := instance.GetAvailableDBInstanceSkus() if err != nil { - return nil, httperrors.NewGeneralError(err) + return input, httperrors.NewGeneralError(err) } if len(skus) == 0 { - return nil, httperrors.NewInputParameterError("not match any dbinstance sku") + return input, httperrors.NewInputParameterError("not match any dbinstance sku") } if len(input.InstanceType) > 0 { //设置下cpu和内存的大小 @@ -454,22 +413,37 @@ func (man *SDBInstanceManager) ValidateCreateData(ctx context.Context, userCred input.VirtualResourceCreateInput, err = man.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput) if err != nil { - return nil, err + return input, err } - input, err = region.GetDriver().ValidateCreateDBInstanceData(ctx, userCred, ownerId, input, skus, network) + driver := region.GetDriver() + secCount := driver.GetRdsSupportSecgroupCount() + if secCount == 0 && len(input.SecgroupIds) > 0 { + return input, httperrors.NewNotSupportedError("%s rds not support secgroup", driver.GetProvider()) + } + if len(input.SecgroupIds) > secCount { + return input, httperrors.NewNotSupportedError("%s rds Support up to %d security groups", driver.GetProvider(), secCount) + } + for i := range input.SecgroupIds { + _, err := validators.ValidateModel(userCred, SecurityGroupManager, &input.SecgroupIds[i]) + if err != nil { + return input, err + } + } + + input, err = driver.ValidateCreateDBInstanceData(ctx, userCred, ownerId, input, skus, network) if err != nil { - return nil, err + return input, err } quotaKeys := fetchRegionalQuotaKeys(rbacutils.ScopeProject, ownerId, region, cloudprovider) pendingUsage := SRegionQuota{Rds: 1} pendingUsage.SetKeys(quotaKeys) if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil { - return nil, httperrors.NewOutOfQuotaError("%s", err) + return input, httperrors.NewOutOfQuotaError("%s", err) } - return input.JSON(input), nil + return input, nil } func (self *SDBInstance) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { @@ -480,14 +454,43 @@ func (self *SDBInstance) PostCreate(ctx context.Context, userCred mcclient.Token if err != nil { log.Errorf("CancelPendingUsage error %s", err) } - self.SetStatus(userCred, api.DBINSTANCE_DEPLOYING, "") - params := data.(*jsonutils.JSONDict) - task, err := taskman.TaskManager.NewTask(ctx, "DBInstanceCreateTask", self, userCred, params, "", "", nil) - if err != nil { - log.Errorf("DBInstanceCreateTask newTask error %s", err) - return + + input := api.DBInstanceCreateInput{} + data.Unmarshal(&input) + if len(input.NetworkId) > 0 { + err := DBInstanceNetworkManager.newNetwork(ctx, userCred, self.Id, input.NetworkId, input.Address) + if err != nil { + log.Errorf("DBInstanceNetworkManager.Insert") + } } + ids := []string{} + for _, secgroupId := range input.SecgroupIds { + if !utils.IsInStringArray(secgroupId, ids) { + err := self.assignSecgroup(ctx, userCred, secgroupId) + if err != nil { + log.Errorf("assignSecgroup") + } + ids = append(ids, secgroupId) + } + } + resetPassword := true + if input.ResetPassword != nil && !*input.ResetPassword { + resetPassword = false + } + self.StartDBInstanceCreateTask(ctx, userCred, resetPassword, input.Password, "") +} + +func (self *SDBInstance) StartDBInstanceCreateTask(ctx context.Context, userCred mcclient.TokenCredential, resetPassword bool, password, parentTaskId string) error { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(password), "password") + params.Add(jsonutils.NewBool(resetPassword), "reset_password") + task, err := taskman.TaskManager.NewTask(ctx, "DBInstanceCreateTask", self, userCred, params, parentTaskId, "", nil) + if err != nil { + return errors.Wrapf(err, "NewTask") + } + self.SetStatus(userCred, api.DBINSTANCE_DEPLOYING, "") task.ScheduleRun(nil) + return nil } func (self *SDBInstance) GetExtraDetails( @@ -523,7 +526,6 @@ func (manager *SDBInstanceManager) FetchCustomizeColumns( CloudregionResourceInfo: regRows[i], } instance := objs[i].(*SDBInstance) - rows[i] = instance.getMoreDetails(rows[i]) vpcIds[i] = instance.VpcId zone1Ids[i] = instance.Zone1 zone2Ids[i] = instance.Zone2 @@ -577,30 +579,6 @@ func (self *SDBInstance) GetVpc() (*SVpc, error) { return vpc.(*SVpc), nil } -func (self *SDBInstance) GetNetwork() (*SNetwork, error) { - dbnet := DBInstanceNetworkManager.Query().SubQuery() - q := NetworkManager.Query() - q = q.Join(dbnet, sqlchemy.Equals(q.Field("id"), dbnet.Field("network_id"))).Filter(sqlchemy.Equals(dbnet.Field("dbinstance_id"), self.Id)) - count, err := q.CountWithError() - if err != nil { - return nil, err - } - if count == 1 { - network := &SNetwork{} - network.SetModelManager(NetworkManager, network) - err = q.First(network) - if err != nil { - return nil, err - } - return network, nil - } - if count > 1 { - return nil, sqlchemy.ErrDuplicateEntry - } - return nil, sql.ErrNoRows - -} - type sDBInstanceZone struct { Id string Name string @@ -643,37 +621,73 @@ func fetchDBInstanceZones(rdsIds []string) map[string][]sDBInstanceZone { return result } -func (self *SDBInstance) getProviderInfo() SCloudProviderInfo { - vpc, _ := self.GetVpc() - provider := vpc.GetCloudprovider() - region := self.GetRegion() - return MakeCloudProviderInfo(region, nil, provider) +func (self *SDBInstance) getSecgroupsByExternalIds(externalIds []string) ([]SSecurityGroup, error) { + sq := SecurityGroupCacheManager.Query("secgroup_id").In("external_id", externalIds).Equals("manager_id", self.ManagerId) + q := SecurityGroupManager.Query().In("id", sq.SubQuery()) + secgroups := []SSecurityGroup{} + err := db.FetchModelObjects(SecurityGroupManager, q, &secgroups) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return secgroups, nil } -func (self *SDBInstance) getMoreDetails(out api.DBInstanceDetails) api.DBInstanceDetails { - if len(self.SecgroupId) > 0 { - if secgroup, _ := self.GetSecgroup(); secgroup != nil { - out.Secgroup = secgroup.Name +func (self *SDBInstance) GetSecgroups() ([]SSecurityGroup, error) { + sq := DBInstanceSecgroupManager.Query("secgroup_id").Equals("dbinstance_id", self.Id).SubQuery() + q := SecurityGroupManager.Query().In("id", sq) + secgroups := []SSecurityGroup{} + err := db.FetchModelObjects(SecurityGroupManager, q, &secgroups) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return secgroups, nil +} + +func (self *SDBInstance) GetDBInstanceSecgroups() ([]SDBInstanceSecgroup, error) { + q := DBInstanceSecgroupManager.Query().Equals("dbinstance_id", self.Id) + secgroups := []SDBInstanceSecgroup{} + err := db.FetchModelObjects(DBInstanceSecgroupManager, q, &secgroups) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return secgroups, nil +} + +func (self *SDBInstance) RevokeSecgroup(ctx context.Context, userCred mcclient.TokenCredential, id string) error { + secgroups, err := self.GetDBInstanceSecgroups() + if err != nil { + return errors.Wrapf(err, "GetDBInstanceSecgroups") + } + for i := range secgroups { + if secgroups[i].SecgroupId == id { + err = secgroups[i].Detach(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "secgroups.Detach %d", secgroups[i].RowId) + } } } - - if skus, _ := self.GetDBInstanceSkus(); len(skus) > 0 { - out.Iops = skus[0].IOPS - } - - network, _ := self.GetNetwork() - if network != nil { - out.Network = network.Name - } - return out + return nil } -func (self *SDBInstance) GetSecgroup() (*SSecurityGroup, error) { - secgroup, err := SecurityGroupManager.FetchById(self.SecgroupId) +func (self *SDBInstance) AssignSecgroup(ctx context.Context, userCred mcclient.TokenCredential, id string) error { + secgroups, err := self.GetDBInstanceSecgroups() if err != nil { - return nil, err + return errors.Wrapf(err, "GetDBInstanceSecgroups") } - return secgroup.(*SSecurityGroup), nil + for i := range secgroups { + if secgroups[i].SecgroupId == id { + return fmt.Errorf("secgroup %s already assign rds %s(%s)", id, self.Name, self.Id) + } + } + return self.assignSecgroup(ctx, userCred, id) +} + +func (self *SDBInstance) assignSecgroup(ctx context.Context, userCred mcclient.TokenCredential, id string) error { + ds := &SDBInstanceSecgroup{} + ds.DBInstanceId = self.Id + ds.SecgroupId = id + ds.SetModelManager(DBInstanceSecgroupManager, ds) + return DBInstanceSecgroupManager.TableSpec().Insert(ctx, ds) } func (self *SDBInstance) GetMasterInstance() (*SDBInstance, error) { @@ -1261,25 +1275,14 @@ func (self *SDBInstance) GetDBParameters() ([]SDBInstanceParameter, error) { return parameters, nil } -func (self *SDBInstance) GetDBNetwork() (*SDBInstanceNetwork, error) { +func (self *SDBInstance) GetDBNetworks() ([]SDBInstanceNetwork, error) { q := DBInstanceNetworkManager.Query().Equals("dbinstance_id", self.Id) - count, err := q.CountWithError() + networks := []SDBInstanceNetwork{} + err := db.FetchModelObjects(DBInstanceNetworkManager, q, &networks) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "db.FetchModelObjects") } - if count == 1 { - network := &SDBInstanceNetwork{} - network.SetModelManager(DBInstanceNetworkManager, network) - err = q.First(network) - if err != nil { - return nil, err - } - return network, nil - } - if count > 1 { - return nil, sqlchemy.ErrDuplicateEntry - } - return nil, sql.ErrNoRows + return networks, nil } func (manager *SDBInstanceManager) SyncDBInstanceMasterId(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, cloudDBInstances []cloudprovider.ICloudDBInstance) { @@ -1564,7 +1567,7 @@ func (self *SDBInstance) SyncWithCloudDBInstance(ctx context.Context, userCred m return errors.Wrap(err, "SyncWithCloudDBInstance.GetProviderFactory") } - if factory.IsSupportPrepaidResources() { + if factory.IsSupportPrepaidResources() && !extInstance.GetExpiredAt().IsZero() { self.BillingType = extInstance.GetBillingType() if expired := extInstance.GetExpiredAt(); !expired.IsZero() { self.ExpiredAt = expired @@ -1622,22 +1625,6 @@ func (manager *SDBInstanceManager) newFromCloudDBInstance(ctx context.Context, u instance.MaintainTime = extInstance.GetMaintainTime() instance.SetZoneIds(extInstance) - if secgroupId := extInstance.GetSecurityGroupId(); len(secgroupId) > 0 { - q := SecurityGroupCacheManager.Query().Equals("manager_id", provider.Id).Equals("external_id", secgroupId) - count, err := q.CountWithError() - if err != nil { - log.Errorf("failed get secgroup cache by externalId %s error: %v", secgroupId, err) - } else if count > 0 { - cache := SSecurityGroupCache{} - err = q.First(&cache) - if err != nil { - log.Errorf("failed get secgroup cache by externalId %s error: %v", secgroupId, err) - } else { - instance.SecgroupId = cache.SecgroupId - } - } - } - if vpcId := extInstance.GetIVpcId(); len(vpcId) > 0 { vpc, err := db.FetchByExternalIdAndManagerId(VpcManager, vpcId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { return q.Equals("manager_id", provider.Id) diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index f09c65a2fa..8a25d1c72d 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -16,7 +16,6 @@ package models import ( "context" - "database/sql" "fmt" "yunion.io/x/log" @@ -1379,12 +1378,36 @@ func (network *SDBInstanceNetwork) purge(ctx context.Context, userCred mcclient. } func (instance *SDBInstance) purgeNetwork(ctx context.Context, userCred mcclient.TokenCredential) error { - network, err := instance.GetDBNetwork() - if err != nil && errors.Cause(err) != sql.ErrNoRows { - return errors.Wrapf(err, "GetDBNetwork") + networks, err := instance.GetDBNetworks() + if err != nil { + return errors.Wrapf(err, "GetDBNetworks") } - if network != nil { - return network.purge(ctx, userCred) + for i := range networks { + err = networks[i].purge(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "networks.purge %d", networks[i].RowId) + } + } + return nil +} + +func (self *SDBInstanceSecgroup) purge(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, self) + defer lockman.ReleaseObject(ctx, self) + + return self.Detach(ctx, userCred) +} + +func (instance *SDBInstance) purgeSecgroups(ctx context.Context, userCred mcclient.TokenCredential) error { + secgroups, err := instance.GetDBInstanceSecgroups() + if err != nil { + return errors.Wrapf(err, "GetDBInstanceSecgroups") + } + for i := range secgroups { + err = secgroups[i].purge(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "secgroups.purge %d", secgroups[i].RowId) + } } return nil } @@ -1429,6 +1452,11 @@ func (instance *SDBInstance) Purge(ctx context.Context, userCred mcclient.TokenC return errors.Wrapf(err, "purgeNetwork") } + err = instance.purgeSecgroups(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "purgeSecgroups") + } + err = instance.PurgeBackups(ctx, userCred, api.BACKUP_MODE_AUTOMATED) if err != nil { return errors.Wrap(err, "instance.purgeBackups") diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index 61f609de71..0cbc50c25e 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -165,7 +165,7 @@ type IDBInstanceDriver interface { IsSupportKeepDBInstanceManualBackup() bool InitDBInstanceUser(ctx context.Context, dbinstance *SDBInstance, task taskman.ITask, desc *cloudprovider.SManagedDBInstanceCreateConfig) error - IsDBInstanceNeedSecgroup() bool + GetRdsSupportSecgroupCount() int ValidateDBInstanceRecovery(ctx context.Context, userCred mcclient.TokenCredential, instance *SDBInstance, backup *SDBInstanceBackup, input api.SDBInstanceRecoveryConfigInput) error diff --git a/pkg/compute/models/secgroups.go b/pkg/compute/models/secgroups.go index 7dfce3ff17..55288c79f8 100644 --- a/pkg/compute/models/secgroups.go +++ b/pkg/compute/models/secgroups.go @@ -145,6 +145,15 @@ func (manager *SSecurityGroupManager) ListItemFilter( q = q.Filter(sqlchemy.OR(filters...)) } + if len(input.DBInstanceId) > 0 { + _, err = validators.ValidateModel(userCred, DBInstanceManager, &input.DBInstanceId) + if err != nil { + return nil, err + } + sq := DBInstanceSecgroupManager.Query("secgroup_id").Equals("dbinstance_id", input.DBInstanceId) + q = q.In("id", sq.SubQuery()) + } + if len(input.Ip) > 0 || len(input.Ports) > 0 { sq := SecurityGroupRuleManager.Query("secgroup_id") if len(input.Ip) > 0 { diff --git a/pkg/compute/regiondrivers/base.go b/pkg/compute/regiondrivers/base.go index 970e4f168d..59a00e363b 100644 --- a/pkg/compute/regiondrivers/base.go +++ b/pkg/compute/regiondrivers/base.go @@ -396,3 +396,7 @@ func (self *SBaseRegionDriver) RequestRemoteUpdateElasticcache(ctx context.Conte func (self *SBaseRegionDriver) IsDBInstanceNeedSecgroup() bool { return false } + +func (self *SBaseRegionDriver) GetRdsSupportSecgroupCount() int { + return 0 +} diff --git a/pkg/compute/regiondrivers/huawei.go b/pkg/compute/regiondrivers/huawei.go index eb1699c37b..66f3f72127 100644 --- a/pkg/compute/regiondrivers/huawei.go +++ b/pkg/compute/regiondrivers/huawei.go @@ -2174,6 +2174,10 @@ func (self *SHuaWeiRegionDriver) ValidateCreateDBInstanceData(ctx context.Contex return input, httperrors.NewMissingParameterError("password") } + if len(input.SecgroupIds) == 0 { + input.SecgroupIds = []string{"default"} + } + return input, nil } @@ -2637,6 +2641,6 @@ func (self *SHuaWeiRegionDriver) GetBackendStatusForAdd() []string { return []string{api.VM_RUNNING, api.VM_READY} } -func (self *SHuaWeiRegionDriver) IsDBInstanceNeedSecgroup() bool { - return true +func (self *SHuaWeiRegionDriver) GetRdsSupportSecgroupCount() int { + return 1 } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 3c3f63ac46..4b72bebcb5 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -40,6 +40,7 @@ import ( "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/onecloud/pkg/util/rand" "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/seclib2" ) type SManagedVirtualizationRegionDriver struct { @@ -1643,12 +1644,10 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont } params := task.GetParams() - networkId, _ := params.GetString("network_external_id") - if len(networkId) == 0 { - return nil, fmt.Errorf("failed to get network externalId") - } - address, _ := params.GetString("address") passwd, _ := params.GetString("password") + if len(passwd) == 0 && jsonutils.QueryBoolean(params, "reset_password", true) { + passwd = seclib2.RandomPassword2(12) + } desc := cloudprovider.SManagedDBInstanceCreateConfig{ Name: dbinstance.Name, Description: dbinstance.Description, @@ -1657,8 +1656,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont VcpuCount: dbinstance.VcpuCount, VmemSizeMb: dbinstance.VmemSizeMb, VpcId: vpc.ExternalId, - NetworkId: networkId, - Address: address, Engine: dbinstance.Engine, EngineVersion: dbinstance.EngineVersion, Category: dbinstance.Category, @@ -1667,6 +1664,19 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont } desc.Tags, _ = dbinstance.GetAllUserMetadata() + networks, err := dbinstance.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 + } + _cloudprovider := dbinstance.GetCloudprovider() desc.ProjectId, err = _cloudprovider.SyncProject(ctx, userCred, dbinstance.ProjectId) if err != nil { @@ -1680,18 +1690,20 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont return nil, err } - if region.GetDriver().IsDBInstanceNeedSecgroup() { - secgroup, _ := dbinstance.GetSecgroup() - if secgroup != nil { - vpcId, err := region.GetDriver().GetSecurityGroupVpcId(ctx, userCred, region, nil, vpc, false) - if err != nil { - return nil, errors.Wrap(err, "GetSecurityGroupVpcId") - } - desc.SecgroupId, err = region.GetDriver().RequestSyncSecurityGroup(ctx, userCred, vpcId, vpc, secgroup, desc.ProjectId) - if err != nil { - return nil, errors.Wrap(err, "SyncSecurityGroup") - } + secgroups, err := dbinstance.GetSecgroups() + if err != nil { + return nil, errors.Wrapf(err, "GetSecgroups") + } + for i := range secgroups { + vpcId, err := region.GetDriver().GetSecurityGroupVpcId(ctx, userCred, region, nil, vpc, false) + if err != nil { + return nil, errors.Wrap(err, "GetSecurityGroupVpcId") } + secId, err := region.GetDriver().RequestSyncSecurityGroup(ctx, userCred, vpcId, vpc, &secgroups[i], desc.ProjectId) + if err != nil { + return nil, errors.Wrap(err, "SyncSecurityGroup") + } + desc.SecgroupIds = append(desc.SecgroupIds, secId) } if dbinstance.BillingType == billing_api.BILLING_TYPE_PREPAID { @@ -1743,16 +1755,15 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstance(ctx cont return nil, errors.Wrapf(err, "create") } + err = db.SetExternalId(dbinstance, 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 { - log.Errorf("timeout for waiting dbinstance running error: %v", err) + return nil, errors.Wrapf(err, "cloudprovider.WaitStatus runing") } - - err = dbinstance.SyncAllWithCloudDBInstance(ctx, userCred, dbinstance.GetCloudprovider(), iRds) - if err != nil { - log.Errorf("SyncAllWithCloudDBInstance error: %v", err) - } - return nil, nil }) diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index 2e6baaf268..e0b3fd9b8d 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -1387,8 +1387,15 @@ func (self *SQcloudRegionDriver) IsSupportedDBInstance() bool { return true } -func (self *SQcloudRegionDriver) IsDBInstanceNeedSecgroup() bool { - return true +func (self *SQcloudRegionDriver) GetRdsSupportSecgroupCount() int { + return 5 +} + +func (self *SQcloudRegionDriver) ValidateCreateDBInstanceData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.DBInstanceCreateInput, skus []models.SDBInstanceSku, network *models.SNetwork) (api.DBInstanceCreateInput, error) { + if input.Engine == api.DBINSTANCE_TYPE_MYSQL && input.Category != api.QCLOUD_DBINSTANCE_CATEGORY_BASIC && len(input.SecgroupIds) == 0 { + input.SecgroupIds = []string{api.SECGROUP_DEFAULT_ID} + } + return input, nil } func (self *SQcloudRegionDriver) IsSupportedBillingCycle(bc billing.SBillingCycle, resource string) bool { diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 759bfcaad1..e9ef76fb7f 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -221,6 +221,7 @@ func InitHandlers(app *appsrv.Application) { models.SnapshotPolicyDiskManager, models.InstanceSnapshotJointManager, models.DnsZoneVpcManager, + models.DBInstanceSecgroupManager, } { db.RegisterModelManager(manager) handler := db.NewJointModelHandler(manager) diff --git a/pkg/mcclient/modules/mod_dbinstancenetworks.go b/pkg/mcclient/modules/mod_dbinstancenetworks.go index 5d0ce5c50c..f0a23b8627 100644 --- a/pkg/mcclient/modules/mod_dbinstancenetworks.go +++ b/pkg/mcclient/modules/mod_dbinstancenetworks.go @@ -24,7 +24,7 @@ func init() { DBInstanceNetworks = NewJointComputeManager( "dbinstancenetwork", "dbinstancenetworks", - []string{"Instance_Id", "Network_Id", "IP_Addr"}, + []string{}, []string{}, &DBInstance, &Networks, diff --git a/pkg/mcclient/options/secgroup.go b/pkg/mcclient/options/secgroup.go index f20af8b742..f4a9a2ba51 100644 --- a/pkg/mcclient/options/secgroup.go +++ b/pkg/mcclient/options/secgroup.go @@ -23,11 +23,12 @@ import ( type SecgroupListOptions struct { BaseListOptions - Equals string `help:"Secgroup ID or Name, filter secgroups whose rules equals the specified one"` - Server string `help:"Filter secgroups bound to specified server"` - Ip string `help:"Filter secgroup by ip"` - Ports string `help:"Filter secgroup by ports"` - Direction string `help:"Filter secgroup by ports" choices:"all|in|out"` + Equals string `help:"Secgroup ID or Name, filter secgroups whose rules equals the specified one"` + Server string `help:"Filter secgroups bound to specified server"` + Ip string `help:"Filter secgroup by ip"` + Ports string `help:"Filter secgroup by ports"` + Direction string `help:"Filter secgroup by ports" choices:"all|in|out"` + DBInstance string `help:"Filter secgroups bound to specified rds" json:"dbinstance"` } func (opts *SecgroupListOptions) Params() (jsonutils.JSONObject, error) { diff --git a/pkg/multicloud/aliyun/dbinstance.go b/pkg/multicloud/aliyun/dbinstance.go index 23e6c05579..224768fe2c 100644 --- a/pkg/multicloud/aliyun/dbinstance.go +++ b/pkg/multicloud/aliyun/dbinstance.go @@ -298,20 +298,21 @@ func (rds *SDBInstance) getZone(index int) string { return fmt.Sprintf("%s%s", rds.RegionId, string(zoneCode)) } -func (rds *SDBInstance) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { +func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { netInfo, err := rds.region.GetDBInstanceNetInfo(rds.DBInstanceId) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "GetDBInstanceNetInfo") } - network := &cloudprovider.SDBInstanceNetwork{} + networks := []cloudprovider.SDBInstanceNetwork{} for _, net := range netInfo { if net.IPType == "Private" { + network := cloudprovider.SDBInstanceNetwork{} network.IP = net.IPAddress network.NetworkId = net.VSwitchId - return network, nil + networks = append(networks, network) } } - return nil, fmt.Errorf("failed to found network for aliyun rds %s", rds.DBInstanceId) + return []cloudprovider.SDBInstanceNetwork{}, nil } func (rds *SDBInstance) fetchNetInfo() error { diff --git a/pkg/multicloud/aws/dbinstance.go b/pkg/multicloud/aws/dbinstance.go index bde30e4dc2..9df7d950c1 100644 --- a/pkg/multicloud/aws/dbinstance.go +++ b/pkg/multicloud/aws/dbinstance.go @@ -261,8 +261,8 @@ func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstan return accounts, nil } -func (rds *SDBInstance) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { - return nil, nil +func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { + return []cloudprovider.SDBInstanceNetwork{}, nil } func (rds *SDBInstance) GetInternalConnectionStr() string { diff --git a/pkg/multicloud/dbinstance_base.go b/pkg/multicloud/dbinstance_base.go index 386dcb2fd2..8ed1d6faa6 100644 --- a/pkg/multicloud/dbinstance_base.go +++ b/pkg/multicloud/dbinstance_base.go @@ -36,8 +36,8 @@ func (instance *SDBInstanceBase) GetInternalConnectionStr() string { return "" } -func (instance *SDBInstanceBase) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { - return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetDBNetwork") +func (instance *SDBInstanceBase) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetDBNetworks") } func (instance *SDBInstanceBase) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) { @@ -76,8 +76,8 @@ func (instance *SDBInstanceBase) GetMasterInstanceId() string { return "" } -func (instance *SDBInstanceBase) GetSecurityGroupId() string { - return "" +func (instance *SDBInstanceBase) GetSecurityGroupIds() ([]string, error) { + return []string{}, errors.Wrapf(cloudprovider.ErrNotImplemented, "GetSecurityGroupIds") } func (instance *SDBInstanceBase) Renew(bc billing.SBillingCycle) error { diff --git a/pkg/multicloud/huawei/dbinstance.go b/pkg/multicloud/huawei/dbinstance.go index 641f409524..456baa7077 100644 --- a/pkg/multicloud/huawei/dbinstance.go +++ b/pkg/multicloud/huawei/dbinstance.go @@ -167,8 +167,8 @@ func (rds *SDBInstance) GetBillingType() string { return billing_api.BILLING_TYPE_PREPAID } -func (rds *SDBInstance) GetSecurityGroupId() string { - return rds.SecurityGroupId +func (rds *SDBInstance) GetSecurityGroupIds() ([]string, error) { + return []string{rds.SecurityGroupId}, nil } func (rds *SDBInstance) fetchFlavor() error { @@ -314,16 +314,16 @@ type SRdsNetwork struct { IP string } -func (rds *SDBInstance) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { +func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { + ret := []cloudprovider.SDBInstanceNetwork{} for _, ip := range rds.PrivateIps { - inetwork := &cloudprovider.SDBInstanceNetwork{ + network := cloudprovider.SDBInstanceNetwork{ IP: ip, NetworkId: rds.SubnetId, } - return inetwork, nil + ret = append(ret, network) } - - return nil, fmt.Errorf("failed to found network for huawei rds %s", rds.Name) + return ret, nil } func (rds *SDBInstance) GetInternalConnectionStr() string { @@ -430,6 +430,10 @@ func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceC zoneIds = append(zoneIds, zone.GetId()) } + if len(desc.SecgroupIds) == 0 { + return nil, fmt.Errorf("Missing secgroupId") + } + params := map[string]interface{}{ "region": region.ID, "name": desc.Name, @@ -444,7 +448,7 @@ func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceC }, "vpc_id": desc.VpcId, "subnet_id": desc.NetworkId, - "security_group_id": desc.SecgroupId, + "security_group_id": desc.SecgroupIds[0], } if len(desc.ProjectId) > 0 { diff --git a/pkg/multicloud/qcloud/rds_mysql.go b/pkg/multicloud/qcloud/rds_mysql.go index 919d5bb584..50bb5caea2 100644 --- a/pkg/multicloud/qcloud/rds_mysql.go +++ b/pkg/multicloud/qcloud/rds_mysql.go @@ -210,8 +210,10 @@ func (self *SMySQLInstance) GetMaintainTime() string { return timeWindow.String() } -func (self *SMySQLInstance) GetDBNetwork() (*cloudprovider.SDBInstanceNetwork, error) { - return &cloudprovider.SDBInstanceNetwork{NetworkId: self.UniqSubnetId, IP: self.Vip}, nil +func (self *SMySQLInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { + return []cloudprovider.SDBInstanceNetwork{ + cloudprovider.SDBInstanceNetwork{NetworkId: self.UniqSubnetId, IP: self.Vip}, + }, nil } func (self *SMySQLInstance) GetConnectionStr() string { @@ -237,22 +239,22 @@ func (self *SMySQLInstance) GetMasterInstanceId() string { return self.MasterInfo.InstanceId } -func (self *SMySQLInstance) GetSecurityGroupId() string { +func (self *SMySQLInstance) GetSecurityGroupIds() ([]string, error) { if len(self.SecurityGroupIds) > 0 { - return self.SecurityGroupIds[0] + return self.SecurityGroupIds, nil } if self.DeviceType == "BASIC" { - return "" + return []string{}, nil } secgroups, err := self.region.DescribeMySQLDBSecurityGroups(self.InstanceId) if err != nil { - log.Errorf("failed to get instance %s security group info %v", self.InstanceId, err) - return "" + return []string{}, errors.Wrapf(err, "DescribeMySQLDBSecurityGroups") } - if len(secgroups) > 0 { - return secgroups[0].SecurityGroupId + ids := []string{} + for i := range secgroups { + ids = append(ids, secgroups[i].SecurityGroupId) } - return "" + return ids, nil } func (self *SMySQLInstance) Renew(bc billing.SBillingCycle) error { @@ -723,8 +725,8 @@ func (self *SRegion) CreateMySQLDBInstance(opts *cloudprovider.SManagedDBInstanc if len(opts.Password) > 0 { params["Password"] = opts.Password } - if len(opts.SecgroupId) > 0 { - params["SecurityGroup.0"] = opts.SecgroupId + for i, secId := range opts.SecgroupIds { + params[fmt.Sprintf("SecurityGroup.%d", i)] = secId } action := "CreateDBInstanceHour" if opts.BillingCycle != nil { @@ -770,7 +772,25 @@ func (self *SRegion) CreateMySQLDBInstance(opts *cloudprovider.SManagedDBInstanc i++ } - resp, err := self.cdbRequest(action, params) + var create = func(action string, params map[string]string) (jsonutils.JSONObject, error) { + startTime := time.Now() + var resp jsonutils.JSONObject + var err error + for time.Now().Sub(startTime) < time.Minute*10 { + resp, err = self.cdbRequest(action, params) + if err != nil { + if strings.Contains(err.Error(), "OperationDenied.OtherOderInProcess") || strings.Contains(err.Error(), "Message=请求已经在处理中") { + time.Sleep(time.Second * 20) + continue + } + return nil, errors.Wrapf(err, "cdbRequest") + } + return resp, nil + } + return resp, err + } + + resp, err := create(action, params) if err != nil { return nil, errors.Wrapf(err, "cdbRequest") }