From b283078c5fdfbdea741184736849859dfa3c0413 Mon Sep 17 00:00:00 2001 From: duanliguo Date: Fri, 28 May 2021 14:45:10 +0800 Subject: [PATCH] Add some features in bbc, bec and ddc --- doc/BBC.md | 21 +++ doc/DDCv2.md | 104 +++++++++++ init.go | 1 + services/bbc/client.go | 19 +++ services/bbc/client_test.go | 11 ++ services/bbc/instance.go | 31 ++++ services/bbc/model.go | 6 + services/bec/api/model.go | 60 ++++++- services/bec/api/util.go | 6 + services/bec/init_test.go | 68 ++++++++ services/bec/node.go | 44 +++++ services/bec/node_test.go | 14 ++ services/bec/scenario_test.go | 304 +++++++++++++++++++++++++++++++++ services/bec/service_test.go | 64 ------- services/ddc/v2/client.go | 19 ++- services/ddc/v2/client_test.go | 78 ++++++++- services/ddc/v2/ddc.go | 91 ++++++++++ services/ddc/v2/ddcrds.go | 44 +++++ services/ddc/v2/model.go | 41 +++++ 19 files changed, 959 insertions(+), 67 deletions(-) create mode 100644 services/bec/init_test.go create mode 100644 services/bec/node.go create mode 100644 services/bec/node_test.go create mode 100644 services/bec/scenario_test.go diff --git a/doc/BBC.md b/doc/BBC.md index 4e902aa2..ead500b3 100644 --- a/doc/BBC.md +++ b/doc/BBC.md @@ -319,6 +319,27 @@ if err := bbcClient.ModifyInstanceDesc(instanceId, modifyInstanceDescArgs); err } ``` +### 实例变更子网 + +如下代码可以变更实例的子网 +```go +instanceChangeVpcArgs := &api.InstanceChangeSubnetArgs{ + InstanceId: instanceId, + SubnetId: subnetId, + Reboot: false, +} +err := bbcClient.InstanceChangeSubnet(args) +if err != nil { + fmt.Println("change instance subnet failed:", err) +} else { + fmt.Println("change instance subnet success") +} +``` + +> **提示:** +> - 变更子网后默认自动重启,用户选择是否执行该操作。 +> - 变更子网的范围目前仅支持在同AZ下变更子网,不支持跨AZ或跨VPC变更子网。 + ### 修改实例VPC 使用以下代码可以修改指定BBC实例所在的VPC: ```go diff --git a/doc/DDCv2.md b/doc/DDCv2.md index b1ba9c43..de561ff8 100644 --- a/doc/DDCv2.md +++ b/doc/DDCv2.md @@ -1358,6 +1358,110 @@ for _, database := range result.Databases { } ``` +## 获取数据表大小 + +使用以下代码可以获取指定库下满足条件的数据表的大小。 +```go +// import ddcrds "github.com/baidubce/bce-sdk-go/services/ddc/v2" + +args := &ddcrds.GetTableAmountArgs{ + // 实例ID + InstanceId: instanceId, + // 指定数据库名称 + DbName: dbName, + // 模糊搜索值, 可选 + Pattern: search, +} +result, err := DDCRDS_CLIENT.GetTableAmount(args) +if err != nil { + fmt.Printf("get table amount error: %+v\n", err) + return +} +fmt.Printf("get table amount success.\n") +fmt.Println("ddc return amount ", result.ReturnAmount) +fmt.Println("ddc total amount ", result.TotalAmount) +for k, v := range result.Tables { + fmt.Println("ddc table ", k, " size: ", v) +} +``` + +## 获取数据库占用磁盘空间 + +使用以下代码可以获取指定实例下的数据库占用的磁盘空间大小以及剩余磁盘空间。 +```go +// import ddcrds "github.com/baidubce/bce-sdk-go/services/ddc/v2" +// dbName可选,不指定数据库时传入空字符串即可 +result, err := DDCRDS_CLIENT.GetDatabaseDiskUsage(instanceId, dbName) +if err != nil { + fmt.Printf("get database disk usage error: %+v\n", err) + return +} +fmt.Printf("get database disk usage success.\n") +fmt.Println("ddc rest disk size(byte) ", result.RestDisk) +fmt.Println("ddc used disk size(byte) ", result.UsedDisk) +for k, v := range result.Databases { + fmt.Println("ddc database ", k, " size(byte): ", v) +} +``` + +## 获取实例数据可恢复时间 + +使用以下代码可以获取实例数据所有的可恢复时间段。 +```go +// import ddcrds "github.com/baidubce/bce-sdk-go/services/ddc/v2" +result, err := DDCRDS_CLIENT.GetRecoverableDateTime(instanceId) +if err != nil { + fmt.Printf("get recoverable datetimes error: %+v\n", err) + return +} +fmt.Printf("get recoverable datetimes success.\n") +for _, e := range result.RecoverableDateTimes { + fmt.Println("recover startTime: ", e.StartDateTime, " endTime: ", e.EndDateTime) +} +``` + +## 恢复数据库 + +使用以下代码可以按时间点恢复指定数据库或数据表到原有实例。 +```go +// import ddcrds "github.com/baidubce/bce-sdk-go/services/ddc/v2" + +args := &ddcrds.RecoverInstanceArgs{ + // 可恢复时间点,从GetRecoverableDateTime()获取 + Datetime: "2021-05-25T03:28:30Z", + // RestoreMode 为database或table + RecoverData: []ddcrds.RecoverData{ + { + // 数据库名称 + DbName: dbName, + // 新库名 + NewDbName: newDbName, + RestoreMode: "database", + }, + { + DbName: dbName, + NewDbName: newDbName, + RestoreMode: "table", + // RestoreMode为table时RecoverTables为必选 + RecoverTables: []ddcrds.RecoverTable{ + { + // 表名 + TableName: tableName, + // 新表名 + NewTableName: newTableName, + }, + }, + }, + }, +} +err := DDCRDS_CLIENT.RecoverToSourceInstanceByDatetime(instanceId, args) +if err != nil { + fmt.Printf("recover instance database error: %+v\n", err) + return +} +fmt.Printf("recover instance database success.\n") +``` + ## 删除特定数据库 使用以下代码可以删除特定数据库信息。 diff --git a/init.go b/init.go index 02a2ead8..ea0e61bc 100644 --- a/init.go +++ b/init.go @@ -26,6 +26,7 @@ import ( _ "github.com/baidubce/bce-sdk-go/services/appblb" _ "github.com/baidubce/bce-sdk-go/services/bbc" _ "github.com/baidubce/bce-sdk-go/services/bcc" + _ "github.com/baidubce/bce-sdk-go/services/bec" _ "github.com/baidubce/bce-sdk-go/services/bie" _ "github.com/baidubce/bce-sdk-go/services/blb" _ "github.com/baidubce/bce-sdk-go/services/bos" diff --git a/services/bbc/client.go b/services/bbc/client.go index 1c04edf0..2832a4bd 100644 --- a/services/bbc/client.go +++ b/services/bbc/client.go @@ -386,6 +386,25 @@ func (c *Client) ModifyInstancePassword(instanceId string, args *ModifyInstanceP return ModifyInstancePassword(c, instanceId, body) } +// InstanceChangeSubnet - change an instance's subnet +// +// PARAMS: +// - args: the arguments to change an instance's subnet +// RETURNS: +// - error: nil if success otherwise the specific error +func (c *Client) InstanceChangeSubnet(args *InstanceChangeSubnetArgs) error { + jsonBytes, jsonErr := json.Marshal(args) + if jsonErr != nil { + return jsonErr + } + body, err := bce.NewBodyFromBytes(jsonBytes) + if err != nil { + return err + } + + return InstanceChangeSubnet(c, body) +} + // InstanceChangeVpc - change an instance's vpc // // PARAMS: diff --git a/services/bbc/client_test.go b/services/bbc/client_test.go index fbb959dd..51d9c897 100644 --- a/services/bbc/client_test.go +++ b/services/bbc/client_test.go @@ -713,6 +713,17 @@ func TestListRecycledInstances(t *testing.T) { } } +func TestInstanceChangeSubnet(t *testing.T) { + args := &InstanceChangeSubnetArgs{ + InstanceId: "i-DFlNGqLf", + SubnetId: "sbn-z1y9tcedqnh6", + Reboot: true, + } + + err := BBC_CLIENT.InstanceChangeSubnet(args) + ExpectEqual(t.Errorf, err, nil) +} + func TestInstanceChangeVpc(t *testing.T) { args := &InstanceChangeVpcArgs{ InstanceId: "i-xxxxx", diff --git a/services/bbc/instance.go b/services/bbc/instance.go index 8cd3c012..d01df355 100644 --- a/services/bbc/instance.go +++ b/services/bbc/instance.go @@ -942,6 +942,33 @@ func BatchDeleteAutoRenewRules(cli bce.Client, reqBody *bce.Body) error { return nil } +// InstanceChangeVpc - change the subnet to which the instance belongs +// +// PARAMS: +// - cli: the client agent which can perform sending request +// - reqBody: request body to change subnet of instance +// RETURNS: +// - error: nil if success otherwise the specific error +func InstanceChangeSubnet(cli bce.Client, reqBody *bce.Body) error { + // Build the request + req := &bce.BceRequest{} + req.SetUri(getChangeSubnetUri()) + req.SetMethod(http.PUT) + req.SetBody(reqBody) + + // Send request and get response + resp := &bce.BceResponse{} + if err := cli.SendRequest(req, resp); err != nil { + return err + } + if resp.IsFail() { + return resp.ServiceError() + } + + defer func() { resp.Body().Close() }() + return nil +} + // InstanceChangeVpc - change the vpc to which the instance belongs // // PARAMS: @@ -1041,6 +1068,10 @@ func getRebuildBatchInstanceUri() string { return URI_PREFIX_V1 + REQUEST_INSTANCE_URI + REQUEST_BATCH_REBUILD_INSTANCE_URI } +func getChangeSubnetUri() string { + return URI_PREFIX_V1 + "/subnet" + "/changeSubnet" +} + func getChangeVpcUri() string { return URI_PREFIX_V1 + REQUEST_VPC_URI + "/changeVpc" } diff --git a/services/bbc/model.go b/services/bbc/model.go index 08950a8c..1dc795fc 100644 --- a/services/bbc/model.go +++ b/services/bbc/model.go @@ -219,6 +219,12 @@ type ModifyInstanceNameArgs struct { Name string `json:"name"` } +type InstanceChangeSubnetArgs struct { + InstanceId string `json:"instanceId"` + SubnetId string `json:"subnetId"` + Reboot bool `json:"reboot"` +} + type InstanceChangeVpcArgs struct { InstanceId string `json:"instanceId"` SubnetId string `json:"subnetId"` diff --git a/services/bec/api/model.go b/services/bec/api/model.go index c6401ae0..d92d82aa 100644 --- a/services/bec/api/model.go +++ b/services/bec/api/model.go @@ -448,10 +448,44 @@ type ImageDetail struct { SharedToUserNum int `json:"sharedToUserNum"` FpgaType string `json:"fpgaType"` } + +type VmInstanceIdVo struct { + VmId string `json:"vmId"` + VmName string `json:"vmName"` + Region string `json:"region"` + City string `json:"city"` + ServiceProvider string `json:"serviceProvider"` +} + +type ResourceStatus string + +const ( + ResourceStatusStarting = "STARTING" + ResourceStatusRunning = "RUNNING" + ResourceStatusException = "EXCEPTION" + ResourceStatusFailed = "FAILED" + ResourceStatusUnknown = "UNKNOWN" + ResourceStatusTerminated = "TERMINATED" + ResourceStatusWaiting = "WAITING" + ResourceStatusStop = "STOP" + ResourceStatusStopping = "STOPPING" + ResourceStatusTerminating = "TERMINATING" + ResourceStatusNormal = "NORMAL" + // part of status for vm instant + ResourceStatusCreating = "CREATING" + ResourceStatusStopped = "STOPPED" + ResourceStatusRestarting = "RESTARTING" + ResourceStatusReinstalling = "REINSTALLING" + ResourceStatusImaging = "IMAGING" + // part of status for lb + ResourceStatusPending = "PENDING" + ResourceStatusBinding = "BINDING" +) + type VmServiceBriefVo struct { ServiceId string `json:"serviceId"` ServiceName string `json:"serviceName"` - Status string `json:"status"` + Status ResourceStatus `json:"status"` TotalCpu int `json:"totalCpu"` TotalMem int `json:"totalMem"` TotalDisk int `json:"totalDisk"` @@ -463,6 +497,7 @@ type VmServiceBriefVo struct { OsImage ImageDetail `json:"osImage"` CreateTime string `json:"createTime"` TotalGpu int `json:"totalGpu"` + Instances []VmInstanceIdVo `json:"instances"` } type CreateVmServiceResult struct { @@ -545,6 +580,7 @@ type GetVmServiceDetailArgs struct { } type VmServiceDetailsVo struct { + VmServiceBriefVo Bandwidth string `json:"bandwidth"` TotalBandwidth string `json:"totalBandwidth"` DataVolumeList []VolumeConfig `json:"dataVolumeList"` @@ -1077,3 +1113,25 @@ type VmPrivateIpResult struct { Result IpamResultVo `json:"result"` Success bool `json:"success"` } + +type ServiceProviderInfo struct { + ServiceProvider ServiceProvider `json:"serviceProvider"` + Name string `json:"name"` + Capability []string `json:"capability"` +} + +type CityInfo struct { + City string `json:"city"` + Name string `json:"name"` + ServiceProviderList []ServiceProviderInfo `json:"serviceProviderList"` +} + +type RegionInfo struct { + Region Region `json:"region"` + Name string `json:"name"` + CityList []CityInfo `json:"cityList"` +} + +type GetBecAvailableNodeInfoVoResult struct { + RegionList []RegionInfo `json:"regionList"` +} diff --git a/services/bec/api/util.go b/services/bec/api/util.go index db82af64..ea43f324 100644 --- a/services/bec/api/util.go +++ b/services/bec/api/util.go @@ -33,6 +33,8 @@ const ( REQUEST_LOADBALANCER_URL = URI_PREFIX + "/blb" REQUEST_VM_INSTANCE_URL = URI_PREFIX + "/vm/instance" + + REQUEST_NODE_URL = URI_PREFIX + "/node" ) /* @@ -116,3 +118,7 @@ func GetVmServiceMetricsURI(serviceId, metricsType string) string { func GetVmInstanceURI() string { return REQUEST_VM_INSTANCE_URL } + +func GetNodeInfoURI() string { + return REQUEST_NODE_URL +} diff --git a/services/bec/init_test.go b/services/bec/init_test.go new file mode 100644 index 00000000..ccf76b74 --- /dev/null +++ b/services/bec/init_test.go @@ -0,0 +1,68 @@ +package bec + +import ( + "encoding/json" + "fmt" + "github.com/baidubce/bce-sdk-go/util/log" + "os" + "path/filepath" + "reflect" + "runtime" +) + +var CLIENT *Client + +type Conf struct { + AK string + SK string + Endpoint string +} + +func init() { + fmt.Printf("init \n") + _, f, _, _ := runtime.Caller(0) + for i := 0; i < 6; i++ { + f = filepath.Dir(f) + } + conf := filepath.Join(f, "config.json") + fp, err := os.Open(conf) + if err != nil { + log.Fatal("config json file of ak/sk not given:", conf) + os.Exit(1) + } + decoder := json.NewDecoder(fp) + confObj := &Conf{} + decoder.Decode(confObj) + + CLIENT, _ = NewClient(confObj.AK, confObj.SK, confObj.Endpoint) + + log.SetLogHandler(log.STDERR) + log.SetLogLevel(log.DEBUG) +} + +// ExpectEqual is the helper function for test each case +func ExpectEqual(alert func(format string, args ...interface{}), + expected interface{}, actual interface{}) bool { + expectedValue, actualValue := reflect.ValueOf(expected), reflect.ValueOf(actual) + equal := false + switch { + case expected == nil && actual == nil: + return true + case expected != nil && actual == nil: + equal = expectedValue.IsNil() + case expected == nil && actual != nil: + equal = actualValue.IsNil() + default: + if actualType := reflect.TypeOf(actual); actualType != nil { + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + equal = reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + } + } + if !equal { + _, file, line, _ := runtime.Caller(1) + alert("%s:%d: missmatch, expect %v but %v", file, line, expected, actual) + return false + } + return true +} diff --git a/services/bec/node.go b/services/bec/node.go new file mode 100644 index 00000000..0441d6c0 --- /dev/null +++ b/services/bec/node.go @@ -0,0 +1,44 @@ +/* + * Copyright 2021 Baidu, Inc. + * + * 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. + */ + +// client.go - define the client for BEC service + +// Package bec defines the BEC services of BCE. The supported APIs are all defined in sub-package + +package bec + +import ( + "fmt" + + "github.com/baidubce/bce-sdk-go/services/bec/api" +) + +// GetBecAvailableNodeInfoVo - get available node +// +// PARAMS: +// - args: the type +// RETURNS: +// - *api.GetBecAvailableNodeInfoVoResult: get available node +// - error: nil if ok otherwise the specific error +func (c *Client) GetBecAvailableNodeInfoVo(getType string) (*api.GetBecAvailableNodeInfoVoResult, error) { + if getType == "" { + return nil, fmt.Errorf("please set argments") + } + + result := &api.GetBecAvailableNodeInfoVoResult{} + req := &api.GetHttpReq{Url: api.GetNodeInfoURI() + "/type/" + getType, Result: result} + err := api.Get(c, req) + + return result, err +} diff --git a/services/bec/node_test.go b/services/bec/node_test.go new file mode 100644 index 00000000..5ac85c3a --- /dev/null +++ b/services/bec/node_test.go @@ -0,0 +1,14 @@ +package bec + +import ( + "testing" +) + +////////////////////////////////////////////// +// node API +////////////////////////////////////////////// +func TestGetBecAvailableNodeInfoVo(t *testing.T) { + res, err := CLIENT.GetBecAvailableNodeInfoVo("vm") + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) +} diff --git a/services/bec/scenario_test.go b/services/bec/scenario_test.go new file mode 100644 index 00000000..77082c74 --- /dev/null +++ b/services/bec/scenario_test.go @@ -0,0 +1,304 @@ +package bec + +import ( + "fmt" + "testing" + "time" + + "github.com/baidubce/bce-sdk-go/services/bec/api" +) + +////////////////////////////////////////////// +// vmService API +////////////////////////////////////////////// +// 定义参数 城市、运营商中文名、镜像名称、服务名称 +func TestVmServiceSce(t *testing.T) { + cityName := "杭州" + serviceProviderName := "移动" + image := "fyy-test" + name := "bec-tst" + //创建无实例虚机服务,并获取服务ID + getReq := &api.CreateVmServiceArgs{ServiceName: name} + resVm, err := CLIENT.CreateVmService(getReq) + serviceId := resVm.Details.ServiceId + // 根据城市与运营商中文名称获取城市与运营商编号还有REGION编号 + var city string + var provider api.ServiceProvider + var region api.Region + resNode, err := CLIENT.GetBecAvailableNodeInfoVo("vm") + if err == nil { + for _, v := range resNode.RegionList { + for _, cityInfo := range v.CityList { + if cityInfo.Name == cityName { + for _, providerInfo := range cityInfo.ServiceProviderList { + if providerInfo.Name == serviceProviderName { + city = cityInfo.City + provider = providerInfo.ServiceProvider + region = v.Region + } + } + } + } + } + } + + // 根据镜像名称获取镜像ID + imageReq := &api.ListVmImageArgs{KeywordType: "name", Keyword: image} + res, err := CLIENT.ListVmImage(imageReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) + return + } + if len(res.Result) == 0 { + err = fmt.Errorf("no such image name %s", image) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", "no such image name") + return + } + imageId := res.Result[0].ImageId + + // 创建虚机,并获取虚机ID + createVmiReq := &api.CreateVmServiceArgs{ImageId: imageId, Cpu: 1, Memory: 2, NeedPublicIp: true, Bandwidth: 50, + DeployInstances: &[]api.DeploymentInstance{api.DeploymentInstance{City: city, Region: region, + ServiceProvider: provider, Replicas: 1}}, AdminPass: "xxxxxx111xxB@", ImageType: api.ImageTypeBec} + createVmiRes, err := CLIENT.CreateVmServiceInstance(serviceId, createVmiReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) + return + } + + vmiId := createVmiRes.Details.Instances[0].VmId + + // 每隔10秒获取一次虚机创建状态,并打印虚机状态,直到虚机进入运行中 + var stateRes *api.VmInstanceDetailsVo + ticker := time.NewTicker(10 * time.Second) + errTime := 3 + in := true + for in { + select { + case <-ticker.C: + //stateRes, err := CLIENT.GetVmServiceDetail(vmId) + stateRes, err = CLIENT.GetVirtualMachine(vmiId) + if err != nil && errTime > 0 { + errTime-- + continue + } + if errTime == 0 { + in = false + break + } + fmt.Println("vm status is ", stateRes.Status) + if stateRes != nil && stateRes.Status == api.ResourceStatusRunning { + in = false + break + } + } + } + + // 打印指定虚机的内外网IP、虚机名称、ID + vmStatusRes, err := CLIENT.GetVirtualMachine(vmiId) + fmt.Printf("ip:%s, internalIp:%s, vmName:%s, vmId: %s\n", vmStatusRes.PublicIp, + vmStatusRes.InternalIp, vmStatusRes.VmName, vmStatusRes.VmId) + + // 添加虚机辅助IP + crateVmPrivateIpReq := &api.CreateVmPrivateIpForm{SecondaryPrivateIpAddressCount: 1} + _, err = CLIENT.CreateVmPrivateIp(vmiId, crateVmPrivateIpReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + return + } + + // 删除虚机辅助IP + GetVmRes, err := CLIENT.GetVirtualMachine(vmiId) + privateIp := GetVmRes.PrivateIps + deleteVmPrivateIpReq := &api.DeleteVmPrivateIpForm{PrivateIps: privateIp} + delRes, err := CLIENT.DeleteVmPrivateIp(vmiId, deleteVmPrivateIpReq) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", delRes) + + // 重置虚机密码 + updateVmReq := &api.UpdateVmDeploymentArgs{Type: "password", KeyConfig: &api.KeyConfig{Type: "password", AdminPass: "12345asdf@"}} + updateVmRes, err := CLIENT.UpdateVmDeployment(vmiId, updateVmReq) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", updateVmRes) + + // 每隔10秒获取一次虚机创建状态,并打印虚机状态,直到虚机进入运行中 + ticker = time.NewTicker(10 * time.Second) + errTime = 3 + in = true + for in { + select { + case <-ticker.C: + //stateRes, err := CLIENT.GetVmServiceDetail(vmId) + stateRes, err = CLIENT.GetVirtualMachine(vmiId) + if err != nil && errTime > 0 { + errTime-- + continue + } + if errTime == 0 { + in = false + break + } + fmt.Println("vm status is ", stateRes.Status) + if stateRes != nil && stateRes.Status == api.ResourceStatusRunning { + in = false + break + } + } + } + + // 关机虚机,每隔10秒获取一次虚机创建状态,并打印虚机状态,直到虚机关机 + operateVmRes, err := CLIENT.OperateVmDeployment(vmiId, api.VmInstanceBatchOperateStop) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", operateVmRes) + return + } + + var vmStateRes *api.VmInstanceDetailsVo + ticker = time.NewTicker(10 * time.Second) + errTime = 3 + in = true + for in { + select { + case <-ticker.C: + //stateRes, err := CLIENT.GetVmServiceDetail(vmId) + vmStateRes, err = CLIENT.GetVirtualMachine(vmiId) + if err != nil && errTime > 0 { + errTime-- + continue + } + if errTime == 0 { + in = false + break + } + fmt.Println("vm status is ", vmStateRes.Status) + if vmStateRes != nil && vmStateRes.Status == api.ResourceStatusStopped { + in = false + break + } + } + } + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", vmStateRes) + // 删除虚机 + deleteVmRes, err := CLIENT.DeleteVmInstance(vmiId) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", deleteVmRes) + // 删除服务 + deleteVmServiceRes, err := CLIENT.DeleteVmService(serviceId) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", deleteVmServiceRes) +} + +// 创建LocalDNS虚机 +func TestCreateVmServiceWithLocalDns(t *testing.T) { + getReq := &api.CreateVmServiceArgs{ServiceName: "xxxxxxx@-local", ImageId: "im-dikfttnj-3-u-guangzhou", AdminPass: "x123xxx@", + SystemVolume: &api.SystemVolumeConfig{VolumeType: api.DiskTypeNVME}, Cpu: 1, Memory: 2, + DeployInstances: &[]api.DeploymentInstance{api.DeploymentInstance{City: "HANGZHOU", Region: api.RegionEastChina, + ServiceProvider: api.ServiceChinaMobile, Replicas: 1}}, ImageType: api.ImageTypeBec, KeyConfig: &api.KeyConfig{Type: "password", AdminPass: "xxxx123@"}, + DnsConfig: &api.DnsConfig{DnsType: "LOCAL"}} + res, err := CLIENT.CreateVmService(getReq) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) +} + +// 创建密钥对虚机 +func TestCreateVmServiceWithKeypair(t *testing.T) { + getReq := &api.CreateVmServiceArgs{ServiceName: "xxxxxxx@-key", ImageId: "im-dikfttnj-3-u-guangzhou", + SystemVolume: &api.SystemVolumeConfig{VolumeType: api.DiskTypeNVME}, Cpu: 1, Memory: 2, + DeployInstances: &[]api.DeploymentInstance{api.DeploymentInstance{City: "HANGZHOU", Region: api.RegionEastChina, + ServiceProvider: api.ServiceChinaMobile, Replicas: 1}}, ImageType: api.ImageTypeBec, KeyConfig: &api.KeyConfig{Type: "bccKeyPair", BccKeyPairIdList: []string{"k-r4FmM6flink"}}} + res, err := CLIENT.CreateVmService(getReq) + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) +} + +func TestBlbScenario(t *testing.T) { + // 创建负载均衡器 + // 每隔10秒获取一次负载均衡器状态,并打印负载均衡器状态与IP,直到创建完成 + getReq := &api.CreateBlbArgs{BlbName: "xxxx-test", Region: api.RegionEastChina, + City: "HANGZHOU", LbType: "vm", ServiceProvider: api.ServiceChinaMobile} + res, err := CLIENT.CreateBlb(getReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", res) + return + } + + lbId := res.Details.BlbId + var stateRes *api.BlbInstanceVo + ticker := time.NewTicker(10 * time.Second) + errTime := 3 + in := true + for in { + select { + case <-ticker.C: + //stateRes, err := CLIENT.GetVmServiceDetail(vmId) + stateRes, err = CLIENT.GetBlbDetail(lbId) + if err != nil && errTime > 0 { + errTime-- + continue + } + if errTime == 0 { + in = false + break + } + fmt.Printf("lb status is %s, ip is %s", stateRes.Status, stateRes.PublicIp) + if stateRes != nil && stateRes.Status == api.ResourceStatusRunning { + in = false + break + } + } + } + + // 创建TCP监听 + blbMonitorReq := &api.BlbMonitorArgs{LbMode: api.LbModeWrr, FrontendPort: &api.Port{Protocol: api.ProtocolTcp, Port: 80}, + BackendPort: 80, HealthCheck: &api.HealthCheck{HealthCheckString: "", HealthCheckType: "tcp", + HealthyThreshold: 1000, UnhealthyThreshold: 1000, TimeoutInSeconds: 60, IntervalInSeconds: 3}} + blbMonitorRes, err := CLIENT.CreateBlbMonitorPort(lbId, blbMonitorReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", blbMonitorRes) + return + } + + vmiId := "vm-brcvrwvt-1-m-hangzhou-ehkrm" + // 按虚机添加后端服务器 + createBlbBindingReq := &api.CreateBlbBindingArgs{BindingForms: &[]api.BlbBindingForm{ + api.BlbBindingForm{DeploymentId: vmiId, PodWeight: &[]api.Backends{ + api.Backends{Name: vmiId, Weight: 100}}, + }}} + createBlbBindingRes, err := CLIENT.CreateBlbBinding(lbId, createBlbBindingReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", createBlbBindingRes) + return + } + + // 查询负载均衡器状态,打印负载均衡器后端服务器 + getBlbBackendPodRes, err := CLIENT.GetBlbBackendPodList(lbId, 0, 0) + if len(getBlbBackendPodRes.Result) > 0 { + for _, v := range getBlbBackendPodRes.Result { + fmt.Println("backend rs is ", v.PodName) + } + } + + // 从后端服务器删除虚机 + deleteBlbBindPodReq := &api.DeleteBlbBindPodArgs{PodWeightList: &[]api.Backends{ + api.Backends{Name: vmiId}}} + deleteBlbBindPodRes, err := CLIENT.DeleteBlbBindPod(lbId, deleteBlbBindPodReq) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", deleteBlbBindPodRes) + } + + // 删除负载均衡器 + deleteRes, err := CLIENT.DeleteBlb(lbId) + if err != nil { + ExpectEqual(t.Errorf, nil, err) + t.Logf("%+v", deleteRes) + } +} diff --git a/services/bec/service_test.go b/services/bec/service_test.go index 40ff381a..80ea761a 100644 --- a/services/bec/service_test.go +++ b/services/bec/service_test.go @@ -1,75 +1,11 @@ package bec import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "reflect" - "runtime" "testing" "github.com/baidubce/bce-sdk-go/services/bec/api" - "github.com/baidubce/bce-sdk-go/util/log" ) -var CLIENT *Client - -type Conf struct { - AK string - SK string - Endpoint string -} - -func init() { - fmt.Printf("init \n") - _, f, _, _ := runtime.Caller(0) - for i := 0; i < 6; i++ { - f = filepath.Dir(f) - } - conf := filepath.Join(f, "config.json") - fp, err := os.Open(conf) - if err != nil { - log.Fatal("config json file of ak/sk not given:", conf) - os.Exit(1) - } - decoder := json.NewDecoder(fp) - confObj := &Conf{} - decoder.Decode(confObj) - - CLIENT, _ = NewClient(confObj.AK, confObj.SK, confObj.Endpoint) - - log.SetLogHandler(log.STDERR) - log.SetLogLevel(log.DEBUG) -} - -// ExpectEqual is the helper function for test each case -func ExpectEqual(alert func(format string, args ...interface{}), - expected interface{}, actual interface{}) bool { - expectedValue, actualValue := reflect.ValueOf(expected), reflect.ValueOf(actual) - equal := false - switch { - case expected == nil && actual == nil: - return true - case expected != nil && actual == nil: - equal = expectedValue.IsNil() - case expected == nil && actual != nil: - equal = actualValue.IsNil() - default: - if actualType := reflect.TypeOf(actual); actualType != nil { - if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { - equal = reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) - } - } - } - if !equal { - _, file, line, _ := runtime.Caller(1) - alert("%s:%d: missmatch, expect %v but %v", file, line, expected, actual) - return false - } - return true -} - ////////////////////////////////////////////// // service API ////////////////////////////////////////////// diff --git a/services/ddc/v2/client.go b/services/ddc/v2/client.go index 2eb7ed3c..1dc66664 100644 --- a/services/ddc/v2/client.go +++ b/services/ddc/v2/client.go @@ -2,10 +2,11 @@ package ddcrds import ( "fmt" + "strings" + "github.com/baidubce/bce-sdk-go/auth" "github.com/baidubce/bce-sdk-go/bce" "github.com/baidubce/bce-sdk-go/services/rds" - "strings" ) const ( @@ -181,6 +182,22 @@ func getDatabaseUriWithDbName(instanceId string, dbName string) string { return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + REQUEST_DDC_DATABASE_URL + "/" + dbName } +func getQueryDatabaseUriWithDbName(instanceId string, dbName string) string { + return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + REQUEST_DDC_DATABASE_URL + "/" + dbName + "/amount" +} + +func getDatabaseDiskUsageUriWithInstanceId(instanceId string) string { + return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + REQUEST_DDC_DATABASE_URL + "/usage" +} + +func getDatabaseRecoverTimeUriWithInstanceId(instanceId string) string { + return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + REQUEST_DDC_DATABASE_URL + "/recoverableDateTimes" +} + +func getRecoverInstanceDatabaseUriWithInstanceId(instanceId string) string { + return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + "/recoveryToSourceInstanceByDatetime" +} + // Account URL func getAccountUriWithInstanceId(instanceId string) string { return URI_PREFIX + REQUEST_DDC_INSTANCE_URL + "/" + instanceId + REQUEST_DDC_ACCOUNT_URL diff --git a/services/ddc/v2/client_test.go b/services/ddc/v2/client_test.go index 0a8decd3..8a8188ad 100644 --- a/services/ddc/v2/client_test.go +++ b/services/ddc/v2/client_test.go @@ -37,7 +37,7 @@ const ( POOL = "xdb_005a2d79-a4f4-4bfb-8284-0ffe9ddaa307_pool" PNETIP = "100.88.65.121" DEPLOY_ID = "ab89d829-9068-d88e-75bc-64bb6367d036" - DDC_INSTANCE_ID = "ddc-mw3by2yl" + DDC_INSTANCE_ID = "ddc-m8rwmbnn" RDS_INSTANCE_ID = "rds-OtTkC1OD" ETAG = "v0" ) @@ -599,6 +599,82 @@ func TestClient_ListDatabase(t *testing.T) { ExpectEqual(t.Errorf, RDSNotSupportError(), err) } +func TestClient_GetTableAmount(t *testing.T) { + args := &GetTableAmountArgs{ + InstanceId: instanceId, + DbName: "test1", + Pattern: "0", + } + result, err := DDCRDS_CLIENT.GetTableAmount(args) + if err != nil { + fmt.Printf("get table amount error: %+v\n", err) + return + } + fmt.Printf("get table amount success.\n") + fmt.Println("ddc return amount ", result.ReturnAmount) + fmt.Println("ddc total amount ", result.TotalAmount) + for k, v := range result.Tables { + fmt.Println("ddc table ", k, " size: ", v) + } +} + +func TestClient_GetDatabaseDiskUsage(t *testing.T) { + result, err := DDCRDS_CLIENT.GetDatabaseDiskUsage(instanceId, "") + if err != nil { + fmt.Printf("get database disk usage error: %+v\n", err) + return + } + fmt.Printf("get database disk usage success.\n") + fmt.Println("ddc rest disk size(byte) ", result.RestDisk) + fmt.Println("ddc used disk size(byte) ", result.UsedDisk) + for k, v := range result.Databases { + fmt.Println("ddc table ", k, " size: ", v) + } +} + +func TestClient_GetRecoverableDateTime(t *testing.T) { + result, err := DDCRDS_CLIENT.GetRecoverableDateTime(instanceId) + if err != nil { + fmt.Printf("get recoverable datetimes error: %+v\n", err) + return + } + fmt.Printf("get recoverable datetimes success.\n") + for _, e := range result.RecoverableDateTimes { + fmt.Println("recover startTime: ", e.StartDateTime, " endTime: ", e.EndDateTime) + } +} + +func TestClient_RecoverToSourceInstanceByDatetime(t *testing.T) { + dbName := "test2" + tableName := "" + args := &RecoverInstanceArgs{ + Datetime: "2021-05-25T03:28:30Z", + RecoverData: []RecoverData{ + { + DbName: dbName, + NewDbName: dbName + "_new", + RestoreMode: "database", + }, + { + DbName: dbName, + NewDbName: dbName + "_new", + RestoreMode: "table", + RecoverTables: []RecoverTable{ + { + TableName: tableName, + NewTableName: tableName + "_new", + }, + }, + }, + }, + } + err := DDCRDS_CLIENT.RecoverToSourceInstanceByDatetime(instanceId, args) + if err != nil { + fmt.Printf("recover instance database error: %+v\n", err) + return + } + fmt.Printf("recover instance database success.\n") +} func TestClient_UpdateDatabaseRemark(t *testing.T) { args := &UpdateDatabaseRemarkArgs{ Remark: DB_REMARK + "_update", diff --git a/services/ddc/v2/ddc.go b/services/ddc/v2/ddc.go index 115a8102..fbcc5576 100644 --- a/services/ddc/v2/ddc.go +++ b/services/ddc/v2/ddc.go @@ -1191,6 +1191,97 @@ func (c *DDCClient) ListDatabase(instanceId string) (*ListDatabaseResult, error) return result, err } +// GetTableAmount - query amount of tables +// +// PARAMS: +// - args: the specific ddc instanceId, dbName and search pattern +// RETURNS: +// - *TableAmountResult: the size of the table that meets the criteria +// - error: nil if success otherwise the specific error +func (c *DDCClient) GetTableAmount(args *GetTableAmountArgs) (*TableAmountResult, error) { + if args == nil { + return nil, fmt.Errorf("unset args") + } + if len(args.InstanceId) < 1 { + return nil, fmt.Errorf("unset instanceId") + } + if len(args.DbName) < 1 { + return nil, fmt.Errorf("unset dbName") + } + result := &TableAmountResult{} + err := bce.NewRequestBuilder(c). + WithMethod(http.GET). + WithQueryParam("pattern", args.Pattern). + WithURL(getQueryDatabaseUriWithDbName(args.InstanceId, args.DbName)). + WithResult(result). + Do() + + return result, err +} + +// GetDatabaseDiskUsage - get the disk footprint and the remaining space for database +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// RETURNS: +// - *ListDatabaseResult: the disk footprint and the remaining space for database +// - error: nil if success otherwise the specific error +func (c *DDCClient) GetDatabaseDiskUsage(instanceId, dbName string) (*DatabaseDiskUsageResult, error) { + result := &DatabaseDiskUsageResult{} + req := bce.NewRequestBuilder(c) + if len(dbName) > 0 { + req.WithQueryParam("pattern", dbName) + } + err := req. + WithMethod(http.GET). + WithURL(getDatabaseDiskUsageUriWithInstanceId(instanceId)). + WithResult(result). + Do() + return result, err +} + +// GetRecoverableDateTime - get a list of recoverable times +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// RETURNS: +// - *GetRecoverableDateTimeResult: the result of list all recoverable datetimes +// - error: nil if success otherwise the specific error +func (c *DDCClient) GetRecoverableDateTime(instanceId string) (*GetRecoverableDateTimeResult, error) { + result := &GetRecoverableDateTimeResult{} + err := bce.NewRequestBuilder(c). + WithMethod(http.GET). + WithURL(getDatabaseRecoverTimeUriWithInstanceId(instanceId)). + WithResult(result). + Do() + return result, err +} + +// RecoverToSourceInstanceByDatetime - recover database or tables for the specific instance by a datetime +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// RETURNS: +// - error: nil if success otherwise the specific error +func (c *DDCClient) RecoverToSourceInstanceByDatetime(instanceId string, args *RecoverInstanceArgs) error { + if args == nil { + return fmt.Errorf("unset args") + } + if len(args.Datetime) < 1 { + return fmt.Errorf("unset datetime. Please query recoverable datetime by GetRecoverableDateTime()") + } + if args.RecoverData == nil || len(args.RecoverData) < 1 { + return fmt.Errorf("unset recover data") + } + err := bce.NewRequestBuilder(c). + WithMethod(http.PUT). + WithURL(getRecoverInstanceDatabaseUriWithInstanceId(instanceId)). + WithHeader(http.CONTENT_TYPE, bce.DEFAULT_CONTENT_TYPE). + WithBody(args). + Do() + return err +} + // CreateAccount - create a account with the specific parameters // // PARAMS: diff --git a/services/ddc/v2/ddcrds.go b/services/ddc/v2/ddcrds.go index 6117067e..2c855d6e 100644 --- a/services/ddc/v2/ddcrds.go +++ b/services/ddc/v2/ddcrds.go @@ -965,6 +965,50 @@ func (c *Client) ListDatabase(instanceId string) (*ListDatabaseResult, error) { return c.ddcClient.ListDatabase(instanceId) } +// GetTableAmount - query amount of tables +// +// PARAMS: +// - args: the specific ddc instanceId, dbName and search pattern +// RETURNS: +// - *TableAmountResult: the size of the table that meets the criteria +// - error: nil if success otherwise the specific error +func (c *Client) GetTableAmount(args *GetTableAmountArgs) (*TableAmountResult, error) { + return c.ddcClient.GetTableAmount(args) +} + +// GetDatabaseDiskUsage - get the disk footprint and the remaining space for database +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// RETURNS: +// - *ListDatabaseResult: the disk footprint and the remaining space for database +// - error: nil if success otherwise the specific error +func (c *Client) GetDatabaseDiskUsage(instanceId, dbName string) (*DatabaseDiskUsageResult, error) { + return c.ddcClient.GetDatabaseDiskUsage(instanceId, dbName) +} + +// GetRecoverableDateTime - get a list of recoverable times +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// RETURNS: +// - *GetRecoverableDateTimeResult: the result of list all recoverable datetimes +// - error: nil if success otherwise the specific error +func (c *Client) GetRecoverableDateTime(instanceId string) (*GetRecoverableDateTimeResult, error) { + return c.ddcClient.GetRecoverableDateTime(instanceId) +} + +// RecoverToSourceInstanceByDatetime - recover database or tables for the specific instance by a datetime +// +// PARAMS: +// - instanceId: the specific ddc Instance's ID +// - args: recover instance args +// RETURNS: +// - error: nil if success otherwise the specific error +func (c *Client) RecoverToSourceInstanceByDatetime(instanceId string, args *RecoverInstanceArgs) error { + return c.ddcClient.RecoverToSourceInstanceByDatetime(instanceId, args) +} + // UpdateAccountPassword - update a account password with the specific parameters // // PARAMS: diff --git a/services/ddc/v2/model.go b/services/ddc/v2/model.go index 7362e6d3..575fbabe 100644 --- a/services/ddc/v2/model.go +++ b/services/ddc/v2/model.go @@ -939,3 +939,44 @@ type CapacityOfZone struct { Slave int `json:"slave"` HA int `json:"HA"` } + +type GetTableAmountArgs struct { + InstanceId string `json:"instanceId"` + DbName string `json:"dbName"` + Pattern string `json:"pattern"` +} + +type TableAmountResult struct { + Tables map[string]int `json:"tables"` + TotalAmount int `json:"totalAmount"` + ReturnAmount int `json:"returnAmount"` +} + +type DatabaseDiskUsageResult struct { + Databases map[string]int `json:"databases"` + RestDisk int64 `json:"restDisk"` + UsedDisk int64 `json:"usedDisk"` +} + +type GetRecoverableDateTimeResult struct { + RecoverableDateTimes []RecoverableDateTime `json:"recoverableDateTimes"` +} +type RecoverableDateTime struct { + StartDateTime string `json:"startDateTime"` + EndDateTime string `json:"endDateTime"` +} + +type RecoverInstanceArgs struct { + Datetime string `json:"datetime"` + RecoverData []RecoverData `json:"recoverData"` +} +type RecoverTable struct { + NewTableName string `json:"newTableName"` + TableName string `json:"tableName"` +} +type RecoverData struct { + RecoverTables []RecoverTable `json:"recoverTables"` + RestoreMode string `json:"restoreMode"` + DbName string `json:"dbName"` + NewDbName string `json:"newDbName"` +}