mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Automatic merge from release/2.3.0 -> release/2.4.0
* commit '492469887b5e55cfec740534125704c8441f92d5': 添加部分aliyun操作 支持腾讯云资源导入
This commit is contained in:
+12
@@ -110,6 +110,14 @@
|
||||
name = "github.com/360EntSecGroup-Skylar/excelize"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tencentcloud/tencentcloud-sdk-go"
|
||||
version = "=v3.0.28"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/nelsonken/cos-go-sdk-v5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/tredoe/osutil"
|
||||
@@ -117,3 +125,7 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/forhappy/cos-go-sdk"
|
||||
|
||||
@@ -37,6 +37,7 @@ func init() {
|
||||
PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud"`
|
||||
AccessURL string `helo:"hello" metavar:"Azure choices: <AzureGermanCloud、AzureChinaCloud、AzureUSGovernmentCloud、AzurePublicCloud>"`
|
||||
Desc string `help:"Description"`
|
||||
Enabled bool `help:"Enabled the account automatically"`
|
||||
|
||||
Import bool `help:"Import all sub account automatically"`
|
||||
AutoSync bool `help:"Enabled the account automatically"`
|
||||
@@ -49,6 +50,10 @@ func init() {
|
||||
params.Add(jsonutils.NewString(args.SECRET), "secret")
|
||||
params.Add(jsonutils.NewString(args.PROVIDER), "provider")
|
||||
|
||||
if args.Enabled {
|
||||
params.Add(jsonutils.JSONTrue, "enabled")
|
||||
}
|
||||
|
||||
if args.Import {
|
||||
params.Add(jsonutils.JSONTrue, "import")
|
||||
if args.AutoSync {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
"yunion.io/x/structarg"
|
||||
|
||||
_ "yunion.io/x/onecloud/pkg/util/qcloud/shell"
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Help bool `help:"Show help"`
|
||||
AppID string `help:"AppID" default:"$QCLOUD_APPID"`
|
||||
SecretID string `help:"Secret" default:"$QCLOUD_SECRET_ID"`
|
||||
SecretKey string `help:"Access key" default:"$QCLOUD_SECRET_KEY"`
|
||||
RegionId string `help:"RegionId" default:"$QCLOUD_REGION"`
|
||||
SUBCOMMAND string `help:"azurecli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParser(&BaseOptions{},
|
||||
"qcloudcli",
|
||||
"Command-line interface to tencentcloud API.",
|
||||
`See "tencentcli help COMMAND" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
subcmd := parse.GetSubcommand()
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
type HelpOptions struct {
|
||||
SUBCOMMAND string `help:"sub-command name"`
|
||||
}
|
||||
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
|
||||
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
|
||||
if e != nil {
|
||||
return e
|
||||
} else {
|
||||
fmt.Print(helpstr)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParser(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
func showErrorAndExit(e error) {
|
||||
log.Errorf("%s", e)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func newClient(options *BaseOptions) (*qcloud.SRegion, error) {
|
||||
if len(options.SecretKey) == 0 {
|
||||
return nil, fmt.Errorf("Missing SecretKey")
|
||||
}
|
||||
|
||||
if len(options.SecretID) == 0 {
|
||||
return nil, fmt.Errorf("Missing SecretID")
|
||||
}
|
||||
|
||||
account := options.SecretID
|
||||
if len(options.AppID) > 0 {
|
||||
account = fmt.Sprintf("%s/%s", account, options.AppID)
|
||||
}
|
||||
|
||||
if cli, err := qcloud.NewQcloudClient("", "", account, options.SecretKey); err != nil {
|
||||
return nil, err
|
||||
} else if region := cli.GetRegion(options.RegionId); region == nil {
|
||||
return nil, fmt.Errorf("No such region %s", options.RegionId)
|
||||
} else {
|
||||
return region, nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
parser, e := getSubcommandParser()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if options.Help {
|
||||
fmt.Print(parser.HelpString())
|
||||
} else {
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
} else {
|
||||
suboptions := subparser.Options()
|
||||
if options.SUBCOMMAND == "help" {
|
||||
e = subcmd.Invoke(suboptions)
|
||||
} else {
|
||||
var region *qcloud.SRegion
|
||||
if len(options.RegionId) == 0 {
|
||||
options.RegionId = qcloud.QCLOUD_DEFAULT_REGION
|
||||
}
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
}
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package guestdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
type SQcloudGuestDriver struct {
|
||||
SManagedVirtualizedGuestDriver
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver := SQcloudGuestDriver{}
|
||||
models.RegisterGuestDriver(&driver)
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) GetHypervisor() string {
|
||||
return models.HYPERVISOR_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) ChooseHostStorage(host *models.SHost, backend string) *models.SStorage {
|
||||
storages := host.GetAttachedStorages("")
|
||||
for i := 0; i < len(storages); i++ {
|
||||
if storages[i].StorageType == backend {
|
||||
return &storages[i]
|
||||
}
|
||||
}
|
||||
for _, stype := range []string{"cloud_basic", "cloud_premium", "cloud_ssd", "local_basic", "local_ssd"} {
|
||||
for i := 0; i < len(storages); i++ {
|
||||
if storages[i].StorageType == stype {
|
||||
return &storages[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{models.VM_READY, models.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
data, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data.Contains("net.0") && data.Contains("net.1") {
|
||||
return nil, httperrors.NewInputParameterError("cannot support more than 1 nic")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
config := guest.GetDeployConfigOnHost(ctx, host, task.GetParams())
|
||||
log.Debugf("RequestDeployGuestOnHost: %s", config)
|
||||
/* onfinish, err := config.GetString("on_finish")
|
||||
if err != nil {
|
||||
return err
|
||||
} */
|
||||
|
||||
action, err := config.GetString("action")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
publicKey, _ := config.GetString("public_key")
|
||||
|
||||
adminPublicKey, _ := config.GetString("admin_public_key")
|
||||
projectPublicKey, _ := config.GetString("project_public_key")
|
||||
oUserData, _ := config.GetString("user_data")
|
||||
|
||||
userData := generateUserData(adminPublicKey, projectPublicKey, oUserData)
|
||||
|
||||
resetPassword := jsonutils.QueryBoolean(config, "reset_password", false)
|
||||
passwd, _ := config.GetString("password")
|
||||
if resetPassword && len(passwd) == 0 {
|
||||
passwd = seclib2.RandomPassword2(12)
|
||||
}
|
||||
|
||||
ihost, err := host.GetIHost()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
desc := SManagedVMCreateConfig{}
|
||||
err = config.Unmarshal(&desc, "desc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if action == "create" {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
nets := guest.GetNetworks()
|
||||
net := nets[0].GetNetwork()
|
||||
vpc := net.GetVpc()
|
||||
|
||||
ivpc, err := vpc.GetIVpc()
|
||||
if err != nil {
|
||||
log.Errorf("getIVPC fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secgrpId, err := ivpc.SyncSecurityGroup(desc.SecGroupId, desc.SecGroupName, desc.SecRules)
|
||||
if err != nil {
|
||||
log.Errorf("SyncSecurityGroup fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iVM, err := ihost.CreateVM(desc.Name, desc.ExternalImageId, desc.SysDiskSize, desc.Cpu, desc.Memory, desc.ExternalNetworkId,
|
||||
desc.IpAddr, desc.Description, passwd, desc.StorageType, desc.DataDisks, publicKey, secgrpId, userData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("VMcreated %s, wait status running ...", iVM.GetGlobalId())
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("VMcreated %s, and status is ready", iVM.GetGlobalId())
|
||||
|
||||
iVM, err = ihost.GetIVMById(iVM.GetGlobalId())
|
||||
if err != nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return nil, err
|
||||
}
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", passwd)
|
||||
return data, nil
|
||||
})
|
||||
} else if action == "deploy" {
|
||||
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
||||
if err != nil || iVM == nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return fmt.Errorf("cannot find vm")
|
||||
}
|
||||
|
||||
params := task.GetParams()
|
||||
log.Debugf("Deploy VM params %s", params.String())
|
||||
|
||||
name, _ := params.GetString("name")
|
||||
description, _ := params.GetString("description")
|
||||
publicKey, _ := config.GetString("public_key")
|
||||
deleteKeypair := jsonutils.QueryBoolean(params, "__delete_keypair__", false)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
|
||||
//腾讯云暂不支持更新自定义用户数据
|
||||
// if len(userData) > 0 {
|
||||
// err := iVM.UpdateUserData(userData)
|
||||
// if err != nil {
|
||||
// log.Errorf("update userdata fail %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
err := iVM.DeployVM(name, passwd, publicKey, deleteKeypair, description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", passwd)
|
||||
return data, nil
|
||||
})
|
||||
} else if action == "rebuild" {
|
||||
|
||||
iVM, err := ihost.GetIVMById(guest.GetExternalId())
|
||||
if err != nil || iVM == nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return fmt.Errorf("cannot find vm")
|
||||
}
|
||||
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
//腾讯云暂不支持更新自定义用户数据
|
||||
// if len(userData) > 0 {
|
||||
// err := iVM.UpdateUserData(userData)
|
||||
// if err != nil {
|
||||
// log.Errorf("update userdata fail %s", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
diskId, err := iVM.RebuildRoot(desc.ExternalImageId, passwd, publicKey, desc.SysDiskSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId)
|
||||
|
||||
err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId())
|
||||
|
||||
maxWaitSecs := 300
|
||||
waited := 0
|
||||
|
||||
for {
|
||||
// hack, wait disk number consistent
|
||||
idisks, err := iVM.GetIDisks()
|
||||
if err != nil {
|
||||
log.Errorf("fail to find VM idisks %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(idisks) < len(desc.DataDisks)+1 {
|
||||
if waited > maxWaitSecs {
|
||||
log.Errorf("inconsistent disk number, wait timeout, must be something wrong on remote")
|
||||
return nil, cloudprovider.ErrTimeout
|
||||
}
|
||||
log.Debugf("inconsistent disk number???? %d != %d", len(idisks), len(desc.DataDisks)+1)
|
||||
time.Sleep(time.Second * 5)
|
||||
waited += 5
|
||||
} else {
|
||||
if idisks[0].GetGlobalId() != diskId {
|
||||
log.Errorf("system disk id inconsistent %s != %s", idisks[0].GetGlobalId(), diskId)
|
||||
return nil, fmt.Errorf("inconsistent sys disk id after rebuild root")
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
data := fetchIVMinfo(desc, iVM, guest.Id, "root", passwd)
|
||||
|
||||
return data, nil
|
||||
})
|
||||
|
||||
} else {
|
||||
log.Errorf("RequestDeployGuestOnHost: Action %s not supported", action)
|
||||
return fmt.Errorf("Action %s not supported", action)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) OnGuestDeployTaskDataReceived(ctx context.Context, guest *models.SGuest, task taskman.ITask, data jsonutils.JSONObject) error {
|
||||
|
||||
if data.Contains("disks") {
|
||||
diskInfo := make([]SDiskInfo, 0)
|
||||
err := data.Unmarshal(&diskInfo, "disks")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disks := guest.GetDisks()
|
||||
if len(disks) != len(diskInfo) {
|
||||
msg := fmt.Sprintf("inconsistent disk number: have %d want %d", len(disks), len(diskInfo))
|
||||
log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
for i := 0; i < len(diskInfo); i += 1 {
|
||||
disk := disks[i].GetDisk()
|
||||
_, err = disk.GetModelManager().TableSpec().Update(disk, func() error {
|
||||
disk.DiskSize = diskInfo[i].Size
|
||||
disk.ExternalId = diskInfo[i].Uuid
|
||||
disk.DiskType = diskInfo[i].DiskType
|
||||
disk.Status = models.DISK_READY
|
||||
disk.BillingType = diskInfo[i].BillingType
|
||||
disk.FsFormat = diskInfo[i].FsFromat
|
||||
disk.AutoDelete = true
|
||||
disk.TemplateId = diskInfo[i].TemplateId
|
||||
disk.DiskFormat = diskInfo[i].DiskFormat
|
||||
disk.ExpiredAt = diskInfo[i].ExpiredAt
|
||||
if len(diskInfo[i].Metadata) > 0 {
|
||||
for key, value := range diskInfo[i].Metadata {
|
||||
if err := disk.SetMetadata(ctx, key, value, task.GetUserCred()); err != nil {
|
||||
log.Errorf("set disk %s mata %s => %s error: %v", disk.Name, key, value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("save disk info failed %s", err)
|
||||
log.Errorf(msg)
|
||||
break
|
||||
} else {
|
||||
db.OpsLog.LogEvent(disk, db.ACT_ALLOCATE, disk.GetShortDesc(), task.GetUserCred())
|
||||
}
|
||||
}
|
||||
}
|
||||
uuid, _ := data.GetString("uuid")
|
||||
if len(uuid) > 0 {
|
||||
guest.SetExternalId(uuid)
|
||||
}
|
||||
|
||||
if metaData, _ := data.Get("metadata"); metaData != nil {
|
||||
meta := make(map[string]string, 0)
|
||||
if err := metaData.Unmarshal(meta); err != nil {
|
||||
log.Errorf("Get guest %s metadata error: %v", guest.Name, err)
|
||||
} else {
|
||||
for key, value := range meta {
|
||||
if err := guest.SetMetadata(ctx, key, value, task.GetUserCred()); err != nil {
|
||||
log.Errorf("set guest %s mata %s => %s error: %v", guest.Name, key, value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guest.SaveDeployInfo(ctx, task.GetUserCred(), data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) AllowReconfigGuest() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
|
||||
iDisk, _ := models.DiskManager.FetchById(diskId)
|
||||
disk := iDisk.(*models.SDisk)
|
||||
providerDisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
|
||||
snapshot := iSnapshot.(*models.SSnapshot)
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
cloudSnapshot, err := providerDisk.CreateISnapshot(snapshot.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("snapshot_id", jsonutils.NewString(cloudSnapshot.GetId()))
|
||||
res.Set("manager_id", jsonutils.NewString(cloudSnapshot.GetManagerId()))
|
||||
cloudRegion, err := models.CloudregionManager.FetchByExternalId("Aliyun/" + cloudSnapshot.GetRegionId())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cloud region not found? %s", err)
|
||||
}
|
||||
res.Set("cloudregion_id", jsonutils.NewString(cloudRegion.GetId()))
|
||||
return res, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package hostdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
type SQcloudHostDriver struct {
|
||||
SBaseHostDriver
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver := SQcloudHostDriver{}
|
||||
models.RegisterHostDriver(&driver)
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) GetHostType() string {
|
||||
return models.HOST_TYPE_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) CheckAndSetCacheImage(ctx context.Context, host *models.SHost, storageCache *models.SStoragecache, task taskman.ITask) error {
|
||||
params := task.GetParams()
|
||||
imageId, err := params.GetString("image_id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
osArch, _ := params.GetString("os_arch")
|
||||
osType, _ := params.GetString("os_type")
|
||||
osDist, _ := params.GetString("os_distribution")
|
||||
|
||||
isForce := jsonutils.QueryBoolean(params, "is_force", false)
|
||||
userCred := task.GetUserCred()
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
lockman.LockRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId))
|
||||
defer lockman.ReleaseRawObject(ctx, "cachedimages", fmt.Sprintf("%s-%s", storageCache.Id, imageId))
|
||||
|
||||
scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), storageCache.Id, imageId)
|
||||
iStorageCache, err := storageCache.GetIStorageCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extImgId, err := iStorageCache.UploadImage(userCred, imageId, osArch, osType, osDist, scimg.ExternalId, isForce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scimg.SetExternalId(extImgId)
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(jsonutils.NewString(extImgId), "image_id")
|
||||
return ret, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask, content *jsonutils.JSONDict) error {
|
||||
iCloudStorage, err := storage.GetIStorage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size, err := content.Int("size")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size = size >> 10
|
||||
iDisk, err := iCloudStorage.CreateIDisk(disk.GetName(), int(size), "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = disk.GetModelManager().TableSpec().Update(disk, func() error {
|
||||
disk.ExternalId = iDisk.GetGlobalId()
|
||||
|
||||
if metaData := iDisk.GetMetadata(); metaData != nil {
|
||||
meta := make(map[string]string)
|
||||
if err := metaData.Unmarshal(meta); err != nil {
|
||||
log.Errorf("Get disk %s Metadata error: %v", disk.Name, err)
|
||||
} else {
|
||||
for key, value := range meta {
|
||||
if err := disk.SetMetadata(ctx, key, value, task.GetUserCred()); err != nil {
|
||||
log.Errorf("set disk %s mata %s => %s error: %v", disk.Name, key, value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Update disk externalId err: %v", err)
|
||||
return err
|
||||
}
|
||||
data := jsonutils.NewDict()
|
||||
data.Add(jsonutils.NewInt(int64(iDisk.GetDiskSizeMB())), "disk_size")
|
||||
data.Add(jsonutils.NewString(iDisk.GetDiskFormat()), "disk_format")
|
||||
task.ScheduleRun(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestDeallocateDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, task taskman.ITask) error {
|
||||
data := jsonutils.NewDict()
|
||||
if iCloudStorage, err := storage.GetIStorage(); err != nil {
|
||||
return err
|
||||
} else if iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId()); err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
task.ScheduleRun(data)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
} else if err := iDisk.Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestResizeDiskOnHostOnline(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error {
|
||||
return self.RequestResizeDiskOnHost(host, storage, disk, size, task)
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestResizeDiskOnHost(host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error {
|
||||
iCloudStorage, err := storage.GetIStorage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iDisk, err := iCloudStorage.GetIDisk(disk.GetExternalId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = iDisk.Resize(size >> 10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(jsonutils.Marshal(map[string]int64{"disk_size": size}))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error {
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask, data jsonutils.JSONObject) error {
|
||||
iDisk, err := disk.GetIDisk()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iStorage, err := disk.GetIStorage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iStoragecache := iStorage.GetIStoragecache()
|
||||
if iStoragecache == nil {
|
||||
return httperrors.NewResourceNotFoundError("fail to find iStoragecache for storage: %s", iStorage.GetName())
|
||||
}
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
snapshot, err := iDisk.CreateISnapshot(fmt.Sprintf("Snapshot-%s", imageId), "PrepareSaveImage")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := task.GetParams()
|
||||
osType, _ := params.GetString("properties", "os_type")
|
||||
|
||||
scimg := models.StoragecachedimageManager.Register(ctx, task.GetUserCred(), iStoragecache.GetId(), imageId)
|
||||
if scimg.Status != models.CACHED_IMAGE_STATUS_READY {
|
||||
scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHING, "request_prepare_save_disk_on_host")
|
||||
}
|
||||
iImage, err := iStoragecache.CreateIImage(snapshot.GetId(), fmt.Sprintf("Image-%s", imageId), osType, "")
|
||||
if err != nil {
|
||||
log.Errorf("fail to create iImage: %v", err)
|
||||
scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
scimg.SetExternalId(iImage.GetId())
|
||||
if _, err := os.Stat(options.Options.TempPath); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(options.Options.TempPath, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
result, err := iStoragecache.DownloadImage(task.GetUserCred(), imageId, iImage.GetId(), options.Options.TempPath)
|
||||
if err != nil {
|
||||
scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_CACHE_FAILED, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if err := iImage.Delete(); err != nil {
|
||||
log.Errorf("Delete iImage %s failed: %v", iImage.GetId(), err)
|
||||
}
|
||||
if err := snapshot.Delete(); err != nil {
|
||||
log.Errorf("Delete snapshot %s failed: %v", snapshot.GetId(), err)
|
||||
}
|
||||
scimg.SetStatus(task.GetUserCred(), models.CACHED_IMAGE_STATUS_READY, "")
|
||||
return result, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudHostDriver) RequestDeleteSnapshotWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error {
|
||||
return httperrors.NewNotImplementedError("not implement")
|
||||
}
|
||||
@@ -31,6 +31,7 @@ const (
|
||||
|
||||
CLOUD_PROVIDER_VMWARE = "VMware"
|
||||
CLOUD_PROVIDER_ALIYUN = "Aliyun"
|
||||
CLOUD_PROVIDER_QCLOUD = "Qcloud"
|
||||
CLOUD_PROVIDER_AZURE = "Azure"
|
||||
)
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ const (
|
||||
HYPERVISOR_ESXI = "esxi"
|
||||
HYPERVISOR_HYPERV = "hyperv"
|
||||
HYPERVISOR_ALIYUN = "aliyun"
|
||||
HYPERVISOR_QCLOUD = "qcloud"
|
||||
HYPERVISOR_AZURE = "azure"
|
||||
|
||||
// HYPERVISOR_DEFAULT = HYPERVISOR_KVM
|
||||
@@ -132,7 +133,7 @@ const (
|
||||
var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_SNAPSHOT_STREAM}
|
||||
var VM_CREATING_STATUS = []string{VM_CREATE_NETWORK, VM_CREATE_DISK, VM_START_DEPLOY, VM_DEPLOYING}
|
||||
|
||||
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE}
|
||||
var HYPERVISORS = []string{HYPERVISOR_KVM, HYPERVISOR_BAREMETAL, HYPERVISOR_ESXI, HYPERVISOR_CONTAINER, HYPERVISOR_ALIYUN, HYPERVISOR_AZURE, HYPERVISOR_QCLOUD}
|
||||
|
||||
var PUBLIC_CLOUD_HYPERVISORS = []string{HYPERVISOR_ALIYUN, HYPERVISOR_AZURE}
|
||||
|
||||
@@ -145,6 +146,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{
|
||||
HYPERVISOR_CONTAINER: HOST_TYPE_KUBELET,
|
||||
HYPERVISOR_ALIYUN: HOST_TYPE_ALIYUN,
|
||||
HYPERVISOR_AZURE: HOST_TYPE_AZURE,
|
||||
HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD,
|
||||
}
|
||||
|
||||
var HOSTTYPE_HYPERVISOR = map[string]string{
|
||||
@@ -154,6 +156,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{
|
||||
HOST_TYPE_KUBELET: HYPERVISOR_CONTAINER,
|
||||
HOST_TYPE_ALIYUN: HYPERVISOR_ALIYUN,
|
||||
HOST_TYPE_AZURE: HYPERVISOR_AZURE,
|
||||
HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD,
|
||||
}
|
||||
|
||||
type SGuestManager struct {
|
||||
|
||||
@@ -43,6 +43,7 @@ const (
|
||||
HOST_TYPE_HYPERV = "hyperv" // # Microsoft Hyper-V
|
||||
HOST_TYPE_XEN = "xen" // # XenServer
|
||||
HOST_TYPE_ALIYUN = "aliyun"
|
||||
HOST_TYPE_QCLOUD = "qcloud"
|
||||
HOST_TYPE_AZURE = "azure"
|
||||
|
||||
HOST_TYPE_DEFAULT = HOST_TYPE_HYPERVISOR
|
||||
@@ -81,7 +82,7 @@ const (
|
||||
HOST_STATUS_UNKNOWN = BAREMETAL_UNKNOWN
|
||||
)
|
||||
|
||||
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE}
|
||||
var HOST_TYPES = []string{HOST_TYPE_BAREMETAL, HOST_TYPE_HYPERVISOR, HOST_TYPE_ESXI, HOST_TYPE_KUBELET, HOST_TYPE_XEN, HOST_TYPE_ALIYUN, HOST_TYPE_AZURE, HOST_TYPE_QCLOUD}
|
||||
var NIC_TYPES = []string{NIC_TYPE_IPMI, NIC_TYPE_ADMIN}
|
||||
|
||||
type SHostManager struct {
|
||||
|
||||
@@ -25,11 +25,12 @@ const (
|
||||
MANUAL = "manual"
|
||||
AUTO = "auto"
|
||||
|
||||
SNAPSHOT_CREATING = "creating"
|
||||
SNAPSHOT_FAILED = "create_failed"
|
||||
SNAPSHOT_READY = "ready"
|
||||
SNAPSHOT_DELETING = "deleting"
|
||||
SNAPSHOT_UNKNOWN = "unknown"
|
||||
SNAPSHOT_CREATING = "creating"
|
||||
SNAPSHOT_ROLLBACKING = "rollbacking"
|
||||
SNAPSHOT_FAILED = "create_failed"
|
||||
SNAPSHOT_READY = "ready"
|
||||
SNAPSHOT_DELETING = "deleting"
|
||||
SNAPSHOT_UNKNOWN = "unknown"
|
||||
)
|
||||
|
||||
type SSnapshotManager struct {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
_ "yunion.io/x/onecloud/pkg/util/aliyun/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/azure/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/esxi/provider"
|
||||
_ "yunion.io/x/onecloud/pkg/util/qcloud/provider"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
|
||||
@@ -115,7 +115,7 @@ type ServerCreateOptions struct {
|
||||
Project string `help:"'Owner project ID or Name" json:"tenant"`
|
||||
User string `help:"Owner user ID or Name"`
|
||||
System *bool `help:"Create a system VM, sysadmin ONLY option" json:"is_system"`
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure"`
|
||||
Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud"`
|
||||
TaskNotify *bool `help:"Setup task notify" json:"-"`
|
||||
Count *int `help:"Create multiple simultaneously" default:"1" json:"-"`
|
||||
DryRun *bool `help:"Dry run to test scheduler" json:"-"`
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
HostHypervisorForKvm = "hypervisor"
|
||||
HostTypeAliyun = "aliyun"
|
||||
HostTypeAzure = "azure"
|
||||
HostTypeQcloud = "qcloud"
|
||||
HostTypeKubelet = "kubelet"
|
||||
|
||||
AggregateStrategyRequire = "require"
|
||||
@@ -50,6 +51,7 @@ var (
|
||||
PublicCloudProviders = sets.NewString(
|
||||
HostTypeAliyun,
|
||||
HostTypeAzure,
|
||||
HostTypeQcloud,
|
||||
)
|
||||
|
||||
ValidGpuTypes = sets.NewString(
|
||||
|
||||
@@ -2,6 +2,7 @@ package aliyun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/aliyun"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
@@ -29,4 +31,47 @@ func init() {
|
||||
shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *aliyun.SRegion, args *ImageDeleteOptions) error {
|
||||
return cli.DeleteImage(args.ID)
|
||||
})
|
||||
|
||||
type ImageCreateOptions struct {
|
||||
SNAPSHOT string `help:"Snapshot id"`
|
||||
NAME string `help:"Image name"`
|
||||
Desc string `help:"Image desc"`
|
||||
}
|
||||
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *aliyun.SRegion, args *ImageCreateOptions) error {
|
||||
imageId, err := cli.CreateImage(args.SNAPSHOT, args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(imageId)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageExportOptions struct {
|
||||
ID string `help:"ID or Name to export"`
|
||||
BUCKET string `help:"Bucket name"`
|
||||
}
|
||||
|
||||
shellutils.R(&ImageExportOptions{}, "image-export", "Export image", func(cli *aliyun.SRegion, args *ImageExportOptions) error {
|
||||
oss, err := cli.GetOssClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exist, err := oss.IsBucketExist(args.BUCKET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return fmt.Errorf("not exist bucket %s", args.BUCKET)
|
||||
}
|
||||
bucket, err := oss.Bucket(args.BUCKET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task, err := cli.ExportImage(args.ID, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(task)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/aliyun"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
@@ -38,8 +40,12 @@ func init() {
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *aliyun.SRegion, args *SnapshotCreateOptions) error {
|
||||
_, err := cli.CreateSnapshot(args.DiskId, args.Name, args.Desc)
|
||||
return err
|
||||
snapshotId, err := cli.CreateSnapshot(args.DiskId, args.Name, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(snapshotId)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -245,6 +245,10 @@ func (self *SRegion) checkBucket(bucketName string) (*oss.Bucket, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateImage(snapshoutId, imageName, imageDesc string) (string, error) {
|
||||
return self.createIImage(snapshoutId, imageName, imageDesc)
|
||||
}
|
||||
|
||||
func (self *SRegion) createIImage(snapshoutId, imageName, imageDesc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["RegionId"] = self.RegionId
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type Placement struct {
|
||||
ProjectId int
|
||||
Zone string
|
||||
}
|
||||
|
||||
type SDisk struct {
|
||||
storage *SStorage
|
||||
|
||||
Attached bool
|
||||
AutoRenewFlagError bool
|
||||
CreateTime time.Time
|
||||
DeadlineError bool
|
||||
DeadlineTime time.Time
|
||||
DifferDaysOfDeadline int
|
||||
DiskChargeType string
|
||||
DiskId string
|
||||
DiskName string
|
||||
DiskSize int
|
||||
DiskState string
|
||||
DiskType string
|
||||
DiskUsage string
|
||||
Encrypt bool
|
||||
InstanceId string
|
||||
IsReturnable bool
|
||||
Placement Placement
|
||||
Portable bool
|
||||
RenewFlag string
|
||||
ReturnFailCode int
|
||||
RollbackPercent int
|
||||
Rollbacking bool
|
||||
SnapshotAbility bool
|
||||
DeleteWithInstance bool
|
||||
}
|
||||
|
||||
type SDiskSet []SDisk
|
||||
|
||||
func (v SDiskSet) Len() int {
|
||||
return len(v)
|
||||
}
|
||||
|
||||
func (v SDiskSet) Swap(i, j int) {
|
||||
v[i], v[j] = v[j], v[i]
|
||||
}
|
||||
|
||||
func (v SDiskSet) Less(i, j int) bool {
|
||||
if v[i].DiskUsage == "SYSTEM_DISK" || v[j].DiskUsage == "DATA_DISK" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SDisk) GetMetadata() *jsonutils.JSONDict {
|
||||
// data := jsonutils.NewDict()
|
||||
|
||||
// // The pricingInfo key structure is 'RegionId::DiskCategory::DiskType
|
||||
// priceKey := fmt.Sprintf("%s::%s::%s", self.RegionId, self.Category, self.Type)
|
||||
// data.Add(jsonutils.NewString(priceKey), "price_key")
|
||||
|
||||
// data.Add(jsonutils.NewString(models.HYPERVISOR_ALIYUN), "hypervisor")
|
||||
|
||||
// return data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisks(instanceId string, zoneId string, category string, diskIds []string, offset int, limit int) ([]SDisk, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
filter := 0
|
||||
|
||||
if len(zoneId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "zone"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = zoneId
|
||||
filter++
|
||||
}
|
||||
|
||||
if len(instanceId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "instance-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = instanceId
|
||||
filter++
|
||||
}
|
||||
|
||||
if len(category) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "disk-type"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = category
|
||||
filter++
|
||||
}
|
||||
if diskIds != nil && len(diskIds) > 0 {
|
||||
for index, diskId := range diskIds {
|
||||
params[fmt.Sprintf("DiskIds.%d", index)] = diskId
|
||||
}
|
||||
}
|
||||
|
||||
body, err := self.cbsRequest("DescribeDisks", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetDisks fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
disks := make([]SDisk, 0)
|
||||
err = body.Unmarshal(&disks, "DiskSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal disk details fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
sort.Sort(SDiskSet(disks))
|
||||
return disks, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisk(diskId string) (*SDisk, error) {
|
||||
disks, total, err := self.GetDisks("", "", "", []string{diskId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &disks[0], nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteDisk(diskId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["DiskIds.0"] = diskId
|
||||
|
||||
_, err := self.cbsRequest("TerminateDisks", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SDisk) Delete() error {
|
||||
return self.storage.zone.region.DeleteDisk(self.DiskId)
|
||||
}
|
||||
|
||||
func (self *SRegion) ResizeDisk(diskId string, sizeGb int64) error {
|
||||
params := make(map[string]string)
|
||||
params["DiskId"] = diskId
|
||||
params["DiskSize"] = fmt.Sprintf("%d", sizeGb)
|
||||
startTime := time.Now()
|
||||
for {
|
||||
_, err := self.cbsRequest("ResizeDisk", params)
|
||||
if err != nil {
|
||||
if strings.Index(err.Error(), "Code=InvalidDisk.Busy") > 0 {
|
||||
log.Infof("The disk is busy, try later ...")
|
||||
time.Sleep(10 * time.Second)
|
||||
if time.Now().Sub(startTime) > time.Minute*20 {
|
||||
return cloudprovider.ErrTimeout
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) Resize(size int64) error {
|
||||
return self.storage.zone.region.ResizeDisk(self.DiskId, size)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetName() string {
|
||||
if len(self.DiskName) > 0 {
|
||||
return self.DiskName
|
||||
}
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SDisk) GetGlobalId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SDisk) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIStorge() cloudprovider.ICloudStorage {
|
||||
return self.storage
|
||||
}
|
||||
|
||||
func (self *SDisk) GetStatus() string {
|
||||
switch self.DiskState {
|
||||
case "ATTACHING", "DETACHING", "EXPANDING", "ROLLBACKING":
|
||||
return models.DISK_ALLOCATING
|
||||
default:
|
||||
return models.DISK_READY
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) Refresh() error {
|
||||
new, err := self.storage.zone.region.GetDisk(self.DiskId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snapshotId, err := self.storage.zone.region.CreateSnapshot(self.DiskId, name, desc)
|
||||
if err != nil {
|
||||
log.Errorf("createSnapshot fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
snapshots, total, err := self.storage.zone.region.GetSnapshots("", "", "", []string{snapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 1 {
|
||||
snapshot := &snapshots[0]
|
||||
err := cloudprovider.WaitStatus(snapshot, string(SnapshotStatusAccomplished), 15*time.Second, 3600*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snapshot, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskType() string {
|
||||
switch self.DiskUsage {
|
||||
case "SYSTEM_DISK":
|
||||
return models.DISK_TYPE_SYS
|
||||
case "DATA_DISK":
|
||||
return models.DISK_TYPE_DATA
|
||||
default:
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) GetFsFormat() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIsNonPersistent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDriver() string {
|
||||
return "scsi"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetCacheMode() string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetMountpoint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) GetBillingType() string {
|
||||
switch self.DiskChargeType {
|
||||
case "PREPAID":
|
||||
return models.BILLING_TYPE_PREPAID
|
||||
case "POSTPAID_BY_HOUR":
|
||||
return models.BILLING_TYPE_POSTPAID
|
||||
default:
|
||||
return models.BILLING_TYPE_PREPAID
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskFormat() string {
|
||||
return "vhd"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskSizeMB() int {
|
||||
return self.DiskSize * 1024
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIsAutoDelete() bool {
|
||||
return self.DeleteWithInstance
|
||||
}
|
||||
|
||||
func (self *SDisk) GetExpiredAt() time.Time {
|
||||
return self.DeadlineTime
|
||||
}
|
||||
|
||||
func (self *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, total, err := self.storage.zone.region.GetSnapshots("", "", "", []string{snapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 1 {
|
||||
return &snapshots[0], nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots := make([]SSnapshot, 0)
|
||||
for {
|
||||
parts, total, err := self.storage.zone.region.GetSnapshots("", self.DiskId, "", []string{}, 0, 20)
|
||||
if err != nil {
|
||||
log.Errorf("GetDisks fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
snapshots = append(snapshots, parts...)
|
||||
if len(snapshots) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
isnapshots := make([]cloudprovider.ICloudSnapshot, len(snapshots))
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].region = self.storage.zone.region
|
||||
isnapshots[i] = &snapshots[i]
|
||||
}
|
||||
return isnapshots, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetTemplateId() string {
|
||||
//return self.ImageId
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRegion) ResetDisk(diskId, snapshotId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["DiskId"] = diskId
|
||||
params["SnapshotId"] = snapshotId
|
||||
_, err := self.cbsRequest("ApplySnapshot", params)
|
||||
if err != nil {
|
||||
log.Errorf("ResetDisk %s to snapshot %s fail %s", diskId, snapshotId, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SDisk) Reset(snapshotId string) error {
|
||||
return self.storage.zone.region.ResetDisk(self.DiskId, snapshotId)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, desc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["DiskType"] = category
|
||||
params["DiskChargeType"] = "POSTPAID_BY_HOUR"
|
||||
params["DiskName"] = name
|
||||
params["Placement.Zone"] = zoneId
|
||||
//params["Encrypted"] = "false"
|
||||
params["DiskSize"] = fmt.Sprintf("%d", sizeGb)
|
||||
params["ClientToken"] = utils.GenRequestId(20)
|
||||
|
||||
body, err := self.cbsRequest("CreateDisks", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
diskIDSet := []string{}
|
||||
err = body.Unmarshal(&diskIDSet, "DiskIdSet")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(diskIDSet) < 1 {
|
||||
return "", fmt.Errorf("Create Disk error")
|
||||
}
|
||||
return diskIDSet[0], nil
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type TInternetChargeType string
|
||||
|
||||
const (
|
||||
InternetChargeByTraffic = TInternetChargeType("PayByTraffic")
|
||||
InternetChargeByBandwidth = TInternetChargeType("PayByBandwidth")
|
||||
)
|
||||
|
||||
const (
|
||||
EIP_STATUS_ASSOCIATING = "Associating"
|
||||
EIP_STATUS_UNASSOCIATING = "Unassociating"
|
||||
EIP_STATUS_INUSE = "InUse"
|
||||
EIP_STATUS_AVAILABLE = "Available"
|
||||
|
||||
EIP_OPERATION_LOCK_FINANCIAL = "financial"
|
||||
EIP_OPERATION_LOCK_SECURITY = "security"
|
||||
|
||||
EIP_INSTANCE_TYPE_ECS = "EcsInstance" // (默认值):VPC类型的ECS实例
|
||||
EIP_INTANNCE_TYPE_SLB = "SlbInstance" // :VPC类型的SLB实例
|
||||
EIP_INSTANCE_TYPE_NAT = "Nat" // :NAT网关
|
||||
EIP_INSTANCE_TYPE_HAVIP = "HaVip" // :HAVIP
|
||||
)
|
||||
|
||||
type SEipAddress struct {
|
||||
region *SRegion
|
||||
|
||||
AddressId string // EIP的ID,是EIP的唯一标识。
|
||||
AddressName string // EIP名称。
|
||||
AddressStatus string // EIP状态。
|
||||
AddressIp string // 外网IP地址
|
||||
InstanceId string // 绑定的资源实例ID。可能是一个CVM,NAT。
|
||||
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
NetworkInterfaceId string // 绑定的弹性网卡ID
|
||||
PrivateAddressIp string // 绑定的资源内网ip
|
||||
IsArrears bool // 资源隔离状态。true表示eip处于隔离状态,false表示资源处于未隔离装填
|
||||
IsBlocked bool // 资源封堵状态。true表示eip处于封堵状态,false表示eip处于未封堵状态
|
||||
IsEipDirectConnection bool // eip是否支持直通模式。true表示eip支持直通模式,false表示资源不支持直通模式
|
||||
AddressType string // eip资源类型,包括"CalcIP","WanIP","EIP","AnycastEIP"。其中"CalcIP"表示设备ip,“WanIP”表示普通公网ip,“EIP”表示弹性公网ip,“AnycastEip”表示加速EIP
|
||||
CascadeRelease bool // eip是否在解绑后自动释放。true表示eip将会在解绑后自动释放,false表示eip在解绑后不会自动释放
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetId() string {
|
||||
return self.AddressId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetName() string {
|
||||
return self.AddressName
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetGlobalId() string {
|
||||
return self.AddressId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetStatus() string {
|
||||
switch self.AddressStatus {
|
||||
case EIP_STATUS_AVAILABLE, EIP_STATUS_INUSE:
|
||||
return models.EIP_STATUS_READY
|
||||
case EIP_STATUS_ASSOCIATING:
|
||||
return models.EIP_STATUS_ASSOCIATE
|
||||
case EIP_STATUS_UNASSOCIATING:
|
||||
return models.EIP_STATUS_DISSOCIATE
|
||||
default:
|
||||
return models.EIP_STATUS_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Refresh() error {
|
||||
if self.IsEmulated() {
|
||||
return nil
|
||||
}
|
||||
new, err := self.region.GetEip(self.AddressId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) IsEmulated() bool {
|
||||
if self.AddressId == self.InstanceId {
|
||||
// fixed Public IP
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetIpAddr() string {
|
||||
return self.AddressIp
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetMode() string {
|
||||
if self.InstanceId == self.AddressId {
|
||||
return models.EIP_MODE_INSTANCE_PUBLICIP
|
||||
}
|
||||
return models.EIP_MODE_STANDALONE_EIP
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetAssociationType() string {
|
||||
switch self.AddressType {
|
||||
case "EIP", "AnycastEIP", "WanIP":
|
||||
return "server"
|
||||
case "CalcIP":
|
||||
return "server"
|
||||
default:
|
||||
log.Fatalf("unsupported type: %s", self.AddressType)
|
||||
return "unsupported"
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetAssociationExternalId() string {
|
||||
return self.InstanceId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Delete() error {
|
||||
return self.region.DeallocateEIP(self.AddressId)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetBandwidth() int {
|
||||
return 100
|
||||
//return self.Bandwidth
|
||||
}
|
||||
|
||||
func (self *SEipAddress) GetInternetChargeType() string {
|
||||
// switch self.InternetChargeType {
|
||||
// case string(InternetChargeByTraffic):
|
||||
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
// case string(InternetChargeByBandwidth):
|
||||
// return models.EIP_CHARGE_TYPE_BY_BANDWIDTH
|
||||
// default:
|
||||
// return models.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
// }
|
||||
return "unkonw"
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Associate(instanceId string) error {
|
||||
err := self.region.AssociateEip(self.AddressId, instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) Dissociate() error {
|
||||
err := self.region.DissociateEip(self.AddressId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, models.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
|
||||
}
|
||||
|
||||
func (self *SEipAddress) ChangeBandwidth(bw int) error {
|
||||
return self.region.UpdateEipBandwidth(self.AddressId, bw)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetEips(eipId string, offset int, limit int) ([]SEipAddress, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
if len(eipId) > 0 {
|
||||
params["AddressIds.0"] = eipId
|
||||
}
|
||||
|
||||
body, err := region.vpcRequest("DescribeAddresses", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeEipAddresses fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
eips := make([]SEipAddress, 0)
|
||||
err = body.Unmarshal(&eips, "AddressSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal EipAddress details fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Int("TotalCount")
|
||||
for i := 0; i < len(eips); i++ {
|
||||
eips[i].region = region
|
||||
}
|
||||
return eips, int(total), nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) {
|
||||
eips, total, err := region.GetEips(eipId, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &eips[0], nil
|
||||
}
|
||||
|
||||
func (region *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType) (*SEipAddress, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
addRessSet := []string{}
|
||||
body, err := region.vpcRequest("AllocateAddresses", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := body.Unmarshal(&addRessSet, "AddressSet"); err == nil && len(addRessSet) > 0 {
|
||||
params["AddressId"] = addRessSet[0]
|
||||
params["AddressName"] = name
|
||||
if _, err := region.vpcRequest("ModifyAddressAttribute", params); err != nil {
|
||||
return nil, err
|
||||
} else if err := region.UpdateEipBandwidth(addRessSet[0], bwMbps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region.GetEip(addRessSet[0])
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string) (cloudprovider.ICloudEIP, error) {
|
||||
var ctype TInternetChargeType
|
||||
switch chargeType {
|
||||
case models.EIP_CHARGE_TYPE_BY_TRAFFIC:
|
||||
ctype = InternetChargeByTraffic
|
||||
case models.EIP_CHARGE_TYPE_BY_BANDWIDTH:
|
||||
ctype = InternetChargeByBandwidth
|
||||
}
|
||||
return region.AllocateEIP(name, bwMbps, ctype)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeallocateEIP(eipId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
params["AddressIds.0"] = eipId
|
||||
|
||||
_, err := region.vpcRequest("ReleaseAddresses", params)
|
||||
if err != nil {
|
||||
log.Errorf("ReleaseAddresses fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) AssociateEip(eipId string, instanceId string) error {
|
||||
params := make(map[string]string)
|
||||
params["AllocationId"] = eipId
|
||||
params["InstanceId"] = instanceId
|
||||
|
||||
_, err := region.vpcRequest("AssociateEipAddress", params)
|
||||
if err != nil {
|
||||
log.Errorf("AssociateEipAddress fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) DissociateEip(eipId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = region.Region
|
||||
params["AddressId"] = eipId
|
||||
|
||||
_, err := region.vpcRequest("DisassociateAddress", params)
|
||||
if err != nil {
|
||||
log.Errorf("UnassociateEipAddress fail %s", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) UpdateEipBandwidth(eipId string, bw int) error {
|
||||
// params := make(map[string]string)
|
||||
// params["Region"] = region.Region
|
||||
// params["AddressIds.0"] = eipId
|
||||
// params["InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bw)
|
||||
|
||||
// _, err := region.vpcRequest("ModifyAddressesBandwidth", params)
|
||||
// return err
|
||||
// 腾讯云这个接口目前有问题
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SHost struct {
|
||||
zone *SZone
|
||||
}
|
||||
|
||||
func (self *SHost) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetId() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.providerId, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) GetName() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.providerName, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.providerId, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) {
|
||||
inst, err := self.zone.region.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inst.host = self
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
func (self *SHost) CreateVM(name string, imgId string, sysDiskSize int, cpu int, memMB int,
|
||||
vswitchId string, ipAddr string, desc string, passwd string,
|
||||
storageType string, diskSizes []int, publicKey string, secgroupId string, userData string) (cloudprovider.ICloudVM, error) {
|
||||
vmId, err := self._createVM(name, imgId, sysDiskSize, cpu, memMB, vswitchId, ipAddr, desc, passwd, storageType, diskSizes, publicKey, secgroupId, userData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm, err := self.GetInstanceById(vmId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, err
|
||||
}
|
||||
|
||||
func (self *SHost) _createVM(name string, imgId string, sysDiskSize int, cpu int, memMB int,
|
||||
networkId string, ipAddr string, desc string, passwd string,
|
||||
storageType string, diskSizes []int, publicKey string, secgroupId string, userData string) (string, error) {
|
||||
|
||||
net := self.zone.getNetworkById(networkId)
|
||||
if net == nil {
|
||||
return "", fmt.Errorf("invalid network ID %s", networkId)
|
||||
}
|
||||
if net.wire == nil {
|
||||
log.Errorf("network's wire is empty")
|
||||
return "", fmt.Errorf("network's wire is empty")
|
||||
}
|
||||
if net.wire.vpc == nil {
|
||||
log.Errorf("network's wire' vpc is empty")
|
||||
return "", fmt.Errorf("network's wire's vpc is empty")
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// if len(secgroupId) == 0 {
|
||||
// secgroups, err := net.wire.vpc.GetISecurityGroups()
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("get security group error %s", err)
|
||||
// }
|
||||
|
||||
// if len(secgroups) == 0 {
|
||||
// secId, err := self.zone.region.createDefaultSecurityGroup(net.wire.vpc.VpcId)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("no secgroup for vpc and failed to create a default One!!")
|
||||
// } else {
|
||||
// secgroupId = secId
|
||||
// }
|
||||
// } else {
|
||||
// secgroupId = secgroups[0].GetId()
|
||||
// }
|
||||
// }
|
||||
|
||||
keypair := ""
|
||||
if len(publicKey) > 0 {
|
||||
keypair, err = self.zone.region.syncKeypair(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
img, err := self.zone.region.GetImage(imgId)
|
||||
if err != nil {
|
||||
log.Errorf("getiamge fail %s", err)
|
||||
return "", err
|
||||
}
|
||||
if img.ImageState != ImageStatusAvailable {
|
||||
log.Errorf("image %s status %s", imgId, img.ImageState)
|
||||
return "", fmt.Errorf("image not ready")
|
||||
}
|
||||
|
||||
err = self.zone.validateStorageType(storageType)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Storage %s not avaiable: %s", storageType, err)
|
||||
}
|
||||
|
||||
disks := make([]SDisk, len(diskSizes)+1)
|
||||
disks[0].DiskSize = img.ImageSize
|
||||
if sysDiskSize > 0 && sysDiskSize > img.ImageSize {
|
||||
disks[0].DiskSize = sysDiskSize
|
||||
}
|
||||
if disks[0].DiskSize < 50 {
|
||||
disks[0].DiskSize = 50
|
||||
}
|
||||
disks[0].DiskType = strings.ToUpper(storageType)
|
||||
|
||||
for i, sz := range diskSizes {
|
||||
disks[i+1].DiskSize = sz
|
||||
disks[i+1].DiskType = strings.ToUpper(storageType)
|
||||
}
|
||||
|
||||
instanceTypes, err := self.zone.region.GetMatchInstanceTypes(cpu, memMB, 0, self.zone.Zone)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(instanceTypes) == 0 {
|
||||
return "", fmt.Errorf("instance type %dC%dMB not avaiable", cpu, memMB)
|
||||
}
|
||||
|
||||
for _, instType := range instanceTypes {
|
||||
instanceTypeId := instType.InstanceType
|
||||
log.Debugf("Try instancetype : %s", instanceTypeId)
|
||||
vmId, err := self.zone.region.CreateInstance(name, imgId, instanceTypeId, secgroupId, self.zone.Zone, desc, passwd, disks, networkId, ipAddr, keypair, userData)
|
||||
if err != nil {
|
||||
log.Errorf("Failed for %s: %s", instanceTypeId, err)
|
||||
} else {
|
||||
return vmId, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Failed to create, specification not supported")
|
||||
}
|
||||
|
||||
func (self *SHost) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessIp() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessMac() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetSN() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuCount() int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetNodeCount() int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuDesc() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuMhz() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetMemSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) GetStatus() string {
|
||||
return models.HOST_STATUS_RUNNING
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostStatus() string {
|
||||
return models.HOST_ONLINE
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostType() string {
|
||||
return models.HOST_TYPE_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageType() string {
|
||||
return models.DISK_TYPE_HYBRID
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorageById(id)
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorages()
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMById(gid string) (cloudprovider.ICloudVM, error) {
|
||||
parts, _, err := self.zone.region.GetInstances(self.zone.Zone, []string{gid}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
parts[0].host = self
|
||||
return &parts[0], nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms := make([]SInstance, 0)
|
||||
for {
|
||||
parts, total, err := self.zone.region.GetInstances(self.zone.Zone, nil, len(vms), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vms = append(vms, parts...)
|
||||
if len(vms) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
ivms := make([]cloudprovider.ICloudVM, len(vms))
|
||||
for i := 0; i < len(vms); i++ {
|
||||
vms[i].host = self
|
||||
ivms[i] = &vms[i]
|
||||
}
|
||||
return ivms, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
return self.zone.GetIWires()
|
||||
}
|
||||
|
||||
func (self *SHost) GetManagerId() string {
|
||||
return self.zone.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SHost) GetSysInfo() jsonutils.JSONObject {
|
||||
info := jsonutils.NewDict()
|
||||
info.Add(jsonutils.NewString(CLOUD_PROVIDER_QCLOUD), "manufacture")
|
||||
return info
|
||||
}
|
||||
|
||||
func (self *SHost) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type ImageStatusType string
|
||||
|
||||
const (
|
||||
ImageStatusCreating ImageStatusType = "Creating"
|
||||
ImageStatusAvailable ImageStatusType = "NORMAL"
|
||||
ImageStatusUnAvailable ImageStatusType = "UnAvailable"
|
||||
ImageStatusCreateFailed ImageStatusType = "CreateFailed"
|
||||
)
|
||||
|
||||
type SImage struct {
|
||||
storageCache *SStoragecache
|
||||
|
||||
ImageId string // 镜像ID
|
||||
OsName string // 镜像操作系统
|
||||
ImageType string // 镜像类型
|
||||
CreatedTime time.Time // 镜像创建时间
|
||||
ImageName string // 镜像名称
|
||||
ImageDescription string // 镜像描述
|
||||
ImageSize int // 镜像大小
|
||||
Architecture string // 镜像架构
|
||||
ImageState ImageStatusType // 镜像状态
|
||||
Platform string // 镜像来源平台
|
||||
ImageCreator string // 镜像创建者
|
||||
ImageSource string // 镜像来源
|
||||
SyncPercent int // 同步百分比
|
||||
IsSupportCloudinit bool // 镜像是否支持cloud-init
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImages(status string, owner string, imageIds []string, name string, offset int, limit int) ([]SImage, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
filter := 0
|
||||
if len(status) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-state"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = status
|
||||
filter++
|
||||
}
|
||||
if imageIds != nil && len(imageIds) > 0 {
|
||||
for index, imageId := range imageIds {
|
||||
params[fmt.Sprintf("ImageIds.%d", index)] = imageId
|
||||
}
|
||||
}
|
||||
if len(owner) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-type"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = owner
|
||||
filter++
|
||||
}
|
||||
|
||||
if len(name) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "image-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = name
|
||||
filter++
|
||||
}
|
||||
|
||||
images := make([]SImage, 0)
|
||||
body, err := self.cvmRequest("DescribeImages", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
err = body.Unmarshal(&images, "ImageSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
for i := 0; i < len(images); i++ {
|
||||
images[i].storageCache = self.getStoragecache()
|
||||
}
|
||||
total, _ := body.Int("TotalCount")
|
||||
return images, int(total), nil
|
||||
}
|
||||
func (self *SImage) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SImage) GetId() string {
|
||||
return self.ImageId
|
||||
}
|
||||
|
||||
func (self *SImage) GetName() string {
|
||||
return self.ImageName
|
||||
}
|
||||
|
||||
func (self *SImage) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SImage) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s")
|
||||
}
|
||||
|
||||
func (self *SImage) Delete() error {
|
||||
return self.storageCache.region.DeleteImage(self.ImageId)
|
||||
}
|
||||
|
||||
func (self *SImage) GetStatus() string {
|
||||
return string(self.ImageState)
|
||||
}
|
||||
|
||||
func (self *SImage) Refresh() error {
|
||||
new, err := self.storageCache.region.GetImage(self.ImageId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImage(imageId string) (*SImage, error) {
|
||||
images, _, err := self.GetImages("", "", []string{imageId}, "", 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil, fmt.Errorf("image %s not found", imageId)
|
||||
}
|
||||
return &images[0], nil
|
||||
}
|
||||
|
||||
func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return self.storageCache
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteImage(imageId string) error {
|
||||
params := make(map[string]string)
|
||||
params["ImageIds.0"] = imageId
|
||||
|
||||
_, err := self.cvmRequest("DeleteImages", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImageStatus(imageId string) (ImageStatusType, error) {
|
||||
image, err := self.GetImage(imageId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return image.ImageState, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImageByName(name string) (*SImage, error) {
|
||||
images, _, err := self.GetImages("", "", nil, name, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &images[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) ImportImage(name string, osArch string, osType string, osVersion string, imageUrl string) (*SImage, error) {
|
||||
params := make(map[string]string)
|
||||
params["ImageName"] = name
|
||||
if _, err := strconv.Atoi(osVersion); len(osVersion) == 0 || err != nil {
|
||||
osVersion = "-"
|
||||
}
|
||||
params["OsVersion"] = osVersion // "6|7|8|-"
|
||||
if len(osType) == 0 || osType == "linux" {
|
||||
osType = "Other Linux"
|
||||
}
|
||||
params["OsType"] = osType // "CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"
|
||||
if len(osArch) == 0 {
|
||||
osArch = "x86_64"
|
||||
}
|
||||
params["Architecture"] = osArch // "x86_64|i386"
|
||||
params["ImageUrl"] = imageUrl
|
||||
params["Force"] = "true"
|
||||
|
||||
log.Debugf("Upload image with params %#v", params)
|
||||
|
||||
if _, err := self.cvmRequest("ImportImage", params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
image, err := self.GetImageByName(name)
|
||||
if err == nil {
|
||||
return image, nil
|
||||
}
|
||||
time.Sleep(time.Minute * time.Duration(i))
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
@@ -0,0 +1,736 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// PENDING:表示创建中
|
||||
// LAUNCH_FAILED:表示创建失败
|
||||
// RUNNING:表示运行中
|
||||
// STOPPED:表示关机
|
||||
// STARTING:表示开机中
|
||||
// STOPPING:表示关机中
|
||||
// REBOOTING:表示重启中
|
||||
// SHUTDOWN:表示停止待销毁
|
||||
// TERMINATING:表示销毁中。
|
||||
|
||||
InstanceStatusStopped = "STOPPED"
|
||||
InstanceStatusRunning = "RUNNING"
|
||||
InstanceStatusStopping = "STOPPING"
|
||||
InstanceStatusStarting = "STARTING"
|
||||
)
|
||||
|
||||
type SystemDisk struct {
|
||||
DiskType string //系统盘类型。系统盘类型限制详见CVM实例配置。取值范围:LOCAL_BASIC:本地硬盘 LOCAL_SSD:本地SSD硬盘 CLOUD_BASIC:普通云硬盘 CLOUD_SSD:SSD云硬盘 CLOUD_PREMIUM:高性能云硬盘 默认取值:CLOUD_BASIC。
|
||||
DiskId string // 系统盘ID。LOCAL_BASIC 和 LOCAL_SSD 类型没有ID。暂时不支持该参数。
|
||||
DiskSize float32 //系统盘大小,单位:GB。默认值为 50
|
||||
}
|
||||
|
||||
type DataDisk struct {
|
||||
DiskSize float32 // 数据盘大小,单位:GB。最小调整步长为10G,不同数据盘类型取值范围不同,具体限制详见:CVM实例配置。默认值为0,表示不购买数据盘。更多限制详见产品文档。
|
||||
DiskType string // 数据盘类型。数据盘类型限制详见CVM实例配置。取值范围:LOCAL_BASIC:本地硬盘 LOCAL_SSD:本地SSD硬盘 CLOUD_BASIC:普通云硬盘 CLOUD_PREMIUM:高性能云硬盘 CLOUD_SSD:SSD云硬盘 默认取值:LOCAL_BASIC。 该参数对ResizeInstanceDisk接口无效。
|
||||
DiskId string // 数据盘ID。LOCAL_BASIC 和 LOCAL_SSD 类型没有ID。暂时不支持该参数。
|
||||
DeleteWithInstance bool // 数据盘是否随子机销毁。取值范围:TRUE:子机销毁时,销毁数据盘 FALSE:子机销毁时,保留数据盘 默认取值:TRUE 该参数目前仅用于 RunInstances 接口。
|
||||
}
|
||||
|
||||
type InternetAccessible struct {
|
||||
InternetChargeType string //网络计费类型。取值范围:BANDWIDTH_PREPAID:预付费按带宽结算 TRAFFIC_POSTPAID_BY_HOUR:流量按小时后付费 BANDWIDTH_POSTPAID_BY_HOUR:带宽按小时后付费 BANDWIDTH_PACKAGE:带宽包用户 默认取值:非带宽包用户默认与子机付费类型保持一致。
|
||||
InternetMaxBandwidthOut int // 公网出带宽上限,单位:Mbps。默认值:0Mbps。不同机型带宽上限范围不一致,具体限制详见购买网络带宽。
|
||||
PublicIpAssigned bool // 是否分配公网IP。取值范围: TRUE:表示分配公网IP FALSE:表示不分配公网IP 当公网带宽大于0Mbps时,可自由选择开通与否,默认开通公网IP;当公网带宽为0,则不允许分配公网IP。
|
||||
}
|
||||
|
||||
type VirtualPrivateCloud struct {
|
||||
VpcId string // 私有网络ID,形如vpc-xxx。有效的VpcId可通过登录控制台查询;也可以调用接口 DescribeVpcEx ,从接口返回中的unVpcId字段获取。
|
||||
SubnetId string // 私有网络子网ID,形如subnet-xxx。有效的私有网络子网ID可通过登录控制台查询;也可以调用接口 DescribeSubnets ,从接口返回中的unSubnetId字段获取。
|
||||
AsVpcGateway bool // 是否用作公网网关。公网网关只有在实例拥有公网IP以及处于私有网络下时才能正常使用。取值范围:TRUE:表示用作公网网关 FALSE:表示不用作公网网关 默认取值:FALSE。
|
||||
PrivateIpAddresses []string // 私有网络子网 IP 数组,在创建实例、修改实例vpc属性操作中可使用此参数。当前仅批量创建多台实例时支持传入相同子网的多个 IP。
|
||||
}
|
||||
|
||||
type LoginSettings struct {
|
||||
Password string //实例登录密码。不同操作系统类型密码复杂度限制不一样,具体如下:Linux实例密码必须8到16位,至少包括两项[a-z,A-Z]、[0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = | { } [ ] : ; ' , . ? / ]中的特殊符号。<br><li>Windows实例密码必须12到16位,至少包括三项[a-z],[A-Z],[0-9] 和 [( ) ~ ! @ # $ % ^ & * - + = { } [ ] : ; ' , . ? /]中的特殊符号。 若不指定该参数,则由系统随机生成密码,并通过站内信方式通知到用户。
|
||||
KeyIds []string // 密钥ID列表。关联密钥后,就可以通过对应的私钥来访问实例;KeyId可通过接口DescribeKeyPairs获取,密钥与密码不能同时指定,同时Windows操作系统不支持指定密钥。当前仅支持购买的时候指定一个密钥。
|
||||
KeepImageLogin string // 保持镜像的原始设置。该参数与Password或KeyIds.N不能同时指定。只有使用自定义镜像、共享镜像或外部导入镜像创建实例时才能指定该参数为TRUE。取值范围: TRUE:表示保持镜像的登录设置 FALSE:表示不保持镜像的登录设置 默认取值:FALSE。
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type SInstance struct {
|
||||
host *SHost
|
||||
|
||||
image *SImage
|
||||
idisks []cloudprovider.ICloudDisk
|
||||
|
||||
Placement Placement
|
||||
InstanceId string
|
||||
InstanceType string
|
||||
CPU int8
|
||||
Memory int
|
||||
RestrictState string //NORMAL EXPIRED PROTECTIVELY_ISOLATED
|
||||
InstanceName string
|
||||
InstanceChargeType InstanceChargeType //PREPAID:表示预付费,即包年包月 POSTPAID_BY_HOUR:表示后付费,即按量计费 CDHPAID:CDH付费,即只对CDH计费,不对CDH上的实例计费。
|
||||
SystemDisk SystemDisk //实例系统盘信息。
|
||||
DataDisks []DataDisk //实例数据盘信息。只包含随实例购买的数据盘。
|
||||
PrivateIpAddresses []string //实例主网卡的内网IP列表。
|
||||
PublicIpAddresses []string //实例主网卡的公网IP列表。
|
||||
InternetAccessible InternetAccessible //实例带宽信息。
|
||||
VirtualPrivateCloud VirtualPrivateCloud //实例所属虚拟私有网络信息。
|
||||
ImageId string // 生产实例所使用的镜像ID。
|
||||
RenewFlag string // 自动续费标识。取值范围:NOTIFY_AND_MANUAL_RENEW:表示通知即将过期,但不自动续费 NOTIFY_AND_AUTO_RENEW:表示通知即将过期,而且自动续费 DISABLE_NOTIFY_AND_MANUAL_RENEW:表示不通知即将过期,也不自动续费。
|
||||
CreatedTime time.Time // 创建时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
ExpiredTime time.Time // 到期时间。按照ISO8601标准表示,并且使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。
|
||||
OsName string // 操作系统名称。
|
||||
SecurityGroupIds []string // 实例所属安全组。该参数可以通过调用 DescribeSecurityGroups 的返回值中的sgId字段来获取。
|
||||
LoginSettings LoginSettings //实例登录设置。目前只返回实例所关联的密钥。
|
||||
InstanceState string // 实例状态。取值范围:PENDING:表示创建中 LAUNCH_FAILED:表示创建失败 RUNNING:表示运行中 STOPPED:表示关机 STARTING:表示开机中 STOPPING:表示关机中 REBOOTING:表示重启中 SHUTDOWN:表示停止待销毁 TERMINATING:表示销毁中。
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstances(zoneId string, ids []string, offset int, limit int) ([]SInstance, int, error) {
|
||||
params := make(map[string]string)
|
||||
if limit < 1 || limit > 50 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
instances := make([]SInstance, 0)
|
||||
if ids != nil && len(ids) > 0 {
|
||||
for index, id := range ids {
|
||||
params[fmt.Sprintf("InstanceIds.%d", index)] = id
|
||||
}
|
||||
} else {
|
||||
if len(zoneId) > 0 {
|
||||
params["Filters.0.Name"] = "zone"
|
||||
params["Filters.0.Values.0"] = zoneId
|
||||
}
|
||||
}
|
||||
body, err := self.cvmRequest("DescribeInstances", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
err = body.Unmarshal(&instances, "InstanceSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return instances, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetMetadata() *jsonutils.JSONDict {
|
||||
data := jsonutils.NewDict()
|
||||
if self.image == nil {
|
||||
image, err := self.host.zone.region.GetImage(self.ImageId)
|
||||
if err == nil {
|
||||
self.image = image
|
||||
}
|
||||
}
|
||||
|
||||
if self.image != nil {
|
||||
data.Add(jsonutils.NewString(self.image.OsName), "os_distribution")
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func (self *SInstance) GetCreateTime() time.Time {
|
||||
return self.CreatedTime
|
||||
}
|
||||
|
||||
func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
|
||||
return self.host
|
||||
}
|
||||
|
||||
func (self *SInstance) GetId() string {
|
||||
return self.InstanceId
|
||||
}
|
||||
|
||||
func (self *SInstance) GetName() string {
|
||||
return self.InstanceName
|
||||
}
|
||||
|
||||
func (self *SInstance) GetGlobalId() string {
|
||||
return self.InstanceId
|
||||
}
|
||||
|
||||
func (self *SInstance) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SInstance) getVpc() (*SVpc, error) {
|
||||
return self.host.zone.region.getVpc(self.VirtualPrivateCloud.VpcId)
|
||||
}
|
||||
|
||||
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks, total, err := self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, 0, 50)
|
||||
if err != nil {
|
||||
log.Errorf("fetchDisks fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
if total > len(disks) {
|
||||
disks, _, err = self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, 0, total)
|
||||
}
|
||||
idisks := make([]cloudprovider.ICloudDisk, len(disks))
|
||||
for i := 0; i < len(disks); i += 1 {
|
||||
store, err := self.host.zone.getStorageByCategory(disks[i].DiskType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disks[i].storage = store
|
||||
idisks[i] = &disks[i]
|
||||
}
|
||||
if utils.IsInStringArray(self.SystemDisk.DiskType, []string{"LOCAL_BASIC", "LOCAL_SSD"}) {
|
||||
storage := SLocalStorage{zone: self.host.zone, storageType: self.SystemDisk.DiskType}
|
||||
disk := SLocalDisk{
|
||||
storage: &storage,
|
||||
DiskId: self.SystemDisk.DiskId,
|
||||
DiskSize: self.SystemDisk.DiskSize,
|
||||
DisktType: self.SystemDisk.DiskType,
|
||||
DiskUsage: "SYSTEM_DISK",
|
||||
}
|
||||
idisks = append(idisks, &disk)
|
||||
}
|
||||
|
||||
for i := 0; i < len(self.DataDisks); i++ {
|
||||
if utils.IsInStringArray(self.DataDisks[i].DiskType, []string{"LOCAL_BASIC", "LOCAL_SSD"}) {
|
||||
storage := SLocalStorage{zone: self.host.zone, storageType: self.DataDisks[i].DiskType}
|
||||
disk := SLocalDisk{
|
||||
storage: &storage,
|
||||
DiskId: self.DataDisks[i].DiskId,
|
||||
DiskSize: self.DataDisks[i].DiskSize,
|
||||
DisktType: self.DataDisks[i].DiskType,
|
||||
DiskUsage: "DATA_DISK",
|
||||
}
|
||||
idisks = append(idisks, &disk)
|
||||
}
|
||||
}
|
||||
|
||||
return idisks, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
|
||||
nics := make([]cloudprovider.ICloudNic, 0)
|
||||
for _, ip := range self.VirtualPrivateCloud.PrivateIpAddresses {
|
||||
nic := SInstanceNic{instance: self, ipAddr: ip}
|
||||
nics = append(nics, &nic)
|
||||
}
|
||||
return nics, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVcpuCount() int8 {
|
||||
return self.CPU
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVmemSizeMB() int {
|
||||
return self.Memory * 1024
|
||||
}
|
||||
|
||||
func (self *SInstance) GetBootOrder() string {
|
||||
return "dcn"
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVga() string {
|
||||
return "std"
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVdi() string {
|
||||
return "vnc"
|
||||
}
|
||||
|
||||
func (self *SInstance) GetOSType() string {
|
||||
if self.image == nil {
|
||||
image, err := self.host.zone.region.GetImage(self.ImageId)
|
||||
if err != nil {
|
||||
return self.OsName
|
||||
}
|
||||
self.image = image
|
||||
}
|
||||
if self.image != nil {
|
||||
switch self.image.Platform {
|
||||
case "Windows":
|
||||
return "Windows"
|
||||
case "CentOS", "Debian", "FreeBSD", "SUSE", "openSUSE":
|
||||
return "Linux"
|
||||
default:
|
||||
return "Linux"
|
||||
}
|
||||
}
|
||||
return self.OsName
|
||||
}
|
||||
|
||||
func (self *SInstance) GetOSName() string {
|
||||
return self.OsName
|
||||
}
|
||||
|
||||
func (self *SInstance) GetBios() string {
|
||||
return "BIOS"
|
||||
}
|
||||
|
||||
func (self *SInstance) GetMachine() string {
|
||||
return "pc"
|
||||
}
|
||||
|
||||
func (self *SInstance) GetStatus() string {
|
||||
switch self.InstanceState {
|
||||
case "PENDING":
|
||||
return models.VM_DEPLOYING
|
||||
case "LAUNCH_FAILED":
|
||||
return models.VM_DEPLOY_FAILED
|
||||
case "RUNNING":
|
||||
return models.VM_RUNNING
|
||||
case "STOPPED":
|
||||
return models.VM_READY
|
||||
case "STARTING", "REBOOTING":
|
||||
return models.VM_STARTING
|
||||
case "STOPPING":
|
||||
return models.VM_STOPPING
|
||||
case "SHUTDOWN":
|
||||
return models.VM_DEALLOCATED
|
||||
case "TERMINATING":
|
||||
return models.VM_DELETING
|
||||
default:
|
||||
return models.VM_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SInstance) Refresh() error {
|
||||
new, err := self.host.zone.region.GetInstance(self.InstanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SInstance) GetHypervisor() string {
|
||||
return models.HYPERVISOR_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SInstance) StartVM() error {
|
||||
timeout := 300 * time.Second
|
||||
interval := 15 * time.Second
|
||||
|
||||
startTime := time.Now()
|
||||
for time.Now().Sub(startTime) < timeout {
|
||||
err := self.Refresh()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("status %s expect %s", self.GetStatus(), models.VM_RUNNING)
|
||||
if self.GetStatus() == models.VM_RUNNING {
|
||||
return nil
|
||||
}
|
||||
if self.GetStatus() == models.VM_READY {
|
||||
err := self.host.zone.region.StartVM(self.InstanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
time.Sleep(interval)
|
||||
}
|
||||
return cloudprovider.ErrTimeout
|
||||
}
|
||||
|
||||
func (self *SInstance) StopVM(isForce bool) error {
|
||||
err := self.host.zone.region.StopVM(self.InstanceId, isForce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, models.VM_READY, 10*time.Second, 300*time.Second) // 5mintues
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
||||
url, err := self.host.zone.region.GetInstanceVNCUrl(self.InstanceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(jsonutils.NewString("https://img.qcloud.com/qcloud/app/active_vnc/index.html?InstanceVncUrl="+url), "url")
|
||||
ret.Add(jsonutils.NewString("qcloud"), "protocol")
|
||||
ret.Add(jsonutils.NewString(self.InstanceId), "instance_id")
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) UpdateVM(name string) error {
|
||||
return self.host.zone.region.UpdateVM(self.InstanceId, name)
|
||||
}
|
||||
|
||||
func (self *SInstance) DeployVM(name string, password string, publicKey string, deleteKeypair bool, description string) error {
|
||||
var keypairName string
|
||||
if len(publicKey) > 0 {
|
||||
var err error
|
||||
keypairName, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return self.host.zone.region.DeployVM(self.InstanceId, name, password, keypairName, deleteKeypair, description)
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
|
||||
keypair := ""
|
||||
if len(publicKey) > 0 {
|
||||
var err error
|
||||
keypair, err = self.host.zone.region.syncKeypair(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, imageId, passwd, keypair, sysSizeGB)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
self.StopVM(true)
|
||||
instance, err := self.host.zone.region.GetInstance(self.InstanceId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.SystemDisk.DiskId, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) ChangeConfig(instanceId string, ncpu int, vmem int) error {
|
||||
return self.host.zone.region.ChangeVMConfig(self.Placement.Zone, self.InstanceId, ncpu, vmem, nil)
|
||||
}
|
||||
|
||||
func (self *SInstance) AttachDisk(diskId string) error {
|
||||
return self.host.zone.region.AttachDisk(self.InstanceId, diskId)
|
||||
}
|
||||
|
||||
func (self *SInstance) DetachDisk(diskId string) error {
|
||||
return self.host.zone.region.DetachDisk(self.InstanceId, diskId)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
|
||||
instances, _, err := self.GetInstances("", []string{instanceId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(instances) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &instances[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateInstance(name string, imageId string, instanceType string, securityGroupId string,
|
||||
zoneId string, desc string, passwd string, disks []SDisk, networkId string, ipAddr string,
|
||||
keypair string, userData string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["ImageId"] = imageId
|
||||
params["InstanceType"] = instanceType
|
||||
//params["SecurityGroupId"] = securityGroupId
|
||||
params["Placement.Zone"] = zoneId
|
||||
params["InstanceName"] = name
|
||||
params["Description"] = desc
|
||||
params["InstanceChargeType"] = "POSTPAID_BY_HOUR"
|
||||
params["InternetAccessible.InternetMaxBandwidthOut"] = "100"
|
||||
params["InternetAccessible.PublicIpAssigned"] = "FALSE"
|
||||
params["HostName"] = name
|
||||
if len(passwd) > 0 {
|
||||
params["LoginSettings.Password"] = passwd
|
||||
} else {
|
||||
params["LoginSettings.KeepImageLogin"] = "TRUE"
|
||||
}
|
||||
|
||||
if len(userData) > 0 {
|
||||
params["UserData"] = userData
|
||||
}
|
||||
|
||||
//params["IoOptimized"] = "optimized"
|
||||
for i, d := range disks {
|
||||
if i == 0 {
|
||||
params["SystemDisk.DiskType"] = d.DiskType
|
||||
params["SystemDisk.DiskSize"] = fmt.Sprintf("%d", d.DiskSize)
|
||||
} else {
|
||||
params[fmt.Sprintf("DataDisks.%d.DiskSize", i-1)] = fmt.Sprintf("%d", d.DiskSize)
|
||||
params[fmt.Sprintf("DataDisks.%d.DiskType", i-1)] = d.DiskType
|
||||
}
|
||||
}
|
||||
network, err := self.GetNetwork(networkId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params["VirtualPrivateCloud.SubnetId"] = networkId
|
||||
params["VirtualPrivateCloud.VpcId"] = network.VpcId
|
||||
if len(ipAddr) > 0 {
|
||||
params["VirtualPrivateCloud.PrivateIpAddresses.0"] = ipAddr
|
||||
}
|
||||
// if len(keypair) > 0 {
|
||||
// params["KeyPairName"] = keypair
|
||||
// }
|
||||
params["ClientToken"] = utils.GenRequestId(20)
|
||||
//log.Errorf("create params: %s", jsonutils.Marshal(params).PrettyString())
|
||||
instanceIdSet := []string{}
|
||||
body, err := self.cvmRequest("RunInstances", params)
|
||||
if err != nil {
|
||||
log.Errorf("RunInstances fail %s", err)
|
||||
return "", err
|
||||
}
|
||||
err = body.Unmarshal(&instanceIdSet, "InstanceIdSet")
|
||||
if err == nil && len(instanceIdSet) > 0 {
|
||||
return instanceIdSet[0], nil
|
||||
}
|
||||
return "", fmt.Errorf("Failed to create instance")
|
||||
}
|
||||
|
||||
func (self *SRegion) doStartVM(instanceId string) error {
|
||||
return self.instanceOperation(instanceId, "StartInstances", nil)
|
||||
}
|
||||
|
||||
func (self *SRegion) doStopVM(instanceId string, isForce bool) error {
|
||||
params := make(map[string]string)
|
||||
if isForce {
|
||||
params["ForceStop"] = "true"
|
||||
} else {
|
||||
params["ForceStop"] = "false"
|
||||
}
|
||||
return self.instanceOperation(instanceId, "StopInstances", params)
|
||||
}
|
||||
|
||||
func (self *SRegion) doDeleteVM(instanceId string) error {
|
||||
params := make(map[string]string)
|
||||
return self.instanceOperation(instanceId, "TerminateInstances", params)
|
||||
}
|
||||
|
||||
func (self *SRegion) StartVM(instanceId string) error {
|
||||
status, err := self.GetInstanceStatus(instanceId)
|
||||
if err != nil {
|
||||
log.Errorf("Fail to get instance status on StartVM: %s", err)
|
||||
return err
|
||||
}
|
||||
if status != InstanceStatusStopped {
|
||||
log.Errorf("StartVM: vm status is %s expect %s", status, InstanceStatusStopped)
|
||||
return cloudprovider.ErrInvalidStatus
|
||||
}
|
||||
return self.doStartVM(instanceId)
|
||||
}
|
||||
|
||||
func (self *SRegion) StopVM(instanceId string, isForce bool) error {
|
||||
status, err := self.GetInstanceStatus(instanceId)
|
||||
if err != nil {
|
||||
log.Errorf("Fail to get instance status on StopVM: %s", err)
|
||||
return err
|
||||
}
|
||||
if status == InstanceStatusStopped {
|
||||
return nil
|
||||
}
|
||||
return self.doStopVM(instanceId, isForce)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteVM(instanceId string) error {
|
||||
status, err := self.GetInstanceStatus(instanceId)
|
||||
if err != nil {
|
||||
log.Errorf("Fail to get instance status on DeleteVM: %s", err)
|
||||
return err
|
||||
}
|
||||
log.Debugf("Instance status on delete is %s", status)
|
||||
if status != InstanceStatusStopped {
|
||||
log.Warningf("DeleteVM: vm status is %s expect %s", status, InstanceStatusStopped)
|
||||
}
|
||||
return self.doDeleteVM(instanceId)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeployVM(instanceId string, name string, password string, keypairName string, deleteKeypair bool, description string) error {
|
||||
instance, err := self.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改密钥时直接返回
|
||||
if deleteKeypair {
|
||||
for i := 0; i < len(instance.LoginSettings.KeyIds); i++ {
|
||||
err = self.DetachKeyPair(instanceId, instance.LoginSettings.KeyIds[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(keypairName) > 0 {
|
||||
err = self.AttachKeypair(instanceId, keypairName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
params := make(map[string]string)
|
||||
|
||||
if len(name) > 0 && instance.InstanceName != name {
|
||||
params["InstanceName"] = name
|
||||
}
|
||||
|
||||
if len(params) > 0 {
|
||||
err := self.modifyInstanceAttribute(instanceId, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(password) > 0 {
|
||||
return self.instanceOperation(instanceId, "ResetInstancesPassword", map[string]string{"Password": password})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) DeleteVM() error {
|
||||
for {
|
||||
err := self.host.zone.region.DeleteVM(self.InstanceId)
|
||||
if err != nil {
|
||||
// if isError(err, "IncorrectInstanceStatus.Initializing") {
|
||||
// log.Infof("The instance is initializing, try later ...")
|
||||
// time.Sleep(10 * time.Second)
|
||||
// } else {
|
||||
// return err
|
||||
// }
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return cloudprovider.WaitDeleted(self, 10*time.Second, 300*time.Second) // 5minutes
|
||||
}
|
||||
|
||||
func (self *SRegion) UpdateVM(instanceId string, hostname string) error {
|
||||
params := make(map[string]string)
|
||||
params["HostName"] = hostname
|
||||
return self.modifyInstanceAttribute(instanceId, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) modifyInstanceAttribute(instanceId string, params map[string]string) error {
|
||||
return self.instanceOperation(instanceId, "ModifyInstancesAttribute", params)
|
||||
}
|
||||
|
||||
func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instanceId
|
||||
params["ImageId"] = imageId
|
||||
params["EnhancedService.SecurityService.Enabled"] = "TRUE"
|
||||
params["EnhancedService.MonitorService.Enabled"] = "TRUE"
|
||||
if len(passwd) > 0 {
|
||||
params["LoginSettings.Password"] = passwd
|
||||
} else {
|
||||
params["LoginSettings.KeepImageLogin"] = "TRUE"
|
||||
}
|
||||
if len(keypairName) > 0 {
|
||||
params["LoginSettings.KeyIds.0"] = keypairName
|
||||
}
|
||||
if sysDiskSizeGB > 0 {
|
||||
params["SystemDisk.DiskSize"] = fmt.Sprintf("%d", sysDiskSizeGB)
|
||||
}
|
||||
_, err := self.cvmRequest("ResetInstance", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error {
|
||||
// todo: support change disk config?
|
||||
params := make(map[string]string)
|
||||
instanceTypes, e := self.GetMatchInstanceTypes(ncpu, vmem, 0, zoneId)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for _, instancetype := range instanceTypes {
|
||||
params["InstanceType"] = instancetype.InstanceType
|
||||
err := self.instanceOperation(instanceId, "ResetInstancesType", params)
|
||||
if err != nil {
|
||||
log.Errorf("Failed for %s: %s", instancetype.InstanceType, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Failed to change vm config, specification not supported")
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instanceId
|
||||
params["DiskId"] = diskId
|
||||
log.Infof("Detach instance %s disk %s", instanceId, diskId)
|
||||
_, err := self.cvmRequest("DetachDisk", params)
|
||||
if err != nil {
|
||||
log.Errorf("DetachDisk %s to %s fail %s", diskId, instanceId, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) AttachDisk(instanceId string, diskId string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instanceId
|
||||
params["DiskIds.0"] = diskId
|
||||
_, err := self.cbsRequest("AttachDisks", params)
|
||||
if err != nil {
|
||||
log.Errorf("AttachDisks %s to %s fail %s", diskId, instanceId, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) error {
|
||||
// if vpc, err := self.getVpc(); err != nil {
|
||||
// return err
|
||||
// } else if len(secgroupId) == 0 {
|
||||
// for index, secgrpId := range self.SecurityGroupIds.SecurityGroupId {
|
||||
// if err := vpc.revokeSecurityGroup(secgrpId, self.InstanceId, index == 0); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// } else if secgrpId, err := vpc.SyncSecurityGroup(secgroupId, name, rules); err != nil {
|
||||
// return err
|
||||
// } else if err := vpc.assignSecurityGroup(secgrpId, self.InstanceId); err != nil {
|
||||
// return err
|
||||
// } else {
|
||||
// for _, secgroupId := range self.SecurityGroupIds.SecurityGroupId {
|
||||
// if secgroupId != secgrpId {
|
||||
// if err := vpc.revokeSecurityGroup(secgroupId, self.InstanceId, false); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// self.SecurityGroupIds.SecurityGroupId = []string{secgrpId}
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
if len(self.PublicIpAddresses) > 0 {
|
||||
eip := SEipAddress{region: self.host.zone.region}
|
||||
eip.AddressIp = self.PublicIpAddresses[0]
|
||||
eip.InstanceId = self.InstanceId
|
||||
eip.AddressId = self.InstanceId
|
||||
eip.AddressName = self.PublicIpAddresses[0]
|
||||
eip.AddressType = "WanIP"
|
||||
eip.AddressStatus = EIP_STATUS_INUSE
|
||||
return &eip, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetBillingType() string {
|
||||
switch self.InstanceChargeType {
|
||||
case PrePaidInstanceChargeType:
|
||||
return models.BILLING_TYPE_PREPAID
|
||||
case PostPaidInstanceChargeType:
|
||||
return models.BILLING_TYPE_POSTPAID
|
||||
default:
|
||||
return models.BILLING_TYPE_PREPAID
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SInstance) GetExpiredAt() time.Time {
|
||||
return self.ExpiredTime
|
||||
}
|
||||
|
||||
func (self *SInstance) UpdateUserData(userData string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SInstanceNic struct {
|
||||
instance *SInstance
|
||||
ipAddr string
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetIP() string {
|
||||
return self.ipAddr
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetMAC() string {
|
||||
ip, _ := netutils.NewIPV4Addr(self.ipAddr)
|
||||
return ip.ToMac("00:16:")
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetDriver() string {
|
||||
return "virtio"
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork {
|
||||
networkId := self.instance.VirtualPrivateCloud.VpcId
|
||||
wires, err := self.instance.host.GetIWires()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(wires); i++ {
|
||||
wire := wires[i].(*SWire)
|
||||
net := wire.getNetworkById(networkId)
|
||||
if net != nil {
|
||||
return net
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package qcloud
|
||||
|
||||
import "yunion.io/x/log"
|
||||
|
||||
// "time"
|
||||
|
||||
// {"CpuCoreCount":1,"EniQuantity":1,"GPUAmount":0,"GPUSpec":"","InstanceTypeFamily":"ecs.t1","InstanceTypeId":"ecs.t1.xsmall","LocalStorageCategory":"","MemorySize":0.500000}
|
||||
// InstanceBandwidthRx":26214400,"InstanceBandwidthTx":26214400,"InstancePpsRx":4500000,"InstancePpsTx":4500000
|
||||
|
||||
type SInstanceType struct {
|
||||
Zone string // 可用区。
|
||||
InstanceType string // 实例机型。
|
||||
InstanceFamily string // 实例机型系列。
|
||||
GPU int // GPU核数,单位:核。
|
||||
CPU int // CPU核数,单位:核。
|
||||
Memory int // 内存容量,单位:GB。
|
||||
CbsSupport string // 是否支持云硬盘。取值范围:TRUE:表示支持云硬盘;FALSE:表示不支持云硬盘。
|
||||
InstanceTypeState string // 机型状态。取值范围:AVAILABLE:表示机型可用;UNAVAILABLE:表示机型不可用。
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceTypes() ([]SInstanceType, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
|
||||
body, err := self.cvmRequest("DescribeInstanceTypeConfigs", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeInstanceTypeConfigs fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceTypes := make([]SInstanceType, 0)
|
||||
err = body.Unmarshal(&instanceTypes, "InstanceTypeConfigSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal instance type details fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return instanceTypes, nil
|
||||
}
|
||||
|
||||
func (self *SInstanceType) memoryMB() int {
|
||||
return int(self.Memory * 1024)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aokoli/goutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SKeypair struct {
|
||||
AssociatedInstanceIds []string
|
||||
CreateTime time.Time
|
||||
Description string
|
||||
KeyId string
|
||||
KeyName string
|
||||
PublicKey string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetKeypairs(name string, keyIds []string, offset int, limit int) ([]SKeypair, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := map[string]string{}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
if len(keyIds) > 0 {
|
||||
for i := 0; i < len(keyIds); i++ {
|
||||
params[fmt.Sprintf("KeyIds.%d", i)] = keyIds[i]
|
||||
}
|
||||
} else {
|
||||
if len(name) > 0 {
|
||||
params["Filters.0.Name"] = "key-name"
|
||||
params["Filters.0.Values.0"] = name
|
||||
}
|
||||
}
|
||||
|
||||
body, err := self.cvmRequest("DescribeKeyPairs", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetKeypairs fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
keypairs := []SKeypair{}
|
||||
err = body.Unmarshal(&keypairs, "KeyPairSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal keypair fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Int("TotalCount")
|
||||
return keypairs, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) ImportKeypair(name string, pubKey string) (*SKeypair, error) {
|
||||
params := map[string]string{}
|
||||
params["PublicKey"] = pubKey
|
||||
params["ProjectId"] = "0"
|
||||
params["KeyName"] = name
|
||||
|
||||
body, err := self.cvmRequest("ImportKeyPair", params)
|
||||
if err != nil {
|
||||
log.Errorf("ImportKeypair fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keypairID, err := body.GetString("KeyId")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keypairs, total, err := self.GetKeypairs("", []string{keypairID}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &keypairs[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) AttachKeypair(instanceId string, keypairId string) error {
|
||||
params := map[string]string{}
|
||||
params["InstanceIds.0"] = instanceId
|
||||
params["KeyIds.0"] = keypairId
|
||||
_, err := self.cvmRequest("AssociateInstancesKeyPairs", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachKeyPair(instanceId string, keypairId string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceIds.0"] = instanceId
|
||||
params["KeyIds.0"] = keypairId
|
||||
_, err := self.cvmRequest("DisassociateInstancesKeyPairs", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateKeyPair(name string) (*SKeypair, error) {
|
||||
params := make(map[string]string)
|
||||
params["KeyName"] = name
|
||||
params["ProjectId"] = "0"
|
||||
body, err := self.cvmRequest("CreateKeyPair", params)
|
||||
keypair := SKeypair{}
|
||||
err = body.Unmarshal(&keypair, "KeyPair")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &keypair, err
|
||||
}
|
||||
|
||||
func (self *SRegion) lookUpAliyunKeypair(publicKey string) (string, error) {
|
||||
keypairs, _, err := self.GetKeypairs("", []string{}, 0, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := 0; i < len(keypairs); i++ {
|
||||
if keypairs[i].PublicKey == publicKey {
|
||||
return keypairs[i].KeyId, nil
|
||||
}
|
||||
}
|
||||
return "", cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) syncKeypair(publicKey string) (string, error) {
|
||||
keypairId, e := self.lookUpAliyunKeypair(publicKey)
|
||||
if e == nil {
|
||||
return keypairId, nil
|
||||
}
|
||||
|
||||
prefix, e := goutils.RandomAlphabetic(6)
|
||||
if e != nil {
|
||||
return "", fmt.Errorf("publicKey error %s", e)
|
||||
}
|
||||
|
||||
name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
|
||||
keypair, err := self.ImportKeypair(name, publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return keypair.KeyId, nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package qcloud
|
||||
|
||||
var LatitudeAndLongitude = map[string]map[string]float32{
|
||||
"ap-bangkok": {"latitude": 13.756330, "longitude": 100.501762}, // 腾讯云 亚太地区(曼谷)
|
||||
"ap-beijing": {"latitude": 39.904202, "longitude": 116.407394}, // 腾讯云 华北地区(北京)
|
||||
"ap-chengdu": {"latitude": 30.572815, "longitude": 104.066803}, // 腾讯云 西南地区(成都)
|
||||
"ap-chongqing": {"latitude": 29.431585, "longitude": 106.912254}, // 腾讯云 西南地区(重庆)
|
||||
"ap-guangzhou": {"latitude": 23.129110, "longitude": 113.264381}, // 腾讯云 华南地区(广州)
|
||||
"ap-guangzhou-open": {"latitude": 23.126593, "longitude": 113.273415}, // 腾讯云 华南地区(广州Open)
|
||||
"ap-hongkong": {"latitude": 22.396427, "longitude": 114.109497}, // 腾讯云 东南亚地区(香港)
|
||||
"ap-mumbai": {"latitude": 19.075983, "longitude": 72.877655}, // 腾讯云 亚太地区(孟买)
|
||||
"ap-seoul": {"latitude": 37.566536, "longitude": 126.977966}, // 腾讯云 东南亚地区(首尔)
|
||||
"ap-shanghai": {"latitude": 31.230391, "longitude": 121.473701}, // 腾讯云 华东地区(上海)
|
||||
"ap-shanghai-fsi": {"latitude": 31.311033, "longitude": 121.536217}, // 腾讯云 华东地区(上海金融)
|
||||
"ap-shenzhen-fsi": {"latitude": 22.531544, "longitude": 114.025467}, // 腾讯云 华南地区(深圳金融)
|
||||
"ap-singapore": {"latitude": 1.352083, "longitude": 103.819839}, // 腾讯云 东南亚地区(新加坡)
|
||||
"ap-tokyo": {"latitude": 35.709026, "longitude": 139.731995}, // 腾讯云 亚太地区(东京)
|
||||
"eu-frankfurt": {"latitude": 51.165691, "longitude": 10.451526}, // 腾讯云 欧洲地区(德国)
|
||||
"eu-moscow": {"latitude": 55.755825, "longitude": 37.617298}, // 腾讯云 欧洲地区(莫斯科)
|
||||
"na-ashburn": {"latitude": 37.431572, "longitude": -78.656891}, // 腾讯云 美国东部(弗吉尼亚)
|
||||
"na-siliconvalley": {"latitude": 37.387474, "longitude": -122.057541}, // 腾讯云 美国西部(硅谷)
|
||||
"na-toronto": {"latitude": 43.653225, "longitude": -79.383186}, // 腾讯云 北美地区(多伦多)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SLocalDisk struct {
|
||||
storage *SLocalStorage
|
||||
DiskId string
|
||||
DiskSize float32
|
||||
DisktType string
|
||||
DiskUsage string
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) CreateISnapshot(name, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetBillingType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetFsFormat() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIsNonPersistent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDriver() string {
|
||||
return "scsi"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetCacheMode() string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetMountpoint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskFormat() string {
|
||||
return "vhd"
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskSizeMB() int {
|
||||
return int(self.DiskSize) * 1024
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIsAutoDelete() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetExpiredAt() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetDiskType() string {
|
||||
switch self.DiskUsage {
|
||||
case "SYSTEM_DISK":
|
||||
return models.DISK_TYPE_SYS
|
||||
case "DATA_DISK":
|
||||
return models.DISK_TYPE_DATA
|
||||
default:
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Reset(snapshotId string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetTemplateId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetStatus() string {
|
||||
return models.DISK_READY
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetName() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetGlobalId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) GetIStorge() cloudprovider.ICloudStorage {
|
||||
return self.storage
|
||||
}
|
||||
|
||||
func (self *SLocalDisk) Resize(size int64) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SLocalStorage struct {
|
||||
zone *SZone
|
||||
storageType string
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetName() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerName, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetGlobalId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks := []SLocalDisk{}
|
||||
idisks := make([]cloudprovider.ICloudDisk, len(disks))
|
||||
for i := 0; i < len(disks); i++ {
|
||||
disks[i].storage = self
|
||||
idisks[i] = &disks[i]
|
||||
}
|
||||
return idisks, nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStorageType() string {
|
||||
return strings.ToLower(self.storageType)
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetMediumType() string {
|
||||
if strings.HasSuffix(self.storageType, "_BASIC") {
|
||||
return models.DISK_TYPE_ROTATE
|
||||
}
|
||||
return models.DISK_TYPE_SSD
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetCapacityMB() int {
|
||||
return 0 // unlimited
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStorageConf() jsonutils.JSONObject {
|
||||
conf := jsonutils.NewDict()
|
||||
return conf
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetManagerId() string {
|
||||
return self.zone.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetStatus() string {
|
||||
return models.STORAGE_ONLINE
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) Refresh() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return self.zone.region.getStoragecache()
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLocalStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) {
|
||||
return &SLocalDisk{storage: self, DiskId: idStr}, nil
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
)
|
||||
|
||||
type SNetwork struct {
|
||||
wire *SWire
|
||||
|
||||
CidrBlock string
|
||||
Zone string
|
||||
SubnetId string
|
||||
VpcId string
|
||||
SubnetName string
|
||||
AvailableIpAddressCount int
|
||||
CreatedTime time.Time
|
||||
EnableBroadcast bool
|
||||
IsDefault bool
|
||||
RouteTableId string
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetId() string {
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetName() string {
|
||||
if len(self.SubnetName) > 0 {
|
||||
return self.SubnetName
|
||||
}
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGlobalId() string {
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SNetwork) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetStatus() string {
|
||||
return models.NETWORK_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SNetwork) Delete() error {
|
||||
return self.wire.zone.region.DeleteNetwork(self.SubnetId)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNetwork(networkId string) error {
|
||||
params := make(map[string]string)
|
||||
params["SubnetId"] = networkId
|
||||
|
||||
_, err := self.vpcRequest("DeleteSubnet", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
|
||||
return self.wire
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
return 120 // 2 minutes
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGateway() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpStart() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
|
||||
startIp = startIp.StepUp() // 1
|
||||
return startIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpEnd() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
endIp = endIp.StepDown() // 253
|
||||
endIp = endIp.StepDown() // 252
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpMask() int8 {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CidrBlock)
|
||||
return pref.MaskLen
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIsPublic() bool {
|
||||
// return self.IsDefault
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetServerType() string {
|
||||
return models.SERVER_TYPE_GUEST
|
||||
}
|
||||
|
||||
func (self *SNetwork) Refresh() error {
|
||||
log.Debugf("network refresh %s", self.SubnetId)
|
||||
new, err := self.wire.zone.region.GetNetwork(self.SubnetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNetwork(zoneId string, vpcId string, name string, cidr string, desc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["Zone"] = zoneId
|
||||
params["VpcId"] = vpcId
|
||||
params["CidrBlock"] = cidr
|
||||
params["SubnetName"] = name
|
||||
body, err := self.vpcRequest("CreateSubnet", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("Subnet", "SubnetId")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package provider
|
||||
@@ -0,0 +1,84 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
)
|
||||
|
||||
type SQcloudProviderFactory struct {
|
||||
// providerTable map[string]*SQcloudProvider
|
||||
}
|
||||
|
||||
func (self *SQcloudProviderFactory) GetId() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SQcloudProviderFactory) GetProvider(providerId, providerName, url, account, secret string) (cloudprovider.ICloudProvider, error) {
|
||||
client, err := qcloud.NewQcloudClient(providerId, providerName, account, secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SQcloudProvider{client: client}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
factory := SQcloudProviderFactory{}
|
||||
cloudprovider.RegisterFactory(&factory)
|
||||
}
|
||||
|
||||
type SQcloudProvider struct {
|
||||
client *qcloud.SQcloudClient
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) IsPublicCloud() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetId() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetName() string {
|
||||
return qcloud.CLOUD_PROVIDER_QCLOUD_CN
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetSysInfo() (jsonutils.JSONObject, error) {
|
||||
regions := self.client.GetIRegions()
|
||||
info := jsonutils.NewDict()
|
||||
info.Add(jsonutils.NewInt(int64(len(regions))), "region_count")
|
||||
info.Add(jsonutils.NewString(qcloud.QCLOUD_API_VERSION), "api_version")
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
return self.client.GetSubAccounts()
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
return self.client.GetIRegions()
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
return self.client.GetIRegionById(id)
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
return self.client.GetIHostById(id)
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
return self.client.GetIVpcById(id)
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
return self.client.GetIStorageById(id)
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
return self.client.GetIStoragecacheById(id)
|
||||
}
|
||||
|
||||
func (self *SQcloudProvider) GetBalance() (float64, error) {
|
||||
return 0.0, nil
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
const (
|
||||
CLOUD_PROVIDER_QCLOUD = models.CLOUD_PROVIDER_QCLOUD
|
||||
CLOUD_PROVIDER_QCLOUD_CN = "腾讯云"
|
||||
|
||||
QCLOUD_DEFAULT_REGION = "ap-beijing"
|
||||
|
||||
QCLOUD_API_VERSION = "2017-03-12"
|
||||
)
|
||||
|
||||
type SQcloudClient struct {
|
||||
providerId string
|
||||
providerName string
|
||||
AppID string
|
||||
SecretID string
|
||||
SecretKey string
|
||||
iregions []cloudprovider.ICloudRegion
|
||||
}
|
||||
|
||||
func NewQcloudClient(providerId string, providerName string, secretID string, secretKey string) (*SQcloudClient, error) {
|
||||
client := SQcloudClient{providerId: providerId, providerName: providerName, SecretID: secretID, SecretKey: secretKey}
|
||||
if account := strings.Split(secretID, "/"); len(account) == 2 {
|
||||
client.SecretID = account[0]
|
||||
client.AppID = account[1]
|
||||
}
|
||||
err := client.fetchRegions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
func jsonRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
domain := "cvm.tencentcloudapi.com"
|
||||
if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") {
|
||||
domain = "cvm." + region + ".tencentcloudapi.com"
|
||||
}
|
||||
return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params)
|
||||
}
|
||||
|
||||
func vpcRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
domain := "vpc.tencentcloudapi.com"
|
||||
// if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") {
|
||||
// domain = "vpc." + region + ".tencentcloudapi.com"
|
||||
// }
|
||||
return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params)
|
||||
}
|
||||
|
||||
func cbsRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
domain := "cbs.tencentcloudapi.com"
|
||||
if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") {
|
||||
domain = "cbs." + region + ".tencentcloudapi.com"
|
||||
}
|
||||
return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params)
|
||||
}
|
||||
|
||||
type QcloudResponse struct {
|
||||
*tchttp.BaseResponse
|
||||
Response *interface{} `json:"Response"`
|
||||
}
|
||||
|
||||
func _jsonRequest(client *common.Client, domain string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
req := &tchttp.BaseRequest{}
|
||||
if region, ok := params["Region"]; ok {
|
||||
client = client.Init(region)
|
||||
}
|
||||
client.WithProfile(profile.NewClientProfile())
|
||||
service := strings.Split(domain, ".")[0]
|
||||
req.Init().WithApiInfo(service, version, apiName)
|
||||
req.SetDomain(domain)
|
||||
|
||||
for k, v := range params {
|
||||
req.GetParams()[k] = v
|
||||
}
|
||||
resp := &QcloudResponse{
|
||||
BaseResponse: &tchttp.BaseResponse{},
|
||||
}
|
||||
err := client.Send(req, resp)
|
||||
if err != nil {
|
||||
log.Errorf("request url: %s\nparams: %s\nerror: %v", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), err)
|
||||
return nil, err
|
||||
}
|
||||
return jsonutils.Marshal(resp.Response), nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetRegions() []SRegion {
|
||||
regions := make([]SRegion, len(client.iregions))
|
||||
for i := 0; i < len(regions); i++ {
|
||||
region := client.iregions[i].(*SRegion)
|
||||
regions[i] = *region
|
||||
}
|
||||
return regions
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) getDefaultClient() (*common.Client, error) {
|
||||
return common.NewClientWithSecretId(client.SecretID, client.SecretKey, QCLOUD_DEFAULT_REGION)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vpcRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cbsRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) jsonRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jsonRequest(cli, apiName, params)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) fetchRegions() error {
|
||||
body, err := client.jsonRequest("DescribeRegions", nil)
|
||||
if err != nil {
|
||||
log.Errorf("fetchRegions fail %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
regions := make([]SRegion, 0)
|
||||
err = body.Unmarshal(®ions, "RegionSet")
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal json error %s", err)
|
||||
return err
|
||||
}
|
||||
client.iregions = make([]cloudprovider.ICloudRegion, len(regions))
|
||||
for i := 0; i < len(regions); i++ {
|
||||
regions[i].client = client
|
||||
client.iregions[i] = ®ions[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
err := client.fetchRegions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subAccount := cloudprovider.SSubAccount{}
|
||||
subAccount.Name = client.providerName
|
||||
subAccount.Account = client.SecretID
|
||||
if len(client.AppID) > 0 {
|
||||
subAccount.Account = fmt.Sprintf("%s/%s", client.SecretID, client.AppID)
|
||||
}
|
||||
return []cloudprovider.SSubAccount{subAccount}, nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
return client.iregions
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) getDefaultRegion() (cloudprovider.ICloudRegion, error) {
|
||||
if len(client.iregions) > 0 {
|
||||
return client.iregions[0], nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
if client.iregions[i].GetGlobalId() == id {
|
||||
return client.iregions[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetRegion(regionId string) *SRegion {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
if client.iregions[i].GetId() == regionId {
|
||||
return client.iregions[i].(*SRegion)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIHostById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIVpcById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIStorageById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
for i := 0; i < len(client.iregions); i++ {
|
||||
ihost, err := client.iregions[i].GetIStoragecacheById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
type SAccountBalance struct {
|
||||
AvailableAmount float64
|
||||
AvailableCashAmount float64
|
||||
CreditAmount float64
|
||||
MybankCreditAmount float64
|
||||
Currency string
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) QueryAccountBalance() (*SAccountBalance, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nelsonken/cos-go-sdk-v5/cos"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SRegion struct {
|
||||
client *SQcloudClient
|
||||
cosClient *cos.Client
|
||||
|
||||
izones []cloudprovider.ICloudZone
|
||||
ivpcs []cloudprovider.ICloudVpc
|
||||
|
||||
storageCache *SStoragecache
|
||||
|
||||
instanceTypes []SInstanceType
|
||||
|
||||
Region string
|
||||
RegionName string
|
||||
RegionState string
|
||||
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
fetchLocation bool
|
||||
}
|
||||
|
||||
func (self *SRegion) GetId() string {
|
||||
return self.Region
|
||||
}
|
||||
|
||||
func (self *SRegion) GetName() string {
|
||||
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_QCLOUD_CN, self.RegionName)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_QCLOUD, self.Region)
|
||||
}
|
||||
|
||||
func (self *SRegion) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SRegion) GetProvider() string {
|
||||
return CLOUD_PROVIDER_QCLOUD
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudprovider.ICloudVpc, error) {
|
||||
params := make(map[string]string)
|
||||
if len(cidr) > 0 {
|
||||
params["CidrBlock"] = cidr
|
||||
}
|
||||
if len(name) > 0 {
|
||||
params["VpcName"] = name
|
||||
}
|
||||
body, err := self.vpcRequest("CreateVpc", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpcId, err := body.GetString("Vpc", "VpcId")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = self.fetchInfrastructure()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return self.GetIVpcById(vpcId)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCosClient() (*cos.Client, error) {
|
||||
if self.cosClient == nil {
|
||||
self.cosClient = cos.New(&cos.Option{
|
||||
AppID: self.client.AppID,
|
||||
SecretID: self.client.SecretID,
|
||||
SecretKey: self.client.SecretKey,
|
||||
Region: self.Region,
|
||||
})
|
||||
}
|
||||
return self.cosClient, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClient() *SQcloudClient {
|
||||
return self.client
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) {
|
||||
eips, total, err := self.GetEips(eipId, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
if total > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
return &eips[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
|
||||
eips, total, err := self.GetEips("", 0, 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for len(eips) < total {
|
||||
var parts []SEipAddress
|
||||
parts, total, err = self.GetEips("", len(eips), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eips = append(eips, parts...)
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudEIP, len(eips))
|
||||
for i := 0; i < len(eips); i++ {
|
||||
ret[i] = &eips[i]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
ihost, err := izones[i].GetIHostById(id)
|
||||
if err == nil {
|
||||
return ihost, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
istore, err := izones[i].GetIStorageById(id)
|
||||
if err == nil {
|
||||
return istore, nil
|
||||
} else if err != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
storageCache := self.getStoragecache()
|
||||
if storageCache.GetGlobalId() == id {
|
||||
return self.storageCache, nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) updateInstance(instId string, name, desc, passwd, hostname string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instId
|
||||
if len(name) > 0 {
|
||||
params["InstanceName"] = name
|
||||
}
|
||||
if len(desc) > 0 {
|
||||
params["Description"] = desc
|
||||
}
|
||||
if len(passwd) > 0 {
|
||||
params["Password"] = passwd
|
||||
}
|
||||
if len(hostname) > 0 {
|
||||
params["HostName"] = hostname
|
||||
}
|
||||
_, err := self.cvmRequest("ModifyInstanceAttribute", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) UpdateInstancePassword(instId string, passwd string) error {
|
||||
return self.updateInstance(instId, "", "", passwd, "")
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
ivpcs, err := self.GetIVpcs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(ivpcs); i++ {
|
||||
if ivpcs[i].GetGlobalId() == id {
|
||||
return ivpcs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) getZoneById(id string) (*SZone, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
zone := izones[i].(*SZone)
|
||||
if zone.Zone == id {
|
||||
return zone, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no such zone %s", id)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
|
||||
if self.ivpcs == nil {
|
||||
err := self.fetchInfrastructure()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.ivpcs, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
if izones[i].GetGlobalId() == id {
|
||||
return izones[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
|
||||
if self.izones == nil {
|
||||
var err error
|
||||
err = self.fetchInfrastructure()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.izones, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) _fetchZones() error {
|
||||
params := make(map[string]string)
|
||||
zones := make([]SZone, 0)
|
||||
body, err := self.cvmRequest("DescribeZones", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = body.Unmarshal(&zones, "ZoneSet")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.izones = make([]cloudprovider.ICloudZone, len(zones))
|
||||
for i := 0; i < len(zones); i++ {
|
||||
zones[i].region = self
|
||||
self.izones[i] = &zones[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) fetchInfrastructure() error {
|
||||
err := self._fetchZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = self.fetchIVpcs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < len(self.ivpcs); i += 1 {
|
||||
for j := 0; j < len(self.izones); j += 1 {
|
||||
zone := self.izones[j].(*SZone)
|
||||
vpc := self.ivpcs[i].(*SVpc)
|
||||
wire := SWire{zone: zone, vpc: vpc}
|
||||
zone.addWire(&wire)
|
||||
vpc.addWire(&wire)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteVpc(vpcId string) error {
|
||||
params := make(map[string]string)
|
||||
params["VpcId"] = vpcId
|
||||
|
||||
_, err := self.vpcRequest("DeleteVpc", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) getVpc(vpcId string) (*SVpc, error) {
|
||||
vpcs, total, err := self.GetVpcs([]string{vpcId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
vpcs[0].region = self
|
||||
return &vpcs[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) fetchIVpcs() error {
|
||||
vpcs := make([]SVpc, 0)
|
||||
for {
|
||||
part, total, err := self.GetVpcs(nil, len(vpcs), 50)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vpcs = append(vpcs, part...)
|
||||
if len(vpcs) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
self.ivpcs = make([]cloudprovider.ICloudVpc, len(vpcs))
|
||||
for i := 0; i < len(vpcs); i += 1 {
|
||||
vpcs[i].region = self
|
||||
self.ivpcs[i] = &vpcs[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetVpcs(vpcIds []string, offset int, limit int) ([]SVpc, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
if vpcIds != nil && len(vpcIds) > 0 {
|
||||
for index, vpcId := range vpcIds {
|
||||
params[fmt.Sprintf("VpcIds.%d", index)] = vpcId
|
||||
}
|
||||
}
|
||||
body, err := self.vpcRequest("DescribeVpcs", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
vpcs := make([]SVpc, 0)
|
||||
err = body.Unmarshal(&vpcs, "VpcSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal vpc fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return vpcs, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLatitude() float32 {
|
||||
if info, ok := LatitudeAndLongitude[self.Region]; ok {
|
||||
return info["latitude"]
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLongitude() float32 {
|
||||
if info, ok := LatitudeAndLongitude[self.Region]; ok {
|
||||
return info["longitude"]
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func (self *SRegion) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStatus() string {
|
||||
if self.RegionState == "AVAILABLE" {
|
||||
return models.CLOUD_REGION_STATUS_INSERVER
|
||||
}
|
||||
return models.CLOUD_REGION_STATUS_OUTOFSERVICE
|
||||
}
|
||||
|
||||
func (self *SRegion) Refresh() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
params["Region"] = self.Region
|
||||
return self.client.vpcRequest(apiName, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) cvmRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
params["Region"] = self.Region
|
||||
return self.client.jsonRequest(apiName, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
params["Region"] = self.Region
|
||||
return self.client.cbsRequest(apiName, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
base := 0
|
||||
if ids != nil && len(ids) > 0 {
|
||||
for index, networkId := range ids {
|
||||
params[fmt.Sprintf("SubnetIds.%d", index)] = networkId
|
||||
}
|
||||
base += len(ids)
|
||||
}
|
||||
if len(vpcId) > 0 {
|
||||
params["Filters.0.Name"] = "vpc-id"
|
||||
params["Filters.0.Values.0"] = vpcId
|
||||
}
|
||||
|
||||
body, err := self.vpcRequest("DescribeSubnets", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeSubnets fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
networks := make([]SNetwork, 0)
|
||||
err = body.Unmarshal(&networks, "SubnetSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal network fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return networks, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNetwork(networkId string) (*SNetwork, error) {
|
||||
networks, total, err := self.GetNetworks([]string{networkId}, "", 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &networks[0], nil
|
||||
}
|
||||
|
||||
func (self *SRegion) getStoragecache() *SStoragecache {
|
||||
if self.storageCache == nil {
|
||||
self.storageCache = &SStoragecache{region: self}
|
||||
}
|
||||
return self.storageCache
|
||||
}
|
||||
|
||||
func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, gpu int, zoneId string) ([]SInstanceType, error) {
|
||||
if self.instanceTypes == nil {
|
||||
types, err := self.GetInstanceTypes()
|
||||
if err != nil {
|
||||
log.Errorf("GetInstanceTypes %s", err)
|
||||
return nil, err
|
||||
}
|
||||
self.instanceTypes = types
|
||||
}
|
||||
|
||||
var available []string
|
||||
if len(zoneId) > 0 {
|
||||
zone, err := self.getZoneById(zoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
available = zone.getAvaliableInstanceTypes()
|
||||
}
|
||||
ret := make([]SInstanceType, 0)
|
||||
for _, t := range self.instanceTypes {
|
||||
if t.CPU == cpu && memMB == t.memoryMB() && gpu == t.GPU {
|
||||
if available == nil || utils.IsInStringArray(t.InstanceType, available) {
|
||||
ret = append(ret, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, memGB int, storageType string, dataDiskSizesGB []int, networkId string, passwd string, publicKey string) (*SInstance, error) {
|
||||
izones, err := self.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(izones); i += 1 {
|
||||
z := izones[i].(*SZone)
|
||||
log.Debugf("Search in zone %s", z.Zone)
|
||||
net := z.getNetworkById(networkId)
|
||||
if net != nil {
|
||||
inst, err := z.getHost().CreateVM(name, imgId, 0, cpu, memGB*1024, networkId, "", "", passwd, storageType, dataDiskSizesGB, publicKey, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return inst.(*SInstance), nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("cannot find network %s", networkId)
|
||||
}
|
||||
|
||||
func (self *SRegion) instanceOperation(instanceId string, opname string, extra map[string]string) error {
|
||||
params := make(map[string]string)
|
||||
params["InstanceIds.0"] = instanceId
|
||||
if extra != nil && len(extra) > 0 {
|
||||
for k, v := range extra {
|
||||
params[k] = v
|
||||
}
|
||||
}
|
||||
_, err := self.cvmRequest(opname, params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["InstanceId"] = instanceId
|
||||
body, err := self.cvmRequest("DescribeInstanceVncUrl", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("InstanceVncUrl")
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) {
|
||||
instance, err := self.GetInstance(instanceId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.InstanceState, nil
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SecurityGroupPolicy struct {
|
||||
vpc *SVpc
|
||||
PolicyIndex int // 安全组规则索引号。
|
||||
Protocol string // 协议, 取值: TCP,UDP, ICMP。
|
||||
Port string // 端口(all, 离散port, range)。
|
||||
ServiceTemplate ServiceTemplateSpecification // 协议端口ID或者协议端口组ID。ServiceTemplate和Protocol+Port互斥。
|
||||
CidrBlock string // 网段或IP(互斥)。
|
||||
SecurityGroupId string // 已绑定安全组的网段或IP。
|
||||
AddressTemplate AddressTemplateSpecification // IP地址ID或者ID地址组ID。
|
||||
Action string // ACCEPT 或 DROP。
|
||||
PolicyDescription string // 安全组规则描述。
|
||||
direction string
|
||||
}
|
||||
|
||||
type ServiceTemplateSpecification struct {
|
||||
ServiceId string // 协议端口ID,例如:ppm-f5n1f8da。
|
||||
ServiceGroupId string // 协议端口组ID,例如:ppmg-f5n1f8da。
|
||||
}
|
||||
|
||||
type AddressTemplateSpecification struct {
|
||||
AddressId string // IP地址ID,例如:ipm-2uw6ujo6。
|
||||
AddressGroupId string // IP地址组ID,例如:ipmg-2uw6ujo6。
|
||||
}
|
||||
|
||||
type SecurityGroupPolicySet struct {
|
||||
Version string
|
||||
Egress []SecurityGroupPolicy // 出站规则。
|
||||
Ingress []SecurityGroupPolicy // 入站规则。
|
||||
}
|
||||
|
||||
type SSecurityGroup struct {
|
||||
vpc *SVpc
|
||||
SecurityGroupId string // 安全组实例ID,例如:sg-ohuuioma。
|
||||
SecurityGroupName string // 安全组名称,可任意命名,但不得超过60个字符。
|
||||
SecurityGroupDesc string // 安全组备注,最多100个字符。
|
||||
ProjectId string // 项目id,默认0。可在qcloud控制台项目管理页面查询到。
|
||||
IsDefault bool // 是否是默认安全组,默认安全组不支持删除。
|
||||
CreatedTime time.Time // 安全组创建时间。
|
||||
SecurityGroupPolicySet SecurityGroupPolicySet
|
||||
}
|
||||
|
||||
type SecurityGroupRuleSet []SecurityGroupPolicy
|
||||
|
||||
func (v SecurityGroupRuleSet) Len() int {
|
||||
return len(v)
|
||||
}
|
||||
|
||||
func (v SecurityGroupRuleSet) Swap(i, j int) {
|
||||
v[i], v[j] = v[j], v[i]
|
||||
}
|
||||
|
||||
func (v SecurityGroupRuleSet) Less(i, j int) bool {
|
||||
if v[i].PolicyIndex < v[j].PolicyIndex {
|
||||
return true
|
||||
} else if v[i].PolicyIndex == v[j].PolicyIndex {
|
||||
return strings.Compare(v[i].String(), v[j].String()) <= 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSecurityGroups(vpcId string, offset int, limit int) ([]SSecurityGroup, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
body, err := self.vpcRequest("DescribeSecurityGroups", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetSecurityGroups fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
secgrps := make([]SSecurityGroup, 0)
|
||||
err = body.Unmarshal(&secgrps, "SecurityGroupSet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal security groups fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return secgrps, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetId() string {
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetGlobalId() string {
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetDescription() string {
|
||||
return self.SecurityGroupDesc
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetName() string {
|
||||
if len(self.SecurityGroupName) > 0 {
|
||||
return self.SecurityGroupName
|
||||
}
|
||||
return self.SecurityGroupId
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) String() string {
|
||||
rules := self.toRules()
|
||||
result := []string{}
|
||||
for _, rule := range rules {
|
||||
result = append(result, rule.String())
|
||||
}
|
||||
return strings.Join(result, ";")
|
||||
}
|
||||
|
||||
func parseCIDR(cidr string) (*net.IPNet, error) {
|
||||
if strings.Index(cidr, "/") > 0 {
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
return ipnet, err
|
||||
}
|
||||
ip := net.ParseIP(cidr)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("Parse ip %s error", cidr)
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}, nil
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) toRules() []secrules.SecurityRule {
|
||||
result := []secrules.SecurityRule{}
|
||||
rule := secrules.SecurityRule{
|
||||
Action: secrules.SecurityRuleAllow,
|
||||
Protocol: secrules.PROTO_ANY,
|
||||
Direction: secrules.TSecurityRuleDirection(self.direction),
|
||||
Ports: []int{},
|
||||
PortStart: -1,
|
||||
PortEnd: -1,
|
||||
}
|
||||
if len(self.SecurityGroupId) != 0 {
|
||||
//安全组关联安全组的规则忽略
|
||||
return nil
|
||||
}
|
||||
if strings.ToLower(self.Action) == "drop" {
|
||||
rule.Action = secrules.SecurityRuleDeny
|
||||
}
|
||||
if utils.IsInStringArray(strings.ToLower(self.Protocol), []string{"tcp", "udp", "icmp"}) {
|
||||
rule.Protocol = strings.ToLower(self.Protocol)
|
||||
}
|
||||
if strings.Index(self.Port, ",") > 0 {
|
||||
for _, _port := range strings.Split(self.Port, ",") {
|
||||
port, err := strconv.Atoi(_port)
|
||||
if err != nil {
|
||||
log.Errorf("parse secgroup port %s %s error %v", self.Port, _port, err)
|
||||
continue
|
||||
}
|
||||
rule.Ports = append(rule.Ports, port)
|
||||
}
|
||||
} else if strings.Index(self.Port, "-") > 0 {
|
||||
ports := strings.Split(self.Port, "-")
|
||||
if len(ports) == 2 {
|
||||
portStart, err := strconv.Atoi(ports[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
portEnd, err := strconv.Atoi(ports[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.PortStart, rule.PortEnd = portStart, portEnd
|
||||
}
|
||||
} else if strings.ToLower(self.Port) != "all" {
|
||||
port, err := strconv.Atoi(self.Port)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.PortStart, rule.PortEnd = port, port
|
||||
}
|
||||
|
||||
if len(self.AddressTemplate.AddressGroupId) > 0 {
|
||||
addressGroup, total, err := self.vpc.region.AddressGroupList(self.AddressTemplate.AddressGroupId, "", 0, 1)
|
||||
if err != nil {
|
||||
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
|
||||
return nil
|
||||
}
|
||||
if total != 1 {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(addressGroup[0].AddressTemplateIdSet); i++ {
|
||||
rules, err := self.getAddressRules(rule, addressGroup[0].AddressTemplateIdSet[i])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, rules...)
|
||||
}
|
||||
} else if len(self.AddressTemplate.AddressId) > 0 {
|
||||
rules, err := self.getAddressRules(rule, self.AddressTemplate.AddressId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result = append(result, rules...)
|
||||
} else if len(self.CidrBlock) > 0 {
|
||||
ipnet, err := parseCIDR(self.CidrBlock)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rule.IPNet = ipnet
|
||||
result = append(result, rule)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *SecurityGroupPolicy) getAddressRules(rule secrules.SecurityRule, addressId string) ([]secrules.SecurityRule, error) {
|
||||
result := []secrules.SecurityRule{}
|
||||
address, total, err := self.vpc.region.AddressList(addressId, "", 0, 1)
|
||||
if err != nil {
|
||||
log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err)
|
||||
return nil, err
|
||||
}
|
||||
if total != 1 {
|
||||
return nil, fmt.Errorf("failed to find address %s", addressId)
|
||||
}
|
||||
for _, ip := range address[0].AddressSet {
|
||||
ipnet, err := parseCIDR(ip)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
rule.IPNet = ipnet
|
||||
result = append(result, rule)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
|
||||
secgroup, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Egress); i++ {
|
||||
secgroup.SecurityGroupPolicySet.Egress[i].direction = "out"
|
||||
}
|
||||
for i := 0; i < len(secgroup.SecurityGroupPolicySet.Ingress); i++ {
|
||||
secgroup.SecurityGroupPolicySet.Ingress[i].direction = "in"
|
||||
}
|
||||
originRules := []SecurityGroupPolicy{}
|
||||
originRules = append(secgroup.SecurityGroupPolicySet.Egress)
|
||||
originRules = append(secgroup.SecurityGroupPolicySet.Ingress)
|
||||
sort.Sort(SecurityGroupRuleSet(originRules))
|
||||
rules := []secrules.SecurityRule{}
|
||||
priority := 100
|
||||
for _, rule := range originRules {
|
||||
rule.vpc = self.vpc
|
||||
subRules := rule.toRules()
|
||||
for i := 0; i < len(subRules); i++ {
|
||||
subRules[i].Priority = priority
|
||||
}
|
||||
if len(subRules) > 0 {
|
||||
priority--
|
||||
}
|
||||
rules = append(rules, subRules...)
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) Refresh() error {
|
||||
if new, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["SecurityGroupId"] = secGroupId
|
||||
|
||||
body, err := self.vpcRequest("DescribeSecurityGroupPolicies", params)
|
||||
if err != nil {
|
||||
log.Errorf("DescribeSecurityGroupAttribute fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secgrp := SSecurityGroup{SecurityGroupId: secGroupId}
|
||||
err = body.Unmarshal(&secgrp.SecurityGroupPolicySet, "SecurityGroupPolicySet")
|
||||
if err != nil {
|
||||
log.Errorf("Unmarshal security group details fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return &secgrp, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSecurityGroup(secGroupId string) error {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["SecurityGroupId"] = secGroupId
|
||||
_, err := self.vpcRequest("DeleteSecurityGroup", params)
|
||||
return err
|
||||
}
|
||||
|
||||
type AddressTemplate struct {
|
||||
AddressSet []string
|
||||
AddressTemplateId string
|
||||
AddressTemplateName string
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
func (self *SRegion) AddressList(addressId, addressName string, offset, limit int) ([]AddressTemplate, int, error) {
|
||||
params := map[string]string{}
|
||||
filter := 0
|
||||
if len(addressId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressId
|
||||
filter++
|
||||
}
|
||||
if len(addressName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressName
|
||||
filter++
|
||||
}
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
if limit == 0 {
|
||||
limit = 20
|
||||
}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
body, err := self.vpcRequest("DescribeAddressTemplates", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
addressTemplates := []AddressTemplate{}
|
||||
err = body.Unmarshal(&addressTemplates, "AddressTemplateSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return addressTemplates, int(total), nil
|
||||
}
|
||||
|
||||
type AddressTemplateGroup struct {
|
||||
AddressTemplateIdSet []string
|
||||
AddressTemplateGroupName string
|
||||
AddressTemplateGroupId string
|
||||
CreatedTime time.Time
|
||||
}
|
||||
|
||||
func (self *SRegion) AddressGroupList(groupId, groupName string, offset, limit int) ([]AddressTemplateGroup, int, error) {
|
||||
params := map[string]string{}
|
||||
filter := 0
|
||||
if len(groupId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupId
|
||||
filter++
|
||||
}
|
||||
if len(groupName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupName
|
||||
filter++
|
||||
}
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
if limit == 0 {
|
||||
limit = 20
|
||||
}
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
body, err := self.vpcRequest("DescribeAddressTemplateGroups", params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
addressTemplateGroups := []AddressTemplateGroup{}
|
||||
err = body.Unmarshal(&addressTemplateGroups, "AddressTemplateGroupSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
return addressTemplateGroups, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSecurityGroup(name, description string) (*SSecurityGroup, error) {
|
||||
params := make(map[string]string)
|
||||
params["Region"] = self.Region
|
||||
params["GroupName"] = name
|
||||
params["GroupDescription"] = description
|
||||
if len(description) == 0 {
|
||||
params["GroupDescription"] = "Customize Create"
|
||||
}
|
||||
secgroup := SSecurityGroup{}
|
||||
if body, err := self.vpcRequest("CreateSecurityGroup", params); err != nil {
|
||||
return nil, err
|
||||
} else if err := body.Unmarshal(&secgroup, "SecurityGroup"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &secgroup, nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
coslib "github.com/nelsonken/cos-go-sdk-v5/cos"
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type CosListOptions struct {
|
||||
}
|
||||
shellutils.R(&CosListOptions{}, "cos-list", "List COS buckets", func(cli *qcloud.SRegion, args *CosListOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := cos.GetBucketList(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result.Buckets.Bucket, len(result.Buckets.Bucket), 0, len(result.Buckets.Bucket), nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CosListBucketOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
}
|
||||
|
||||
shellutils.R(&CosListBucketOptions{}, "cos-bucket-list", "List content of a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := cos.ListBucketContents(context.Background(), args.BUCKET, &coslib.QueryCondition{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result.Contents, len(result.Contents), 0, len(result.Contents), nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&CosListBucketOptions{}, "cos-bucket-create", "Create a OSS bucket", func(cli *qcloud.SRegion, args *CosListBucketOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.CreateBucket(context.Background(), args.BUCKET, &coslib.AccessControl{})
|
||||
})
|
||||
|
||||
type CosUploadOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"Object key"`
|
||||
FILE string `help:"Local file path"`
|
||||
Acl string `help:"Object ACL" choices:"private|public-read|public-read-write"`
|
||||
}
|
||||
shellutils.R(&CosUploadOptions{}, "cos-upload", "Upload a file to a Cos bucket", func(cli *qcloud.SRegion, args *CosUploadOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
|
||||
})
|
||||
|
||||
type CosDownloadOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
NAME string `help:"File name"`
|
||||
}
|
||||
shellutils.R(&CosDownloadOptions{}, "cos-download", "Download a file", func(cli *qcloud.SRegion, args *CosDownloadOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//file
|
||||
return cos.Bucket(args.BUCKET).DownloadObject(context.Background(), args.NAME, os.Stdout)
|
||||
//return cos.Bucket(args.BUCKET).UploadObjectBySlice(context.Background(), args.KEY, args.FILE, 3, nil)
|
||||
})
|
||||
|
||||
type CosObjectAclOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"object key"`
|
||||
ACL string `help:"ACL" choices:"private|public-read|public-read-write"`
|
||||
}
|
||||
shellutils.R(&CosObjectAclOptions{}, "cos-set-acl", "Set acl for a object", func(cli *qcloud.SRegion, args *CosObjectAclOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.SetBucketACL(context.Background(), args.KEY, &coslib.AccessControl{ACL: args.ACL})
|
||||
})
|
||||
|
||||
type CosDeleteOptions struct {
|
||||
BUCKET string `help:"bucket name"`
|
||||
KEY string `help:"Object key"`
|
||||
}
|
||||
|
||||
shellutils.R(&CosDeleteOptions{}, "cos-delete", "Delete a file from a Cos bucket", func(cli *qcloud.SRegion, args *CosDeleteOptions) error {
|
||||
cos, err := cli.GetCosClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cos.Bucket(args.BUCKET).DeleteObject(context.Background(), args.KEY)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DiskListOptions struct {
|
||||
Instance string `help:"Instance ID"`
|
||||
Zone string `help:"Zone ID"`
|
||||
Category string `help:"Disk category"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *qcloud.SRegion, args *DiskListOptions) error {
|
||||
disks, total, e := cli.GetDisks(args.Instance, args.Zone, args.Category, nil, args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(disks, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
}
|
||||
shellutils.R(&DiskOptions{}, "disk-delete", "Delete disks", func(cli *qcloud.SRegion, args *DiskOptions) error {
|
||||
e := cli.DeleteDisk(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskOptions{}, "disk-show", "Show disk", func(cli *qcloud.SRegion, args *DiskOptions) error {
|
||||
disk, err := cli.GetDisk(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskCreateOptions struct {
|
||||
ZONE string `help:"Zone ID"`
|
||||
CATEGORY string `help:"Disk category" choices:"CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
|
||||
NAME string `help:"Disk Name"`
|
||||
SIZE int `help:"Disk Size GB"`
|
||||
}
|
||||
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *qcloud.SRegion, args *DiskCreateOptions) error {
|
||||
diskId, err := cli.CreateDisk(args.ZONE, args.CATEGORY, args.NAME, args.SIZE, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(diskId)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskResizeOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
SIZE int64 `help:"Disk Size GB"`
|
||||
}
|
||||
shellutils.R(&DiskResizeOptions{}, "disk-resize", "Resize disk", func(cli *qcloud.SRegion, args *DiskResizeOptions) error {
|
||||
return cli.ResizeDisk(args.ID, args.SIZE)
|
||||
})
|
||||
|
||||
type DiskResetOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
SNAPSHOT string `help:"Snapshot ID"`
|
||||
}
|
||||
shellutils.R(&DiskResetOptions{}, "disk-reset", "Reset disk", func(cli *qcloud.SRegion, args *DiskResetOptions) error {
|
||||
return cli.ResetDisk(args.ID, args.SNAPSHOT)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type EipListOptions struct {
|
||||
Eip string `help:"EIP ID"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *qcloud.SRegion, args *EipListOptions) error {
|
||||
eips, total, err := cli.GetEips(args.Eip, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(eips, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipAllocateOptions struct {
|
||||
BANDWIDTH int `help:"EIP bandwoidth"`
|
||||
NAME string `help:"EIP Name"`
|
||||
ChargeType qcloud.TInternetChargeType `help:"EIP ChargeType"`
|
||||
}
|
||||
shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *qcloud.SRegion, args *EipAllocateOptions) error {
|
||||
eip, err := cli.AllocateEIP(args.NAME, args.BANDWIDTH, args.ChargeType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipReleaseOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
shellutils.R(&EipReleaseOptions{}, "eip-delete", "Release an EIP", func(cli *qcloud.SRegion, args *EipReleaseOptions) error {
|
||||
return cli.DeallocateEIP(args.ID)
|
||||
})
|
||||
|
||||
type EipShowOptions struct {
|
||||
ID string `help:"EIP ID"`
|
||||
}
|
||||
shellutils.R(&EipShowOptions{}, "eip-show", "Show an EIP", func(cli *qcloud.SRegion, args *EipShowOptions) error {
|
||||
eip, err := cli.GetEip(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipAssociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
INSTANCE string `help:"Instance ID"`
|
||||
}
|
||||
shellutils.R(&EipAssociateOptions{}, "eip-associate", "Associate an EIP", func(cli *qcloud.SRegion, args *EipAssociateOptions) error {
|
||||
return cli.AssociateEip(args.ID, args.INSTANCE)
|
||||
})
|
||||
|
||||
type EipDissociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *qcloud.SRegion, args *EipDissociateOptions) error {
|
||||
return cli.DissociateEip(args.ID)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ImageListOptions struct {
|
||||
Status string `help:"Image status"`
|
||||
Owner string `help:"Image owner" choices:"PRIVATE_IMAGE|PUBLIC_IMAGE|MARKET_IMAGE|SHARED_IMAGE"`
|
||||
Image string `help:"Image Id"`
|
||||
Name string `help:"Image Name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *qcloud.SRegion, args *ImageListOptions) error {
|
||||
imageIds := []string{}
|
||||
if len(args.Image) > 0 {
|
||||
imageIds = append(imageIds, args.Image)
|
||||
}
|
||||
images, total, err := cli.GetImages(args.Status, args.Owner, imageIds, args.Name, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(images, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageCreateOptions struct {
|
||||
NAME string `helo:"Image name"`
|
||||
OSTYPE string `helo:"Operation system" choices:"CentOS|Ubuntu|Debian|OpenSUSE|SUSE|CoreOS|FreeBSD|Other Linux|Windows Server 2008|Windows Server 2012|Windows Server 2016"`
|
||||
OSARCH string `help:"OS Architecture" choices:"x86_64|i386"`
|
||||
osVersion string `help:"OS Version"`
|
||||
URL string `helo:"Cos URL"`
|
||||
}
|
||||
|
||||
shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *qcloud.SRegion, args *ImageCreateOptions) error {
|
||||
image, err := cli.ImportImage(args.NAME, args.OSARCH, args.OSTYPE, args.osVersion, args.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageDeleteOptions struct {
|
||||
ID string `helo:"Image ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *qcloud.SRegion, args *ImageDeleteOptions) error {
|
||||
return cli.DeleteImage(args.ID)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type InstanceListOptions struct {
|
||||
Id []string `help:"IDs of instances to show"`
|
||||
Zone string `help:"Zone ID"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *qcloud.SRegion, args *InstanceListOptions) error {
|
||||
instances, total, e := cli.GetInstances(args.Zone, args.Id, args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(instances, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceCrateOptions struct {
|
||||
NAME string `help:"name of instance"`
|
||||
IMAGE string `help:"image ID"`
|
||||
CPU int `help:"CPU count"`
|
||||
MEMORYGB int `help:"MemoryGB"`
|
||||
Disk []int `help:"Data disk sizes int GB"`
|
||||
STORAGE string `help:"Storage type" choices:"LOCAL_BASIC|LOCAL_SSD|CLOUD_BASIC|CLOUD_PREMIUM|CLOUD_SSD"`
|
||||
NETWORK string `help:"Network ID"`
|
||||
PASSWD string `help:"password"`
|
||||
PublicKey string `help:"PublicKey"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceCrateOptions{}, "instance-create", "Create a instance", func(cli *qcloud.SRegion, args *InstanceCrateOptions) error {
|
||||
instance, e := cli.CreateInstanceSimple(args.NAME, args.IMAGE, args.CPU, args.MEMORYGB, args.STORAGE, args.Disk, args.NETWORK, args.PASSWD, args.PublicKey)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(instance)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceDiskOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
DISK string `help:"disk ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDiskOperationOptions{}, "instance-attach-disk", "Attach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
|
||||
err := cli.AttachDisk(args.ID, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceDiskOperationOptions{}, "instance-detach-disk", "Detach a disk to instance", func(cli *qcloud.SRegion, args *InstanceDiskOperationOptions) error {
|
||||
err := cli.DetachDisk(args.ID, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
}
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.StartVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-vnc", "Get a instance VNC url", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
url, err := cli.GetInstanceVNCUrl(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(url)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceStopOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Force bool `help:"Force stop instance"`
|
||||
}
|
||||
shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *qcloud.SRegion, args *InstanceStopOptions) error {
|
||||
err := cli.StopVM(args.ID, args.Force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *qcloud.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.DeleteVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
/*
|
||||
server-change-config 更改系统配置
|
||||
server-reset
|
||||
*/
|
||||
type InstanceDeployOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Name string `help:"new instance name"`
|
||||
Hostname string `help:"new hostname"`
|
||||
Keypair string `help:"Keypair Name"`
|
||||
DeleteKeypair bool `help:"Remove SSH keypair"`
|
||||
Password string `help:"new password"`
|
||||
// ResetPassword bool `help:"Force reset password"`
|
||||
Description string `help:"new instances description"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceDeployOptions) error {
|
||||
err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.DeleteKeypair, args.Description)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceRebuildRootOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Image string `help:"Image ID"`
|
||||
Password string `help:"pasword"`
|
||||
Keypair string `help:"keypair name"`
|
||||
Size int `help:"system disk size in GB"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *qcloud.SRegion, args *InstanceRebuildRootOptions) error {
|
||||
diskID, err := cli.ReplaceSystemDisk(args.ID, args.Image, args.Password, args.Keypair, args.Size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("New diskID is %s", diskID)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceChangeConfigOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Ncpu int `help:"number of CPU"`
|
||||
Vmem int `help:"MiB of memory"`
|
||||
Disk []int `help:"Data disk sizes int GB"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *qcloud.SRegion, args *InstanceChangeConfigOptions) error {
|
||||
instance, e := cli.GetInstance(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// todo : add create disks
|
||||
err := cli.ChangeVMConfig(instance.Placement.Zone, args.ID, args.Ncpu, args.Vmem, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceUpdatePasswordOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
PASSWD string `help:"new password"`
|
||||
}
|
||||
shellutils.R(&InstanceUpdatePasswordOptions{}, "instance-update-password", "Update instance password", func(cli *qcloud.SRegion, args *InstanceUpdatePasswordOptions) error {
|
||||
err := cli.UpdateInstancePassword(args.ID, args.PASSWD)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type KeyPairListOptions struct {
|
||||
Name string `help:"Keypair Name"`
|
||||
IDs []string `help:"Keypari ids"`
|
||||
Offset int `help:"List offset"`
|
||||
Limit int `help:"List limit"`
|
||||
}
|
||||
shellutils.R(&KeyPairListOptions{}, "keypair-list", "List keypair", func(cli *qcloud.SRegion, args *KeyPairListOptions) error {
|
||||
keypairs, total, err := cli.GetKeypairs(args.Name, args.IDs, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(keypairs, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeyPairCreateOptions struct {
|
||||
NAME string `help:"Keypair Name"`
|
||||
}
|
||||
shellutils.R(&KeyPairCreateOptions{}, "keypair-create", "Create keypair", func(cli *qcloud.SRegion, args *KeyPairCreateOptions) error {
|
||||
keypair, err := cli.CreateKeyPair(args.NAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(keypair)
|
||||
return nil
|
||||
})
|
||||
|
||||
type KeyPairAssociateOptions struct {
|
||||
KEYPAIRID string `help:"Keypair ID"`
|
||||
INSTANCEID string `help:"Instance ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&KeyPairAssociateOptions{}, "keypair-associate-instance", "Attach Keypair to a instance", func(cli *qcloud.SRegion, args *KeyPairAssociateOptions) error {
|
||||
return cli.AttachKeypair(args.INSTANCEID, args.KEYPAIRID)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type NetworkListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&NetworkListOptions{}, "network-list", "List networks", func(cli *qcloud.SRegion, args *NetworkListOptions) error {
|
||||
networks, total, e := cli.GetNetworks(nil, "", args.Offset, args.Limit)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(networks, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type NetworkOptions struct {
|
||||
ID string `help:"Network ID"`
|
||||
}
|
||||
shellutils.R(&NetworkOptions{}, "network-delete", "Delete network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
|
||||
return cli.DeleteNetwork(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&NetworkOptions{}, "network-show", "Show network", func(cli *qcloud.SRegion, args *NetworkOptions) error {
|
||||
network, err := cli.GetNetwork(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(network)
|
||||
return nil
|
||||
})
|
||||
|
||||
type NetworkCreateOptions struct {
|
||||
ZONE string `help:"Zone ID"`
|
||||
VPC string `help:"VPC ID"`
|
||||
CIDR string `help:"Network CIDR"`
|
||||
NAME string `help:"Network Name"`
|
||||
}
|
||||
shellutils.R(&NetworkCreateOptions{}, "network-create", "Create network", func(cli *qcloud.SRegion, args *NetworkCreateOptions) error {
|
||||
networkId, err := cli.CreateNetwork(args.ZONE, args.VPC, args.NAME, args.CIDR, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(networkId)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package shell
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/util/printutils"
|
||||
|
||||
func printList(data interface{}, total, offset, limit int, columns []string) {
|
||||
printutils.PrintInterfaceList(data, total, offset, limit, columns)
|
||||
}
|
||||
|
||||
func printObject(obj interface{}) {
|
||||
printutils.PrintInterfaceObject(obj)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type RegionListOptions struct {
|
||||
}
|
||||
shellutils.R(&RegionListOptions{}, "region-list", "List regions", func(cli *qcloud.SRegion, args *RegionListOptions) error {
|
||||
regions := cli.GetClient().GetRegions()
|
||||
printList(regions, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type SecurityGroupListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "List SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupListOptions) error {
|
||||
secgrps, total, err := cli.GetSecurityGroups("", args.Limit, args.Offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(secgrps, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type SecurityGroupOptions struct {
|
||||
ID string `help:"SecurityGroup ID"`
|
||||
}
|
||||
shellutils.R(&SecurityGroupOptions{}, "security-group-show", "Show SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
|
||||
secgroup, err := cli.GetSecurityGroupDetails(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(secgroup)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&SecurityGroupOptions{}, "security-group-delete", "Delete SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error {
|
||||
return cli.DeleteSecurityGroup(args.ID)
|
||||
})
|
||||
|
||||
type SecurityGroupCreateOptions struct {
|
||||
NAME string `help:"SecurityGroup Name"`
|
||||
Desc string `help:"SecurityGroup Description"`
|
||||
}
|
||||
|
||||
shellutils.R(&SecurityGroupCreateOptions{}, "security-group-create", "Create SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupCreateOptions) error {
|
||||
secgrp, err := cli.CreateSecurityGroup(args.NAME, args.Desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(secgrp)
|
||||
return nil
|
||||
})
|
||||
|
||||
type AddressShowOptions struct {
|
||||
Id string `help:"IP address ID"`
|
||||
Name string `help:"IP address name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&AddressShowOptions{}, "address-list", "Show address", func(cli *qcloud.SRegion, args *AddressShowOptions) error {
|
||||
address, total, err := cli.AddressList(args.Id, args.Name, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(address, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type SnapshotListOptions struct {
|
||||
DiskId string `help:"Disk ID"`
|
||||
InstanceId string `help:"Instance ID"`
|
||||
SnapshotIds []string `helo:"Snapshot ids"`
|
||||
Name string `help:"Snapshot Name"`
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&SnapshotListOptions{}, "snapshot-list", "List snapshot", func(cli *qcloud.SRegion, args *SnapshotListOptions) error {
|
||||
snapshots, total, err := cli.GetSnapshots(args.InstanceId, args.DiskId, args.Name, args.SnapshotIds, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(snapshots, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type SnapshotDeleteOptions struct {
|
||||
ID string `help:"Snapshot ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotDeleteOptions{}, "snapshot-delete", "Delete snapshot", func(cli *qcloud.SRegion, args *SnapshotDeleteOptions) error {
|
||||
return cli.DeleteSnapshot(args.ID)
|
||||
})
|
||||
|
||||
type SnapshotCreateOptions struct {
|
||||
DISK string `help:"Disk ID"`
|
||||
Name string `help:"Snapeshot Name"`
|
||||
Desc string `help:"Snapshot Desc"`
|
||||
}
|
||||
|
||||
shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "Create snapshot", func(cli *qcloud.SRegion, args *SnapshotCreateOptions) error {
|
||||
_, err := cli.CreateSnapshot(args.DISK, args.Name, args.Desc)
|
||||
return err
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type VpcListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&VpcListOptions{}, "vpc-list", "List vpcs", func(cli *qcloud.SRegion, args *VpcListOptions) error {
|
||||
vpcs, total, err := cli.GetVpcs(nil, args.Offset, args.Limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(vpcs, total, args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type VpcCreateOptions struct {
|
||||
NAME string `help:"Name for vpc"`
|
||||
CIDR string `help:"Cidr for vpc" choices:"10.0.0.0/16|172.16.0.0/12|192.168.0.0/16"`
|
||||
}
|
||||
shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *qcloud.SRegion, args *VpcCreateOptions) error {
|
||||
vpc, err := cli.CreateIVpc(args.NAME, "", args.CIDR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(vpc)
|
||||
return nil
|
||||
})
|
||||
|
||||
type VpcDeleteOptions struct {
|
||||
ID string `help:"VPC ID or Name"`
|
||||
}
|
||||
shellutils.R(&VpcDeleteOptions{}, "vpc-delete", "Delete vpc", func(cli *qcloud.SRegion, args *VpcDeleteOptions) error {
|
||||
return cli.DeleteVpc(args.ID)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/util/qcloud"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ZoneListOptions struct {
|
||||
Limit int `help:"page size"`
|
||||
Offset int `help:"page offset"`
|
||||
}
|
||||
shellutils.R(&ZoneListOptions{}, "zone-list", "List zones", func(cli *qcloud.SRegion, args *ZoneListOptions) error {
|
||||
zones, err := cli.GetIZones()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(zones, len(zones), args.Offset, args.Limit, []string{})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SnapshotStatusType string
|
||||
|
||||
const (
|
||||
SnapshotStatusAccomplished SnapshotStatusType = "NORMAL"
|
||||
SnapshotStatusProgress SnapshotStatusType = "CREATING"
|
||||
SnapshotStatusFailed SnapshotStatusType = "failed"
|
||||
)
|
||||
|
||||
type SSnapshot struct {
|
||||
region *SRegion
|
||||
|
||||
SnapshotId string // 快照ID。
|
||||
Placement Placement // 快照所在的位置。
|
||||
DiskUsage string // 创建此快照的云硬盘类型。取值范围:SYSTEM_DISK:系统盘 DATA_DISK:数据盘。
|
||||
DiskId string // 创建此快照的云硬盘ID。
|
||||
DiskSize int32 // 创建此快照的云硬盘大小,单位GB。
|
||||
SnapshotState SnapshotStatusType // 快照的状态。取值范围: NORMAL:正常 CREATING:创建中 ROLLBACKING:回滚中 COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
|
||||
SnapshotName string // 快照名称,用户自定义的快照别名。调用ModifySnapshotAttribute可修改此字段。
|
||||
Percent int // 快照创建进度百分比,快照创建成功后此字段恒为100。
|
||||
CreateTime time.Time // 快照的创建时间。
|
||||
DeadlineTime time.Time // 快照到期时间。如果快照为永久保留,此字段为空。
|
||||
Encrypt bool // 是否为加密盘创建的快照。取值范围:true:该快照为加密盘创建的 false:非加密盘创建的快照。
|
||||
IsPermanent bool // 是否为永久快照。取值范围: true:永久快照 false:非永久快照。
|
||||
CopyingToRegions []string // 快照正在跨地域复制的目的地域,默认取值为[]。
|
||||
CopyFromRemote bool // 是否为跨地域复制的快照。取值范围:true:表示为跨地域复制的快照。 false:本地域的快照。
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, total, err := self.GetSnapshots("", "", "", []string{snapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total > 1 {
|
||||
return nil, cloudprovider.ErrDuplicateId
|
||||
}
|
||||
if total == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return &snapshots[0], nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetStatus() string {
|
||||
// NORMAL:正常
|
||||
// CREATING:创建中
|
||||
// ROLLBACKING:回滚中
|
||||
// COPYING_FROM_REMOTE:跨地域复制快照拷贝中。
|
||||
switch self.SnapshotState {
|
||||
case "NORMAL", "COPYING_FROM_REMOTE":
|
||||
return models.SNAPSHOT_READY
|
||||
case "CREATING":
|
||||
return models.SNAPSHOT_CREATING
|
||||
case "ROLLBACKING":
|
||||
return models.SNAPSHOT_ROLLBACKING
|
||||
}
|
||||
return models.SNAPSHOT_UNKNOWN
|
||||
}
|
||||
|
||||
func (self *SSnapshot) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SSnapshot) Refresh() error {
|
||||
snapshots, total, err := self.region.GetSnapshots("", "", "", []string{self.SnapshotId}, 0, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if total > 1 {
|
||||
return cloudprovider.ErrDuplicateId
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return cloudprovider.ErrNotFound
|
||||
}
|
||||
return jsonutils.Update(self, snapshots[0])
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSnapshots(instanceId string, diskId string, snapshotName string, snapshotIds []string, offset int, limit int) ([]SSnapshot, int, error) {
|
||||
if limit > 50 || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
params := make(map[string]string)
|
||||
params["Limit"] = fmt.Sprintf("%d", limit)
|
||||
params["Offset"] = fmt.Sprintf("%d", offset)
|
||||
|
||||
filter := 0
|
||||
if len(instanceId) > 0 {
|
||||
}
|
||||
if len(diskId) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "disk-id"
|
||||
params[fmt.Sprintf("Filters.%d.Values", filter)] = diskId
|
||||
filter++
|
||||
}
|
||||
if len(snapshotName) > 0 {
|
||||
params[fmt.Sprintf("Filters.%d.Name", filter)] = "snapshot-name"
|
||||
params[fmt.Sprintf("Filters.%d.Values", filter)] = snapshotName
|
||||
filter++
|
||||
}
|
||||
if snapshotIds != nil && len(snapshotIds) > 0 {
|
||||
for index, snapshotId := range snapshotIds {
|
||||
params[fmt.Sprintf("SnapshotIds.%d", index)] = snapshotId
|
||||
}
|
||||
}
|
||||
snapshots := []SSnapshot{}
|
||||
body, err := self.cbsRequest("DescribeSnapshots", params)
|
||||
if err != nil {
|
||||
log.Errorf("GetSnapshots fail %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
body.Unmarshal(&snapshots, "SnapshotSet")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total, _ := body.Float("TotalCount")
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].region = self
|
||||
}
|
||||
return snapshots, int(total), nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, total, err := self.GetSnapshots("", "", "", []string{}, 0, 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for len(snapshots) < total {
|
||||
var parts []SSnapshot
|
||||
parts, total, err = self.GetSnapshots("", "", "", []string{}, len(snapshots), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshots = append(snapshots, parts...)
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudSnapshot, len(snapshots))
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
ret[i] = &snapshots[i]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetRegionId() string {
|
||||
return self.region.GetId()
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetSize() int32 {
|
||||
return self.DiskSize
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetDiskId() string {
|
||||
return self.DiskId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetId() string {
|
||||
return self.SnapshotId
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s", self.SnapshotId)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetName() string {
|
||||
return self.SnapshotName
|
||||
}
|
||||
|
||||
func (self *SSnapshot) Delete() error {
|
||||
if self.region == nil {
|
||||
return fmt.Errorf("not init region for snapshot %s", self.SnapshotId)
|
||||
}
|
||||
return self.region.DeleteSnapshot(self.SnapshotId)
|
||||
}
|
||||
|
||||
func (self *SSnapshot) GetDiskType() string {
|
||||
switch self.DiskUsage {
|
||||
case "SYSTEM_DISK":
|
||||
return models.DISK_TYPE_SYS
|
||||
case "DATA_DISK":
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
return models.DISK_TYPE_DATA
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSnapshot(snapshotId string) error {
|
||||
params := map[string]string{"SnapshotIds.0": snapshotId}
|
||||
_, err := self.cbsRequest("DeleteSnapshots", params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSnapshot(diskId, name, desc string) (string, error) {
|
||||
params := make(map[string]string)
|
||||
params["DiskId"] = diskId
|
||||
params["SnapshotName"] = name
|
||||
|
||||
body, err := self.cbsRequest("CreateSnapshot", params)
|
||||
if err != nil {
|
||||
log.Errorf("CreateSnapshot fail %s", err)
|
||||
return "", err
|
||||
}
|
||||
return body.GetString("SnapshotId")
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SStorage struct {
|
||||
zone *SZone
|
||||
storageType string
|
||||
}
|
||||
|
||||
func (self *SStorage) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetName() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerName, self.zone.GetId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s-%s", self.zone.region.client.providerId, self.zone.GetGlobalId(), self.storageType)
|
||||
}
|
||||
|
||||
func (self *SStorage) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks := make([]SDisk, 0)
|
||||
for {
|
||||
parts, total, err := self.zone.region.GetDisks("", self.zone.GetId(), self.storageType, nil, len(disks), 50)
|
||||
if err != nil {
|
||||
log.Errorf("GetDisks fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
disks = append(disks, parts...)
|
||||
if len(disks) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
idisks := make([]cloudprovider.ICloudDisk, len(disks))
|
||||
for i := 0; i < len(disks); i++ {
|
||||
disks[i].storage = self
|
||||
idisks[i] = &disks[i]
|
||||
}
|
||||
return idisks, nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStorageType() string {
|
||||
return strings.ToLower(self.storageType)
|
||||
}
|
||||
|
||||
func (self *SStorage) GetMediumType() string {
|
||||
if strings.HasSuffix(self.storageType, "_BASIC") {
|
||||
return models.DISK_TYPE_ROTATE
|
||||
}
|
||||
return models.DISK_TYPE_SSD
|
||||
}
|
||||
|
||||
func (self *SStorage) GetCapacityMB() int {
|
||||
return 0 // unlimited
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStorageConf() jsonutils.JSONObject {
|
||||
conf := jsonutils.NewDict()
|
||||
return conf
|
||||
}
|
||||
|
||||
func (self *SStorage) GetManagerId() string {
|
||||
return self.zone.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStatus() string {
|
||||
return models.STORAGE_ONLINE
|
||||
}
|
||||
|
||||
func (self *SStorage) Refresh() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return self.zone.region.getStoragecache()
|
||||
}
|
||||
|
||||
func (self *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) {
|
||||
diskId, err := self.zone.region.CreateDisk(self.zone.Zone, self.storageType, name, sizeGb, desc)
|
||||
if err != nil {
|
||||
log.Errorf("createDisk fail %s", err)
|
||||
return nil, err
|
||||
}
|
||||
//腾讯刚创建完成的磁盘,需要稍微等待才能查询
|
||||
for i := 0; i < 3; i++ {
|
||||
disk, err := self.zone.region.GetDisk(diskId)
|
||||
if err == nil {
|
||||
disk.storage = self
|
||||
return disk, nil
|
||||
}
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
log.Errorf("getDisk fail %s id %s", err, diskId)
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDisk(idStr string) (cloudprovider.ICloudDisk, error) {
|
||||
disk, err := self.zone.region.GetDisk(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk.storage = self
|
||||
return disk, nil
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
|
||||
coslib "github.com/nelsonken/cos-go-sdk-v5/cos"
|
||||
)
|
||||
|
||||
type SStoragecache struct {
|
||||
region *SRegion
|
||||
|
||||
iimages []cloudprovider.ICloudImage
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetId() string {
|
||||
return fmt.Sprintf("%s-%s", self.region.client.providerId, self.region.GetId())
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetName() string {
|
||||
return fmt.Sprintf("%s-%s", self.region.client.providerName, self.region.GetId())
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetStatus() string {
|
||||
return "available"
|
||||
}
|
||||
|
||||
func (self *SStoragecache) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s", self.region.client.providerId, self.region.GetGlobalId())
|
||||
}
|
||||
|
||||
func (self *SStoragecache) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) {
|
||||
// if imageId, err := self.region.createIImage(snapshoutId, imageName, imageDesc); err != nil {
|
||||
// return nil, err
|
||||
// } else if image, err := self.region.GetImage(imageId); err != nil {
|
||||
// return nil, err
|
||||
// } else {
|
||||
// image.storageCache = self
|
||||
// iimage := make([]cloudprovider.ICloudImage, 1)
|
||||
// iimage[0] = image
|
||||
// if err := cloudprovider.WaitStatus(iimage[0], compute.IMAGE_STATUS_ACTIVE, 15*time.Second, 3600*time.Second); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return iimage[0], nil
|
||||
// }
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imageId string, extId string, path string) (jsonutils.JSONObject, error) {
|
||||
//return self.downloadImage(userCred, imageId, extId, path)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) fetchImages() error {
|
||||
images := make([]SImage, 0)
|
||||
for {
|
||||
parts, total, err := self.region.GetImages("", "PRIVATE_IMAGE", nil, "", len(images), 50)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images = append(images, parts...)
|
||||
if len(images) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
self.iimages = make([]cloudprovider.ICloudImage, len(images))
|
||||
for i := 0; i < len(images); i += 1 {
|
||||
images[i].storageCache = self
|
||||
self.iimages[i] = &images[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) {
|
||||
if self.iimages == nil {
|
||||
err := self.fetchImages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.iimages, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) UploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, extId string, isForce bool) (string, error) {
|
||||
if len(extId) > 0 {
|
||||
log.Debugf("UploadImage: Image external ID exists %s", extId)
|
||||
|
||||
status, err := self.region.GetImageStatus(extId)
|
||||
if err != nil {
|
||||
log.Errorf("GetImageStatus error %s", err)
|
||||
}
|
||||
if status == ImageStatusAvailable && !isForce {
|
||||
return extId, nil
|
||||
}
|
||||
} else {
|
||||
log.Debugf("UploadImage: no external ID")
|
||||
}
|
||||
return self.uploadImage(userCred, imageId, osArch, osType, osDist, isForce)
|
||||
}
|
||||
|
||||
func (self *SRegion) getCosUrl(bucket, object string) string {
|
||||
//signature := cosauth.NewSignature(self.client.AppID, bucket, self.client.SecretID, time.Now().Add(time.Minute*30).String(), time.Now().String(), "yunion", object).SignOnce(self.client.SecretKey)
|
||||
return fmt.Sprintf("http://%s-%s.cos.%s.myqcloud.com/%s", bucket, self.client.AppID, self.Region, object)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) uploadImage(userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist string, isForce bool) (string, error) {
|
||||
// first upload image to oss
|
||||
s := auth.GetAdminSession(options.Options.Region, "")
|
||||
|
||||
meta, reader, err := modules.Images.Download(s, imageId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tmpFile := fmt.Sprintf("%s/%s", options.Options.TempPath, imageId)
|
||||
defer os.Remove(tmpFile)
|
||||
f, err := os.Create(tmpFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := io.Copy(f, reader); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Infof("meta data %s", meta)
|
||||
cos, err := self.region.GetCosClient()
|
||||
if err != nil {
|
||||
log.Errorf("GetOssClient err %s", err)
|
||||
return "", err
|
||||
}
|
||||
bucketName := strings.ToLower(fmt.Sprintf("imgcache-%s", self.region.GetId()))
|
||||
err = cos.BucketExists(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
log.Debugf("Bucket %s not exists, to create ...", bucketName)
|
||||
err := cos.CreateBucket(context.Background(), bucketName, &coslib.AccessControl{ACL: "public-read"})
|
||||
if err != nil {
|
||||
log.Errorf("Create bucket error %s", err)
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
log.Debugf("Bucket %s exists", bucketName)
|
||||
}
|
||||
log.Debugf("To upload image to bucket %s ...", bucketName)
|
||||
err = cos.Bucket(bucketName).UploadObjectBySlice(context.Background(), imageId, tmpFile, 3, map[string]string{})
|
||||
if err != nil {
|
||||
log.Errorf("UploadObject error %s %s", imageId, err)
|
||||
return "", err
|
||||
}
|
||||
// 腾讯云镜像名称需要小于20个字符
|
||||
imageBaseName := imageId[:10]
|
||||
if imageBaseName[0] >= '0' && imageBaseName[0] <= '9' {
|
||||
imageBaseName = fmt.Sprintf("img%s", imageId[:10])
|
||||
}
|
||||
imageName := imageBaseName
|
||||
nameIdx := 1
|
||||
|
||||
// check image name, avoid name conflict
|
||||
for {
|
||||
_, err = self.region.GetImageByName(imageName)
|
||||
if err != nil {
|
||||
if err == cloudprovider.ErrNotFound {
|
||||
break
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
imageName = fmt.Sprintf("%s-%d", imageBaseName, nameIdx)
|
||||
nameIdx++
|
||||
}
|
||||
|
||||
log.Debugf("Import image %s", imageName)
|
||||
if image, err := self.region.ImportImage(imageName, osArch, osType, osDist, self.region.getCosUrl(bucketName, imageId)); err != nil {
|
||||
return "", err
|
||||
} else if cloudprovider.WaitStatus(image, string(ImageStatusAvailable), 15*time.Second, 3600*time.Second); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return image.ImageId, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
)
|
||||
|
||||
type SVpc struct {
|
||||
region *SRegion
|
||||
|
||||
iwires []cloudprovider.ICloudWire
|
||||
|
||||
secgroups []cloudprovider.ICloudSecurityGroup
|
||||
|
||||
CidrBlock string
|
||||
CreatedTime time.Time
|
||||
DhcpOptionsId string
|
||||
DnsServerSet []string
|
||||
DomainName string
|
||||
EnableMulticast bool
|
||||
IsDefault bool
|
||||
VpcId string
|
||||
VpcName string
|
||||
}
|
||||
|
||||
func (self *SVpc) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetName() string {
|
||||
if len(self.VpcName) > 0 {
|
||||
return self.VpcName
|
||||
}
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetGlobalId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SVpc) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIsDefault() bool {
|
||||
return self.IsDefault
|
||||
}
|
||||
|
||||
func (self *SVpc) GetCidrBlock() string {
|
||||
return self.CidrBlock
|
||||
}
|
||||
|
||||
func (self *SVpc) GetStatus() string {
|
||||
return models.VPC_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SVpc) Delete() error {
|
||||
return self.region.DeleteVpc(self.VpcId)
|
||||
}
|
||||
|
||||
func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) {
|
||||
secgroups := make([]SSecurityGroup, 0)
|
||||
for {
|
||||
parts, total, err := self.region.GetSecurityGroups(self.VpcId, len(secgroups), 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secgroups = append(secgroups, parts...)
|
||||
if len(secgroups) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
isecgroups := make([]cloudprovider.ICloudSecurityGroup, len(secgroups))
|
||||
for i := 0; i < len(secgroups); i++ {
|
||||
secgroups[i].vpc = self
|
||||
isecgroups[i] = &secgroups[i]
|
||||
}
|
||||
return isecgroups, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) getWireByZoneId(zoneId string) *SWire {
|
||||
for i := 0; i <= len(self.iwires); i++ {
|
||||
wire := self.iwires[i].(*SWire)
|
||||
if wire.zone.Zone == zoneId {
|
||||
return wire
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) fetchNetworks() error {
|
||||
networks, total, err := self.region.GetNetworks(nil, self.VpcId, 0, 50)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if total > len(networks) {
|
||||
networks, _, err = self.region.GetNetworks(nil, self.VpcId, 0, total)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
wire := self.getWireByZoneId(networks[i].Zone)
|
||||
networks[i].wire = wire
|
||||
wire.addNetwork(&networks[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) {
|
||||
if self.iwires == nil {
|
||||
err := self.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(self.iwires); i += 1 {
|
||||
if self.iwires[i].GetGlobalId() == wireId {
|
||||
return self.iwires[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
if self.iwires == nil {
|
||||
err := self.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.iwires, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetManagerId() string {
|
||||
return self.region.client.providerId
|
||||
}
|
||||
|
||||
func (self *SVpc) GetRegion() cloudprovider.ICloudRegion {
|
||||
return self.region
|
||||
}
|
||||
|
||||
func (self *SVpc) Refresh() error {
|
||||
new, err := self.region.getVpc(self.VpcId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SVpc) SyncSecurityGroup(secgroupId string, name string, rules []secrules.SecurityRule) (string, error) {
|
||||
secgrpId := ""
|
||||
// if secgroup, err := self.region.getSecurityGroupByTag(self.VpcId, secgroupId); err != nil {
|
||||
// if secgrpId, err = self.region.createSecurityGroup(self.VpcId, name, ""); err != nil {
|
||||
// return "", err
|
||||
// } else if err := self.region.addTagToSecurityGroup(secgrpId, "id", secgroupId, 1); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// //addRules
|
||||
// log.Debugf("Add Rules for %s", secgrpId)
|
||||
// for _, rule := range rules {
|
||||
// if err := self.region.addSecurityGroupRule(secgrpId, &rule); err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// //syncRules
|
||||
// secgrpId = secgroup.SecurityGroupId
|
||||
// log.Debugf("Sync Rules for %s", secgroup.GetName())
|
||||
// if secgroup.GetName() != name {
|
||||
// if err := self.region.modifySecurityGroup(secgrpId, name, ""); err != nil {
|
||||
// log.Errorf("Change SecurityGroup name to %s failed: %v", name, err)
|
||||
// }
|
||||
// }
|
||||
// self.region.syncSecgroupRules(secgrpId, rules)
|
||||
// }
|
||||
return secgrpId, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) addWire(wire *SWire) {
|
||||
if self.iwires == nil {
|
||||
self.iwires = make([]cloudprovider.ICloudWire, 0)
|
||||
}
|
||||
self.iwires = append(self.iwires, wire)
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SWire struct {
|
||||
zone *SZone
|
||||
vpc *SVpc
|
||||
|
||||
inetworks []cloudprovider.ICloudNetwork
|
||||
}
|
||||
|
||||
func (self *SWire) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetId() string {
|
||||
return fmt.Sprintf("%s-%s", self.vpc.GetId(), self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SWire) GetName() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SWire) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SWire) GetStatus() string {
|
||||
return "available"
|
||||
}
|
||||
|
||||
func (self *SWire) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s", self.vpc.GetGlobalId(), self.zone.GetGlobalId())
|
||||
}
|
||||
|
||||
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
|
||||
return self.vpc
|
||||
}
|
||||
|
||||
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
}
|
||||
|
||||
func (self *SWire) GetBandwidth() int {
|
||||
return 10000
|
||||
}
|
||||
|
||||
func (self *SWire) GetNetworkById(networkId string) *SNetwork {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.Debugf("search for networks %d", len(networks))
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
log.Debugf("search %s", networks[i].GetName())
|
||||
network := networks[i].(*SNetwork)
|
||||
if network.SubnetId == networkId {
|
||||
return network
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) {
|
||||
networkId, err := self.zone.region.CreateNetwork(self.zone.Zone, self.vpc.VpcId, name, cidr, desc)
|
||||
if err != nil {
|
||||
log.Errorf("CreateNetwork error %s", err)
|
||||
return nil, err
|
||||
}
|
||||
self.inetworks = nil
|
||||
network := self.GetNetworkById(networkId)
|
||||
if network == nil {
|
||||
log.Errorf("cannot find vswitch after create????")
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
return network, nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
if networks[i].GetGlobalId() == netid {
|
||||
return networks[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SWire) getNetworkById(networkId string) *SNetwork {
|
||||
networks, err := self.GetINetworks()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.Debugf("search for networks %d", len(networks))
|
||||
for i := 0; i < len(networks); i += 1 {
|
||||
log.Debugf("search %s", networks[i].GetName())
|
||||
network := networks[i].(*SNetwork)
|
||||
if network.SubnetId == networkId {
|
||||
return network
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) {
|
||||
if self.inetworks == nil {
|
||||
err := self.vpc.fetchNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return self.inetworks, nil
|
||||
}
|
||||
|
||||
func (self *SWire) addNetwork(network *SNetwork) {
|
||||
if self.inetworks == nil {
|
||||
self.inetworks = make([]cloudprovider.ICloudNetwork, 0)
|
||||
}
|
||||
find := false
|
||||
for i := 0; i < len(self.inetworks); i += 1 {
|
||||
if self.inetworks[i].GetId() == network.SubnetId {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
self.inetworks = append(self.inetworks, network)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type InstanceChargeType string
|
||||
|
||||
const (
|
||||
PrePaidInstanceChargeType InstanceChargeType = "PREPAID"
|
||||
PostPaidInstanceChargeType InstanceChargeType = "POSTPAID_BY_HOUR"
|
||||
CdhPaidInstanceChargeType InstanceChargeType = "CDHPAID"
|
||||
DefaultInstanceChargeType = PostPaidInstanceChargeType
|
||||
)
|
||||
|
||||
type SZone struct {
|
||||
region *SRegion
|
||||
|
||||
iwires []cloudprovider.ICloudWire
|
||||
|
||||
host *SHost
|
||||
|
||||
istorages []cloudprovider.ICloudStorage
|
||||
|
||||
instanceTypes []string
|
||||
refreshTime time.Time
|
||||
|
||||
Zone string
|
||||
ZoneId string
|
||||
ZoneName string
|
||||
ZoneState string
|
||||
}
|
||||
|
||||
func (self *SZone) GetMetadata() *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetId() string {
|
||||
return self.Zone
|
||||
}
|
||||
|
||||
func (self *SZone) GetName() string {
|
||||
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_QCLOUD_CN, self.ZoneName)
|
||||
}
|
||||
|
||||
func (self *SZone) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s/%s", self.region.GetGlobalId(), self.Zone)
|
||||
}
|
||||
|
||||
func (self *SZone) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SZone) Refresh() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetStatus() string {
|
||||
if self.ZoneState == "AVAILABLE" {
|
||||
return models.ZONE_ENABLE
|
||||
}
|
||||
return models.ZONE_SOLDOUT
|
||||
}
|
||||
|
||||
func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
host := self.getHost()
|
||||
if host.GetGlobalId() == id {
|
||||
return host, nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
|
||||
return []cloudprovider.ICloudHost{self.getHost()}, nil
|
||||
}
|
||||
|
||||
func (self *SZone) getHost() *SHost {
|
||||
if self.host == nil {
|
||||
self.host = &SHost{zone: self}
|
||||
}
|
||||
return self.host
|
||||
}
|
||||
|
||||
func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
|
||||
return self.region
|
||||
}
|
||||
|
||||
func (self *SZone) fetchStorages() error {
|
||||
self.istorages = []cloudprovider.ICloudStorage{}
|
||||
for _, storageType := range []string{"CLOUD_BASIC", "CLOUD_PREMIUM", "CLOUD_SSD"} {
|
||||
storage := SStorage{zone: self, storageType: storageType}
|
||||
self.istorages = append(self.istorages, &storage)
|
||||
}
|
||||
for _, localstorageType := range []string{"LOCAL_BASIC", "LOCAL_SSD"} {
|
||||
storage := SLocalStorage{zone: self, storageType: localstorageType}
|
||||
self.istorages = append(self.istorages, &storage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
if self.istorages == nil {
|
||||
self.fetchStorages()
|
||||
}
|
||||
return self.istorages, nil
|
||||
}
|
||||
|
||||
func (self *SZone) getLocalStorageByCategory(category string) (*SLocalStorage, error) {
|
||||
if utils.IsInStringArray(strings.ToLower(category), []string{"local_basic", "local_ssd"}) {
|
||||
return &SLocalStorage{zone: self, storageType: strings.ToUpper(category)}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("No such storage %s", category)
|
||||
}
|
||||
|
||||
func (self *SZone) validateStorageType(category string) error {
|
||||
if utils.IsInStringArray(strings.ToLower(category), []string{"local_basic", "local_ssd", "cloud_basic", "cloud_ssd", "cloud_permium"}) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("No such storage %s", category)
|
||||
}
|
||||
|
||||
func (self *SZone) getStorageByCategory(category string) (*SStorage, error) {
|
||||
storages, err := self.GetIStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(storages); i++ {
|
||||
if utils.IsInStringArray(storages[i].GetStorageType(), []string{"local_basic", "local_ssd"}) {
|
||||
continue
|
||||
//return &SStorage{zone: self, storageType: strings.ToUpper(storages[i].GetStorageType())}, nil
|
||||
}
|
||||
storage := storages[i].(*SStorage)
|
||||
if storage.storageType == category {
|
||||
return storage, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("No such storage %s", category)
|
||||
}
|
||||
|
||||
func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
if self.istorages == nil {
|
||||
self.fetchStorages()
|
||||
}
|
||||
for i := 0; i < len(self.istorages); i += 1 {
|
||||
if self.istorages[i].GetGlobalId() == id {
|
||||
return self.istorages[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SZone) addWire(wire *SWire) {
|
||||
if self.iwires == nil {
|
||||
self.iwires = make([]cloudprovider.ICloudWire, 0)
|
||||
}
|
||||
self.iwires = append(self.iwires, wire)
|
||||
}
|
||||
|
||||
func (self *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
return self.iwires, nil
|
||||
}
|
||||
|
||||
func (self *SZone) getNetworkById(networkId string) *SNetwork {
|
||||
log.Debugf("Search in wires %d", len(self.iwires))
|
||||
for i := 0; i < len(self.iwires); i += 1 {
|
||||
log.Debugf("Search in wire %s", self.iwires[i].GetName())
|
||||
wire := self.iwires[i].(*SWire)
|
||||
net := wire.getNetworkById(networkId)
|
||||
if net != nil {
|
||||
return net
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SZone) fetchInstanceTypes() {
|
||||
self.instanceTypes = []string{}
|
||||
params := map[string]string{}
|
||||
params["Region"] = self.region.Region
|
||||
params["Filters.0.Name"] = "zone"
|
||||
params["Filters.0.Values.0"] = self.Zone
|
||||
if body, err := self.region.cvmRequest("DescribeInstanceTypeConfigs", params); err != nil {
|
||||
log.Errorf("DescribeInstanceTypeConfigs error: %v", err)
|
||||
} else if configSet, err := body.GetArray("InstanceTypeConfigSet"); err != nil {
|
||||
log.Errorf("Get InstanceTypeConfigSet error: %v", err)
|
||||
} else {
|
||||
for _, config := range configSet {
|
||||
if instanceType, err := config.GetString("InstanceType"); err == nil && !utils.IsInStringArray(instanceType, self.instanceTypes) {
|
||||
self.instanceTypes = append(self.instanceTypes, instanceType)
|
||||
}
|
||||
}
|
||||
self.refreshTime = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SZone) getAvaliableInstanceTypes() []string {
|
||||
if self.instanceTypes == nil || len(self.instanceTypes) == 0 || time.Now().Sub(self.refreshTime).Hours() > refreshHours() {
|
||||
self.fetchInstanceTypes()
|
||||
}
|
||||
return self.instanceTypes
|
||||
}
|
||||
|
||||
func refreshHours() float64 {
|
||||
return 5
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Signer interface {
|
||||
Sign(secretKey string) (string, error)
|
||||
SignOnce(secretKey string) (string, error)
|
||||
}
|
||||
|
||||
// 生成签名, 腾讯移动服务通过签名来验证请求的合法性,
|
||||
// 开发者通过将签名授权给客户端, 使其具备上传下载及管理指定资源的能力,
|
||||
// 签名分为多次有效签名和单次有效签名.
|
||||
// 生成签名所需信息包括项目 ID(AppId),空间名称(Bucket,文件资源的组织管理单元),项目的 Secret ID 和 Secret Key,
|
||||
// 获取这些信息的方法如下:
|
||||
// 1) 登录 云对象存储, 进入云对象存储空间;
|
||||
// 2) 如开发者未创建空间,可添加空间,空间名称(Bucket)由用户自行输入
|
||||
// 3) 点击“获取secretKey”,获取 Appid,Secret ID 和 Secret Key
|
||||
type Signature struct {
|
||||
AppId string
|
||||
Bucket string
|
||||
SecretId string
|
||||
ExpiredTime string
|
||||
CurrentTime string
|
||||
Rand string
|
||||
FileId string
|
||||
}
|
||||
|
||||
// 构造签名类, 可使用该类的 Sign 和 SignOnce 接口分别进行多次有效签名和单次有效签名
|
||||
func NewSignature(appId, bucket, secretId, expiredTime, currentTime, rand, fileId string) *Signature {
|
||||
return &Signature{
|
||||
AppId: appId,
|
||||
Bucket: bucket,
|
||||
SecretId: secretId,
|
||||
ExpiredTime: expiredTime,
|
||||
CurrentTime: currentTime,
|
||||
Rand: rand,
|
||||
FileId: fileId,
|
||||
}
|
||||
}
|
||||
|
||||
// 多次有效签名, secretKey 为项目的 Secret Key
|
||||
func (s *Signature) Sign(secretKey string) string {
|
||||
stringToSign := fmt.Sprintf("a=%s&k=%s&e=%s&t=%s&r=%s&f=%s&b=%s",
|
||||
s.AppId,
|
||||
s.SecretId,
|
||||
s.ExpiredTime,
|
||||
s.CurrentTime,
|
||||
s.Rand,
|
||||
"",
|
||||
s.Bucket,
|
||||
)
|
||||
|
||||
hmacSha1 := hmac.New(sha1.New, []byte(secretKey))
|
||||
hmacSha1.Write([]byte(stringToSign))
|
||||
bytesSign := hmacSha1.Sum(nil)
|
||||
bytesSign = append(bytesSign, []byte(stringToSign)...)
|
||||
signature := base64.StdEncoding.EncodeToString(bytesSign)
|
||||
return signature
|
||||
}
|
||||
|
||||
// 单次有效签名, secretKey 为项目的 Secret Key
|
||||
func (s *Signature) SignOnce(secretKey string) string {
|
||||
stringToSign := fmt.Sprintf("a=%s&k=%s&e=%s&t=%s&r=%s&f=%s&b=%s",
|
||||
s.AppId,
|
||||
s.SecretId,
|
||||
"0",
|
||||
s.CurrentTime,
|
||||
s.Rand,
|
||||
s.FileId,
|
||||
s.Bucket,
|
||||
)
|
||||
|
||||
hmacSha1 := hmac.New(sha1.New, []byte(secretKey))
|
||||
hmacSha1.Write([]byte(stringToSign))
|
||||
bytesSign := hmacSha1.Sum(nil)
|
||||
bytesSign = append(bytesSign, []byte(stringToSign)...)
|
||||
signature := base64.StdEncoding.EncodeToString(bytesSign)
|
||||
return signature
|
||||
}
|
||||
|
||||
// 字符串签名类
|
||||
func (s *Signature) String() string {
|
||||
str := fmt.Sprintf("a=%s&b=%s&k=%s&e=%s&t=%s&r=%s&f=%s",
|
||||
s.AppId,
|
||||
s.SecretId,
|
||||
s.ExpiredTime,
|
||||
s.CurrentTime,
|
||||
s.Rand,
|
||||
s.FileId,
|
||||
s.Bucket,
|
||||
)
|
||||
|
||||
return str
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Jonathan Leibiusky and Marcos Lilljedahl
|
||||
https://github.com/franela/goreq
|
||||
|
||||
Copyright (c) 2016 Haiping Fu
|
||||
https://github.com/forhappy/goreq
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (conn *Conn) signHeader(req *http.Request, params map[string]interface{}, headers map[string]string) {
|
||||
signTime := getSignTime()
|
||||
signature := conn.getSignature(req, params, headers, signTime)
|
||||
authStr := fmt.Sprintf("q-sign-algorithm=sha1&q-ak=%s&q-sign-time=%s&q-key-time=%s&q-header-list=%s&q-url-param-list=%s&q-signature=%s",
|
||||
conn.conf.SecretID, signTime, signTime, getHeadKeys(headers), getParamKeys(params), signature)
|
||||
|
||||
req.Header.Set("Authorization", authStr)
|
||||
}
|
||||
|
||||
func getSignTime() string {
|
||||
now := time.Now()
|
||||
expired := now.Add(time.Second * 1800)
|
||||
return fmt.Sprintf("%d;%d", now.Unix(), expired.Unix())
|
||||
}
|
||||
|
||||
func getHeadKeys(headers map[string]string) string {
|
||||
if headers == nil || len(headers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
tmp := []string{}
|
||||
for k := range headers {
|
||||
tmp = append(tmp, strings.ToLower(k))
|
||||
}
|
||||
sort.Strings(tmp)
|
||||
|
||||
return strings.Join(tmp, ";")
|
||||
}
|
||||
|
||||
func getParamKeys(params map[string]interface{}) string {
|
||||
if params == nil || len(params) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
tmp := []string{}
|
||||
for k := range params {
|
||||
tmp = append(tmp, strings.ToLower(k))
|
||||
}
|
||||
sort.Strings(tmp)
|
||||
|
||||
return strings.Join(tmp, ";")
|
||||
}
|
||||
|
||||
func (conn *Conn) getSignature(req *http.Request, params map[string]interface{}, headers map[string]string, signTime string) string {
|
||||
httpString := fmt.Sprintf("%s\n%s\n%s\n%s\n", strings.ToLower(req.Method),
|
||||
req.URL.Path, getParamStr(params), getHeadStr(headers))
|
||||
|
||||
httpString = sha(httpString)
|
||||
signKey := hmacSha(conn.conf.SecretKey, signTime)
|
||||
signStr := fmt.Sprintf("sha1\n%s\n%s\n", signTime, httpString)
|
||||
|
||||
return hmacSha(signKey, signStr)
|
||||
}
|
||||
|
||||
func interfaceToString(i interface{}) string {
|
||||
switch x := i.(type) {
|
||||
case string:
|
||||
return x
|
||||
case int:
|
||||
return strconv.Itoa(x)
|
||||
case int64:
|
||||
return strconv.FormatInt(x, 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(x, 10)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getParamStr(params map[string]interface{}) string {
|
||||
if params == nil || len(params) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
tmp := []string{}
|
||||
for k, v := range params {
|
||||
str := strings.ToLower(fmt.Sprintf("%s=%s", k, interfaceToString(v)))
|
||||
tmp = append(tmp, str)
|
||||
}
|
||||
sort.Strings(tmp)
|
||||
|
||||
return strings.Join(tmp, "&")
|
||||
}
|
||||
|
||||
func getHeadStr(headers map[string]string) string {
|
||||
if headers == nil || len(headers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
tmp := []string{}
|
||||
for k, v := range headers {
|
||||
str := fmt.Sprintf("%s=%s", strings.ToLower(k), escape(v))
|
||||
tmp = append(tmp, str)
|
||||
}
|
||||
sort.Strings(tmp)
|
||||
|
||||
return strings.Join(tmp, "&")
|
||||
}
|
||||
|
||||
func sha(s string) string {
|
||||
sha := sha1.New()
|
||||
sha.Write([]byte(s))
|
||||
b := sha.Sum(nil)
|
||||
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
func hmacSha(k, s string) string {
|
||||
enc := hmac.New(sha1.New, []byte(k))
|
||||
enc.Write([]byte(s))
|
||||
b := enc.Sum(nil)
|
||||
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Bucket bucket
|
||||
type Bucket struct {
|
||||
Name string
|
||||
conn *Conn
|
||||
}
|
||||
|
||||
// ObjectSlice object slice
|
||||
type ObjectSlice struct {
|
||||
UploadID string
|
||||
Size int64
|
||||
Offset int64
|
||||
Number int
|
||||
MD5 string
|
||||
Dst string
|
||||
Result bool
|
||||
}
|
||||
|
||||
// 获得云存储上文件信息
|
||||
func (b *Bucket) HeadObject(ctx context.Context, object string) error {
|
||||
resq, err := b.conn.Do(ctx, http.MethodHead, b.Name, object, nil, nil, nil)
|
||||
if err == nil {
|
||||
defer resq.Body.Close()
|
||||
} else {
|
||||
for k, v := range resq.Header {
|
||||
value := fmt.Sprintf("%s", v)
|
||||
fmt.Printf("%-18s: %s\n", k, strings.Replace(strings.Replace(value, "[", "", -1), "]", "", -1))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bucket) UploadObject(ctx context.Context, object string, content io.Reader, acl *AccessControl) error {
|
||||
res, err := b.conn.Do(ctx, http.MethodPut, b.Name, object, nil, acl.GenHead(), content)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bucket) CopyObject(ctx context.Context, src, dst string, acl *AccessControl) error {
|
||||
srcURL := fmt.Sprintf("%s-%s.cos.%s.%s/%s", b.Name, b.conn.conf.AppID, b.conn.conf.Region, b.conn.conf.Domain, dst)
|
||||
header := map[string]string{
|
||||
"x-cos-source-url": srcURL,
|
||||
}
|
||||
|
||||
res, err := b.conn.Do(ctx, http.MethodPut, b.Name, dst, nil, header, nil)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bucket) DeleteObject(ctx context.Context, obj string) error {
|
||||
res, err := b.conn.Do(ctx, http.MethodDelete, b.Name, obj, nil, nil, nil)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bucket) DownloadObject(ctx context.Context, object string, w io.Writer) error {
|
||||
res, err := b.conn.Do(ctx, http.MethodGet, b.Name, object, nil, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, res.Body)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UploadObjectBySlice upload by slice
|
||||
func (b *Bucket) UploadObjectBySlice(ctx context.Context, dst, src string, taskNum int, headers map[string]string) error {
|
||||
if taskNum < 1 {
|
||||
return ParamError{"taskNum 必须大于1"}
|
||||
}
|
||||
|
||||
uploadID, err := b.InitSliceUpload(ctx, dst, headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fd, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
slices, err := b.PerformSliceUpload(ctx, dst, uploadID, fd, taskNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.CompleteSliceUpload(ctx, dst, uploadID, fd, slices)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// InitSliceUpload init upload by slice
|
||||
func (b *Bucket) InitSliceUpload(ctx context.Context, obj string, headers map[string]string) (string, error) {
|
||||
param := map[string]interface{}{
|
||||
"uploads": "",
|
||||
}
|
||||
res, err := b.conn.Do(ctx, http.MethodPost, b.Name, obj, param, headers, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
imur := &InitiateMultipartUploadResult{}
|
||||
err = XMLDecode(res.Body, imur)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return imur.UploadID, nil
|
||||
}
|
||||
|
||||
// CompleteSliceUpload finish slice Upload
|
||||
func (b *Bucket) CompleteSliceUpload(ctx context.Context, dst, uploadID string, fd *os.File, slice []*ObjectSlice) error {
|
||||
cmu := &CompleteMultipartUpload{}
|
||||
cmu.Part = []struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}{}
|
||||
|
||||
for _, osl := range slice {
|
||||
cmu.Part = append(cmu.Part, struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}{PartNumber: osl.Number, ETag: osl.MD5})
|
||||
}
|
||||
|
||||
cmuXML, err := xml.Marshal(cmu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
param := map[string]interface{}{
|
||||
"uploadId": uploadID,
|
||||
}
|
||||
res, err := b.conn.Do(ctx, http.MethodPost, b.Name, dst, param, nil, bytes.NewReader(cmuXML))
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// PerformSliceUpload perform slice upload
|
||||
func (b *Bucket) PerformSliceUpload(ctx context.Context, dst, uploadID string, fd *os.File, taskNum int) ([]*ObjectSlice, error) {
|
||||
oss, err := b.getFileSlices(fd, uploadID, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jobNum := len(oss)
|
||||
jobs := make(chan *ObjectSlice, jobNum)
|
||||
result := make(chan *ObjectSlice, jobNum)
|
||||
|
||||
for i := 0; i < taskNum; i++ {
|
||||
go b.Worker(ctx, fd, jobs, result)
|
||||
}
|
||||
|
||||
for _, osl := range oss {
|
||||
jobs <- osl
|
||||
}
|
||||
close(jobs)
|
||||
|
||||
for i := 0; i < jobNum; i++ {
|
||||
res := <-result
|
||||
if !res.Result {
|
||||
return nil, SliceError{fmt.Sprintf("part info : num:%d, md5:%s", res.Number, res.MD5)}
|
||||
}
|
||||
}
|
||||
|
||||
return oss, nil
|
||||
}
|
||||
|
||||
// Worker woker for slice upload
|
||||
func (b *Bucket) Worker(ctx context.Context, fd *os.File, jobs <-chan *ObjectSlice, result chan<- *ObjectSlice) {
|
||||
for job := range jobs {
|
||||
content, err := getFilePartContent(fd, job.Offset, job.Size)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = b.UploadSlice(ctx, job.UploadID, job.Dst, job.Number, job.MD5, content)
|
||||
if err == nil {
|
||||
job.Result = true
|
||||
} else {
|
||||
job.Result = false
|
||||
}
|
||||
|
||||
result <- job
|
||||
}
|
||||
}
|
||||
|
||||
// UploadSlice upload one slice
|
||||
func (b *Bucket) UploadSlice(ctx context.Context, uploadID, dst string, number int, etag string, content io.Reader) error {
|
||||
param := map[string]interface{}{
|
||||
"PartNumber": number,
|
||||
"uploadId": uploadID,
|
||||
}
|
||||
res, err := b.conn.Do(ctx, http.MethodPut, b.Name, dst, param, nil, content)
|
||||
|
||||
if err != nil {
|
||||
return FileError{"PUT数据错误:" + err.Error()}
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if strings.Trim(res.Header.Get("Etag"), "\"") != etag {
|
||||
return FileError{"cos-etag与文件MD5不匹配"}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bucket) getFileSlices(fd *os.File, uploadID, dst string) ([]*ObjectSlice, error) {
|
||||
sliceSize := b.conn.conf.PartSize
|
||||
fi, err := fd.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileSize := fi.Size()
|
||||
oss := []*ObjectSlice{}
|
||||
var i int
|
||||
var offset int64
|
||||
for fileSize > 0 {
|
||||
var size int64
|
||||
if fileSize > sliceSize {
|
||||
size = sliceSize
|
||||
} else {
|
||||
size = fileSize
|
||||
}
|
||||
i++
|
||||
md5, err := getFileMD5(fd, offset, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
osl := &ObjectSlice{}
|
||||
osl.Size = size
|
||||
osl.Number = i
|
||||
osl.Offset = offset
|
||||
osl.UploadID = uploadID
|
||||
osl.MD5 = md5
|
||||
osl.Dst = dst
|
||||
oss = append(oss, osl)
|
||||
|
||||
fileSize -= sliceSize
|
||||
offset += sliceSize
|
||||
}
|
||||
|
||||
return oss, nil
|
||||
}
|
||||
|
||||
func getFileMD5(fd *os.File, offset, size int64) (string, error) {
|
||||
buf := make([]byte, size)
|
||||
_, err := fd.ReadAt(buf, offset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encoder := md5.New()
|
||||
encoder.Write(buf)
|
||||
b := encoder.Sum(nil)
|
||||
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func getFilePartContent(fd *os.File, offset, size int64) (io.Reader, error) {
|
||||
buf := make([]byte, size)
|
||||
_, err := fd.ReadAt(buf, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes.NewReader(buf), nil
|
||||
}
|
||||
|
||||
func (b *Bucket) AbortUpload(ctx context.Context, obj, uploadID string) error {
|
||||
param := map[string]interface{}{
|
||||
"uploadId": uploadID,
|
||||
}
|
||||
_, err := b.conn.Do(ctx, http.MethodDelete, b.Name, obj, param, nil, nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ObjectExists object exists
|
||||
func (b *Bucket) ObjectExists(ctx context.Context, obj string) error {
|
||||
_, err := b.conn.Do(ctx, http.MethodHead, b.Name, obj, nil, nil, nil)
|
||||
|
||||
return err
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client 客户端, cos的句柄
|
||||
type Client struct {
|
||||
conn *Conn
|
||||
}
|
||||
|
||||
// New cos包的入口
|
||||
func New(o *Option) *Client {
|
||||
client := Client{}
|
||||
conf := getDefaultConf()
|
||||
conf.AppID = o.AppID
|
||||
conf.SecretID = o.SecretID
|
||||
conf.SecretKey = o.SecretKey
|
||||
conf.Region = o.Region
|
||||
|
||||
if o.Domain != "" {
|
||||
conf.Domain = o.Domain
|
||||
}
|
||||
|
||||
conn := Conn{&http.Client{}, conf}
|
||||
client.conn = &conn
|
||||
|
||||
return &client
|
||||
}
|
||||
|
||||
// GetTimeoutCtx 获取一个带超时的context
|
||||
func GetTimeoutCtx(timeout time.Duration) context.Context {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Bucket get bucket
|
||||
func (c *Client) Bucket(name string) *Bucket {
|
||||
return &Bucket{name, c.conn}
|
||||
}
|
||||
|
||||
// GetBucketList 获取bucketlist
|
||||
func (c *Client) GetBucketList(ctx context.Context) (*ListAllMyBucketsResult, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, "http://service.cos.myqcloud.com/", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
c.conn.signHeader(req, nil, nil)
|
||||
res, err := c.conn.c.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
res, err = checkHTTPErr(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labr := &ListAllMyBucketsResult{}
|
||||
err = XMLDecode(res.Body, labr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return labr, err
|
||||
}
|
||||
|
||||
// CreateBucket 建立bucket
|
||||
func (c *Client) CreateBucket(ctx context.Context, name string, acl *AccessControl) error {
|
||||
res, err := c.conn.Do(ctx, http.MethodPut, name, "", nil, acl.GenHead(), nil)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteBucket delete a bucket
|
||||
func (c *Client) DeleteBucket(ctx context.Context, name string) error {
|
||||
_, err := c.conn.Do(ctx, http.MethodDelete, name, "", nil, nil, nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetBucketACL get bucket's acl
|
||||
func (c *Client) GetBucketACL(ctx context.Context, name string) (*AccessControlPolicy, error) {
|
||||
params := map[string]interface{}{"acl": ""}
|
||||
res, err := c.conn.Do(ctx, http.MethodGet, name, "", params, nil, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
aclp := &AccessControlPolicy{}
|
||||
|
||||
err = XMLDecode(res.Body, aclp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aclp, nil
|
||||
}
|
||||
|
||||
// SetBucketACL set bucket's acl
|
||||
func (c *Client) SetBucketACL(ctx context.Context, name string, acl *AccessControl) error {
|
||||
params := map[string]interface{}{"acl": ""}
|
||||
res, err := c.conn.Do(ctx, http.MethodPut, name, "", params, acl.GenHead(), nil)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// BucketExists bucket exists?
|
||||
func (c *Client) BucketExists(ctx context.Context, name string) error {
|
||||
res, err := c.conn.Do(ctx, http.MethodHead, name, "", nil, nil, nil)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ListBucketContents list
|
||||
func (c *Client) ListBucketContents(ctx context.Context, name string, qc *QueryCondition) (*ListBucketResult, error) {
|
||||
resp, err := c.conn.Do(ctx, http.MethodGet, name, "", qc.GenParams(), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
lbr := &ListBucketResult{}
|
||||
err = XMLDecode(resp.Body, lbr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lbr, nil
|
||||
}
|
||||
|
||||
// ListUploading list uploading task
|
||||
func (c *Client) ListUploading(ctx context.Context, bucket string, lu *ListUploadParam) (*ListMultipartUploadsResult, error) {
|
||||
res, err := c.conn.Do(ctx, http.MethodGet, bucket, "", lu.GenParams(), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
lmur := &ListMultipartUploadsResult{}
|
||||
err = XMLDecode(res.Body, lmur)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lmur, nil
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cos
|
||||
|
||||
const (
|
||||
defaultPartSize = 80 * 1024 * 1024
|
||||
defaultRetryTimes = 3
|
||||
defaultUA = "cos-go-sdk-v5.2.9"
|
||||
defaultDomain = "myqcloud.com"
|
||||
)
|
||||
|
||||
// Conf config struct
|
||||
type Conf struct {
|
||||
AppID string
|
||||
SecretID string
|
||||
SecretKey string
|
||||
Region string
|
||||
PartSize int64
|
||||
RetryTimes int
|
||||
UA string
|
||||
Domain string
|
||||
Bucket string
|
||||
}
|
||||
|
||||
func getDefaultConf() *Conf {
|
||||
conf := Conf{}
|
||||
conf.PartSize = defaultPartSize
|
||||
conf.RetryTimes = defaultRetryTimes
|
||||
conf.UA = defaultUA
|
||||
conf.Domain = defaultDomain
|
||||
|
||||
return &conf
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Conn http 请求类
|
||||
type Conn struct {
|
||||
c *http.Client
|
||||
conf *Conf
|
||||
}
|
||||
|
||||
func (conn *Conn) Do(ctx context.Context, method, bucket, object string, params map[string]interface{}, headers map[string]string, body io.Reader) (*http.Response, error) {
|
||||
queryStr := getQueryStr(params)
|
||||
url := conn.buildURL(bucket, object, queryStr)
|
||||
|
||||
switch body.(type) {
|
||||
case *bytes.Buffer, *bytes.Reader, *strings.Reader:
|
||||
default:
|
||||
if body != nil {
|
||||
b, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewReader(b)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.signHeader(req, params, headers)
|
||||
req.Header.Set("User-Agent", conn.conf.UA)
|
||||
setHeader(req, headers)
|
||||
|
||||
res, err := conn.c.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
defer res.Body.Close()
|
||||
return checkHTTPErr(res)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getQueryStr(params map[string]interface{}) string {
|
||||
if params == nil || len(params) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString("?")
|
||||
for k, v := range params {
|
||||
buf.WriteString(k)
|
||||
vs := interfaceToString(v)
|
||||
if vs == "" {
|
||||
buf.WriteString("&")
|
||||
continue
|
||||
}
|
||||
buf.WriteString("=")
|
||||
buf.WriteString(vs)
|
||||
buf.WriteString("&")
|
||||
}
|
||||
|
||||
return strings.Trim(buf.String(), "&")
|
||||
}
|
||||
|
||||
func (conn *Conn) buildURL(bucket, object, queryStr string) string {
|
||||
domain := fmt.Sprintf("%s-%s.cos.%s.%s", bucket, conn.conf.AppID, conn.conf.Region, conn.conf.Domain)
|
||||
url := fmt.Sprintf("http://%s/%s%s", domain, escape(object), queryStr)
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func escape(str string) string {
|
||||
//go语言中将空格编码为+,需要改为%20
|
||||
return strings.Replace(url.QueryEscape(str), "+", "%20", -1)
|
||||
}
|
||||
|
||||
func setHeader(req *http.Request, headers map[string]string) {
|
||||
if headers == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func checkHTTPErr(res *http.Response) (*http.Response, error) {
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
err := HTTPError{}
|
||||
err.Code = res.StatusCode
|
||||
if res.StatusCode >= 300 && res.StatusCode < 400 {
|
||||
err.Message = "资源被重定向"
|
||||
}
|
||||
|
||||
if res.StatusCode >= 400 && res.StatusCode < 500 {
|
||||
err.Message = "请求被拒绝"
|
||||
}
|
||||
|
||||
if res.StatusCode >= 500 {
|
||||
err.Message = "cos服务器错误"
|
||||
}
|
||||
|
||||
if res.ContentLength > 0 {
|
||||
resErr := &Error{}
|
||||
e := XMLDecode(res.Body, resErr)
|
||||
if e != nil {
|
||||
return nil, err
|
||||
}
|
||||
err.Message += resErr.Message
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// XMLDecode xml解析方法
|
||||
func XMLDecode(r io.Reader, i interface{}) error {
|
||||
jd := xml.NewDecoder(r)
|
||||
err := jd.Decode(i)
|
||||
|
||||
return err
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package cos
|
||||
|
||||
type Option struct {
|
||||
AppID string `mapstructure:"app_id" json:"app_id"`
|
||||
SecretID string `mapstructure:"secret_id" json:"secret_id"`
|
||||
SecretKey string `mapstructure:"secret_key" json:"secret_key"`
|
||||
Region string `mapstructure:"region" json:"region"`
|
||||
Domain string `mapstructure:"domain" json:"domain"`
|
||||
Bucket string `mapstructure:"bucket" json:"bucket"`
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package cos
|
||||
|
||||
// AccessControl privilige
|
||||
type AccessControl struct {
|
||||
ACL string
|
||||
GrantRead string
|
||||
GrantWrite string
|
||||
FullControl string
|
||||
}
|
||||
|
||||
// GenHead 生成http head
|
||||
func (acl *AccessControl) GenHead() map[string]string {
|
||||
header := map[string]string{
|
||||
"x-cos-acl": acl.ACL,
|
||||
"x-cos-grant-read": acl.GrantRead,
|
||||
"x-cos-grant-write": acl.GrantWrite,
|
||||
"x-cos-grant-full-control": acl.FullControl,
|
||||
}
|
||||
|
||||
for k, v := range header {
|
||||
if v == "" {
|
||||
delete(header, k)
|
||||
}
|
||||
}
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
// QueryCondition query condition
|
||||
type QueryCondition struct {
|
||||
Prefix string
|
||||
Delimiter string
|
||||
EncodingType string
|
||||
Marker string
|
||||
MaxKeys int
|
||||
}
|
||||
|
||||
// GenParams generate params:map[string]interface{}
|
||||
func (qc *QueryCondition) GenParams() map[string]interface{} {
|
||||
params := map[string]interface{}{
|
||||
"prefix": qc.Prefix,
|
||||
"delimiter": qc.Delimiter,
|
||||
"encoding-type": qc.EncodingType,
|
||||
"marker": qc.Marker,
|
||||
"max-keys": qc.MaxKeys,
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
if v == "" {
|
||||
delete(params, k)
|
||||
}
|
||||
if v == 0 {
|
||||
delete(params, k)
|
||||
}
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// ListUploadParam list upload param
|
||||
type ListUploadParam struct {
|
||||
Prefix string
|
||||
Delimiter string
|
||||
EncodingType string
|
||||
MaxUploads int
|
||||
KeyMarker string
|
||||
UploadIDMarker string
|
||||
}
|
||||
|
||||
// GenParams generate params for request
|
||||
func (lup *ListUploadParam) GenParams() map[string]interface{} {
|
||||
params := map[string]interface{}{
|
||||
"prefix": lup.Prefix,
|
||||
"delimiter": lup.Delimiter,
|
||||
"encoding-type": lup.EncodingType,
|
||||
"max-uploads": lup.MaxUploads,
|
||||
"key-marker": lup.KeyMarker,
|
||||
"upload-id-marker": lup.UploadIDMarker,
|
||||
}
|
||||
|
||||
for k, v := range params {
|
||||
if v == "" {
|
||||
delete(params, k)
|
||||
}
|
||||
|
||||
if v == 0 {
|
||||
delete(params, k)
|
||||
}
|
||||
}
|
||||
params["uploads"] = ""
|
||||
|
||||
return params
|
||||
|
||||
}
|
||||
|
||||
// CompleteMultipartUpload compelete slice upload
|
||||
type CompleteMultipartUpload struct {
|
||||
Part []struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package cos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ListAllMyBucketsResult 获取bucket列表的结果
|
||||
type ListAllMyBucketsResult struct {
|
||||
Owner struct {
|
||||
ID string
|
||||
DisplayName string
|
||||
}
|
||||
|
||||
Buckets struct {
|
||||
Bucket []struct {
|
||||
Name string
|
||||
Location string
|
||||
CreateDate string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error 错误消息
|
||||
type Error struct {
|
||||
Code string
|
||||
Message string
|
||||
Resource string
|
||||
RequestID string `xml:"RequestId"`
|
||||
TraceID string `xml:"TaceId"`
|
||||
}
|
||||
|
||||
// HTTPError http error struct
|
||||
type HTTPError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error error interface
|
||||
func (he HTTPError) Error() string {
|
||||
return fmt.Sprintf("%d:%s", he.Code, he.Message)
|
||||
}
|
||||
|
||||
// AccessControlPolicy acl return
|
||||
type AccessControlPolicy struct {
|
||||
Owner struct {
|
||||
ID string
|
||||
DisplayName string
|
||||
}
|
||||
AccessControlList struct {
|
||||
Grant []struct {
|
||||
Grantee struct {
|
||||
ID string
|
||||
DisplayName string
|
||||
}
|
||||
Permission string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListBucketResult list bucket contents result
|
||||
type ListBucketResult struct {
|
||||
Name string
|
||||
EncodingType string `xml:"Encoding-Type"`
|
||||
Prefix string
|
||||
Marker string
|
||||
MaxKeys int
|
||||
IsTruncated bool
|
||||
NextMarker string
|
||||
Contents []struct {
|
||||
Key string
|
||||
LastModified string
|
||||
ETag string
|
||||
Size int64
|
||||
Owner struct {
|
||||
ID string
|
||||
}
|
||||
StorageClass string
|
||||
}
|
||||
CommonPrefixes []struct {
|
||||
Prefix string
|
||||
}
|
||||
}
|
||||
|
||||
// ListMultipartUploadsResult list uploading task
|
||||
type ListMultipartUploadsResult struct {
|
||||
Bucket string
|
||||
EncodingType string `xml:"Encoding-Type"`
|
||||
KeyMarker string
|
||||
UploadIDMarker string `xml:"UploadIdMarker"`
|
||||
NextKeyMarker string
|
||||
NextUploadIDMarker string `xml:"NextUploadIdMarker"`
|
||||
MaxUploads int
|
||||
IsTruncated bool
|
||||
Prefix string
|
||||
Delimiter string
|
||||
Upload []struct {
|
||||
Key string
|
||||
UploadID string
|
||||
StorageClass string
|
||||
Initiator struct {
|
||||
UIN string
|
||||
}
|
||||
Owner struct {
|
||||
UID string
|
||||
}
|
||||
Initiated string
|
||||
}
|
||||
CommonPrefixes []struct {
|
||||
Prefix string
|
||||
}
|
||||
}
|
||||
|
||||
// InitiateMultipartUploadResult init slice upload
|
||||
type InitiateMultipartUploadResult struct {
|
||||
Bucket string
|
||||
Key string
|
||||
UploadID string `xml:"UploadId"`
|
||||
}
|
||||
|
||||
// CompleteMultipartUploadResult compeleted slice upload
|
||||
type CompleteMultipartUploadResult struct {
|
||||
Location string
|
||||
Bucket string
|
||||
Key string
|
||||
ETag string
|
||||
}
|
||||
|
||||
// SliceError slice upload err
|
||||
type SliceError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (se SliceError) Error() string {
|
||||
return fmt.Sprintf("上传分片失败:%s", se.Message)
|
||||
}
|
||||
|
||||
// ParamError slice upload err
|
||||
type ParamError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (pe ParamError) Error() string {
|
||||
return fmt.Sprintf("参数错误:%s", pe.Message)
|
||||
}
|
||||
|
||||
// FileError slice upload err
|
||||
type FileError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (fe FileError) Error() string {
|
||||
return fmt.Sprintf("文件错误:%s", fe.Message)
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) 2017-2018 Tencent Ltd.
|
||||
|
||||
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.
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
region string
|
||||
httpClient *http.Client
|
||||
httpProfile *profile.HttpProfile
|
||||
credential *Credential
|
||||
signMethod string
|
||||
debug bool
|
||||
}
|
||||
|
||||
func (c *Client) Send(request tchttp.Request, response tchttp.Response) (err error) {
|
||||
if request.GetDomain() == "" {
|
||||
domain := c.httpProfile.Endpoint
|
||||
if domain == "" {
|
||||
domain = tchttp.GetServiceDomain(request.GetService())
|
||||
}
|
||||
request.SetDomain(domain)
|
||||
}
|
||||
err = tchttp.ConstructParams(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tchttp.CompleteCommonParams(request, c.GetRegion())
|
||||
err = signRequest(request, c.credential, c.signMethod)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
httpRequest, err := http.NewRequest(request.GetHttpMethod(), request.GetUrl(), request.GetBodyReader())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if request.GetHttpMethod() == "POST" {
|
||||
httpRequest.Header["Content-Type"] = []string{"application/x-www-form-urlencoded"}
|
||||
}
|
||||
//log.Printf("[DEBUG] http request=%v", httpRequest)
|
||||
httpResponse, err := c.httpClient.Do(httpRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tchttp.ParseFromHttpResponse(httpResponse, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) GetRegion() string {
|
||||
return c.region
|
||||
}
|
||||
|
||||
func (c *Client) Init(region string) *Client {
|
||||
c.httpClient = &http.Client{}
|
||||
c.region = region
|
||||
c.signMethod = "HmacSHA256"
|
||||
c.debug = false
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) WithSecretId(secretId, secretKey string) *Client {
|
||||
c.credential = NewCredential(secretId, secretKey)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) WithProfile(clientProfile *profile.ClientProfile) *Client {
|
||||
c.signMethod = clientProfile.SignMethod
|
||||
c.httpProfile = clientProfile.HttpProfile
|
||||
c.httpClient.Timeout = time.Duration(c.httpProfile.ReqTimeout) * time.Second
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) WithSignatureMethod(method string) *Client {
|
||||
c.signMethod = method
|
||||
return c
|
||||
}
|
||||
|
||||
func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) {
|
||||
client = &Client{}
|
||||
client.Init(region).WithSecretId(secretId, secretKey)
|
||||
return
|
||||
}
|
||||
Generated
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
package common
|
||||
|
||||
type Credential struct {
|
||||
SecretId string
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
func NewCredential(secretId, secretKey string) *Credential {
|
||||
return &Credential{
|
||||
SecretId: secretId,
|
||||
SecretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Credential) GetCredentialParams() map[string]string {
|
||||
return map[string]string{
|
||||
"SecretId": c.SecretId,
|
||||
}
|
||||
}
|
||||
|
||||
type TokenCredential struct {
|
||||
SecretId string
|
||||
SecretKey string
|
||||
Token string
|
||||
}
|
||||
|
||||
func NewTokenCredential(secretId, secretKey, token string) *TokenCredential {
|
||||
return &TokenCredential{
|
||||
SecretId: secretId,
|
||||
SecretKey: secretKey,
|
||||
Token: token,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TokenCredential) GetCredentialParams() map[string]string {
|
||||
return map[string]string{
|
||||
"SecretId": c.SecretId,
|
||||
"Token": c.Token,
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TencentCloudSDKError struct {
|
||||
Code string
|
||||
Message string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
func (e *TencentCloudSDKError) Error() string {
|
||||
return fmt.Sprintf("[TencentCloudSDKError] Code=%s, Message=%s, RequestId=%s", e.Code, e.Message, e.RequestId)
|
||||
}
|
||||
|
||||
func NewTencentCloudSDKError(code, message, requestId string) error {
|
||||
return &TencentCloudSDKError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
RequestId: requestId,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *TencentCloudSDKError) GetCode() string {
|
||||
return e.Code
|
||||
}
|
||||
|
||||
func (e *TencentCloudSDKError) GetMessage() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *TencentCloudSDKError) GetRequestId() string {
|
||||
return e.RequestId
|
||||
}
|
||||
Generated
Vendored
+231
@@ -0,0 +1,231 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"io"
|
||||
//"log"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
POST = "POST"
|
||||
GET = "GET"
|
||||
|
||||
RootDomain = "tencentcloudapi.com"
|
||||
Path = "/"
|
||||
)
|
||||
|
||||
type Request interface {
|
||||
GetAction() string
|
||||
GetBodyReader() io.Reader
|
||||
GetDomain() string
|
||||
GetHttpMethod() string
|
||||
GetParams() map[string]string
|
||||
GetPath() string
|
||||
GetService() string
|
||||
GetUrl() string
|
||||
GetVersion() string
|
||||
SetDomain(string)
|
||||
SetHttpMethod(string)
|
||||
}
|
||||
|
||||
type BaseRequest struct {
|
||||
httpMethod string
|
||||
domain string
|
||||
path string
|
||||
params map[string]string
|
||||
formParams map[string]string
|
||||
|
||||
service string
|
||||
version string
|
||||
action string
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetAction() string {
|
||||
return r.action
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetHttpMethod() string {
|
||||
return r.httpMethod
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetParams() map[string]string {
|
||||
return r.params
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetPath() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetDomain() string {
|
||||
return r.domain
|
||||
}
|
||||
|
||||
func (r *BaseRequest) SetDomain(domain string) {
|
||||
r.domain = domain
|
||||
}
|
||||
|
||||
func (r *BaseRequest) SetHttpMethod(method string) {
|
||||
switch strings.ToUpper(method) {
|
||||
case POST:
|
||||
{
|
||||
r.httpMethod = POST
|
||||
}
|
||||
case GET:
|
||||
{
|
||||
r.httpMethod = GET
|
||||
}
|
||||
default:
|
||||
{
|
||||
r.httpMethod = GET
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetService() string {
|
||||
return r.service
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetUrl() string {
|
||||
if r.httpMethod == GET {
|
||||
return "https://" + r.domain + r.path + "?" + getUrlQueriesEncoded(r.params)
|
||||
} else if r.httpMethod == POST {
|
||||
return "https://" + r.domain + r.path
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetVersion() string {
|
||||
return r.version
|
||||
}
|
||||
|
||||
func getUrlQueriesEncoded(params map[string]string) string {
|
||||
values := url.Values{}
|
||||
for key, value := range params {
|
||||
if value != "" {
|
||||
values.Add(key, value)
|
||||
}
|
||||
}
|
||||
return values.Encode()
|
||||
}
|
||||
|
||||
func (r *BaseRequest) GetBodyReader() io.Reader {
|
||||
if r.httpMethod == POST {
|
||||
s := getUrlQueriesEncoded(r.params)
|
||||
//log.Printf("[DEBUG] body: %s", s)
|
||||
return strings.NewReader(s)
|
||||
} else {
|
||||
return strings.NewReader("")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BaseRequest) Init() *BaseRequest {
|
||||
r.httpMethod = GET
|
||||
r.domain = ""
|
||||
r.path = Path
|
||||
r.params = make(map[string]string)
|
||||
r.formParams = make(map[string]string)
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *BaseRequest) WithApiInfo(service, version, action string) *BaseRequest {
|
||||
r.service = service
|
||||
r.version = version
|
||||
r.action = action
|
||||
return r
|
||||
}
|
||||
|
||||
func GetServiceDomain(service string) (domain string) {
|
||||
domain = service + "." + RootDomain
|
||||
return
|
||||
}
|
||||
|
||||
func CompleteCommonParams(request Request, region string) {
|
||||
params := request.GetParams()
|
||||
params["Region"] = region
|
||||
if request.GetVersion() != "" {
|
||||
params["Version"] = request.GetVersion()
|
||||
}
|
||||
params["Action"] = request.GetAction()
|
||||
params["Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
|
||||
params["Nonce"] = strconv.Itoa(rand.Int())
|
||||
params["RequestClient"] = "SDK_GO_3.0.28"
|
||||
}
|
||||
|
||||
func ConstructParams(req Request) (err error) {
|
||||
value := reflect.ValueOf(req).Elem()
|
||||
err = flatStructure(value, req, "")
|
||||
//log.Printf("[DEBUG] params=%s", req.GetParams())
|
||||
return
|
||||
}
|
||||
|
||||
func flatStructure(value reflect.Value, request Request, prefix string) (err error) {
|
||||
//log.Printf("[DEBUG] reflect value: %v", value.Type())
|
||||
valueType := value.Type()
|
||||
for i := 0; i < valueType.NumField(); i++ {
|
||||
tag := valueType.Field(i).Tag
|
||||
nameTag, hasNameTag := tag.Lookup("name")
|
||||
if !hasNameTag {
|
||||
continue
|
||||
}
|
||||
field := value.Field(i)
|
||||
kind := field.Kind()
|
||||
if kind == reflect.Ptr && field.IsNil() {
|
||||
continue
|
||||
}
|
||||
if kind == reflect.Ptr {
|
||||
field = field.Elem()
|
||||
kind = field.Kind()
|
||||
}
|
||||
key := prefix + nameTag
|
||||
if kind == reflect.String {
|
||||
s := field.String()
|
||||
if s != "" {
|
||||
request.GetParams()[key] = s
|
||||
}
|
||||
} else if kind == reflect.Bool {
|
||||
request.GetParams()[key] = strconv.FormatBool(field.Bool())
|
||||
} else if kind == reflect.Int || kind == reflect.Int64 {
|
||||
request.GetParams()[key] = strconv.FormatInt(field.Int(), 10)
|
||||
} else if kind == reflect.Uint || kind == reflect.Uint64 {
|
||||
request.GetParams()[key] = strconv.FormatUint(field.Uint(), 10)
|
||||
} else if kind == reflect.Float64 {
|
||||
request.GetParams()[key] = strconv.FormatFloat(field.Float(), 'f', -1, 64)
|
||||
} else if kind == reflect.Slice {
|
||||
list := value.Field(i)
|
||||
for j := 0; j < list.Len(); j++ {
|
||||
vj := list.Index(j)
|
||||
key := prefix + nameTag + "." + strconv.Itoa(j)
|
||||
kind = vj.Kind()
|
||||
if kind == reflect.Ptr && vj.IsNil() {
|
||||
continue
|
||||
}
|
||||
if kind == reflect.Ptr {
|
||||
vj = vj.Elem()
|
||||
kind = vj.Kind()
|
||||
}
|
||||
if kind == reflect.String {
|
||||
request.GetParams()[key] = vj.String()
|
||||
} else if kind == reflect.Bool {
|
||||
request.GetParams()[key] = strconv.FormatBool(vj.Bool())
|
||||
} else if kind == reflect.Int || kind == reflect.Int64 {
|
||||
request.GetParams()[key] = strconv.FormatInt(vj.Int(), 10)
|
||||
} else if kind == reflect.Uint || kind == reflect.Uint64 {
|
||||
request.GetParams()[key] = strconv.FormatUint(vj.Uint(), 10)
|
||||
} else if kind == reflect.Float64 {
|
||||
request.GetParams()[key] = strconv.FormatFloat(vj.Float(), 'f', -1, 64)
|
||||
} else {
|
||||
flatStructure(vj, request, key+".")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
flatStructure(reflect.ValueOf(field.Interface()), request, prefix+nameTag+".")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Generated
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
// "log"
|
||||
"net/http"
|
||||
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||||
)
|
||||
|
||||
type Response interface {
|
||||
ParseErrorFromHTTPResponse(body []byte) error
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Response struct {
|
||||
Error struct {
|
||||
Code string `json:"Code"`
|
||||
Message string `json:"Message"`
|
||||
} `json:"Error" omitempty`
|
||||
RequestId string `json:"RequestId"`
|
||||
} `json:"Response"`
|
||||
}
|
||||
|
||||
type DeprecatedAPIErrorResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
CodeDesc string `json:"codeDesc"`
|
||||
}
|
||||
|
||||
func (r *BaseResponse) ParseErrorFromHTTPResponse(body []byte) (err error) {
|
||||
resp := &ErrorResponse{}
|
||||
err = json.Unmarshal(body, resp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if resp.Response.Error.Code != "" {
|
||||
return errors.NewTencentCloudSDKError(resp.Response.Error.Code, resp.Response.Error.Message, resp.Response.RequestId)
|
||||
}
|
||||
|
||||
deprecated := &DeprecatedAPIErrorResponse{}
|
||||
err = json.Unmarshal(body, deprecated)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if deprecated.Code != 0 {
|
||||
return errors.NewTencentCloudSDKError(deprecated.CodeDesc, deprecated.Message, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseFromHttpResponse(hr *http.Response, response Response) (err error) {
|
||||
defer hr.Body.Close()
|
||||
body, err := ioutil.ReadAll(hr.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//log.Printf("[DEBUG] Response Body=%s", body)
|
||||
err = response.ParseErrorFromHTTPResponse(body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(body, &response)
|
||||
return
|
||||
}
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
package profile
|
||||
|
||||
type ClientProfile struct {
|
||||
HttpProfile *HttpProfile
|
||||
SignMethod string
|
||||
}
|
||||
|
||||
func NewClientProfile() *ClientProfile {
|
||||
return &ClientProfile{
|
||||
HttpProfile: NewHttpProfile(),
|
||||
SignMethod: "HmacSHA256",
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
package profile
|
||||
|
||||
type HttpProfile struct {
|
||||
ReqMethod string
|
||||
ReqTimeout int
|
||||
Endpoint string
|
||||
Protocol string
|
||||
}
|
||||
|
||||
func NewHttpProfile() *HttpProfile {
|
||||
return &HttpProfile{
|
||||
ReqMethod: "POST",
|
||||
ReqTimeout: 60,
|
||||
Endpoint: "",
|
||||
Protocol: "HTTPS",
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
|
||||
)
|
||||
|
||||
const (
|
||||
SHA256 = "HmacSHA256"
|
||||
SHA1 = "HmacSHA1"
|
||||
)
|
||||
|
||||
func Sign(s, secretKey, method string) string {
|
||||
hashed := hmac.New(sha1.New, []byte(secretKey))
|
||||
if method == SHA256 {
|
||||
hashed = hmac.New(sha256.New, []byte(secretKey))
|
||||
}
|
||||
hashed.Write([]byte(s))
|
||||
|
||||
return base64.StdEncoding.EncodeToString(hashed.Sum(nil))
|
||||
}
|
||||
|
||||
func signRequest(request tchttp.Request, credential *Credential, method string) (err error) {
|
||||
if method != SHA256 {
|
||||
method = SHA1
|
||||
}
|
||||
checkAuthParams(request, credential, method)
|
||||
s := getStringToSign(request)
|
||||
signature := Sign(s, credential.SecretKey, method)
|
||||
request.GetParams()["Signature"] = signature
|
||||
return
|
||||
}
|
||||
|
||||
func checkAuthParams(request tchttp.Request, credential *Credential, method string) {
|
||||
params := request.GetParams()
|
||||
credentialParams := credential.GetCredentialParams()
|
||||
for key, value := range credentialParams {
|
||||
params[key] = value
|
||||
}
|
||||
params["SignatureMethod"] = method
|
||||
delete(params, "Signature")
|
||||
}
|
||||
|
||||
func getStringToSign(request tchttp.Request) string {
|
||||
method := request.GetHttpMethod()
|
||||
domain := request.GetDomain()
|
||||
path := request.GetPath()
|
||||
|
||||
text := method + domain + path + "?"
|
||||
|
||||
params := request.GetParams()
|
||||
// sort params
|
||||
keys := make([]string, 0, len(params))
|
||||
for k, _ := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for i := range keys {
|
||||
k := keys[i]
|
||||
if params[k] == "" {
|
||||
continue
|
||||
}
|
||||
text += fmt.Sprintf("%v=%v&", strings.Replace(k, "_", ".", -1), params[k])
|
||||
}
|
||||
text = text[:len(text)-1]
|
||||
return text
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package common
|
||||
|
||||
func IntPtr(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
func Int64Ptr(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func UintPtr(v uint) *uint {
|
||||
return &v
|
||||
}
|
||||
|
||||
func Uint64Ptr(v uint64) *uint64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func Float64Ptr(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func StringPtr(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
func StringValues(ptrs []*string) []string {
|
||||
values := make([]string, len(ptrs))
|
||||
for i := 0; i < len(ptrs); i++ {
|
||||
if ptrs[i] != nil {
|
||||
values[i] = *ptrs[i]
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func StringPtrs(vals []string) []*string {
|
||||
ptrs := make([]*string, len(vals))
|
||||
for i := 0; i < len(vals); i++ {
|
||||
ptrs[i] = &vals[i]
|
||||
}
|
||||
return ptrs
|
||||
}
|
||||
|
||||
func BoolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
Reference in New Issue
Block a user