Automatic merge from release/2.3.0 -> release/2.4.0

* commit '492469887b5e55cfec740534125704c8441f92d5':
  添加部分aliyun操作
  支持腾讯云资源导入
This commit is contained in:
邱剑
2018-11-10 13:35:06 +08:00
73 changed files with 8627 additions and 10 deletions
+12
View File
@@ -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"
+5
View File
@@ -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 {
+127
View File
@@ -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)
}
}
}
}
+345
View File
@@ -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
}
+209
View File
@@ -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")
}
+1
View File
@@ -31,6 +31,7 @@ const (
CLOUD_PROVIDER_VMWARE = "VMware"
CLOUD_PROVIDER_ALIYUN = "Aliyun"
CLOUD_PROVIDER_QCLOUD = "Qcloud"
CLOUD_PROVIDER_AZURE = "Azure"
)
+4 -1
View File
@@ -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 {
+2 -1
View File
@@ -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 {
+6 -5
View File
@@ -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 {
+1
View File
@@ -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"
+1 -1
View File
@@ -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:"-"`
+2
View File
@@ -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(
+1
View File
@@ -2,6 +2,7 @@ package aliyun
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
+45
View File
@@ -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
})
}
+8 -2
View File
@@ -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
})
}
+4
View File
@@ -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
+375
View File
@@ -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
}
+295
View File
@@ -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
}
+272
View File
@@ -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
}
+196
View File
@@ -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
}
+736
View File
@@ -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] 和 [( ) ~ ! @ # $ % ^ & * - + = &#124; { } [ ] : ; ' , . ? / ]中的特殊符号。<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
}
+40
View File
@@ -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
}
+42
View File
@@ -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)
}
+143
View File
@@ -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
}
+23
View File
@@ -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}, // 腾讯云 北美地区(多伦多)
}
+128
View File
@@ -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
}
+98
View File
@@ -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
}
+132
View File
@@ -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")
}
+1
View File
@@ -0,0 +1 @@
package provider
+84
View File
@@ -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
}
+259
View File
@@ -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(&regions, "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] = &regions[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
}
+538
View File
@@ -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
}
+415
View File
@@ -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
}
+106
View File
@@ -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)
})
}
+77
View File
@@ -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)
})
}
+71
View File
@@ -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)
})
}
+55
View File
@@ -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)
})
}
+176
View File
@@ -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
})
}
+45
View File
@@ -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)
})
}
+54
View File
@@ -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
})
}
+11
View File
@@ -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)
}
+16
View File
@@ -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
})
}
+66
View File
@@ -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
})
}
+45
View File
@@ -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
})
}
+41
View File
@@ -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)
})
}
+21
View File
@@ -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
})
}
+218
View File
@@ -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")
}
+131
View File
@@ -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
}
+208
View File
@@ -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
}
}
+193
View File
@@ -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)
}
+142
View File
@@ -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)
}
}
+214
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
@@ -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
}
@@ -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,
}
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -0,0 +1,13 @@
package profile
type ClientProfile struct {
HttpProfile *HttpProfile
SignMethod string
}
func NewClientProfile() *ClientProfile {
return &ClientProfile{
HttpProfile: NewHttpProfile(),
SignMethod: "HmacSHA256",
}
}
@@ -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",
}
}
@@ -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
}
@@ -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
}