Merge pull request #11637 from ioito/feat/qx-tdsql

fix(region): add qcloud tdsql
This commit is contained in:
Zexi Li
2021-07-15 09:44:00 +08:00
committed by GitHub
9 changed files with 663 additions and 13 deletions
+1
View File
@@ -114,6 +114,7 @@ const (
QCLOUD_DBINSTANCE_CATEGORY_BASIC = "basic" //基础版
QCLOUD_DBINSTANCE_CATEGORY_HA = "ha" //高可用
QCLOUD_DBINSTANCE_CATEGORY_FINANCE = "fe" //金融版
QCLOUD_DBINSTANCE_CATEGORY_TDSQL = "tdsql" //TDSQL
//华为云实例类型
HUAWEI_DBINSTANCE_CATEGORY_HA = "ha" //主备
+13 -13
View File
@@ -25,32 +25,32 @@ import (
func (self *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
ret := []cloudprovider.ICloudDBInstance{}
mysql := []SMySQLInstance{}
for {
part, total, err := self.ListMySQLInstances([]string{}, len(mysql), 50)
if err != nil {
return nil, errors.Wrapf(err, "ListMySQLInstances")
}
mysql = append(mysql, part...)
if len(mysql) >= total {
break
}
mysqls, err := self.GetIMySQLs()
if err != nil {
return nil, errors.Wrapf(err, "GetIMySQLs")
}
for i := range mysql {
mysql[i].region = self
ret = append(ret, &mysql[i])
ret = append(ret, mysqls...)
tdsqls, err := self.GetITDSQLs()
if err != nil {
return nil, errors.Wrapf(err, "GetITDSQLs")
}
ret = append(ret, tdsqls...)
return ret, nil
}
func (self *SRegion) GetIDBInstanceById(id string) (cloudprovider.ICloudDBInstance, error) {
if strings.HasPrefix(id, "cdb") {
return self.GetMySQLInstanceById(id)
} else if strings.HasPrefix(id, "tdsqlshard") {
return self.GetTDSQL(id)
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) CreateIDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
if opts.Category == api.QCLOUD_DBINSTANCE_CATEGORY_TDSQL {
return nil, cloudprovider.ErrNotImplemented
}
switch opts.Engine {
case api.DBINSTANCE_TYPE_MYSQL:
rds, err := self.CreateMySQLDBInstance(opts)
+16
View File
@@ -65,6 +65,7 @@ const (
QCLOUD_CDN_API_VERSION = "2018-06-06"
QCLOUD_MONGODB_API_VERSION = "2019-07-25"
QCLOUD_ES_API_VERSION = "2018-04-16"
QCLOUD_DCDB_API_VERSION = "2018-04-11"
)
type QcloudClientConfig struct {
@@ -185,6 +186,12 @@ func redisRequest(client *common.Client, apiName string, params map[string]strin
return _jsonRequest(client, domain, QCLOUD_REDIS_API_VERSION, apiName, params, debug, true)
}
// tdsql
func dcdbRequest(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) {
domain := apiDomain("dcdb", params)
return _jsonRequest(client, domain, QCLOUD_DCDB_API_VERSION, apiName, params, debug, true)
}
// mongodb
func mongodbRequest(client *common.Client, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) {
domain := apiDomain("mongodb", params)
@@ -653,6 +660,15 @@ func (client *SQcloudClient) redisRequest(apiName string, params map[string]stri
return redisRequest(cli, apiName, params, client.debug)
}
func (client *SQcloudClient) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
cli, err := client.getDefaultClient()
if err != nil {
return nil, err
}
return dcdbRequest(cli, apiName, params, client.debug)
}
func (client *SQcloudClient) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
cli, err := client.getDefaultClient()
if err != nil {
+20
View File
@@ -912,3 +912,23 @@ func (self *SMySQLInstance) GetTags() (map[string]string, error) {
func (self *SMySQLInstance) SetTags(tags map[string]string, replace bool) error {
return self.region.SetResourceTags("cdb", "instanceId", []string{self.InstanceId}, tags, replace)
}
func (self *SRegion) GetIMySQLs() ([]cloudprovider.ICloudDBInstance, error) {
ret := []cloudprovider.ICloudDBInstance{}
mysql := []SMySQLInstance{}
for {
part, total, err := self.ListMySQLInstances([]string{}, len(mysql), 50)
if err != nil {
return nil, errors.Wrapf(err, "ListMySQLInstances")
}
mysql = append(mysql, part...)
if len(mysql) >= total {
break
}
}
for i := range mysql {
mysql[i].region = self
ret = append(ret, &mysql[i])
}
return ret, nil
}
+389
View File
@@ -0,0 +1,389 @@
// 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 qcloud
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type STDSQL struct {
multicloud.SVirtualResourceBase
multicloud.SBillingBase
multicloud.QcloudTags
region *SRegion
AppId int `json:"AppId"`
AutoRenewFlag int `json:"AutoRenewFlag"`
CPU int `json:"Cpu"`
CreateTime time.Time `json:"CreateTime"`
DbEngine string `json:"DbEngine"`
DbVersion string `json:"DbVersion"`
DcnDstNum int `json:"DcnDstNum"`
DcnFlag int `json:"DcnFlag"`
DcnStatus int `json:"DcnStatus"`
ExclusterId string `json:"ExclusterId"`
Id int `json:"Id"`
InstanceId string `json:"InstanceId"`
InstanceName string `json:"InstanceName"`
InstanceType int `json:"InstanceType"`
Ipv6Flag int `json:"Ipv6Flag"`
IsAuditSupported int `json:"IsAuditSupported"`
IsTmp int `json:"IsTmp"`
IsolatedTimestamp string `json:"IsolatedTimestamp"`
Locker int `json:"Locker"`
Memory int `json:"Memory"`
NodeCount int `json:"NodeCount"`
Paymode string `json:"Paymode"`
PeriodEndTime string `json:"PeriodEndTime"`
Pid int `json:"Pid"`
ProjectId int `json:"ProjectId"`
Region string `json:"Region"`
ShardCount int `json:"ShardCount"`
ShardDetail []ShardDetail `json:"ShardDetail"`
Status int `json:"Status"`
StatusDesc string `json:"StatusDesc"`
Storage int `json:"Storage"`
SubnetId int `json:"SubnetId"`
Uin string `json:"Uin"`
UniqueSubnetId string `json:"UniqueSubnetId"`
UniqueVpcId string `json:"UniqueVpcId"`
UpdateTime string `json:"UpdateTime"`
Vip string `json:"Vip"`
Vipv6 string `json:"Vipv6"`
VpcId int `json:"VpcId"`
Vport int `json:"Vport"`
WanDomain string `json:"WanDomain"`
WanPort int `json:"WanPort"`
WanPortIpv6 int `json:"WanPortIpv6"`
WanStatus int `json:"WanStatus"`
WanStatusIpv6 int `json:"WanStatusIpv6"`
WanVip string `json:"WanVip"`
WanVipv6 string `json:"WanVipv6"`
Zone string `json:"Zone"`
}
type ShardDetail struct {
CPU int `json:"Cpu"`
Createtime string `json:"Createtime"`
Memory int `json:"Memory"`
NodeCount int `json:"NodeCount"`
Pid int `json:"Pid"`
ShardId int `json:"ShardId"`
ShardInstanceId string `json:"ShardInstanceId"`
ShardSerialId string `json:"ShardSerialId"`
Status int `json:"Status"`
Storage int `json:"Storage"`
}
func (self *STDSQL) GetName() string {
return self.InstanceName
}
func (self *STDSQL) GetId() string {
return self.InstanceId
}
func (self *STDSQL) GetGlobalId() string {
return self.InstanceId
}
// 0 创建中,1 流程处理中, 2 运行中,3 实例未初始化,-1 实例已隔离,-2 实例已删除,4 实例初始化中,5 实例删除中,6 实例重启中,7 数据迁移中
func (self *STDSQL) GetStatus() string {
switch self.Status {
case 0, 1, 3, 4:
return api.DBINSTANCE_DEPLOYING
case 2:
return api.DBINSTANCE_RUNNING
case -1, -2, 5:
return api.DBINSTANCE_DELETING
case 6:
return api.DBINSTANCE_REBOOTING
case 7:
return api.DBINSTANCE_MIGRATING
default:
return fmt.Sprintf("%d", self.Status)
}
}
func (self *STDSQL) GetPort() int {
return self.Vport
}
func (self *STDSQL) GetVmemSizeMB() int {
return self.Memory * 1024
}
func (self *STDSQL) GetDiskSizeGB() int {
return self.Storage
}
func (self *STDSQL) GetVcpuCount() int {
return self.CPU
}
func (self *STDSQL) GetCreatedAt() time.Time {
return self.CreateTime.Add(time.Hour * -8)
}
func (self *STDSQL) GetBillingType() string {
return self.Paymode
}
func (self *STDSQL) GetProjectId() string {
return fmt.Sprintf("%d", self.ProjectId)
}
func (self *STDSQL) Refresh() error {
sql, err := self.region.GetTDSQL(self.InstanceId)
if err != nil {
return err
}
return jsonutils.Update(self, sql)
}
func (self *STDSQL) Reboot() error {
return cloudprovider.ErrNotSupported
}
func (self *STDSQL) GetMasterInstanceId() string {
return ""
}
func (self *STDSQL) GetSecurityGroupIds() ([]string, error) {
ret := []string{}
groups, err := self.region.GetTDSQLSecurityGroups(self.InstanceId)
if err != nil {
return ret, err
}
for i := range groups {
ret = append(ret, groups[i].SecurityGroupId)
}
return ret, nil
}
func (self *STDSQL) SetSecurityGroups(ids []string) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) GetEngine() string {
return self.DbEngine
}
func (self *STDSQL) GetEngineVersion() string {
return self.DbVersion
}
func (self *STDSQL) GetInstanceType() string {
return fmt.Sprintf("%dC%dG", self.GetVcpuCount(), self.GetVmemSizeMB()/1024)
}
func (self *STDSQL) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedDBInstanceChangeConfig) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) ClosePublicConnection() error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) OpenPublicConnection() error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) CreateAccount(opts *cloudprovider.SDBInstanceAccountCreateConfig) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) CreateDatabase(opts *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) CreateIBackup(opts *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) {
return "", cloudprovider.ErrNotImplemented
}
func (self *STDSQL) GetMaintainTime() string {
return ""
}
func (self *STDSQL) GetStorageType() string {
return ""
}
func (self *STDSQL) GetIVpcId() string {
return self.UniqueVpcId
}
func (self *STDSQL) Delete() error {
if self.GetBillingType() == billing_api.BILLING_TYPE_PREPAID {
return self.region.DeletePrepaidTDSQL(self.InstanceId)
}
return self.region.DeletePostpaidTDSQL(self.InstanceId)
}
func (self *STDSQL) GetCategory() string {
return api.QCLOUD_DBINSTANCE_CATEGORY_TDSQL
}
func (self *STDSQL) GetConnectionStr() string {
if len(self.WanDomain) > 0 {
return fmt.Sprintf("%s:%d", self.WanDomain, self.WanPort)
}
return ""
}
func (self *STDSQL) GetInternalConnectionStr() string {
if len(self.Vip) > 0 {
return fmt.Sprintf("%s:%d", self.Vip, self.Vport)
}
return ""
}
func (self *STDSQL) GetZone1Id() string {
return self.Zone
}
func (self *STDSQL) GetZone2Id() string {
return ""
}
func (self *STDSQL) GetZone3Id() string {
return ""
}
func (self *STDSQL) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQL) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
ret := []cloudprovider.SDBInstanceNetwork{}
if len(self.Vip) > 0 && len(self.UniqueSubnetId) > 0 {
ret = append(ret, cloudprovider.SDBInstanceNetwork{NetworkId: self.UniqueSubnetId, IP: self.Vip})
}
return ret, nil
}
func (self *SRegion) GetTDSQL(id string) (*STDSQL, error) {
sqls, _, err := self.GetTDSQLs([]string{id}, 1, 0)
if err != nil {
return nil, errors.Wrapf(err, "GetTDSQLs")
}
for i := range sqls {
if sqls[i].InstanceId == id {
sqls[i].region = self
return &sqls[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "id: [%s]", id)
}
func (self *SRegion) GetTDSQLs(ids []string, limit, offset int) ([]STDSQL, int, error) {
if limit < 1 || limit > 100 {
limit = 100
}
params := map[string]string{
"Limit": fmt.Sprintf("%d", limit),
"Offset": fmt.Sprintf("%d", offset),
}
for idx, id := range ids {
params[fmt.Sprintf("InstanceIds.%d", idx)] = id
}
resp, err := self.dcdbRequest("DescribeDCDBInstances", params)
if err != nil {
return nil, 0, errors.Wrapf(err, "DescribeDCDBInstances")
}
ret := []STDSQL{}
err = resp.Unmarshal(&ret, "Instances")
if err != nil {
return nil, 0, errors.Wrapf(err, "resp.Unmarshal")
}
totalCount, _ := resp.Float("TotalCount")
return ret, int(totalCount), nil
}
func (self *SRegion) GetITDSQLs() ([]cloudprovider.ICloudDBInstance, error) {
ret := []cloudprovider.ICloudDBInstance{}
for {
part, total, err := self.GetTDSQLs(nil, 100, len(ret))
if err != nil {
return nil, errors.Wrapf(err, "GetTDSQLs")
}
for i := range part {
part[i].region = self
ret = append(ret, &part[i])
}
if len(ret) >= total {
break
}
}
return ret, nil
}
type STDSQLSecurityGroup struct {
CreateTime string `json:"CreateTime"`
ProjectID int `json:"ProjectId"`
SecurityGroupId string `json:"SecurityGroupId"`
SecurityGroupName string `json:"SecurityGroupName"`
SecurityGroupRemark string `json:"SecurityGroupRemark"`
}
func (self *SRegion) GetTDSQLSecurityGroups(id string) ([]STDSQLSecurityGroup, error) {
params := map[string]string{
"Product": "dcdb",
"InstanceId": id,
}
resp, err := self.dcdbRequest("DescribeDBSecurityGroups", params)
if err != nil {
return nil, errors.Wrapf(err, "DescribeDBSecurityGroups")
}
ret := []STDSQLSecurityGroup{}
err = resp.Unmarshal(&ret, "Groups")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal")
}
return ret, nil
}
func (self *STDSQL) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) {
return []cloudprovider.ICloudDBInstanceBackup{}, nil
}
func (self *STDSQL) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) DeletePostpaidTDSQL(id string) error {
params := map[string]string{
"InstanceId": id,
}
_, err := self.dcdbRequest("DestroyHourDCDBInstance", params)
return errors.Wrapf(err, "DestroyHourDCDBInstance")
}
func (self *SRegion) DeletePrepaidTDSQL(id string) error {
params := map[string]string{
"InstanceId": id,
}
_, err := self.dcdbRequest("DestroyDCDBInstance", params)
return errors.Wrapf(err, "DestroyDCDBInstance")
}
+5
View File
@@ -686,6 +686,11 @@ func (self *SRegion) redisRequest(apiName string, params map[string]string) (jso
return self.client.redisRequest(apiName, params)
}
func (self *SRegion) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
params["Region"] = self.Region
return self.client.dcdbRequest(apiName, params)
}
func (self *SRegion) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
params["Region"] = self.Region
return self.client.mongodbRequest(apiName, params)
+36
View File
@@ -0,0 +1,36 @@
// 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 shell
import (
"yunion.io/x/onecloud/pkg/multicloud/qcloud"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type TDSQLListOptions struct {
Ids []string
Limit int
Offset int
}
shellutils.R(&TDSQLListOptions{}, "tdsql-list", "List tdsql", func(cli *qcloud.SRegion, args *TDSQLListOptions) error {
tdsqls, _, err := cli.GetTDSQLs(args.Ids, args.Limit, args.Offset)
if err != nil {
return err
}
printList(tdsqls, 0, 0, 0, []string{})
return nil
})
}
+99
View File
@@ -0,0 +1,99 @@
// 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 qcloud
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type STDSQLAccount struct {
multicloud.SDBInstanceAccountBase
multicloud.QcloudTags
rds *STDSQL
UserName string
Host string
Description string
CreateTime string
UpdateTime string
ReadOnly int
DelayThresh int
}
func (self *STDSQLAccount) GetName() string {
return self.UserName
}
func (self *STDSQLAccount) GetHost() string {
return self.Host
}
func (self *STDSQLAccount) Delete() error {
return self.rds.region.DeleteTDSQLAccount(self.rds.InstanceId, self.UserName, self.Host)
}
func (self *STDSQLAccount) GrantPrivilege(database, privilege string) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQLAccount) RevokePrivilege(database string) error {
return cloudprovider.ErrNotImplemented
}
func (self *STDSQLAccount) GetIDBInstanceAccountPrivileges() ([]cloudprovider.ICloudDBInstanceAccountPrivilege, error) {
return nil, cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetTDSQLAccount(id string) ([]STDSQLAccount, error) {
params := map[string]string{
"InstanceId": id,
}
resp, err := self.dcdbRequest("DescribeAccounts", params)
if err != nil {
return nil, errors.Wrapf(err, "DescribeAccounts")
}
ret := []STDSQLAccount{}
err = resp.Unmarshal(&ret, "Users")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal")
}
return ret, nil
}
func (self *STDSQL) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
accounts, err := self.region.GetTDSQLAccount(self.InstanceId)
if err != nil {
return nil, errors.Wrapf(err, "GetTDSQLAccount")
}
ret := []cloudprovider.ICloudDBInstanceAccount{}
for i := range accounts {
accounts[i].rds = self
ret = append(ret, &accounts[i])
}
return ret, nil
}
func (self *SRegion) DeleteTDSQLAccount(id, name, host string) error {
params := map[string]string{
"InstanceId": id,
"UserName": name,
"Host": host,
}
_, err := self.dcdbRequest("DeleteAccount", params)
return errors.Wrapf(err, "DeleteAccount")
}
+84
View File
@@ -0,0 +1,84 @@
// 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 qcloud
import (
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type STDSQLDatabase struct {
rds *STDSQL
multicloud.SResourceBase
multicloud.QcloudTags
DbName string
}
func (self *STDSQLDatabase) GetStatus() string {
return api.DBINSTANCE_DATABASE_RUNNING
}
func (self *STDSQLDatabase) GetId() string {
return self.DbName
}
func (self *STDSQLDatabase) GetName() string {
return self.DbName
}
func (self *STDSQLDatabase) GetGlobalId() string {
return self.DbName
}
func (self *STDSQLDatabase) GetCharacterSet() string {
return ""
}
func (self *STDSQLDatabase) Delete() error {
return cloudprovider.ErrNotSupported
}
func (self *SRegion) GetTDSQLDatabases(id string) ([]STDSQLDatabase, error) {
params := map[string]string{
"InstanceId": id,
}
resp, err := self.dcdbRequest("DescribeDatabases", params)
if err != nil {
return nil, errors.Wrapf(err, "DescribeDatabases")
}
ret := []STDSQLDatabase{}
err = resp.Unmarshal(&ret, "Databases")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal")
}
return ret, nil
}
func (self *STDSQL) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
dbs, err := self.region.GetTDSQLDatabases(self.InstanceId)
if err != nil {
return nil, errors.Wrapf(err, "GetTDSQLDatabases")
}
ret := []cloudprovider.ICloudDBInstanceDatabase{}
for i := range dbs {
dbs[i].rds = self
ret = append(ret, &dbs[i])
}
return ret, nil
}